【手把手教你学会51单片机】 定时器

注:本文章转载自《手把手教你学习51单片机》!因转载需要原文链接,故无法选择转载!
如若侵权,请联系我进行删除!上传至网络博客目的为了记录自己学习的过程的同时,同时能够帮助其他一同学习的小伙伴!

定时器

定时器的学习

定时器是单片机系统的一个重点,但并不是难点,大家一定要完全理解并且熟练掌握定时器的应用

定时器的初步认识

时钟周期: 时钟周期T是时序中最小的单位时间,具体计算的方法就是1/钟源频率,我们KST-51单片机开发板上用的晶振是11.0592M,那么对于我们这个单片机系统来说,时钟周期 = 1/11059200秒。
机器周期: 我们的单片机完成一个操作的最短时间。机器周期主要针对汇编语言而言,在汇编语言下程序的每一条语句执行所使用的时间都是机器周期的整数倍,而且语句占用的时间是可以计算出来的,而C语言一条语句的时间是不确定的,收到诸多因素影响。51单片机系列,在其标准架构下一个机器周期就是12个时钟周期,也就是12/11059200秒。现在有不少增强型的51单片机,其速度都比较快,有的1个机器周期等于4个时钟周期,有的1个机器周期就等于1个时钟周期,也就是说大体上其速度可以达到标准51架构的3倍或者12倍。因为我们是讲标准51单片机,所以我们后面如若遇到这个概念,全部是指12个时钟周期。
这两个概念了解即可!下面我们来介绍重头戏:定时器和计数器。定时器和计数器是单片机内部的同一个模块,通过配置SFR(特殊功能寄存器)可以实现两种不同的功能,我们大多数情况下使用定时器功能,因此我们的课程也是主要讲解定时器功能,计数器的功能大家自己了解一下即可。

顾名思义,定时器就是用来进行定时的。

定时器内部有一个寄存器,我们让它开始计数后,这个寄存器的值每经过一个机器周期就会自动加1,因此,我们可以把机器周期理解成定时器的计数周期。就像我们的钟表,每经过1秒,数字自动加1,而这个定时器就是每过一个机器周期的时间,也就是12/11059200秒,数字自动加1。还有一个特别注意的地方,就是钟表是加到60后,秒就自动成了0了,这种情况下在单片机或计算机里我们称为溢出。
那么定时器加到多少才会溢出呢?后面会讲到定时器有多种工作模式,分别使用不同的位宽(指使用多少个二进制位),假如是16位的计数器,也就是2个字节,最大值就是65535,那么加到65535之后,再加1就会溢出,如果有其他位的话,道理是一样的,对于51单片机来说,溢出后这个值就会直接变成0.从某一个初始值开始,经过确定的时间后溢出,这个过程就是定时的含义。

定时器的寄存器

