历史上的今天

今天是:2025年08月20日(星期三)

正在发生

2019年08月20日 | 51单片机之定时器/计数器应用实例(方式0、1、2、3)

发布者:xi24 来源: eefocus关键字:51单片机  定时器  计数器 手机看文章 扫描二维码
随时随地手机看文章

硬件:STC89C52RC

开发工具:Keil uVision4

     对于刚接触单片机的同学来说可能会对定时器/计数器的应用很蒙圈,特别是初值的计算和各种定时方式的选择。下面希望能给你带来一个清晰的思路。


定时器:一般用于软件计时,给定时器设置一个时间,时间到了系统停止当前的工作跳转到事先定义好的定时器中断函数里,函数里可以做一些周期性的事情。


计数器:一般用于检测外来脉冲信号,给计数器设置一个次数,次数到了系统停止当前的工作跳转到事先定义好的计数器中断函数里,函数里做相应的事情。


先说一下相关的寄存器,也可以直接跳过,看后面的实例分析。


配置定时器或者计数器就是对相应的寄存器进行赋值,下面是相关的寄存器描述: 


第一部分寄存器:


对照着上面这一字节的每一位,进一步解析:


位(符号)


功能


TMOD.7 (GATE)


置1时,只有在脚为高、TR1=1时才可打开定时器/计数器1


置0时,TR1=1即可打开定时器/计数器1


TMOD.3 (GATE)


置1时,只有在脚为高、TR1=1时才可打开定时器/计数器0


置0时,TR1=1即可打开定时器/计数器0。


TMOD.6  ( /)


置1时,用作计数器1(从T1/P3.5脚输入)


置0时,用作定时器1


TMOD.2  (/ )


置1时,用作计数器0(从T0/P3.4脚输入)


置0时,用作定时器0


TMOD.5/TMOD.4  (M1、M0)


定时器/计数器1 选择工作方式


方式0:M1=0,M0=0 ,13位定时器/计数器


方式1:M1=0,M0=1 ,16位定时器/计数器


方式2:M1=1,M0=0 ,8位自动重载定时器


方式3:M1=1,M0=1 ,定时器/计数器1 此时无效


TMOD.1/TMOD.0  (M1、M0)


定时器/计数器0 选择工作方式


方式0:M1=0,M0=0 ,13位定时器/计数器


方式1:M1=0,M0=1 ,16位定时器/计数器


方式2:M1=1,M0=0 ,8位自动重载定时器


方式3:M1=0,M0=0 ,双8位定时器/计数器


/*1*/  TMOD|=0x00;  //选择定时器0,工作方式为0,

 

/*2*/  TMOD|=0x30;  //选择定时器1,工作方式为1

 

/*3*/  TMOD|=0x40;  //选择计数器1,工作方式为0

用或运算是为了在给相应位赋值时不会影响无关位。可以试着解读TMOD=0xDA


第二部分寄存器:


主要看T开头的,I开头是外部中断,先不管。


位(符号)


功能


TCON.7 (TF1)


定时器/计数器1溢出标志位。当 T1 被允许计数后T1从初值开始加1计数,最高位产生溢出时,置“1 ”TF1 ,并向 CPU请求中断,当CPU响应时,由硬件清“0 ”TF1 ,TF1也可以由程序查询或清“0 ”。


TCON.5 (TR1)


定时器 T1 的运行控制位。该位由软件置位和清零。当 GATE(TMOD.7)=0,TR1=1 时就允许T1开始计数,TR1=0 时禁止 T1 计数。当 GATE(TMOD.7)=1,TR1=1 且 INT1 输入高电平时,才允许 T1 计数。


TCON.4 (TF0)


定时器/计数器 0 溢出标志位。当T0被允许计数后T0 从初值开始加 1 计数,最高位产生溢出时,置“1”TF0,并向CPU请求中断,当 CPU 响应时,由硬件清“0”TF0,TF0也可以由程序查询或清“0”。


 


TCON.3 (TR0)


定时器 T0 的运行控制位。该位由软件置位和清零。当 GATE(TMOD.3)=0,TR0=1 时就允许T0开始计数,TR1=0 时禁止 T0 计数。当 GATE(TMOD.3)=1,TR0=1 且 INT0 输入高电平时,才允许 T0 计数


除了TCON、TMOD还有TL0、TH0和TL1、TH1,它们分别是定时器0的Timer寄存器和定时器1的Timer寄存器。这个参数没有单位,不是毫秒或是其他,所以设置定时器的时间要通过一定的计算得来,也就是后面要说的重点部分。



