定时器与计数器/Timer And Counter
文章内容有误可以直接在最下面评论
在描述问题的时候把标题加在前面,要求最好精确到问题出现位置的小标题。
比如:
汇编-寻址方式-立即寻址:{问题描述}
错别字就没有什么必要了,我也懒得改(doge)
前言
在 中断的应用举例中 我们注意到里面有一个
delay()
函数。
1
2
3
4
5
6
while(1) {
led=1;
delay(65535);
led=0;
delay(65535);
}上面的代码段就是
delay()
函数的调用。因为单片机执行每条指令速度很快,如果只是单纯的对led进行更改:
1
2
3
4
while(1) {
led=1;
led=0;
}因为速度非常快,而又因为人类的视觉残留,就会导致看上去并没有什么变化,这时就需要一个
delay()
函数使CPU空转来达到延时的效果但软件调用总归是有偏差的,能否使用硬件来执行延时呢?
定时器和计数器的概念
定时器和计数器的原理其实大差不差,都是对脉冲进行计数直至溢出,但有区别的是:
定时器是对等间隔脉冲进行计数,这个脉冲是来自于单片机内部,所以它的频率和幅值都是稳定的;
计数器是对普通脉冲进行计数,这个脉冲来自于单片机外部,它的频率和幅值都是随机变化的
在我们本学期学习的知识中,因为定时器对等间隔脉冲进行计数的特性,定时器的运用是比较多的,主要涉及到的有以下两点:
前言部分所说的延时,分别有查询法和中断法;
下一节串口中要涉及到的波特率发生器。
需要着重了解并掌握的就是定时器初值的计算,以及在C语言代码中如何赋初值。
相关特殊功能寄存器
首先是控制定时器如何工作的TMOD
定时器工作方式寄存器/TMOD
TMOD的字节地址是89H
,根据 存储器 章节中所学知识,该字节地址无法被8整除,所以TMOD不可以位寻址。
TMOD中高四位和低四位分别对应控制T1和T0的工作方式,每四位从高到低分别是:
GATE: 门控位,为1时有外部中断源控制,一般直接为0;
但是在第七、八、九章作业客观题中出现过一道题:
要想测试INT0引脚上的一个正脉冲宽度,那么特殊功能寄存器TMOD的内容应为 A. 09H;B. 87H;C. 00H;D. 80H
。根据上面所述只要GATE位为1就可以了,且一般使用T0,也就是答案选A。C/T:功能选择位,为1时选择计数器,为0时选择定时器;
M1、M0:工作方式选择位,有方式0-3,可定义四种 工作方式 。
因为TMOD不能位寻址只能字节寻址,所以变更数据时必须一次性更改所有位的数据;
例如需要T1工作在方式2,C语言代码就为
1 |
|
有一个技巧就是,当使用定时器时(即门控和C/T位都为0),选T1就是修改高位数。选T0就是修改低位数变化,且变化的数是几,就使用方式几。
控制寄存器/TCON
这个我们在 中断 里已经学过低四位,而定时器就是用的高四位。
TCON每两位对应一个中断源,从低到高分别是:INT0、INT1、T0、T1;
而两位中,高位(IEx、TFx)都为中断标志,即在申请中断时会置1;
低位(ITx、TRx)对于外部中断,就是它的触发方式;对于定时器,就是启停标志,即当该位置1,定时器就开始对脉冲进行计时。
TRx的C语言代码举例:
1 |
|
中断相关
还记得“ 外内外内串 ”吗?
定时器T0、T1用到的就是这两个“内”,即ETx、PTx分别是对应了定时器的中断允许位和 中断优先级 位。
定时器的工作方式
M1 M0 | 方式 | 说明 | 最大计数值 | 最大定时时间(当Tcy为1us时) |
---|---|---|---|---|
00 | 0 | 13位定时器/计数器(THx的8位和TLx的低5位) | 8192($2^{13}$) | 8192us |
01 | 1 | Tx都为16位定时器/计数器 | 65536($2^{16}$) | 65536us |
10 | 2 | Tx为自动重赋初值的8位定时器/计数器 | 256($2^{8}$) | 256us |
11 | 3 | T0分成两个独立的8位定时器/计数器, T1在方式3时停止工作 | 256 | 256us |
这里提到了一个THx和TLx,这两个就是对脉冲计数的寄存器,每个有8位,当使用方式0时,就是THx的8位和TLx的低5位在计数;当使用方式1时就是16位一起计数,所以方式1一共会计$2^{16}$=65536个数;而方式2和3的THx和TLx两个相等,即$2^{8}$=256个数。
需要注意的是,四个方式里只有方式2会自动赋初值,即在定时器计数溢出后会自动回到赋的初值,而其他三个方式会直接清零,所以在溢出后需要重新赋初值。
这里其实可以和数电联动一波:数电里学习的计数器有两种重置方法——清零和置数,方式二其实就相当于接了置数端,并且将初值赋给了置数的几个输入口,而其他几个接的是清零端,溢出后会直接清零。
定时器赋初值
首先要知道两个概念,计数值和Tcy:
计数值就是每个方式对应的最大值减去需要设定的初值;
Tcy,即机器周期,也就是晶振频率的倒数乘上12,即$T_{cy}=\frac{12}{f_{osc}}$。
由此我们可以得出以下公式:
$$
计数值=最大值-初值\
$$
$$
定时时间=计数值\times{T_{cy}}\
$$
$$
T_{cy}={12}/{f_{osc}}
$$
所以在已经得出Tcy的情况下,初值可以直接这么计算
$$
初值=最大值-定时时间/T_{cy}
$$
据此,就可以算出初值了。
例如选择方式1,记50ms:最大值就是65536,Tcy为1us,定时时间就是$50\times10^{3}us$,即计数值就为50000,那么初值就是最大值减初值即为15536。
在赋值的时候需要注意,方式1是分为高八位和低八位的。
应用举例
查询法
这里引用的是定时器实验里的源码,我对一些细节进行了一些修改。
1 |
|
我修改的部分里首先要指出的是,赋初值的部分我并没有像源码一样:
1 |
|
他其实就是人工计算出来然后直接贴上去,但我们都在用单片机何必多次一举,所以直接将公式贴进去让单片机帮我们算就行,但是考试是不免会考到让你手算初值,所以计算初值的方法还是有必要学的。
然后就是高八位和低八位的处理,他是直接赋值,所以直接把计算好的结果化成16进制在拆开赋值就行,我们这里是让单片机计算,那就可以用C语言整型变量除法和取余的特性,除以2的8次方即为取高八位,对值取2的8次方的余数就是取低8位。
查询法的原理其实就是运用了当定时器溢出时,会向CPU申请中断的特性,此时对应的中断标志会置1,当while
循环检测到中断标志为1后,就会进入while
循环,而while
循环里就是我们想要在定时器定时结束后执行的逻辑代码。因为没有进入中断服务子程序,中断标志没有硬件自动置0,所以还需要手动软件置0.
需要注意的是,无论有没有在IE特殊功能寄存器内让定时器允许中断,当他计时溢出后中断标志依旧会硬件置1,只是CPU不会处理这个这个中断,即不会进入中断服务子程序。查询法本来就不需要进入中断服务子程序,所以也不需要对IE寄存器进行赋值。
中断法
中断法其实就是利用了中断服务子程序来执行逻辑代码,也就是相当于把查询法内while (TF0) {}
里面的那段拿出来单独做成了中断服务子程序。
参考代码依旧是定时器实验里的源码:
1 |
|
定时器与计数器/Timer And Counter