• 硬件: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寄存器。这个参数没有单位,不是毫秒或是其他,所以设置定时器的时间要通过一定的计算得来,也就是后面要说的重点部分。

定时器的应用:

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

  1. 对TMOD赋值,以确定T0和T1的工作方式。
  2. 计算初值,并将初值写入TH0,TL0或TH1,TL1。
  3. 中断方式时,对IE赋值,开放中断。
  4. 使TR0或TR1置位,启动定时器/计数器定时或计数。

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


方式0:

#include<reg52.h>#define uchar unsigned char
#define uint  unsigned intsbit led1=P1^0;
uchar num;void TIM0init(void)
{TMOD=0x00;            //设置定时器0为工作方式0TH0=(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<reg52.h>#define uchar unsigned char
#define uint  unsigned intsbit led1=P1^0;
uint num;void TIM0init(void)
{TMOD=0x02;    //设置定时器0为工作方式2TH0=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<reg52.h>#define uchar unsigned char
#define uint  unsigned intsbit 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;   //启动定时器0TR1=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单片机之定时器/计数器应用实例(方式0、1、2、3)相关推荐

  1. 51单片机之定时器\计数器的工作原理

    51单片机之定时器\计数器的工作原理 定时器/计数器的结构: 何时处于计数器方式?何时处于定时器方式? TCON和TMOD特殊功能寄存器: 定时器/计数器工作方式: 方式0:13位的定时器/计数器 方 ...

  2. 【51单片机】定时器/计数器的工作原理和结构(一)

    [51单片机]定时器/计数器的工作原理和结构 回看下单片机中断系统内部结构: 分析内部中断触发: 先看图 TCON:和外部中断相比少了IT位设置触发方式 :1跳沿触发 0电平触发:此处我的理解是:就好 ...

  3. 51单片机笔记:定时器/计数器

    单片机笔记 定时器/计数器 定时器/计数器的结构 AT89S51内部两个16位定时器/计数器:T0(P3.4),T1(P3.5),定时器/计数器T0由特殊寄存器TH0,TL0构成,T1由特殊功能寄存器 ...

  4. 六、51单片机之定时器/计数器_理论

    1.什么是定时器.计数器 定时器就是单片机设定一个时间间隔,时间间隔到后通知单片机.例如设置100ms的定时器,100ms后定时器通知单片机时间到了. (1)定时器是单片机的一种内部外设.(以前的单片 ...

  5. 51单片机内部定时器/计数器实验

    51单片机内部定时器/计数器实验 一.实验内容 使用MCS-51内部定时/计数器,定时1秒钟,CPU运用定时中断方式,实现每1秒钟输出状态发生一次反转,即发光管每隔1秒钟亮一次(P1.7接LED). ...

  6. 51单片机计算定时器初值

    51单片机计算定时器初值 前言 理论分析 工作方式寄存器 TMOD GATE 门控位 C/T' 计数器模式和定时器模式选择位 M1 M0 工作方式选择位 定时器/计数器控制寄存器 TCON TCON补 ...

  7. 51单片机学习--定时器--中断--串口通信

    51单片机学习–定时器–中断–串口通信 定时器–中断–串口通信 中断分类 定时器中断 外部中断 串口中断 基本概念 对于单片机来讲, 中断是指 CPU 在处理某一事件 A 时, 发生了另一事件 B, ...

  8. (六)51单片机基础——定时器

    定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成. 定时器主要作用: 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作. 替代长时间的Dela ...

  9. 51单片机之定时器篇

    首先,学好单片机必须要搞懂定时器,定时器是单片机重要的组成部分之一,总之,学不好定时器,单片机相当于没学,下面就让我介绍如何学好单片机定时器. 学习单片机首先要明白的: 1,51单片机有两组定时器/计 ...

最新文章

  1. spring_通过Spring Boot了解H2 InMemory数据库
  2. C++ 之虚函数的实现原理
  3. MapReduce输入输出的处理流程及combiner
  4. 粤港澳大湾区菜篮子-农业大健康·林裕豪:从玉谋定功能产业
  5. 两个map中的数据,按照相同键,将所对应的值相加方法
  6. 千年鸿蒙 盼尔来兮是什么意思,鸿蒙是什么意思_鸿蒙的意思和出处_我爱历史网...
  7. 远程服务器 上传公钥,SecureCRT+Ubuntu SSH服务器的远程公钥登陆
  8. SAP License:别在走SAP学习的误区
  9. php 数组指向下一个值,比较数组值并根据自定义值(PHP)在数组中查找下一个值 - php...
  10. java server2008_在windows server 2008下搭建简单的java学习环境
  11. 弱网测试用什么农_弱网测试--使用fiddler进行弱网测试
  12. 机器学习必须要会的:方差、标准差、相对标准偏差、正态分布的概念
  13. 白杨SEO对话老姜:聊聊第三方平台站内SEO,第三方平台的引流的逻辑是什么?
  14. Swiper参数说明(swiper参数配置)
  15. android 远程调试工具,【教程】搭配Android studio,如何实现app远程真机debug...
  16. 使用 hugegraph-studio 插入电影数据并查询
  17. 关于友善之臂出的Nanopi R2S盒子的TTL乱码解决方法
  18. 好消息,个税起征点要提至每月5000了!
  19. 虚拟定位的实现android,王者荣耀战区哪里分最低 王者荣耀低分战区推荐
  20. 一部以中国现实为大背景的英国科幻电影《未来密码46》

热门文章

  1. apache phoenix 安装试用
  2. Windows Server 2008 R2 DHCP服务器安装和配置案例
  3. 关于Window操作系统中对Oracle的性能监控
  4. MySQL学习笔记(十二)—— MySQL的命令集(2)
  5. 格式化输出--对齐及补全
  6. 清华镜像:zookeeper
  7. yapi 事件创建、修改等接口事件监听
  8. 解决win11 WSL下通过systemd无法启动docker的问题:改为dockerd手动启动
  9. k8s 通用的java项目迁移流程
  10. k8s部署postgresql(含postgis插件)