昨天我们最后提及了定时器的初始化过程:
- 对**TMOD(工作方式寄存器)**赋值,以确定T1和T0的模式和工作方式。
- 计算处置,并将初值写入TH0、TL0或TH1、TL1中。
- 中断方式时,则对**IE(中断允许寄存器)**赋值,开放中断。
- 使TR0或TR1置位,启动定时器/计数器定时或计数。
今天来举例子具体了解各个步骤
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit led1=P1^0;
uchar num;
void main()
{
TMOD=0x01; //设置定时器0的工作方式1(M1M0为01)
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
while(1); //程序停止在这里等待中断发生
}
void T0_time() interrupt 1
{
TH0=(65536-50000)/256; //重装初值
TL0=(65536-50000)%256;
num++; //num每加一次,判断一次是否到达20次
if(num=20) //如果到达20次,表示1s到了
{
num=0; //清零,重新开始
led1=~led1; //让发光二极管状态取反
}
}
具体解析:
TMOD这个寄存器一共有8位,每一位如果不特殊说明,在单片机复位的时候都会变成0,也就是说定时器0的GATE和C/T已经默认是0,也就是定时器/计数器启动与停止仅受TCON寄存器中的TR1和TR0控制;TMOD目前为定时器模式。
TMOD是不能进行位寻址的,也就是说我们不能直接输入
M1=0;M0=1;
要通过对TMOD的赋值来间接控制4个位。
上面是TMOD的示意图,定时器0处于低4位,每一位的数字只有1和0,这就类似于二进制,当我们默认GATE和C/T为0时,要使用第一种工作方式,也就是M1=0,M2=1,那么就对TMOD赋值0x01,就意味着D0位是1,其余是0 。我们需要哪一位变成1,就需要把相应的二进制数转换成十六进制,直接赋值给TMOD。
确定工作方式后,就要向IE(中断控制允许器)申请开放中断,最后通过TR0直接开始定时器的工作。
主函数中有一个while(1),因此主函数会一直卡在最后一步,也就是前面各种赋值依旧保留。当我们
TR0=1
后,定时器就开始计数了,**只要我们的TR0一直为1,那么计数就不会停止。每次定时器计数满后,就会将TF0置1,向CUP申请中断,进入中断程序,并且定时器会自动清零。**中断程序结束后,回到主程序,由于还是停留在while (1)
这边,所以定时器还是在计数的,就构成了定时器的无限计数。再来讲讲中断函数里面,中断函数里面存在着对两个8位寄存器的赋初值,是**因为每次定时器存满了就会发出中断申请然后自动清零,所以为了确保从中断函数里出来的时候寄存器的初值仍是要求的值,我们就要在中断函数中赋值。**由于我们要求的时间间隔时1s,所以要执行20次的中断,用IF函数来实现。
注意:在中断服务程序中,一般不要写过多的处理语句,一旦语句过多,语句都没有处理完,下次的中断信号又来了(这里意思是在处理中断服务程序时,定时器也在计数。),会造成程序乱套的。
因此我们可以把中断函数再次简洁处理
void T0_time() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
num++;
}
主函数的最后改成
while (1)
{
if(num=20)
{
num=0;
led1=~led1;
}
}
定时器就这样啦