3.13 TIMER0控制单只LED闪烁 |
|
此前有关单只或多只LED闪烁的程序设计均使用延时子程序使LED按一定延时导通或截止,形成闪烁效果,图3-14所示电路则通过TIMER0定时器实现了对LED的闪烁控制。
图3-14 T/C0控制单只LED闪烁电路
使用定时器时主要有两种方法:
① 溢出中断处理:定时/计数寄存器溢出时触发中断,自动调用预先设计的中断子程序。
② 溢出标志位查询:循环检查定时器溢出标志位(TFx),TFx=1时执行指定程序。
本例为实现LED的闪烁控制,选择使用的是溢出中断处理法,需要完成的工作如下:
① 设置定时器工作模式(设置TMOD);
② 设置定时/计数寄存器初值(设置TH0/TL0或TH1/TL1);
③ 允许定时器中断(设置IE,或单独设置EA及ET0、ET1);
④ 启动定时器(设置TCON或单独设置TR0、TR1)。
其中TMOD寄存器位如表3-3所示。
表3-3 T/Cx模式寄存器TMOD(Timer/Counter 0 and 1 Modes)
当然,最重要的工作是编写定时器中断例程,当定时器时间到达(计数值溢出)将即触发中断,所编写的相应中断例程将被自动调用。
对于模式0、1、3,如果希望定时器能按设定的某固定时间间隔不断触发,还需要在定时中断例程内恢复定时寄存器初值(TH0/TL0或TH1/TL1),对于模式2则不需要,因为该模式下定时寄存器低字节(TL0或TL1)独立完成计数工作,在定时中断发生时,定时寄存器高字节(TH0或TH1)的值会自动重新赋给低字节,高字节的值保持不变。
需要注意的是,如果仅在主程序中设置了定时寄存器初值,中断例程中未重新给定时寄存器赋值,中断程序将以该模式下的最大定时(计数)值工作,例如,模式0为13位,最大值为2 13 -1=8 191;模式1为16位,最大值为2 16 -1=65 535,分别到8 192及65 536时溢出。
主程序中设置TIMER0工作于模式0,其工作示意图如图3-15所示。主程序设置TIMER0工作于模式0的语句为TMOD=0x00(实际起作用的是TMOD的低4位)。
图3-15 TIMERx模式0工作示意图
由图3-16可知,一个机器周期由6个状态周期,即12个振荡器周期构成,对于12MHz振荡器频率,其机器周期T cy = 1/(12MHz/12) = 1µs,如果振荡器频率为11.0592MHz,则T cy = 1/(11.0592MHz/12)≈1.0851µs。在开启定时器后,每个机器周期都将使定时/计数寄存器累加1。
图3-16 标准模式下的8051单片机时钟
在工作模式0下,最多8 192个机器周期后定时/计数寄存器将溢出。对于12MHz及11.0592MHz振荡频率,最大定时长度分别为8 192×1µs =8 192µs、8 192×1.0851µs≈8 889µs,也就是说在这两种振荡器频率下,其最大溢出时间分别为8 192µs及8 889µs。现假设要配置定时长度为t(定时器将在t后溢出),由于每过T cy 定时器将累加一次,t后累加次数共计为t×T cy ,且由于定时/计数寄存器在时钟驱动下累加计数直至溢出,于是定时初值应为8 192-t×T cy 。根据上述分析,可得定时初值计算公式为8 192-F OSC /12t。
为实现本例5ms(5 000µs)的定时:
① 对于12MHz振荡器,定时初值应为8 192-12/12×5 000=8 192-5 000;
② 对于11.0592MHz振荡器,初值应则为8 192-11.0592/12×5 000≈8 192-4 607。
在设置本例的定时初值8 192-5 000(3 192=0x0C78=110001111000)时,由图3-15可知,TH0、TL0分别保存高8位与低5位,将3 192(110001111000)右移5位可得高8位(1100011),将3192与0x1F可得低5位(11000),故有
TH0 = (8 192 - 5 000) >> 5; TL0 = (8 192 - 5 000) & 0x1F;
上述语句还可以写为如下的等价形式:
TH0 = (8192 - 5 000) / 32; TL0 = (8 192 - 5 000) % 32;
Keil C编译这两行时会计算出TH0与TL0的实际字节常量,这两行不会在单片机执行代码过程中再进行计算而占用单片机时间,设计源程序时不必计算出它们的常量值,只需要写入常量表达式即可,这样可提高程序的可读性。
为启用定时器中断,主程序中有IE = 0x82(10000010),它置EA=1、ET0=1,开总中断并使能TIMER0中断。
由TIMER0模式0工作示意图可知,启动TIMER0开始工作需要置TR0 = 1,在定时寄存器溢出时将触发中断,由于所选择的晶振为12MHz,所设置的初值3 192(即8 192-5 000=3 192)每微秒递增1,5 000µs后3 192+5 000=8 192,模式0是13位定时寄存器模式,最大值为8 191,因此在递增为8192时产生溢出,触发定时器中断,硬件置位TF1。
对于定时器中断子程序,TIMER0和TIMER1的中断号分别为1、3,不要误设为0、1,本例编写的是TIMER0中断子程序,因此子程序后面添加了interrupt 1。
对于中断函数内的代码还有两点需要说明:
① 由于本例TIMER0不是工作于模式2,因此必须在中断函数内重新用代码设置TH0与TL0的初值。要注意中断例程内即使没有重设初值,程序也能照样运行,这是因为第一个5 000µs后,定时寄存器溢出,所有的13位变为全0,中断例程被执行,而13位已为全0的定时寄存器再次继续累加。在8 192次累加后再次溢出,所有的13位再次为全0,中断再次被触发,如此继续下去。可见,没有重设5 000的两行语句,中断例程将以当前模式的最大时间跨度继续不断触发。本例中5 000与8 192比较接近,略去这两行设置5 000的语句时,会发现LED闪烁并无明显差异,如果将定时5 000µs改成定时1 000µs,再编译调试运行,会发现LED的闪烁速度明显不同。
② 由于在13位模式下,最大定时不到9ms,因此仅用定时器无法实现更长的定时设置,本例每隔5ms触发中断时并没有让LED闪烁,而是累加全局变量T_Count,直到其值为50时才让LED闪烁,这样即实现了250ms的定时设置。T_Count除了可定义为全局变量以外,还可以定义为中断函数内的静态变量,另外,如果取值T_Count要超过255,还要注意修改其数据类型。
① 用TIMER0定时器控制LED每隔0.5s点亮,并在2s后熄灭,如此不断重复。
② 用TIMER0定时器控制数码管以0.5s的间隔循环显示数字0~9。