定时器的应用:

编写单片机定时器程序的步骤:


对TMOD赋值,以确定T0和T1的工作方式。

计算初值,并将初值写入TH0,TL0或TH1,TL1。

中断方式时,对IE赋值,开放中断。

使TR0或TR1置位,启动定时器/计数器定时或计数。

下面以定时器0为例,阐述不同的方式的编程过程。


方式0:

#include

 

#define uchar unsigned char

#define uint  unsigned int

 

sbit led1=P1^0;

uchar num;

 

void TIM0init(void)

{

     TMOD=0x00;            //设置定时器0为工作方式0

     TH0=(8192-5000)/32;   //装入初值,怎么计算,下面分析

     TL0=(8192-5000)%32;    

     EA=1;    //开总中断

     ET0=1;   //开定时器中断

     TR0=1;   //启动定时器0

}

/*

interrupt 0  指明是外部中断0;

interrupt 1  指明是定时器中断0; 

interrupt 2  指明是外部中断1;

interrupt 3  指明是定时器中断1;

interrupt 4  指明是串行口中断;

函数名字可以随便起,但定时器0的中断号是固定为1的

*/

void T0_time()  interrupt 1      

{

     TH0=(8192-5000)/32; //重装初值,如果不重装,中断只触发一次

     TL0=(8192-5000)%32;

     num++;

}

  

void main()

{

    TIM0init(); 

    while(1)

    {

  if(num==200)     //如果到了200,说明一秒时间到

          {

             num=0;

     led1=~led1;   //让发光管状态取反

  }

    }

}

假设单片机用的晶振是12MHz,上面的中断函数每过5ms会被调用一次,也就是发光管每一秒状态取反一次。那么怎么计算初值以确定TL0和TH0的值呢?


定时器方式0是指13位定时器,=8192;也就是说,当设置好初值后,系统会在这个初值的隔一个机器周期就会自增1,当累加到8192的时候溢出,然后触发中断。所以(8192-初值)*机器周期=定时器产生一次中断的时间。


如果我们要设定的定时器产生一次中断的时间为5ms,那么:


                                                 机器周期=12*(1/12MHz)=1μs


                                                 初值=(8192-5ms/1μs)=3192


13位定时器中,TH0整个 8 位全用,TL0只用低 5 位参与分频。


TH0


bit7


bit6


bit5


bit4


bit3


bit2


bit1


bit0


TL0


bit7


bit6


bit5


bit4


bit3


bit2


bit1


bit0


因:3192=“110001111000”


所以TH0=“1100011”,TL0=“11000”


即TH0=(8192-5000)/32,TL0=(8192-5000)%32


如果用的是11.0592MHz的晶振,机器周期就不是整数了,12*(1/11059200)≈1.0851μs.


关于机器周期:




方式0跟方式1差不多的,不同的是方式1中TH0、TL0所有位全用。两个字节,=65536.


方式2:

   在定时器的方式0和方式1中,当计数溢出后,计数器变为0,因此在循环定时或循环计数时必须用软件反复设置计数初值,这必然会影响到定时的精度,同时也给程序设计带来很多麻烦。


     方式2被称为8位初值自动重装的8位定时器/计数器,TL(0/1)从初值开始计数,当溢出时,在溢出标志TF(0/1)置1的同时,自动将TH(0/1)中的常数重新装入TL(0/1)中,使TL(0/1)从初值开始重新计数,这样避免了认为软件重新装初值所带来的时间误差,从而提高了定时的精度。


#include

 

#define uchar unsigned char

#define uint  unsigned int

 

sbit led1=P1^0;

uint num;

 

void TIM0init(void)

{

     TMOD=0x02;    //设置定时器0为工作方式2

     TH0=6;   //装入初值

     TL0=6;    

     EA=1;    //开总中断

     ET0=1;   //开定时器中断

     TR0=1;   //启动定时器0

}

 

void T0_time()  interrupt 1      

{

     //相比上面的方式0,这里不需要认为加入重装初值的代码

     num++;

}

  

void main()

{

    TIM0init(); 

    while(1)

    {

  if(num==4000)     //如果到了4000,说明1秒时间到

          {

             num=0;

     led1=~led1;   //让发光管状态取反

  }

    }

}

    这个也是基于12MHz的振荡频率,TL0跟TL1必然是相同的,计算初值的方法跟上面一样。方式2为8位定时器/计数器,最多能装载=256个,相对方式0的13位和方式1的16位的少。方式2经历256个机器周期该计数器就会溢出。


    还有一个值得注意的是num变量的类型变了,因为4000已经超出了uchar的方位,所以改为uint。


