本教程作者为云汉电子社区网友 ,回帖参与讨论、提问、分享,就能赢取超多丰厚奖励哦~
也将随时和大家交流学习中的问题。(PS:本教程未经允许谢绝转载)
学教程,送奖励,活动说明详见:从〇开始学51系列
有了前面对于中断的理解
很多外设,就能够轻松驾驭了
今次咱聊聊有关定时器/计数器,下面简称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次,;
工作方式1——16位T/C工作模式,最多可计数2的16次方 次,即:65536次,;
工作方式2——8位T/C工作模式,计算次数最多为2^8,即256,,;
工作方式3——8位定T/C工作模式 ,计算次数最多为2^8,即256,,;
51单片机设计了2个8位的特殊功能寄存器
用来控制T/C的工作状态
这两个特殊功能寄存器分别是
TMOD和TCON
都在特殊功能寄存器区
贴两个定时器中断寄存器,有前面中断的基础
应该是比较好理解的了
工作模式控制寄存器TMOD
这个寄存器主要是用来设置T/C的工作模式和工作性质(是计数器还是定时器)
工作状态控制寄存器TCON
其实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产生中断的效果
来做个试验
#includesbit 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来实现吧
继续闪个灯
#includesbit 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的基本的操作就是这么些
嗯,还有奖品未送出
这里设计几个题目吧
1、 可以用定时器中断来完成按键的去抖吗?拿点证据出来
2、 可以用计数器来测量外部计数信号吗?拿点证据出来
3、 试试模式3
根据回帖,选出获奖的小伙伴哟
不错哟
赞一个
不是外部中断1,是定时器中断0~~~,后面的1是中断向量号
按键在没有稳定之前是在一直抖动的,而不是一直按下的。
我觉得在定时器中断0里面不用检测按键,而是10ms后再来检测。
我以前消抖就是这样检测的,你也可以实际测试一下看看效果。
哈哈,这不是为了在实物上也可以应用上? 学习之必要嘛
不过哪里有错的请给我指点出来哈!谢过谢过!
用外部中断0响应定时器中断:
当有按键按下时,开启定时器中断,定时器的开启是为了按键消抖延时。
现象:每按一下按键,LED亮灭交替
注:这个方法只用来学习中断跟定时 ,并不适用于实际中,因为当外部中断进行时,会导致计时不准确.
*/