上次我们讲了如何使用延时来做小灯闪烁的程序,相信大家都有所了解了,这次我们来看如何使用定时器实现小灯闪烁。
1、不使用中断的方法:
首先我们还是来看头文件,因为这次使用了定时器(timer),所以头文件要包含timer.h。
选择闪烁的小灯,在此我们选择PC5小灯,将其使能,作为output。
下一步是使能定时器的过程。有如下步骤:
1、在系统中使能定时器。 函数:SysCtlPeripheralEnable()
2、选择定时器的使用类型,函数:TimerConfigure(), 参数可以写 TIMER_CFG_32_BIT_PER (32位周期)/ TIMER_CFG_16_BIT_PAIR|TIMER_CFG_A_PERIODIC (16位TIMER0A周期) TIMER_CFG_A_CAP_COUNT(计数模式)等..
(注:LM3S811一共有4个计数器TIMER0/1/2/3 ,默认使用减计数模式, 每个timer可以作为一个32位计数器使用,也可以作为两个16位计数器使用。具体使用方法请看技术文档)
3、给定时器装载值:使用TimerLoadSet() 函数。
4、使能定时器。
剩下的思路很简单,让程序进入一个死循环。使用轮询检测定时器是否溢出,如果溢出则改变小灯的亮灭。因为系统的频率是一定的(在本程序中是6MHZ),所以通过改变定时器中装载的数值就可以控制小灯亮灭的时间。
代码如下:
-
- #include "inc/hw_types.h"
- #include "inc/hw_memmap.h"
- #include "driverlib/sysctl.h"
- #include "driverlib/gpio.h"
- #include "driverlib/timer.h" //包含定时器操作的API
- int main()
- {
- SysCtlClockSet(SYSCTL_OSC_MAIN|SYSCTL_XTAL_6MHZ|SYSCTL_USE_OSC|SYSCTL_SYSDIV_1);
- SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
-
- GPIODirModeSet(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_DIR_MODE_OUT);
- GPIOPadConfigSet(GPIO_PORTC_BASE,GPIO_PIN_5,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD);
-
- SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
- TimerConfigure(TIMER0_BASE,TIMER_CFG_32_BIT_PER);
- TimerLoadSet(TIMER0_BASE,TIMER_A,SysCtlClockGet()/2);
- TimerEnable(TIMER0_BASE,TIMER_A);
- while(1)
- {
- while(!(TimerIntStatus(TIMER0_BASE,false)&TIMER_TIMA_TIMEOUT));
- TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
- GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,0xff);
- while(!(TimerIntStatus(TIMER0_BASE,false)&TIMER_TIMA_TIMEOUT));
- TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
- GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_5,0x00);
- }
- }
设置timer的装载值为 SysCtlClockGet()/2,即为系统时钟的一半,即0.5秒溢出一次,所以小灯每秒亮灭一次。
2、使用中断的方法:
中断是单片机中很重要的一部分,可以说一个完成的程序一般都会用到中断。中断,顾名思义就是在程序正常执行的时候用来打断CPU进程的,比如:当我们的程序在死循环中执行时,外部收到一个按钮的信号,这时程序就先执行我们的中断代码,接着才会继续进行循环操作。可以提高程序的效率。
更加深刻的了解可以在http://zhidao.baidu.com/question/14979539里查找到一些有趣的解释。
(1) 初步设置
因为使用了中断,在头文件中我们要加入 #include "inc/hw_ints.h" 和 #include "driverlib/interrupt.h"。(定义了与中断相关的宏 和 包含中断控制器的API)
熟练地设置好主频、输出口、定时器之后,为了使用中断我们要学习以下几个函数:
TimerPrescaleSet(TIMER0_BASE,TIMER_A,100); 这个函数将TIMER0A 设为分频100,即现在为6MHZ/100=60000HZ
然后用TimerLoadSet()给TIMER0A装载30000这个数值,这样就是每0.5秒造成计数器溢出一次。
TimerIntEnable() 设置定时器的中断模式,前面一个参数是选择定时器,后面是溢出模式,有TIMER_TIMA_TIMEOUT(溢出) TIMER_CAPA_MATCH(匹配)等,我们以后都会使用。
IntEnable()用来使中断控制器接受定时器的中断请求, IntMasterEnable()用来让全局的中断使能。
然后主程序就可以进入死循环,因为我们的小灯会通过中断来控制。
(2) 写中断函数
在程序中断后,会自动跳转到我们预先写好的中断函数,执行我们的命令。
在此我们写中断函数 void Timer0ATimeoutIntHandler(void) ; 在函数里我们用TimerIntClear()将中断清除,防止再次进入中断,然后控制小灯的值反向。
写好后要在程序的最前面声明,并且在startup.s中的相应位置上写好。首先打开工程中的startup.s文件,然后将TIMER0A前的函数改为我们自定义的函数,如图:
再在上面进行声明:
小提示,在这里面写的时候,函数只有自己的名字,没有括号,返回值类型等东西。在写的时候,不要顶格写,笔者就因为这个错误郁闷了半天。
下面我们来看看整个程序的代码:
- #include "inc/hw_memmap.h"
- #include "inc/hw_types.h"
- #include "inc/hw_ints.h" //定义了与中断相关的宏
- #include "driverlib/sysctl.h"
- #include "driverlib/gpio.h"
- #include "driverlib/timer.h"
- #include "driverlib/interrupt.h" //包含中断控制器的API
- void Timer0ATimeoutIntHandler(void);
- int main()
- {
- SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN|SYSCTL_XTAL_6MHZ);
- SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
- GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE,GPIO_PIN_4);
-
- SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
- TimerConfigure(TIMER0_BASE,TIMER_CFG_16_BIT_PAIR|TIMER_CFG_A_PERIODIC);
- TimerPrescaleSet(TIMER0_BASE,TIMER_A,100);
- TimerLoadSet(TIMER0_BASE,TIMER_A,30000);
- TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
- IntEnable(INT_TIMER0A);
- IntMasterEnable();
- TimerEnable(TIMER0_BASE,TIMER_A);
- while(1);
- }
- void Timer0ATimeoutIntHandler(void)
- {
- TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
- GPIOPinWrite(GPIO_PORTC_BASE,GPIO_PIN_4,~GPIOPinRead(GPIO_PORTC_BASE,GPIO_PIN_4));
- }
在此给大家说一个小技巧,在设定GPIO端口的模式时,另外有一个函数是 GPIOPinTypeGPIOOutput() ,使用此函数能达到上面第一个代码中GPIODirModeSet()加上GPIOPadConfigSet()的效果,更简单,如GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE,GPIO_PIN_4) 就可以直接将PC4设为输出口,当然如果有高级设置,比如设置上拉电流,还是要使用代码中的方法的。