方式3:

     当选择方式3时,定时器T0就会被分成两个独立的计数器或者定时器。此时,TL0为8位计数器,计数溢出好置位TF0,并向CPU申请中断,之后需要软件重装初值; TH0也被固定为8位计数器,不过TL0已经占用了TF0和TR0,因此TH0将占T1的中断请求标志TF1和定时器启动控制为TR1。


     为了防止中断冲突,定时器T0在方式3时,T1不能产生中断,但可以正常工作在方式0、1、2下。通常这种情况下T1将用作串行口的波特率发生器


下面的例子是利用定时器方式3,TL0计数器对应的8位定时器实现第一个发光管以1s亮灭闪烁,用TH0计数器对应的8位定时器实现第二个发光管以0.5s亮灭闪烁。


#include

 

#define uchar unsigned char

#define uint  unsigned int

 

sbit led1=P1^0;

sbit led2=P1^1;

uint num1,num2;

 

void TIMEinit(void)

{

     TMOD=0x03;  //设置定时器0为工作方式3

     TH0=6;      //装初值

     TL0=6;

     EA=1; //开总中断

     ET0=1;      //开定时器0中断

     ET1=1;      //开定时器1中断

     TR0=1; //启动定时器0

     TR1=1; //启动定时器0的高8位计数器

}

 

void TL0_time()  interrupt 1

{

     TL0=6;  //重装初值

     num1++;

}

 

void TH0_time()  interrupt 3  //占用T1定时器的中断号

{

     TH0=6;  //重装初值

     num2++;

}

  

void main()

{

     TIMEinit();

     while(1)

     {

if(num1>=4000)  //12*(1/12MHz)*(256-6)*4000=1s

         {   

     num1=0;

     led1=~led1;

}

         if(num2>=2000)  //12*(1/12MHz)*(256-6)*2000=0.5s

         {

     num2=0;

     led2=~led2 ;

         } 

    }

}

这里的num1>=4000而不是num1==4000,是为了稳妥起见,万一定时器计数超过了4000,而主循环还没来得及判断,则会错过4000.那led1就不能实现取反了。


仅供参考,错误之处以及不足之处还望多多指教。

关键字:51单片机  定时器  计数器 引用地址:51单片机之定时器/计数器应用实例(方式0、1、2、3)

上一篇:51单片机之外部中断应用实例(电平触发、边沿触发)
下一篇:51单片机 小车 L298N pwm调速 串口控制 按键控制

推荐阅读

来源:内容来自「莫尼塔研究」,谢谢。 被动元件最初是台湾电子行业对某些电子元器件的叫法,区别于主动元件。而国内此前则称无源器件和有源器件。被动元件内部不需要电源驱动,其本身不消耗电能,只需输入信号就可以做出放大、震荡、计算等响应,无需外部激励单元。各种电子产品中含有被动元件,是电子电路产业的基石。图表1电子元件的分类 资料来源:莫...
业界有消息称新 iPhone 将首次支持 Apple Pencil 触控笔,以此增添卖点,刺激销量。如果用户想要体验 Apple Pencil,需要额外购买。 Apple Pencil 并非首次被预测用于 iPhone 设备事实上,iPhone 将搭载 Apple Pencil 并非空穴来风。早在 2017 年 12 月,就有相关报道曝光了苹果公司的新专利。从专利配图可以看出,苹果移动设备 iPhone...
Trustzone可以追溯到十多年前,ARMv7公布的时候就有了,可惜一直没有什么实际应用。直到近几年开始,才真正的有厂商开始把这个方案大规模用于芯片里。目前看到的主要有四个应用领域:第一是无人机芯片,大疆已经走在了最前面,第二名连影子都没看见。无人机上几大应用,图像传输,图像处理,识别,飞控,存储,每一块都有安全的诉求。利用Trustzone可以做...
上一篇文章,我们从安全功能的层面向大家介绍了仙工智能的新品控制器——SRC 3000FS 安全型 AMR 控制器,那么关于 SRC-3000FS 还有什么强大的地方呢?一款为安全而生的控制器人机协作密切度极高的机器人,更需要保证其安全性。SRC-3000FS 在设计环节就将安全方法和措施导入产品,并经过严格的验证测试,千锤百炼锻造出的高品质产品。目前 SRC-3...

史海拾趣

小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved