大家好,欢迎来到IT知识分享网。
有了前面对于中断的理解,很多外设,就能够轻松驾驭了。今次咱聊聊有关定时器/计数器,下面简称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的基本的操作就是这么些
了解更多51系列教程,可登录云汉电子社区官方网站(bbs.ickey.cn)或者关注“云汉电子社区”官方微信公众号,还可以将疑问发给公众号平台,我们会及时答复!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/170021.html