08-定时器的基本原理与应用

定时器的基本原理与应用

1、什么是定时/计数器?
在没有钟表的时候,定时的方式通过有一注香的时间,或者一桶水的时间。前者烧香不断减少是减法,后者滴水不断增加是加法。
定时/计数器,是一种能够对内部时钟信号或外部输入信号进行计数,当计数值达到设定要求时,向CPU提出中断处理请求,从而实现定时或者计数功能的外设。定时/计数器的最基本工作原理是进行计数。作为定时器时,计数信号的来源选择周期性的内部时钟脉冲;用作计数器时,计数信号的来源选择非周期性的外部输入信号。
不管是定时器还是计数器,本质上都是计数器
定时器的工作原理与计数初值计算,可以参考以下例子:

112649npg4n9cd4f119u4r.png.thumb

2、51单片机的定时/计数器
51单片机有两个定时/计数器T0和T1,为16位加法计数器,由低8位TLx和高8位THx两个寄存器组成,最大计数值为65535个计数脉冲
该加1计数器的计数脉冲来源有2个:
<1> 系统时钟振荡器输出的12分频
<2> T0或T1引脚输入的外部脉冲信号
每接收到一个计数脉冲,计数器就会加1,当计数值累计至全为1时(8位255,13位8191,16位65535),再输入一个计数脉冲,计数器便会溢出回零,并且计数器的溢出是TCON寄存器的TF0或TF1位置1,同时向内核提出中断请求。如果定时/计数器工作于定时模式,则表示间隔定时时间到,如果工作与计数模式,则表示计数值已满。
假设单片机的外部晶振为12MHz,那么,经过12分频后输入计数器的计数脉冲为1MHz,即每个脉冲的周期为1us。因此定时器T0的16位工作模式最大的定时时间为65535us,65.5ms。如果要定时10ms的话,计数器就不能够从0开始计数了,必须给它一个计数初值。怎么计算这个初值呢?
要定时10ms,则相当于计数10000个脉冲后计数器的值就到达65535了,那么开始计数的这个地方就是计数初值。
65535 - 10000 = 55535 = 0xd8ef
把这个计算得到的初值写入TH0和TL0寄存器即可:
TH0 = 0xd8; 或者 TH0 = (65535 - 10000) / 256;
TL0 = 0xef; 或者   TL0 = (65535 - 10000) % 256;

3、定时/计数器相关寄存器
与定时/计数器相关的寄存器除了计数初值寄存器THxTLx之外,就是TMOD寄存器和TCON寄存器,务必掌握。
<1> TMOD模式控制寄存器,不能进行位寻址,只能字节操作。

115216kelaqaauajq4zyll.png.thumb

<2> TCON中断标志寄存器,参考手册: IAP15F2K61S2_PDF_数据手册_Datasheet_规格书(semiee.com)

4、定时/计数器的编程思想
在定时/计数器的程序设计中,通常有两个函数:初始化函数中断服务函数
在初始化函数中,一般需要进行以下几个配置:
<1> 配置工作模式,即对TMOD寄存器编程。
<2> 计算技术初值,即对THx和TLx寄存器进行赋值。
<3> 使能定时/计数器中断,即ET0或ET1置1。
<4> 打开总中断,即EA =1。
<5> 启动定时器,即TR0或TR1置1。
在中断服务函数中,一般需要进行以下的编程:
<1> 如果不是自动重装模式,需要对THx和TLx重新赋值。
<2> 进行间隔定时到达的逻辑处理(越少越好)。
其程序框架和代码编写基本上差不多:

120001ebmt1mbf10pmhrlz.png.thumb

20231005164730

本次单片机为12HMZ晶振的单片机

为了大家的需求,以下以11.0592MHZ的单片机为例,12HMZ同理

1Hz:1秒内电流往返一次

晶振 11.0592MHz =11059200Hz

时钟周期  1/11059200 s(晶振的倒数)

机器周期是 12/11059200 s (标准框架下51单片机一个机器是12个时钟周期)

因为定时器是每个机器周期加1所以定时时间为 N*时钟机器周期时间=所定时间T

所以要经过的机器周期是N*(12/11059200 s)=T

​ N=t/(12/11059200) t为定时时间单位为s

初值等于 65536-N(因为16位的定时器65535再加1才溢出)

例如:

需要定时50ms=0.05s

0.05/(12/11059200)=0.05(11059200/12)=0.05921600=46080

​ 快速计算只需更改这个 ↑

初值y=65536-46080=19456=0X4C00

1.晶振12M

​ 12MHz除12为1MHz,也就是说一秒=1000000次机器周期。10ms=10000次 机器周期。

2.晶振11.0592M

​ 11.0592MHz除12为921600Hz,就是一秒921600次机器周期,10ms=9216次机器周期。

以上单位训练代码如下:

copy
#include <REGX52.H> sbit L1 = P0^0; sbit L8 = P0^7; void _74HC138(unsigned char n) { switch(n) { case 4: P2 = (P2& 0x1f) | 0x80; break; case 5: P2 = (P2& 0x1f) | 0xa0; break; case 6: P2 = (P2& 0x1f) | 0xc0; break; case 7: P2 = (P2& 0x1f) | 0xe0; break; } } void System_Init(void) { _74HC138(5); P0 = 0x00; _74HC138(4); P0 = 0xFF; } void InitTimer0() { TMOD = 0x01; //1Hz:1秒内电流往返一次 //晶振 11.0592MHz =11059200Hz // 时钟周期  1/11059200 s(晶振的倒数) //机器周期是 12/11059200 s (标准框架下51单片机一个机器是12个时钟周期) //因为定时器是每个机器周期加1所以定时时间为 N*时钟机器周期时间=所定时间T //所以要经过的机器周期是N*(12/11059200 s)=T // N=t/(12/11059200) //初值等于 65536-N(因为16位的定时器65535再加1才溢出) // THx = (65535 - N) / 256; // TLx = (65535 - N) % 256; 11.0592MHZ = 11059200HZ // 机器周期是N*(12/11059200) = T // N=t/(12/11059200) t 单位s // 50000us = 50ms TH0 = (65535 - 50000) / 256; TL0 = (65535 - 50000) % 256; // 打开定时中断 ET0 = 1; // 总中断使能 EA = 1; // 打开定时器T0 TR0 = 1; } void main(void) { InitTimer0(); _74HC138(4); System_Init(); while(1) { } } unsigned char count =0; void Timer0_Rountine(void) interrupt 1 { TH0 = (65535 - 50000) / 256; TL0 = (65535 - 50000) % 256; count++; // 0.5s 0.5*500 = 250s if(count % 10 == 0) { L1 = ~L1; } if(count == 100) { L8 = ~L8; count=0; } }
posted @   夏日清凉~  阅读(317)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起