目录
上两节是有关定时器和计数器工作原理的详解版。定时器/计数器的知识比较杂,要多花点时间搞懂!为了能更清晰地了解定时器和计数器的工作原理,所以写了一个简述版本!
如果觉得看前两节详解版没有耐心看下去,可以只看这个简述版,如果你耐心看完这篇还不懂定时计数器的话,可以私聊喷我!(不是,开个玩笑)
反正,这篇耐心看完绝对不会浪费你的流量和时间!
话不多说,进入正题!
51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成
定时器作用。什么意思呢?
简单来说就是只有单独的一个单片机就能实现定时器的功能,因为它是属于单片机内部
的设备。
定时器的介绍
接下来就开始定时器的介绍:
我们知道STC89系列的51单片机,它有三个定时器,分别是定时器0,定时器1和定时器2。要注意的是,基本的51系列的单片机都含有两个定时器,也就是定时器0和定时器1,那么不是说所有的51单片机都含有定时器2的,一些增强型的一些51单片机可能就有定时器2。
本节介绍的就是定时器0和定时器1的使用,至于定时器2的使用方法,它是和前两个是一样的。
在介绍定时器之前,我们先来讲解一下几个知识:
震荡周期,是为这个单片机提供定时信号的一个震荡源的周期,通常,我们会给单片机外部晶振引脚提供一个晶振,像我们开发板上面就有一个12兆,或者是11.0592兆的一个晶振,晶振频率就是给我们的一个震荡周期,那么周期是等于频率的导数的(T=1/f),所以为了方便计算,我们这里就讲以12兆为例来讲解,那么11.0592兆是一样的。 这样震荡周期是不是就等于1/12微秒,因为它的单位是兆嘛,所以转换成周期的话就是微秒,所以1/12微秒这是它的一个震荡周期。
状态周期,是两个震荡周期为一个状态周期,我们通常记为S,也就是说一个S等于两个震荡周期,两个震荡周期也就是等于1/6的微秒,这个是我们状态周期。
机器周期,机器周期是等于6个状态周期,那么状态周期又是等于两个震荡周期,所以它是等于12×1/12微秒。也就是一个机器周期等于多少一微秒。
这个是以我们12兆为例来进行介绍的,那对于11.0592兆,那它肯定不是一微秒,它是接近一微秒,这里为了方便计算,我们就以12兆为晶振来介绍我们的这个震荡周期,状态周期和机器周期的一个计算关系。
指令周期,是完成一条指令所占用的全部时间,它以机器周期为单位,通常,它的指定周期一般是1~4个指定周期,通常是1~4微秒。 我们用的比较多的主要是机器周期,那么机器周期我们知道了,如果是12兆的一个晶振源的话,那我们的机器周期是等于1微秒的,所以我们知道了机器周期,我们后续在计算定时器的定时时间的时候就很方便了。
在学习定时器之前,我们还要介绍几个知识点:
第一个,51单机它有两组定时计数器,既可以实现定时,又可以实现计数,所以它也称为定时器计数器。
第二个,定时器和计数器,它和单片机的CPU是相互独立的,也就是说定时计数器工作的过程,它是自动完成,不需要CPU来参与。
第三个,51单机中的定时计数器是根据机器内部的时钟或者是外部的脉冲信号来对这个寄存器中的数据进行加1,所以定时和计数是依据定时器内部的时钟或者是外部的一个脉冲信号。
比如说我们要使用定时计数器的计数功能,那我们通常是用来计数外部的脉冲信号的个数,所以我们这里是依靠的是外部脉冲信号的个数来对寄存器的数据加1。
如果是我们使用的是定时器功能,那么通常是通过机器内部的时钟来对它进行一个计数加1,计数加1也就是完成了一个机器周期,完成多少个机器周期,那就可以根据机器周期一个的时间乘以完成的计数的次数,就是它的定时时间。
定时计数器的作用:
(1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作;
(2)替代长时间的Delay,提高CPU的运行效率和处理速度
(…)
那有了定时计数器之后,可以增加单片机的一个效率,一些简单的重复加1的工作就可以交给我们定时计数器了,CPU它去处理一些其他的一些复杂的事情,通过定时计数器的学习,我们可以实现一个精确的定时。像我们前面学习的Led闪烁等等,我们写的延时函数通常是使用循环来延时,这种延时是不精确的,只能说是一个大概的一个延时时间,如果说你要得到一个精确的延时,精确的时间,那么我们可以通过定时器来完成。
定时计数器的定时原理
下面,我们来介绍一下定时器的定时原理。
STC这个单片机,它有两个可编程的定时计数器,有T0和T1。 还有特殊功能定时器T2,通常我们介绍T0和T1, T2是针对一些增强型的一些单片机,它的功能和T0和T1是类似的,所以我们先学会这些基本的,共有的这个定时器,其他的定时器的使用方法是类似的。
这个定时计数器的实质是进行加1的计数器,因为我们的定时器它含有16位,分为高8位和低8位,组合在一起就是16位。 也就是通过由TH高8位和TL低8位组成。
随着这个计数器的输入脉冲,进行自动加1,也就是每来一个脉冲,它计数器就自动加1。那当加到计数器全为1的时候,再输入脉冲过来之后,它就会溢出,也就是回0,这个时候,计数器的溢出使相应的中断标志位会置1。那如果是我们允许了中断功能的话,那它就会向CPU发出中断请求。定时计数器工作就会进入中断,完成我们相应的中断函数。
如果是定时计数器处于定时模式,像这种溢出的话,表示定时时间已经到达。如果定时计数器器处于计数模式的话,表示,我们定时计数器已经计数满了。
这个定时时间和计数的次数怎么去算?通常是由我们的溢出的计数值减去初值,我们后面会介绍定时器初值的设置。
比如说我们定时计数器假设初值为10,那溢出的话,定时器是16位,那它计到65535再加1的话就是66536。用这个值减掉初值,那计数了多少次,就是我们的定时时间,或者是计数的次数。
定时计数器的内部结构
下面来看一下定时计数器的内部结构。
这个图是51单片机的定时计数器的内部结构图。
从这张图当中可以看到,这两个引脚T0和T1,分别对应的是单片机的P3.4引脚和P3.5引脚的附加功能。
51单片机定时计数器的工作是由两个特殊功能寄存器来控制的,TMOD这个寄存器是8位的,TMOD主要是用于定时计数器的工作方式的确定,确定了这个定时器的工作方式和功能,因为定时计数器它可以工作在定时模式,还可以工作在计数模式,那你使用哪一种,就通过TMOD来进行设置。
然后,还有TCON这个寄存器,这也是8位的,其中低4位是用于控制外部中断相关的一些功能位,然后高4位主要是用于定时器的启动和停止,以及溢出的一些标志,是通过这几个位来进行设置。
然后通过这两个功能特殊功能寄存器来控制的定时器的工作。T1和T0这两个定时器是独立的,分为定时器0高8位和定时器0低8位。同理,定时器1也是一样分为两个8位,组合在一起就是16位。
他们的脉冲计数的次数是通过机器周期的脉冲,因为定时器计数器可以工作在定时模式,还可以工作在计数模式,如果工作在定时模式的话,那它的定时的时钟来源是取决于的机器周期的脉冲,它会自动去给对这个脉冲进行计数。这个机器周期的脉冲数就是机器周期的时间,前面已经分析了,如果外部晶振是12兆的话,机器周期计数一次,脉冲它就是一微秒。通过这个就可以实现定时,我只要知道我的计数寄存器里面计数的次数,我就知道它的定时时间。
然后T0和T1,它是外部的引脚,这个外部引脚是有什么用?主要是用来实现这个计数的功能,计数功能的话需要对外部的脉冲信号进行计数,那它就需要把外部信号传入给对应的引脚。比如说我使用T0来进行脉冲计数的话,我就把外部的信号提供给的T0引脚,也就是P3.4这个引脚,然后让它处于计数模式,就可以对这个脉冲进行计数。
这是内部结构的分析。
两种控制寄存器
接下来具体来看一下两种控制寄存器。
(1)工作方式寄存器TMOD
工作方式寄存器TMOD,最高位GATE是用来设置门控位。那何为门控位?是用于控制定时器的启动。
如果说这个GATE等于0的话,只要用软件TCON中的TR0或者是TR1这两个位来设置(如果你是使用定时器0,那就TR0,如果使用定时器1,那就TR1),如果TR0或者TR1为1的话,就是启动对应的定时器工作。
TR0和TR1是在TCON控制寄存器,前面也说过。
如果说这个GATE等于1的话,TR0或者TR1 等于1,用来控制它的定时计数器的启动,同时它还要外部中断引脚INT0=1或者INT1=1,它才能启动的定时器工作。
所以通过这个GATE门控位就可以使定时器处于哪种模式。
- 当GATE=0的时候,定时器要工作的话,必须是TR0=1或TR1=1才启动定时器工作。
- 如果GATE=1的话,那它除了让这个TR0/TR1=1之外,还需要对这个外部中断引脚设置INT0或者INT1=1,它才能启动的定时器。
以上是它的两种方式的对比。
通常用的比较多的是GATE=0的方式。
然后再看一下C/T,它是用于定时计数器模式的选择位。
C是用于计数计数模式,T用于定时模式。定时模式T上面有一横,可以理解它是低电平有效,所以当C/T位等于0时,是处于T的这个模式,也就是定时模式。
如果等于C/T=1的话,它是处于C,也就是计数的这个模式。再看M1和M0
M1和M0是工作方式的设置,因为定时器可以工作在四种方式,这两位可以有四种组合,00它是工作方式0,01它是工作方式1, 10方式2, 11是方式3。
这这张图也给大家列出来了,可以看到如果是方式0的话,它是13位的定时计数器的。 工作方式0,它只有13位用于定时计数器。
然后工作方式1的话,它的高八位和低八位全部用于定时计数器,也就是16位。
然后方式2的话,它只有八位,可以自动重装定时计数器,那这个主要是用于一些精确的一些脉冲发生器,像串口通信的时候,会用这种方式。
然后方式3,它是T0分成了两个独立的八位定时器,T1这个时候是停止计数的。
很少使用方式3。用的比较多的就是方式1和方式2,方式1通常是用来对定时计数器的工作,方式2主要是用来对这个串口通信的波特率的生成。
那后面的这第4位是跟前面是一样的。
这是因为定时器有两个T0和T1,TMOD这8位中低4位是控制的T0,高四位是控制T1,所以有8位,它们是重复一样的一样的功能。只不过这四位是用来管理的T0定时器,然后高四位是用来管理T1定时器。
(2)控制寄存器TCON
接下来再来看一下工作方式控制寄存器TCON。
外部中断主要是通过TCON的低四位来进行控制,然后,高四位主要是用来对定时器进行启动、中断的升级等等。
首先看一下TF1:
TF1是T1计数器的溢出中断标志。T1计数溢出时,是由硬件自动置1的,这个是自动完成,不需要软件去设置。CPU响应这个中断后TF1由硬件自动清零,即CPU响应的这个中断的时候,TF1就会自动清零,等待下一次的再次响应。 T1工作时候,CPU可随时查询TF1的状态,所以TF1可用于来查询测试定时计数器是否溢出,所以如果说不使用中断的话,那可以通过软件的查询的方式查询这个标志TF1。
如果使用定时器T1就是TF1,如果使用定时器T0的话,就查询TF0。这两者功能是一样,一个负责定时器1,一个负责定时器0。
当然,TF1和TF0也可以通过软件来置1或者清零,它的效果跟硬件的质疑和清零是一样的。
接下来再看一下的TR1。
TR1是用来对T1定时计数器的运行控制位,当这个TR1设置为1的时候,就让定时器1开始工作,如果当这个TR1为0的时候,就停止定时机的工作,这个是通过的软件来设置的,所以在配置这个定时器进行工作的时候,这个TR1或者TR0都要进行设置为1,让它开启,如果你是设置为0,它就是停止的。
这是定时计数器的开始和停止的控制位。 然后TF0,这刚才已经说了,跟TF1是一样的,一个负责定时器1,一个是负责定时器0,那TR0也是一样。
定时计数器的工作方式
下面我们来介绍一下单片机定时计数器的工作方式。
前面我们已经说了,通过M0、M1这两个位可以有四种工作方式,那我们来分别介绍一下这四种。
方式0
首先我们来看一下方式0,它是13位的计数。由TL0的低五位和TH0的八位组成了13位的计数。其中TL0的高三位是没有用到。TL0的低5位溢出的时候,就是说这5位已经全部计到5个1了,如果它再计的话就溢出,溢出的时候它就会向这个TH0进位。当这个TH0溢出的时候,由于TF0本身就是溢出标志位,它就会让这个TF0自动置1向我们CPU发出中断的请求。
我们来看一下后面的这一部分结构。
这是我们定时器内部的结构图,它是处于方式0的情况。
方式0、方式1和方式2、方式3,它主要区别是在这一块。
那我们来看一下门控位,刚才我们说过通过门控位可以控制我们这个定时器,它的工作是由TR0或TR1软件来直接控制,还是说通过TR0和TR1并且通过外部中断管脚INT0或者INT1来控制。
GATE=0的时候,过来碰到一个非门,0经过非门转换后就是1。
再过来这里是个或门,或门的特点只要是有1,它就为真,所以这里不管你这个INT0引脚是什么电平,它这里都是1。
然后过来是与门,与门的话必须要两个都为真,也就两个都为1才为1,所以,当我们GATE位等于0的时候,定时器能不能启动就取决于TR0,只有当TR0为1的时候,这个定时器才能进行工作,就是控制这个开关闭合,我们定时器才能进行计数。
当GATE=1的时候,经过非门就是0。0过来碰到一个或门,或门这里就取决于我们这个INT0了。
如果这个INT0=1的话,那这里就是1,如果INT0=0的话,那这里就是0, 0经过与门肯定也是等于0,也就是关闭这个定时器的工作。
如果说当INT0=1的时候,这个或门就是1,或门1的话,那它也取决于TR0。
所以当这个GATE位等于1的时候,它要通过TR0/TR1以及INT0/INT1同时决定定时器的开启和关闭。
然后我们看这个C/T。
C/T是计数/定时的工作模式选择。
当C/T设置为1的时候,是属于处于计数的模式,它就会将这个开关打到上面,也就是等于1的时候,T0引脚,就是对应到单片机的P3.4引脚。如果是1T引脚,就是对应到P3.5引脚。这个引脚就可以检测外部的脉冲,进行计数,传到这个计数器当中来。
当C/T设置为0的时候,这个是定时模式,也就是说这个开关会打到下面0的位置,计数源机器周期,机器周期过来它计数一次,一个机器周期过来计数一次。当外部晶振是12兆的情况下,机器周期一次就是一微秒,那这样的话,计数了多少次就多少微秒,定时时间就可以确定了。
刚才我们是以TR0或INT0为例,那对于定时器1是一样的,只是TR1和INT1的区别而已。
对于方式0来说,当计数器进行计数的时候,计数个数是怎么来的?方式0的时候,定时计数器是13位的,所以是2的13次方减掉N,N是定时的初值,当溢出的时候,肯定是2的13次方,所以2的13次方减掉初值就是我们计数的次数,就是对应的时间/脉冲个数。
方式1
那我们再来看一下方式1。方式1其实跟我们前面的方式0的介绍是一样的,不同的区别就是方式1是使用的是16位的计数,低8位计数满,它会向高8位进行借位,那高八位计数满,它会溢出,就是说高8位计数满并且低八位计数满,那它就会产生溢出,溢出就会向中断进行CPU申请中断,如果我们配置中断,它就会进入中断服务函数来执行。
只有这前面这一部分的区别,后面的话是完全一样的,不再重复了。
因为方式1是16位的所以它计数的计算关系是2的16次方减掉初始值。
方式2
然后我们再来看一下方式2。 它这一部分,其实跟我们前面介绍也是一样,不同之处是计数的位数不一样。
方式2是自动重装初值的八位计数器,可以看得到它是以TL0开始计数,TL0计数满了,它会产生溢出,溢出的时候,这个TH0是保存这个重装载的初值,然后这个初值会自动的加载到TL0当中,然后又开始进行计数,计数满了之后又溢出,溢出来又从这个TH0重新装载初值,就是说我们只要第一次装载了初纸之后,后续就不需要再改变,它自动会完成装载,重新计数。
这种方式只比较适用于精确的脉冲信号的发生器,像我们后续要学习的串口通信UURT,在串口通信当中,我们需要将这个定时器设置为方式2,也就是这种方式。 这种方式的计算初值和计数的个数是2的8次方减掉N。
方式3
我们再来看一下方式3。
方式3只适用于定时计数器的T0,,只适用于T0,那对于T1的话,它是不适用的。T1处于方式3的话相当于TR0/TR1等于0,停止计数。所以这种方式只适用于定时器0工作。 它是处于8位的,是通过TL0这8位来进行计数计数溢出产生溢出标志,然后向CPU发出中断请求,那后面那一部分和我们前面介绍是类似的。
这四种方式中,我们通常应用比较多的是方式1和方式2,那对于方式0和方式3,很少使用,所以我们重点掌握方式1和方式2。
定时器的配置步骤
下面我们来介绍一下这个定时器的配置。 在使用定时器的时候,应该如何配置它的工作,它的步骤是怎么样的?
我们这里列举出来了,当然这几个步骤,它的顺序也是可以任意的,不一定要按照这个1234这四个顺序来。
第一步,对TMOD的赋值
首先是对TMOD的赋值,TMOD的寄存器,我们前面已经说了,它是用来确定我们定时器的工作方式,像里面包含了T0和T1这两个定时器的工作方式,那你使用的是T0或者是T1,那就对这个TMOD的高4位或者是低4位进行赋值。 TMOD这个寄存器的赋值关系就参考这个寄存器了,你要对哪个进行工作方式设置,你是使用计数器还是定时器,这个功能就要对C/T进行设置。
第二步,定时的时间计数初值
第二步,根据所要定时的时间计数初值,并将初值写入到对应的寄存器当中,TH0或者TR0。这是定时器T0的高8位和低8位。
如果是对定时器1进行定时的话,那我们要将定时的初值写入到TH1和TL1当中。
比如说初值设置为10,那当这个定时器进行计数的时候,计数到溢出了,就是65536的时候,溢出的时候,那你计出了多少次?它不是65536,因为它有初值,它要减掉10才是计数次数。所以,我们知道了这个计数的次数,再换算成的时间,因为我们定时器计数一次是一微秒,是外部的晶振12兆的情况下一微秒,所以我们计数了多少次就是多少微秒,时间就确定了。
第三步,对EA赋值
第三步,如果使用中断的话,就要对对应的中断使能位进行设置,尤其是对这个EA它这个总中断进行设置为1,也就开启这个中断,并且把定时器的中断使能位也要开启。
第四步,对TR0和TR1置位
第四步,就是使能定时器的工作,我们前面介绍了TR0和TR1,这是用来开启这个定时器的。如果说你使用定时器0或者定时1的话,你不把这两个位进行设置的话,你的定时器是处于关闭的,如果要让它开启,就要设置为1。 就是当GATE位等于0的情况下,只能通过这个TR0或TR1进行软件设置开启,如果是等于1的情况下,还需要通过外部中断的引脚为高电平才能开启定时器。
GATE=1的这种情况我们很少使用,所以这一部分我们不用担心,我们直接将GATE设置为0就可以了。这一部分是在前面TMOD来进行设置,设置了TR0和TR1置位的话就启动了定时计数器的定时或计数的功能。
这是定时器的配置步骤。
如何计算定时时间
那如何计算定时时间?。
这里我们就以外部晶振为12兆的这个时钟频率来进行讲解,至于如果使用11.0592兆的话,它的计算就相对来说麻烦一点,你会了12兆,那你自己在计算的时候用11.0592兆去换算也是一样的。到了我们后面,我们会使用一种小工具来省略我们手动计算的过程,那我们先来手动计算一下,教大家如何使用这个12兆来进行计算。
我们外部晶振使用12兆,那机器周期就是等于1微秒,如果我要让这个定时器定时1ms。那这个初值怎么去计算?要用1ms除以1微秒看一下有多少个计数次数(一毫秒就是一千个微秒),也就等于一千次,那就65536(2的16次方:65536)减掉1000等于64536。 所以我们把这个64536这个初值写入到TH1或者TL1当中就可以定时一毫秒,因为从这个值开始计数,你计数到溢出就是1000次,1000次就是1ms,因为一次等于一微秒。
那64536怎么写到这个TL或者TH当中? 我们要将它转换成16进制。 16进制它就有高八位和低八位,那我们把高八位写到TH,低八位写到TL。比如我们这里的64536,转换成16进制就是FC18。 这个时候如果你使用定时器0的话就TH0,如果使用定时器1的话就TH1,它是等于0XFC的,然后TL0或者TL1的话,它是等于0X18。 所以,可以将我们对应的FC18写入到TH和TL当中,就可以进行初值的设置了,这就是定时1ms的计算。
那至于其他的时间,大家也可以按照这种方法去计算的。
如果要500ms闪烁一次,那对应我们刚才的1ms,它的值是非常大的,非常大的初值可能已经超过了我们16位。其实,在对于时间比较大的定时的时候,是不建议直接用这种方式去定时。
比较小的数值,如果我们就定时1ms或者10ms,我们可以简单计算出来的值。然后在中断函数里面,因为进入中断一次,代表我就定时到了10ms或者1ms。 那我就用记录变量进行加一,增加一次就是10ms或者1ms,再增加一次就是20ms或者2ms,以此类推,所以我只要判断这个值它等于几就代表用这个值乘以10ms或者乘以1ms。如果我是定时的1ms,那500次我肯定是计数到了500。那我判断这个值如果等于500,代表我们定时了500ms,所以我就可以控制灯进行状态的切换。 那我们来给大家演示一下工具。
我们通常使用的是方式11。
对于定时计数器的话,我们使用方式1,对于波特率的话,我们使用方式2,波特率也有计算工具,后续我们会演示。
然后晶振频率的话,假设我们是12兆,刚才我们演示的是12兆,然后定时1ms,点击确定FC18跟刚才是一样的。
配置代码示例
接下来我们举个例子,以定时器0为例,来介绍我们这个定时器的是如何来配置的。
首先我们来配置TMOD来确定我们定时器的工作模式和工作方式。
那我们来看一下,这里使用的是0x01,我们可以看到高四位它是0,说明我们现在使用的是低4位,它低四位是用于定时器0的管控,定时机1是高四位来控制的。
那定时器0低4位是0001,对应的寄存器的值是0001
首先看一下GATE设置为0,说明定时器的启动是通过TR0来控制的,然后这个C/T设置为0,说明它的工作与T模式,也就是定时器模式,然后,M1和M0=01,01的话是工作方式1,也就是16位定时计数器。 所以,我们通过这种设置可以知道我们定时器它处于定时器0,并且是16位,方式1,而且GATE是0,可以直接通过TR0来进行启动。
这是第一步TMOD的设置。
然后我们可以看到这里TMOD这里用了或“|”运算,它这个“|”等于就相当于TMOD,等于TMOD或上0X01,那为什么这里要用或运算?这是方便于我们对这个TMOD的值设置的时候不干扰其他的位。
然后定时器的初值的设置,这个初值的设置我们刚才已经说了,比如说我们外部晶振12兆,那定时1ms就是FC18,所以把FC18复赋值给我们TH和TL。
第三步,如果使用中断的话,我们要将ET0打开,ET0是负责我们这个定时器0的中断允许位,所以,你要把中断允许位给使能,也就是给ET0赋值1,如果你使用定时T1的话,也就是ET1。
开启了这个的话,我们还要把总中断打开。 总中断打开了之后,我们中断全部已经打开了。
接下来就让定时器工作,定时器开启的话,我们是通过TR0或者TR1来进行控制的。
所以,我们把TR0=1,这就代表整个定时器0全部配置了.
我们配置完之后,说明我们定时器0是处于方式1,并且定时时间是1ms,开启了中断,当1ms到达的时候,它会进入中断。 定时器1的话,那就是方法一样的,把低4位换成高4位就可以了,比如说TMOD|=0x10,然后TH0换成TH1,TL0换成TL1。ET0换成ET1,TR0换成TR1。
下一节我们来讲讲定时器和中断系统是如果连起来工作的!