此前有关单只或多只LED闪烁的程序设计均使用延时子程序使LED按一定延时亮或灭,形成闪烁效果。图3-15所示电路则通过TIMER0定时器实现了对LED的闪烁控制。TIMER0通常简称为T/C0或T0。
图3-15 TIMER0控制单只LED闪烁电路
传统型8051单片机仅有2个16位定时/计数器T/C0与T/C1(简称T0与T1)。STC15W4K32S4单片机内置了5个16位定时/计数器:T/C0~T/C4(简称T0~T4)。T0~T4均可工作于计数方式或定时方式。表3-5给出了STC15的定时/计数器相关寄存器。
表3-5 STC15的定时/计数器相关寄存器
对于T0与T1,无论是STC15系列单片机,还是在传统型8051单片机,其工作方式均由TMOD寄存器配置,但两者的模式配置并不完全相同。T0、T1模式寄存器TMOD如表3-6所示。
表3-6 T0、T1模式寄存器TMOD
使用T0、T1时,还涉及控制寄存器TCON,它包括TF1、TF0、TR1、TR0、IT1、IT0、IE1、IE0共8位。其中,TFx为定时器溢出标志位;TRx为定时器运行控制位。
使用定时/计数器,主要有以下两种方法。
· 溢出中断处理:定时/计数寄存器溢出时触发中断,自动调用预先设计的中断子程序。
· 溢出标志位查询:循环检查定时/计数器溢出标志位(TFx);当TFx=1时,执行指定程序。
本案例通过STC15的T0控制LED闪烁,并使用上述方法2实现,要完成的工作如下。
· 设置辅助寄存器AUXR配置时钟分频(仅针对T0~T2)。
· 定时/计数器工作模式设置(对于T0、T1的设置通过TMOD完成)。
· 设置定时/计数寄存器初值(对于T0、T1,对应为TH0/TL0、TH1/TL1)。
· 允许定时/计数器中断(对于T0、T1,可设置IE,或单独设置EA及ET0、ET1)。
· 启动定时/计数器(对于T0、T1,可设置TCON或单独设置TR0、TR1)。
对于STC15拓展的其他定时/计数器,所对应的配置寄存器会存在差异。例如,T3与T4的分频配置使用T4T3M寄存器完成。
完成上述设置工作后,余下的最为重要的工作是编写定时/计数器中断子程序,当定时/计数器时间到达或计数值溢出即触发中断,所编写的相应中断子程序将被自动调用。
对于STC15的T0,其技术手册推荐使用其模式0,即16自动重装载模式。16位的最大值为2 16 -1=65 535,计数至65 536时溢出,其16位的原始值将被恢复,计数重新开始。图3-16给出了STC15的T0模式0的工作原理。
图3-16 STC15的T0模式0工作原理
由图3-16可知,对STC15而言,驱动T0工作的系统时钟可以被设置为8051单片机的12分频,也可以被设置为1分频。对于12MHz振荡器频率,在12分频模式下,T0计数周期 T cy=1/(12MHz/12)=11s,如果振荡器频率为11.059 2MHz,在12分频模式下, T cy =1/(11.059 2MHz/12)≈1.085 11s。在开启定时/计数器后,每个计数周期都将使定时/计数寄存器累加1。
为使用T0实现12分频模式下的5ms(5 0000s)定时,使用STC-ISP工具,在定时/计算器中可生成图3-17所示代码。
图3-17 使用STC-ISP工具生成定时器初始化代码
对于AUXR &=0x7F(01111111),根据图3-16可知,AUXR的最高位T0x12被置0,选择12分频;对于TMOD &=0xF0,其低4位GATE0、C/T0、M10、M00全部被置0。其中,GATE0=0表示只要TR0=1即可启动T0;C/T0=0表示T0在内部时钟驱动下计数;M10 M00=00表示T0工作于模式0,即STC15的16位自动重装载模式。
在图3-17中,TL0=0x78,TH0=0xEC,定时初值0xEC78=60 536,它们等价于TL0=(65 536-5 000) & 0x00FF与TH0=(65 536-5 000)>>8,或者是TL0=-5 000 & 0x00FF与TH0=-5 000>>8。
之所以可直接使用“负数”,是因为65 536即17位二进制数1 0000 0000 0000 0000的最高位为1,其余16位为0。对于16位的寄存器,65 536与0是相等的。为检验这一结果,可打开Win10附件中的计算器,切换到“程序员”模式,如图3-18所示,选择DEC(十进制),计算“65 536-5 000”,得到的结果为60 536;对应HEX(十六进制)的结果为EC78,即TL0=0x78,TH0 T 0xEC;如果输入“0-5 000”,可得结果为-5 000,即十六进制数FFFF FFFF FFFF EC78。其末尾数位同样为EC78,所得结果中仅有EC78(共4个十六进制位,也就是共16位二进制位)被分别保存到两个8位的寄存器TH0与TL0。
图3-18 使用Win10计算器程序员模式验算定时初值
在程序中,可直接写入看似烦琐的常量表达式,这样可提高程序可读性。
由图3-16可知,启动T0开始工作时,可直接置TR0 T 1,在12MHz/12=1MHz计数时钟驱动下,对所设置的定时/计数寄存器初值0xEC78(即60 536),该值将会每微秒递增1,5 0000s后该值为65 536(60 53605 000)。由于模式0为16位,最大值为65 535,定时/计数寄存器的值递增至65 536时产生溢出,触发T0中断,硬件置位TF0。
在中断子程序中,T0和T1的中断号分别为1、3,不要将其误设为0、1。本案例编写的是T0中断子程序,该子程序后面添加了interrupt 1。
对于中断函数内的代码还有以下两点说明。
① 由于本案例T0工作于模式0,所设置TL0与TH0初值将被同时保存至与TL0、TH0共用地址的,被隐藏的RL_TL0、RL_TH0寄存器中,其中RL表示重装载(ReLoad)。当TL0、TH0溢出时将触发T0中断,参照图3-16可知,RL_TL0、RL_TH0的初值将被重装回TL0、TH0。8051单片机的T0模式0为13位模式,且TL0、TH0初值的重装要通过软件代码完成。
② 在当前12MHz时钟、12分频模式下,T0最大可独立实现65ms定时,此时定时/计数初值将为65 536-65 000=536=0x0218,也就是TL0=0x18、TH0=0x02,其中的65 000对应65ms(65 0006s),如果要实现更大延时,则要引入静态变量或全局变量,如T_Count,通过变量累加来实现更长定时。
① 用TIMER0控制LED每隔0.5s被点亮,并在2s后熄灭,如此不断重复。
② 用TIMER0控制数码管以0.5s的间隔循环显示数字0~9。