【51系列教程第六讲】 无法逃避的宿命--定时器

-回复 -浏览
楼主 2021-01-12 09:21:12
举报 只看此人 收藏本贴 楼主

了前面对于中断的理解,很多外设,就能够轻松驾驭了。今次咱聊聊有关定时器/计数器,下面简称T/C(Timer/Counter)。其实T/C在51内部同属于一个电路模块,只不过计数器由外部事件驱动,接受的是外部脉冲。对外部脉冲进行计数,而定时器则是对51单片机自身产生的脉冲进行计数。无论是计数器还是定时器,计数或者定时达到一定数值,就会产生中断。

看这个框图




晶振的震荡频率经过12分频后,作为定时器的计数脉冲,如果是12MHz的晶振,不难推算,定时器的计数脉冲频率就是1Mhz。也就是说每1μs,定时器就会自己进行加1计数,值得一提的是,51单片机只能够进行加计数,而很多其他类型的单片机则可以进行加减计数。那么计数器呢

图中可以看出来,外部引脚T0/T1



对应P3.4和P3.5管脚所输入的信号,则作为计数器的计数脉冲。每一次有效信号到达,计数器就会自行加1计数,直至计数达到某一数值,产生中断,完成中断服务程序。So,定时器和计数器只是触发来源不同而已,那么究竟什么时候,T/C才会进中断呢?咱就得给T/C的计数寄存器赋初值了。如果计数达到这个初值,便产生中断。最简单的理解就是杯子理论,51的T/C比较简单,只能够加计数

所以。如果把一个杯子的容积看成T/C的计数大小,每一次计数都是向杯子里加水的话,那么杯子装满水后,继续加水便会溢出,也就是51产生中断的时刻。如果我们提前在被子里面倒上一些水,也就是赋初值,那么,就可以在我们需要的时间内出现中断。这种办法很容易被用来作为软件循环的时间基准,譬如定时1ms,然后在每xxms做一件什么事情,高深点,也就是心跳啥的了。

下面再来聊聊T/C的几种不同模式,一共有4种工作模式,以定时器0为例

直接上框图更加直观,先是工作模式0:




在工作模式0时,TL0的低5位(D0-D4)和TH0的全部8位构成了一个13位的寄存器,每次计数脉冲过来,这个13位的寄存器都会加1(如果有预设值的话,从预设值开始)。这个13位的寄存器自加满并溢出时,内核会把这个13位的寄存器清零,并且把TF0位置1,产生中断。如果我们需要继续使用定时器

那么在中断来临或者其他必要的时候,重新将TF0清零,且将TH0和TL0进行赋初值

看看工作模式1:



模式1和模式0基本上完全一样,唯一的区别就是计数寄存器。模式0是13位,而模式1变成了TL0的全8位和TH0的全8位构成的16位计数寄存器,这么看来,模式1的计数/定时范围更大

再是工作模式2:



这个模式里面,可以自动装入预置数,不过牺牲了TH0的8位寄存器,将TH0作为保存预置数的寄存器。TL0作为计数寄存器,进行计数自加,显而易见,模式3虽然可以不需要手动赋初值。这种方式给编程带来了方便,但是计数范围就小了很多。适合作为一些通信的时间基准,譬如模式2可作为串口通信的波特率发生器

最后是工作模式3:




要注意的是,T1/C1其实是没有工作模式3的,只有T0/C0才有,这种模式简单点理解,就是把T0/C0划为两个T/C。每个都是8位的计数寄存器,不过一个由TH0计数,一个由TL0计数,上面几张图很明显地可以看出各种模式之间的区别

简单总结一下:

工作方式0——13位T/C工作模式,最多可计数2的13次方 次,即:8192次,[0,8191];

工作方式1——16位T/C工作模式,最多可计数2的16次方 次,即:65536次,[0,65535];

工作方式2——8位T/C工作模式,计算次数最多为2^8,即256,,[0,255];

工作方式3——8位定T/C工作模式 ,计算次数最多为2^8,即256,,[0,255];

51单片机设计了2个8位的特殊功能寄存器,用来控制T/C的工作状态;这两个特殊功能寄存器分别是

TMOD和TCON,都在特殊功能寄存器区,贴两个定时器中断寄存器,有前面中断的基础。应该是比较好理解的了,工作模式控制寄存器TMOD



其实TCON只有高4位是和T/C有关,低4位其实与外部中断相关。Reg51.h头文件中都定义了这些寄存器的位,可以直接寻址并赋值十分方便。那么预设值该怎么计算呢?预置数的计算公式:预置数=最大值-需要计数的次数;举个栗子:

我要1ms产生一个中断,假设外部晶振是12MHz,采用模式1,那么T/C会1μs进行一次计数,T/C计数1000次,便是1ms;计数最大值就是2的16次方,那么 预置数 = 65536 – 1000 =64536,64536的十六进制便是 FC18,所以,将TH0 = FC,TL0=18作为预设值,并使能中断,就能达到1ms产生中断的效果。

来做个试验


#include <reg51.h>
sbit LED2 = P1^0;
void main()
{
TMOD=0x01;//设置定时器0为工作方式1
TH0=0xfc;//设置初值,计划1ms定时器溢出中断(我用的是11.0592Mhz晶振)
TL0=0X66;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0
while(1)
{}
}
void time0() interrupt 1
{
LED2 = ~LED2;
/*重新配置计数器*/
TF0 = 0;
TH0 = 0xfc;
TL0 = 0x66;
}


简单分析一下这个代码,让T0工作在方式1,赋初值FC66。计数器自加溢出后产生中断,中断里面LED2口(P1.0口)发生电平翻转,产生一个周期2ms的方波

有图有真相



上图中实测电平保持时间是1.0159ms,与预期的1ms 稍有差别,大约在20us左右。这是由多方面因素造成,一是测量仪器的精度和IO口翻转的延时;二是中断里面执行了数条语句,别忘了51执行语句也是us级别的哟,一条c语言语句被IDE编译后,可能等效于好几条汇编机器语言。不过话说回来,指望用单片机完成十分精确的定时,本身就是个不靠谱的想法,要求严格的时钟,还是用专用IC来实现吧

继续闪个灯


#include <reg51.h>
sbit LED = P1^0;
unsigned int Count=0; //定义一个无符号整型变量
void main()
{
TMOD=0x01;//设置定时器0为工作方式1
TH0=0xfc; //定时1ms
TL0=0X66;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0
while(1)
{
if(Count ==500) //如果有500次自加,也就是500ms
{
Count = 0;
LED = ~LED; //LED取反
}
}
}
void time0() interrupt 1
{
Count ++; //变量每1ms自加1
TF0 = 0;
TH0 = 0xfc;
TL0 = 0x66;
}


上个GIF



呃,绿灯被我玩坏了,换了个高冷的冰蓝LED。So,到这里,T/C的基本的操作就是这么些!!


关于云汉电子社区

云汉电子社区(bbs.ickey.cn)是云汉芯城的特色频道,2013年7月份上线至今,已发展成为国内最大的电子工程师社区,同时也是IC原厂最集中的样片申请中心和最专业的电子工程师技术交流论坛,并为电子工程师提供研发阶段的小批量电子元器件采购服务。


我要推荐
转发到

友情链接