标准的51单片机内部有T0和T1这两个定时器,T就是Timer的缩写,现在很多51系列单片机还会增加额外的定时器,在这里我们先讲解定时器0和1.前面提到过,对于单片机的每一个功能模块,都是由它的SFR,也就是特殊功能寄存器来控制。
与定时器有关的特殊功能寄存器有以下几个,大家不需要去记忆这些寄存器的名字和作用,你只要大概知道就行,用的时候,随时可以查阅手册,找到每个寄存器的名字和每个寄存器的作用。
表5-1的寄存器就是存储定时器的计数值的。TH0/TL0用于T0,TH1和TL1用于T1。
20230115_1
表5-2是定时器控制寄存器的位分配,表5-3则是对应每一位的具体含义的描述
20230115_2
20230115_3
大家注意表5-3中的描述,只要写到硬件置1或者清0的就是指一旦符合条件,单片机将自动完成的动作,只要写软件置1或清0的,是指我们必须用程序去完成这个动作,后续遇到此类描述就不再另作说明了。
对于TCON这个SFR,其中TF1,TR1,TF0,TR0这四位需要我们理解清,它们分别对应于T1和T0,我们以定时器1为例讲解,那么定时器0同理。先看TR1,当我们程序中写TR1 = 1以后,定时器就会每经过一个机器周期自动加一,当我们程序中写TR1 = O之后,定时器会停止加1,其值会保持不变。TF1,这个是一个标志位,它的作用是告诉我们定时器溢出了。比如我们的定时器设置成16位的模式,那么每经过一个机器周期,TL1加1一次,当TL1加到255之后,再加1,TL1变成0,TH1就会加1一次,如此一直加到TH1和TL1都是255(即TH1和TL1组成的16位整型数为65535)以后,再加1一次,就会溢出了,TH1和TL1同时变为0,只要一溢出,TF1马上自动变成1,告诉我们定时器溢出了,仅仅是提供给我们一个信号,让我们知道定时器溢出了,它不会对定时器是否继续运行产生任何影响。
本节开头我们就提到了定时器有多种工作模式,工作模式的选择由TMOD来控制,TMOD的位分配和描述见表5-4到5-6所示,TMOD的位功能如表5-5所示。
20230115_4
20230115_6
可能你已经注意到了,表5-2的TCON最后标注了"可位寻址",而表5-4的TMOD标注的是"不可位寻址"。意思就是说:比如TCON有一个位叫TR1,我们可以在程序中直接进行TR1 = 1这样的操作。但是对于TMOD里的位比如说(T1)M1 = 1这样的操作就是错误的。我们要位操作就必须一次操作这整个字节,也即是必须一次性对TMOD的所有位操作,不能对其中某一位单独操作。 那么我们能不能只修改其中的一位而不影响其他位的值呢?当然可以,在后续课程中我们就会学习到方法的,所以现在先不关心它!
表5-6列出的就是定时器的4中工作模式,其中模式0是为了兼容老的8048系列单片机而设计的,现在的51几乎不会用到这种模式,而模式3根据我的应用经验,它的功能用模式2完全可以取代,所以基本也是不会用的,那么我们重点来学习模式1和模式2.
模式1: 是THn和TLn组成的一个16位定时器,计数范围是0~65535,溢出后只要不对THn和TLn重新赋值,则从0开始计数。模式2: 是8位自动重载模式,只有TLn做加1计数,计数范围为0~255,THn的值并不发生变化,而是保持原值,TLn溢出后,TFn就直接置1了,并且THn原先的值直接赋给TLn,然后TLn从新赋值的这个数字开始计数。这个功能可以用来产生串口的通信波特率,我们将串口时会用到,本章节我们重点学习模式1.为了加深大家理解定时器的原理,我们来看一下他的模式1的电路示意图5-2.
20230115_7
我带领大家来一起分析一遍这个示意图,日后如果再遇到类似的图,大家就可以自己研究了。OSC框表示时钟频率,因为1个机器周期等于12个时钟周期,所以d就等于12。下边的GATE右边的那个门是一个非门电路,再右侧是一个或门,再往右是一个与门电路。
图上可以看出来,下边部分电路是控制了上边部分,那么我们先来看下边是如何控制的,我们以定时器0为例。

  1. TR0和下边的或门电路结果要进行与运算,TR0如果是0的话,与运算结束肯定是0,所以如果要让定时器工作,那么TR0就必须置1.
  2. 这里的与门结果要想得到1,那么前面的或门出来的结果必须也得是1才可以。再GATE位为1的情况下,经过一个非门变成0,或门电路结果要想是1的话,那INT0即P3.2引脚必须是1的情况下,这个时候定时器才会工作,而INT0引脚是0的话,定时器不工作,这就是GATE位的作用。
  3. 当GATE位为0的时候,经过一个非门会变成1,那么不管INT0引脚是什么电平,经过或门电路后都肯定是1,定时器就会工作。
  4. 要想让定时器工作,就是自动加1,从图上看有两种方式,第一种是那个开关打到上面的箭头,就是C/T=0的时候,一个机器周期TL就会加1一次,当开关达到下边的箭头,即C/T=1的时候,T0引脚即P3.4来一个脉冲,TL就加1一次,这就是计数器功能。
定时器的应用

了解了定时器相关的寄存器,那么我们下面就来做一个定时器的程序,巩固一下我们学到的内容。我们这节课的程序先使用定时器0,在使用定时器的时候,需要以下几个步骤:

第一步:设置特殊功能寄存器TMOD,配置好工作模式。
第二步:设置计数寄存器TH0和TL0的初值。
第三步:设置TCON,通过TR0置1来让定时器开始计数。
第四步:判断TCON寄存器的TF0位,检测定时器溢出情况。

写程序之前,我们要先学会计算如何用定时器定时时间。我们的晶振是11.0592M,时钟周期就是1/11059200,机器周期就是12/11059200,加入要定时20ms,就是0.02秒,要经过x个机器周期得到0.02秒,我们来计算一下:

x*12/11059200 = 0.02
得到 x = 18432

16位定时器的溢出值是65536(因为65535再加1才溢出)。于是我们就可以这样操作,先给TH0和TL0一个初始值,让它们经过18435个机器周期后刚好达到65536,也就是溢出,溢出后可以通过检测TF0的值得知,就刚好是0.02秒。那么

初始y = 65536-18432 = 47014

转成16进制就是0xB800,也就是TH0 = 0xB8,TL0 = 0x00
这样0.02秒的定时我们就做出来了,细心的同学会发现,如果初值直接给一个0x0000,一直到65536溢出,定时器定时值最大也就71ms左右,那么我们想要定时更长时间该怎么办?用你小学学过的逻辑,倍数关系就可以解决此问题。
好了,我们用程序来实现这个功能:

#include <reg52.h>

sbit LED = P0^0;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

void main()
{
    unsigned char cnt = 0;  //定义一个计数变量,记录T0溢出次数

    ENLED = 0//使能U3,选择独立LED
    ADDR3 = 1;
    ADDR2 = 1;
    ADDR1 = 1;
    ADDR0 = 0;
    TMOD = 0X01;    //设置T0为模式1
    TH0 = 0xB8;     //为T0赋初值为0xB800
    TL0 = 0X00;
    TR0 = 1;    //  启动T0

    while(1)
    {
        if(TF0 == 1)    //判断T0是否溢出
        {
            TF = 0;     //T0溢出后,清零中断位
            TH0 = 0XB8; //并重新赋值
            TL0 = 0x00;
            cnt++;      //计数值自加1
            if(cnt>=50) //判断T0溢出是否到达50次
            {
                cnt = 0;    //达到50次之后计数值清零
                LED = ~LED; //LED取反
            }
        }

    }
}

程序中都写了注释,结合前面几章学习的内容,自己分析一下,不难理解。本程序实现的结果是开发板上最右边的小灯点亮一秒,熄灭一秒,也就是以0.5Hz的频率进行闪烁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值