文章目录
- 引言
- 定时器工作原理
- TMOD定时器/计数器工作模式寄存器
- 定时器工作模式
- 模式0(13位定时器/计数器)
- 模式1(16位定时器/计数器)
- 模式2(8位自动重装模式)
- 模式3(两个8位计数器)
- 定时器配置流程
- 代码演示——LED1间隔1秒闪烁
- 代码演示——按键1控制LED流水灯状态
- 代码演示——LCD1602定时器时钟显示
引言
定时器和计数器是用于测量时间间隔或计数事件的硬件模块。它们可以在许多应用中使用,例如生成精确的时间延迟、测量频率、计数外部事件等。51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。本文将详细介绍51单片机中的定时器和计数器的工作原理、配置方法及其应用。
本章节将涉及中断相关的知识,具体内容可参考:中断系统讲解
注意:定时器的资源和单片机的型号是关联在一起的,不同型号的单片机可能会有不同的定时器数量和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的。
定时器工作原理
定时器在单片机内部就像一个小闹钟,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一。当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行。
定时器/计数器的工作原理基于时钟脉冲。定时器模式下,它们使用内部时钟源来计数;计数器模式下,它们使用外部脉冲源来计数。每个定时器/计数器都有一个寄存器,用于存储当前的计数值。
TMOD定时器/计数器工作模式寄存器
定时和计数功能由特殊功能寄存器TMOD的控制位 C T C\sqrt{T} CT进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有四种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述:
定时器工作模式
通过对寄存器TMOD寄存器中的M1、M0的设置,定时器/计数器0和1有四种不同的工作模式。
模式0(13位定时器/计数器)
工作模式图如下:
模式1(16位定时器/计数器)
模式1除了使用了TH0及TL0全部16位外,其他与模式0完全相同。此模式下,TL0的8位溢出向TH0进位,TH0溢出置位TCON中的溢出标志位TF0。
当GATE=0(TMOD.3)时,如TR0=1,则定时器计数。GATE=1时,允许由外部输入INTO控制定时器0,这样可实现脉宽测量。TRO为TCON寄存器内的控制位,TCON寄存器各位的具体功能描述见上节TCON寄存器的介绍。
注意:STC89C51RC/RD+系列单片机的定时器有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同;另外一种是6T模式,每6个时钟加1,速度是传统8051单片机的2倍T0的速率在烧录用户程序时在STC-ISP编程器中设置。
模式2(8位自动重装模式)
此模式下定时器/计数器可自动重装载8位计数器,TL0的溢出不仅置位TF0,而且将TH0内容重新装入TL0。(TH0可先设置好,重装时TH0内容不变)
模式3(两个8位计数器)
对定时器0,在此模式中,定时器1停止计数,效果与将TR1设置为0相同。
对定时器0,此模式下定时器0的TL0及TH0作为2个独立的8位计数器。下图为模式3时的定时器0逻辑图。TL0占用定时器0的控制位: C T C\sqrt{T} CT、GATE、TRO、INTO及TFO。THO限定为定时器功能(计数器周期),占用定时器1的TR1及TF1。此时,TH0控制定时器1中断。
模式3是为了增加一个附加的8位定时器/计数器而提供的,使单片机具有三个定时器/计数器。模式3只适用于定时器/计数器0,定时器T1处于模式3时相当于TR1-0,停止计数,而T0可作为两个定时器用。
定时器配置流程
- 对TMOD赋值,以确定T0和T1的工作方式。
- 根据所要定时的时间计算初值,并将其写入TH0、TL0或TH1、TL1。
- 如果使用中断,则对EA赋值,开放定时器总中断。
- 使TR0或TR1置位,启动定时/计数器定时或计数。
注意:STC-ISP烧写工具自带计算所要定时的时间计算初值,可根据自己选择的模式,进行复制代码即可
代码演示——LED1间隔1秒闪烁
通过定时器0中断控制LED1指示灯间隔1秒闪烁,实物图连接:D1连接到P2_0引脚即可,使用的12.000MHz的时钟,每次间隔一毫秒触发中断,在中断触发函数进行计数,当为1000时,就是一秒。
#include <REGX52.H>sbit LED1 = P2^0;void External0_ISR(void) interrupt 1
{static unsigned int count = 0;TL0 = 0x18; //需要手动复原 TH0 = 0xFC; //需要手动复原// 中断处理代码if(count == 1000){count = 0;LED1 = !LED1;}count++;
}void Timer0_Init(void) //1毫秒@12.000MHz
{TMOD &= 0xF0; //设置定时器模式TMOD |= 0x01; //设置定时器模式TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值TF0 = 0; //清除TF0标志ET0 = 1;//打开T0中断EA = 1;//打开总中断TR0 = 1; //定时器0开始计时
}void main()
{Timer0_Init();while(1){}
}
代码演示——按键1控制LED流水灯状态
在此演示中,使用了INTRINS.H库中的_crol_左移和_cror_右移函数,当按下KEY1按键时,LED流水灯就会切换方向状态进行闪烁。实物图连接:K1连接P0_0引脚,LED八个灯插入P2引脚。
- cror(unsigned char val, unsigned char n): 字符循环右移,将 val 循环右移 n 位。
- crol(unsigned char val, unsigned char n): 字符循环左移,将 val 循环左移 n 位
#include <REGX52.H>
#include <INTRINS.H>sbit KEY1 = P0^0;
sbit KEY2 = P0^1;
unsigned char LEDMode;void DelayXms(unsigned int xms) //@12.000MHz
{unsigned char data i, j;while(xms){i = 2;j = 239;do{while (--j);} while (--i);xms--;}
}void Timer0_Init(void) //1毫秒@12.000MHz
{TMOD &= 0xF0; //设置定时器模式TMOD |= 0x01; //设置定时器模式TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值TF0 = 0; //清除TF0标志ET0 = 1;//打开T0中断EA = 1;//打开总中断TR0 = 1; //定时器0开始计时
}void External0_ISR(void) interrupt 1
{static unsigned int count = 0;TL0 = 0x18; //需要手动复原 TH0 = 0xFC; //需要手动复原// 中断处理代码if(count == 1000){count = 0;if(LEDMode == 0)P2 = _crol_(P2,1);if(LEDMode == 1)P2 = _cror_(P2,1);}count++;
}unsigned char Getkey()
{unsigned char keyNumber = 0;if(KEY1 == 0){DelayXms(5);while(KEY1 == 0);DelayXms(5);keyNumber = 1;}if(KEY2 == 0){DelayXms(5);while(KEY2 == 0);DelayXms(5);keyNumber = 2;}return keyNumber;
}void main()
{unsigned char keyNum = 0;P2 = 0xfe;Timer0_Init();while(1){keyNum = Getkey();if(keyNum){if(keyNum == 1){LEDMode++;if(LEDMode>=2){LEDMode = 0; }}}}
}
代码演示——LCD1602定时器时钟显示
此演示代码使用LCD1602模块作为显示,LCD1602模块不过多讲解,后续会提供专门的章节说明。需要LCD1602库可以私聊我。实物图连接:板子插上LCD1602模块即可。
#include <REGX52.H>
#include "LCD1602.h"unsigned char Sec=55,Min=59,Hour;//秒分时void DelayXms(unsigned int xms) //@12.000MHz
{unsigned char data i, j;while(xms){i = 2;j = 239;do{while (--j);} while (--i);xms--;}
}void Timer0_Init(void) //1毫秒@12.000MHz
{TMOD &= 0xF0; //设置定时器模式TMOD |= 0x01; //设置定时器模式TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值TF0 = 0; //清除TF0标志ET0 = 1;//打开T0中断EA = 1;//打开总中断TR0 = 1; //定时器0开始计时
}void External0_ISR(void) interrupt 1
{static unsigned int count = 0;TL0 = 0x18; //需要手动复原 TH0 = 0xFC; //需要手动复原// 中断处理代码if(count == 1000){count = 0;Sec++;if(Sec == 60){Sec = 0;Min++;if(Min == 60){Min = 0;Hour++;if(Hour == 24){Hour = 0;}}}}count++;
}void main()
{Timer0_Init();LCD_Init();LCD_ShowString(1,1,"Time:");LCD_ShowString(2,1,"00:00:00");while(1){LCD_ShowNum(2,1,Hour,2);LCD_ShowNum(2,4,Min,2);LCD_ShowNum(2,7,Sec,2);}
}