系列目录
预热篇——
- 软件安装与使用
- 编程
入门篇——
- 1、 STC8951最小系统
- 2、 GPIO口的配置与复用
- 3、 中断系统
- 4、 定时器/计数器
- 5、 串口通讯
进阶篇——
- 5、 定时器
- 6、 串口通讯
- 7、 AD/DA
- 8、 IIC协议/SPI协议
外设篇——
- 7、 OLED12864
- 8、 USB_TTL
- 9、 待续… …
文章目录
前言
在嵌入式系统开发中,51单片机因其强大的功能和广泛的应用而备受关注。其中,定时器作为51单片机的重要组件,具有丰富的功能和灵活性。本文将详细介绍51单片机的定时器及其使用方法,帮助读者更好地理解和应用这一强大工具。通过本文,读者将了解51单片机定时器的原理、特点、使用方法和常见问题,从而更好地进行相关项目的开发。
本文以定时器0的使用为例,其余定时器可触类旁通,原理是一样的
一、定时器是什么?
定时器/计数器T0和T1是STC89C51RC和RD+系列单片机中的核心组件,具有16位的计数容量,可以用于计数和定时两种工作模式。
在定时模式下,T0和T1可以用来测量时间间隔,即从系统时钟开始到定时器溢出所经过的时间。通过在TMOD寄存器中设置适当的控制位,可以将T0或T1配置为定时模式。在定时模式下,计数器的输入脉冲是系统时钟,每12个时钟周期或每6个时钟周期计数器会增加1。因此,可以通过计算计数器的计数值来得到经过的时间。
在计数模式下,T0和T1可以用来对外部事件进行计数。当单片机外部引脚上的信号发生变化时,相应的定时器/计数器就会开始计数。计数器的输入脉冲是外部引脚上的信号,每来一个脉冲计数器就会增加1。因此,通过检测计数器的计数值,可以判断外部事件的次数。
需要注意的是,T0和T1的计数容量都是16位的,因此在计数或定时时需要注意溢出问题。为了避免溢出,可以在计数或定时之前对计数器的最大值进行预计算,并在计数或定时过程中进行比较,当计数器达到最大值时及时复位。
1.内部结构
首先还是这个图
从这个系统图中我们可以看到要进行定时器的配置需要通过以下几个寄存器
1)-TCON
我们可以看到他是可位寻址的,也就是说可以单独操作里面的各8位
例如:TR0=1;
等等
各自的功能:
简单来说,TF0,TF1
一般情况下我们不用管,因为有硬件清零;
TR0,TR1
是打开定时器的开关;
IE0,IE1
从刚才的系统结构图可以看出它的重要性,它会发出请求;
IT0,IT1
是中断中的触发方式选择
2)-TMOD
定时器工作模式寄存器 不可以位寻址,所以只能赋值操作
例如,TMOD=0X01;
所以说在这里就考验大家的进制转换基本功啦
下面是它各自对应的功能位:
简单来说,GATE 位
会让我们打开定时器多了一个外部条件 , 初学者很少会用到;
C/T 位
也很少用到,因为我们一般用的是默认的Timer功能;
M1, M0 位
是用来定义工作方式的,这两位组合出四种可能,对应四种工作方式
2、T0的工作模式
在51单片机中,定时器0是一个非常重要的组件,用于产生精确的定时或计数功能。它有多种工作模式,每种模式都有其特定的应用场景和特点。本部分将重点介绍定时器0的四种工作模式。
1)-模式0
模式0是13位定时/计数模式。在此模式下,定时器0的高6位和低7位被分为两个独立的6位计数器,分别由TH0和TL0控制。这种模式主要用于短时间或低精度的定时/计数任务。
2)-模式1
模式1是16位定时/计数模式。在此模式下,定时器0的TH0和TL0作为两个独立的8位寄存器使用,提供16位的定时/计数范围。这种模式适用于需要更高精度和更长时间的任务。
3)-模式2
模式2是8位 自动重装模式。在此模式下,当TH0的内容超过TL0的内容时,定时器0溢出并产生中断。此时,TH0会自动将预装载的值重新加载到TL0,并继续计数。这种模式特别适用于产生固定频率的信号或周期性中断。
4)-模式3
模式3是两个 独立的8位定时/计数模式。在此模式下,定时器0的TH0和TL0独立工作,可以分别设置初值和启动/停止。这种模式提供了更大的灵活性,允许同时进行两个独立的定时/计数任务。
又称:双定时器/计数器模式
综上,初学者可以先了解模式0与模式1,二者类似,不过位数不同;
在51单片机中,定时器0通常用作定时器或计数器。
在定时器模式下,定时器0可以用于产生中断,并且具有定时范围为0-255的特性。这意味着它可以用来实现定时功能,例如定时器溢出后触发特定事件。
在计数器模式下,定时器0可以计数外部脉冲。由于其计数范围为0-65535,它可以用于对外部信号进行计数,例如测量脉冲宽度、频率或实现测频功能。
当然这些只是一个习惯和经验之谈,具体可以怎么用全看使用者
3.T1的工作模式
与T0类似,过分雷同,不多赘述
51单片机的定时器1具有四种工作模式,每种模式都有其特定的应用场景和特点。通过合理地选择工作模式,可以更好地发挥定时器1的功能,实现各种时间测量和控制的需求。在实际应用中,需要根据具体需求选择适当的工作模式,并注意处理可能的溢出问题。
在51单片机中,波特率的产生通常使用定时器1或定时器2,具体取决于所使用的模式。
对于模式0和模式2,由于它们的波特率是固定的,因此不使用定时器来产生波特率。对于模式1和模式3,波特率是可变的,可以通过配置定时器为自动重装载模式来产生。
二、实践讲解
在工程项目中,定时器可以用来制定精准的时基,用于进行相应的任务调度,脉冲调制等操作。
1.定时
时钟电路在51单片机最小系统中起到的作用主要包括以下两个方面:
1. 时钟信号的提供:51单片机需要一个稳定的时钟信号才能准确地进行指令的执行。
2. 时钟信号提供了单片机各个模块的同步,保证了程序的正确运行和数据的准确传输。
3. 定时器的实现:时钟电路还经常被用于实现定时器功能。
例如,我们可以利用时钟电路提供的时钟信号来控制计数器的计数,从而实现一个定时器。
定时器广泛应用于各个领域,如测量、计时、定时等等。
时钟电路在51单片机最小系统中起到的作用主要包括以下两个方面:
1. 时钟信号的提供:51单片机需要一个稳定的时钟信号才能准确地进行指令的执行。
2. 时钟信号提供了单片机各个模块的同步,保证了程序的正确运行和数据的准确传输。
3. 定时器的实现:时钟电路还经常被用于实现定时器功能。
例如,我们可以利用时钟电路提供的时钟信号来控制计数器的计数,从而实现一个定时器。
定时器广泛应用于各个领域,如测量、计时、定时等等。
时钟电路在51最小系统中扮演着至关重要的角色,它是保证指令的顺利执行,数据的准确传输和定时器功能的实现的基础。
示例部分
下面我们以秒表为演示
主函数中定时器初始化
TMOD = 0x01;
TH0 = (65535-50000)/256;
TL0 = (65535-50000)%256;
通过按键管理定时器状态
void Key_Event_Handle()
{
if(Key_State == 0)
{
Key_Flag_Idx=(Key_Flag_Idx+1)%3;
switch(Key_Flag_Idx)
{
case 1:EA=1;ET0=1;TR0=1;break;
case 2:EA=0;ET0=0;TR0=0;break;
case 0:P0=0x3f;P2=0x3f;i=0;Second_Counts=0;
}
}
}
中断任务程序编写
void DSY_Refresh() interrupt 1
{
TH0 = (65535-50000)/256;
TL0 = (65535-50000)%256;
if(++i==2)
{
i = 0;
++Second_Counts;
P0 = DSY_CODE[Second_Counts/10];
P2 = DSY_CODE[Second_Counts%10];
if(Second_Counts == 100) Second_Counts = 0;
}
}
2.PWM调制
1)理论部分
用定时器进行PWM调制是常见的控制信号生成方法之一。
PWM(Pulse Width Modulation)调制是指通过改变信号的脉冲宽度来控制信号的幅度。在51单片机中,可以利用定时器来生成PWM信号。
在前面,我们了解到:定时器是一种能够按照一定的时间间隔产生中断的硬件设备。51单片机内部有多个定时器,其功能各不相同。在生成PWM信号时,通常使用其中的一个定时器,比如定时器0(T0)。
PWM的基本原理是通过改变脉冲的高电平时间来控制信号的幅度。在定时器中,可以通过设置定时器的定时值和计数值来确定脉冲的周期和高电平时间。定时器会在计数值达到定时值时产生中断,并重新开始计数。
操作部分
下面是一个使用定时器0进行PWM调制的示例操作:
- 初始化定时器0:设置定时器的工作方式为定时模式,并设置定时值和计数值。
- 配置引脚:51配置引脚较为简单,赋值0/1即可
- 设置占空比:根据具体需求,设置脉冲的高电平时间,即占空比。可以通过改变计数值和定时值来实现不同的占空比。
- 启动定时器0:使定时器开始计数。
- 等待定时器中断:等待定时器计数值达到定时值,定时器产生中断。
- 处理定时器中断:在中断处理函数中,将定时器引脚设置为低电平,表示脉冲的低电平时间。
- 重新启动定时器:在中断处理函数结束后,重新启动定时器,开始新的计数。
示例部分
下面我们以呼吸灯和电机控速为演示
#include <reg51.h>
#define LED P1_0 // 假设LED连接到P1口的第0位
// 定时器0中断服务程序
void Timer0_ISR() interrupt 1 {
static unsigned char pwm_count = 0; // PWM计数器,用于逐渐增加和减少PWM占空比
if (pwm_count < 255) { // 逐渐增加PWM占空比
pwm_count++;
} else { // 逐渐减小PWM占空比
pwm_count--;
}
TH0 = (65536 - 50000) / 256; // 重新加载定时器初值,产生约1ms的定时中断
TL0 = (65536 - 50000) % 256;
// 设置PWM输出,根据pwm_count的值调整LED亮度
if (pwm_count > 128) { // 高电平时间大于低电平时间,LED较亮
LED = 1;
} else { // 低电平时间大于高电平时间,LED较暗
LED = 0;
}
}
void Timer0_Init() {
TMOD |= 0x01; // 设置定时器0为模式1(16位定时器)
TH0 = (65536 - 50000) / 256; // 加载定时器初值,产生约1ms的定时中断
TL0 = (65536 - 50000) % 256;
ET0 = 1; // 开启定时器0中断
EA = 1; // 开启总中断
TR0 = 1; // 启动定时器0
}
void main() {
LED = 0; // 初始化LED为熄灭状态
Timer0_Init(); // 初始化定时器0
while (1) {
// 主循环保持空闲,所有操作在定时器中断服务程序中完成
}
}
-
这个代码示例假设LED连接到P1口的第0位。
-
定时器的初值(TH0和TL0)设置为产生约1ms的定时中断。这取决于你的单片机的时钟频率。你可能需要根据你的实际硬件调整这些值。
这个示例使用了定时器0的中断服务程序来逐渐增加和减少PWM占空比,从而产生呼吸灯效果。主循环保持空闲,所有操作都在中断服务程序中完成。
呼吸灯效果的速率和亮度范围可以通过调整PWM计数器的范围和定时器的中断频率来调整。
同样的,我们可以将这个程序应用到电机上,只不过单片机不能够直接驱动电机,需要电机驱动电路。
下面就是一个经典 的H桥电机驱动电路
3.任务调度
RTOS(实时操作系统)中的任务调度是指RTOS将任务划分为多个优先级,并响应最高优先级任务的请求。RTOS强调实时性,并采用分而治之的方法来实现功能划分为多个任务。在RTOS中,任务调度是核心功能之一,用于确保任务在合适的时间以合适的方式执行,以满足系统的实时性要求。
但是对于初学者,我们不做要求,在这里我们以万年历程序带大家,入门任务标志,结合以后的学习逐渐理解任务调度。所以【以下不是RTOS程序】,而是任务标志状态管理
4.数据采样
定时器也可以用于数据捕捉或信号捕获。通常,当外部事件发生时,你可能希望捕捉到该事件,并在特定的时间点(由定时器触发)对其进行处理。
下面以基于阈值的信号捕获为例
#include <reg51.h>
#define THRESHOLD 100 // 假设的阈值
volatile bit signal_captured = 0; // 用于指示是否捕获到信号的标志
// 模拟的ADC读取函数,实际情况中需要根据您的硬件实现
unsigned char ReadADC(void) {
// 这里应该是从ADC硬件读取值的代码
// 但这部分算是后面的知识,我们在这里做黑箱处理
-------------------------
}
void Timer0_Init() {
TMOD |= 0x01; // 设置Timer0为模式1 (16位定时器)
TH0 = 0x00; // 设置定时器初值(这里需要根据您的定时需求来设置)
TL0 = 0x00; // 设置定时器初值
ET0 = 1; // 使能Timer0中断
TR0 = 1; // 启动Timer0
}
void External_Interrupt_Init() {
IT0 = 1; // 设置INT0为边沿触发(这里需要根据您的硬件和需求来设置)
EX0 = 1; // 使能INT0中断
}
void Timer0_ISR() interrupt 1 {
TH0 = 0x00; // 重新加载定时器初值
TL0 = 0x00; // 重新加载定时器初值
unsigned char adcValue = ReadADC(); // 读取ADC值
if (adcValue > THRESHOLD) { // 如果ADC值大于阈值
if (!signal_captured) { // 且信号尚未被捕获
signal_captured = 1; // 设置信号捕获标志
// 在这里记录或处理事件,例如点亮LED或执行其他任务
}
}
}
void External_Interrupt_ISR() interrupt 0 {
signal_captured = 0; // 重置信号捕获标志(如果需要的话)
}
void main() {
Timer0_Init();
External_Interrupt_Init();
EA = 1; // 使能全局中断
while (1) {
// 主循环代码,例如在这里可以关闭或点亮LEDs,或者执行其他任务
}
}
5.通讯协议
定时器是通信协议正常运行的基本要素之一,主要用于各种定时和帧重传的任务。由于硬件资源有限,不可能为每一个单独任务分配一个硬件定时器,因此,通常需要通过单个硬件定时器模拟多个软件定时器的方法来实现。
一般我们会通过定时器产生一定的波特率进行通讯,但这是后话,在这里不做介绍了。
总结
51定时器,单片机核心组件,重点在于其精确的时间控制功能。难点在于如何合理配置定时器参数,以实现特定的延时或定时任务。在工程实践中,常用定时器实现定时中断、延时操作等,提高系统效率和稳定性。掌握定时器的工作原理和配置方法,是单片机编程的关键。通过合理设置定时器初值、选择适当的工作模式,并结合中断服务程序,可实现精准的时间控制,满足各种工程需求。