控制/MCU
定时器中断,顾名思义就是在规定的时间内发送中断请求,要实现定时器中断,单片机需要能够正确地计算时间,单片机是如何进行计时的呢?
51单片机内部有两个16位可编程的定时器/计数器T0和T1,每个定时器/计数器由高8位和低8位寄存器组成,TL0为T0的低8位寄存器,TH0为T0的高8位寄存器,TL1为T1的低8位寄存器,TH1为T1的高8位寄存器。
T0和T1的主要作用就是计数,单片机加电或复位后,T0和T1都初始化为零,单片机一旦设置开启定时功能后,T0和T1便在晶振的作用下自动开始计数,当T0或T1计数溢出后,通过特定威廉希尔官方网站 产生内部中断,向单片机发送定时器中断请求,T0或T1会自动清零。T0和T1计数加1的操作需要一个机器周期,假设单片机的时钟周期(晶振频率)为12MHZ,12个时钟周期为一个机器周期,一个机器周期的时间就是1μs(微秒),因此计满T0或T1需要2^16-1个数,需要65536μs,约为65.5ms。若希望定时器每间隔50ms发送1次中断请求,就需要给T0或T1一个初始值,初始值为65536μs与50000μs的差值,T0或T1在初值的基础上再计50000个数后,T0或T1溢出并发送中断请求,刚好是50ms,T0和T1清零后再次赋初值给T0和T1,准备下一次中断请求。
定时器的内部结构如下图所示:
TH1和TL1是T1的高位寄存器和低位寄存器,TH0和TL0是T0的高位寄存器和低位寄存器,每个寄存器的长度是8位,在机器周期的作用下,计数从低位寄存器开始,当低位寄存器计满后向高位寄存器进一位,直到把高位寄存器也计满,此时T0或T1溢出,设置TCON控制寄存器的TF0或TF1位为1,并发送定时器中断请求。
TCON控制寄存器的TF0是T0定时器计数的溢出标注,TF1是T1定时器计数的溢出标注,当定时器计数溢出后,由硬件设置TF0或TF1位为1,并且发送中断请求,进入中断服务程序后,由硬件将TF0或TF1位自动清零。
TR1和TR0为定时器T1和T0的运行控制位,TR1和TR0与TMOD工作方式寄存器的GATE位共同控制定时器的启动与关闭。若GATE位为零,定时器T1和T0的启动与关闭仅受TR1和TR0的控制,当TR1和TR0位为1时,定时器T1和T0启动,当TR1和TR0位为0时,定时器T1和T0关闭;若GATE位为1时,定时器T1和T0的启动与关闭由TR1和TR0和外部中断引脚(INT0和INT1)上的电平状态来共同控制。
TMOD寄存器的C/T位用来设置定时器的工作模式,C/T位为1时,定时器的工作模式为计数器模式,C/T位为0时,定时器的工作模式为定时器模式;M1和M0位用来设置定时器工作位数的长度,M1和MO位都为零时,定时器工作位数为13位,M1为零,M0为1时,定时器工作位数为16位。TMOD寄存器的高四位为T1控制位,TMOD寄存器的低四位为T0控制位。
TCON寄存器的内存地址为88H,位地址(由低位到高位)分别是88H ~ 8FH,可以进行位寻址;TMOD寄存器的内存地址为89H,该寄存器只能字节寻址,不能进行位寻址。有的同学可能会有疑问,TCON寄存器和TMOD寄存器的内存地址会不会发生冲突?首先确定的是TCON寄存器和TMOD寄存器的内存地址不会发生冲突,TCON寄存器是位寻址寄存器,在51单片机中,位寻址寄存器位于单独的内存空间,每个位都有自己的内存地址,TCON寄存器的88H地址包含了8个可寻址位,即88.0~88.7,88.0对应88H,88.1对应89H,……,因此TCON寄存器与TMOD寄存器的内存地址并不会发生冲突。
定时器中断应用案例:应用定时器T0,实现一个发光二极管定时闪烁,闪烁间隔时间为1秒。
单片机内运行的完整C程序如下:
#include
sbit led0 = P1^0;
unsigned char num;
void main()
{
TMOD = 0x01;
TH0 = (15536 > >8)&0x00ff;
TL0 = 15536&0x00ff;
EA = 1;
ET0 = 1;
TR0 = 1;
num = 0;
led0 = 1;
while(1){
if( num == 20 )
{
num = 0;
led0 = ~led0;
}
}
}
void t0() interrupt 1
{
TH0 = (15536 > >8)&0x00ff;
TL0 = 15536&0x00ff;
num++;
}
案例要求发光二极管定时闪烁,闪烁间隔时间为1秒,要实现该功能,最好的方法是使用定时器中断。若单片机的时钟周期为12MHZ,定时器使用一个机器周期做一次计数,计时器计满需要65536个数,约为65536μs,即定时器每间隔65536μs就发送一次定时器中断请求,约为65.5ms,在程序中可以设置一个中断次数计数器num,num的初值为0,在中断处理程序中做num加1操作,当num值大于或等于1000/65.5时,可以认为是1秒时间,对变量led0做取反操作,num初始化为零。
考虑到65.5ms不方便计算,可以让定时器每间隔50ms发送定时器中断请求,当num计数为20时,正好是1秒的时间。若需要定时器每间隔50ms发送中断请求,就需要给T0赋一个初值,该值为65536μs与50000μs的差值15536μs,T0由TH0和TL0两个8位的寄存器构成,TH0存储15536的高8位,TL0存储15536的低8位,取一个16位数的高字节和低字节可以采用下面的代码:
TH0 = (15536 > >8)&0x00ff;
TL0 = 15536&0x00ff;
案例代码t0()为定时器T0的中断处理函数,我们需要在中断处理函数中,每次为TH0和TL0重新赋初值,并将中断次数计数变量num做加1操作。需要注意的是,尽量不要在定时器中断处理函数中编写过多的处理语句,因为若处理语句过多,当前中断处理函数还没执行完毕,下一次中断就会出现,我们就会丢失这次中断,因此能在主程序完成的功能,不要放在中断处理函数内编写,中断处理函数的代码要保持简洁和高效。
下图是定时器中断案例威廉希尔官方网站 设计图:
定时器中断案例C程序编译完成,将执行程序加载到单片机内,启动单片机,发光二极管定时闪烁,闪烁间隔时间为1秒。
全部0条评论
快来发表一下你的评论吧 !