基本介绍
51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成
定时器作用:
- 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作
- 替代长时间的Delay,提高CPU的运行效率和处理速度
STC89C52有3个定时器(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源
注意定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的
STC89C52的T0和T1均有四种工作模式:
- 模式0:13位定时器/计数器
- 模式1:16位定时器/计数器(常用)
- 模式2:8位自动重装模式
- 模式3:两个8位计数器
我们一般选模式1
工作模式1框图:
时钟的来源:
1.外部时钟,晶振
2.外部引脚T0,T1
12MHz,每1微妙触发一次计数
相关寄存器
TCON:定时器/计数器T0,T1的控制寄存器,目前我们只要配置其中TF0和TR0即可。
TMOD:定时器模式寄存器,不可位寻址,高八位为定时器1工作模式设置,第八位为定时器0工作模式设置,但是我们只要定时器0,16位的工作模式,因此设置M1为0,M0为1,其它不用设置,因此TMOD低八位为0x01
涉及到的中断,我们只关注红框中的几个
PT0为中断优先级,目前不做特殊关注。中断号
根据上面的资料,我们可以设置一个定时器0的初始化函数,调出1个100us的定时器周期。
TH0,TL0,是计数器的高8位和低8位,最大65535,溢出则触发中断,1个机器周期为1us,那么100us的初始值应该是65535-100=65435。
void Timer0_Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 65435%256; //设置计数器初值低八位
TH0 = 65435/256; //设置计数器初值高八位
TF0 = 0; //累计溢出时置1,中断响应后置0,清除TF0标志,防止生效后就产生中断
TR0 = 1; //定时器0开始计时
ET0=1; //开启定时器0中断
EA=1; //中断中开关
PT0=0; //中断优先级,默认为0
}
定时器应用
LED闪烁
#include <REGX52.H>
#include <Timer0.h>
sbit LED=P1^0;
unsigned int T0Count=0;
void main(){
Timer0_Init();
while(1);
}
void Timer0_Routine() interrupt 1
{
TH0 = 64535/256; //设置定时初值
TL0 = 64535%256; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
P1_0=~P1_0;
}
}
Timer0.h
#include <REGX52.H>
/**
* @brief 定时器0初始化,100us@12.000MHz
* @param 无
* @retval 无
*/
void Timer0_Init(void)
{
TMOD &= 0xF0; //设置定时器模式保留定时器1的配置
TMOD |= 0x01; //设置定时器模式0 0 0 1
TH0 = 64535/256; //设置定时初值
TL0 = 64535%256; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
那么定时器的回调函数执行的时间周期是怎么来的?
一个机器周期包括12个时钟周期,设单片机工作于12Mhz晶振,则时钟周期为1/12us,则它的一个机器周期就是12*(1/12 )=1us,定时器是cpu的一部分,它定时的最小单位就是一个机器周期
小试身手
下面来尝试下用定时器来实现倒计时,代码如下:
#include <REGX52.H>
#include <Timer0.h>
#include <LCD1602.h>
#include <Delay.h>
sbit LED=P1^0;
sbit BEER =P2^3 ;
unsigned int T0Count=0;
unsigned char hour,minute,sec;//时分秒
unsigned long int totelSec;//总秒数
void main(){
Timer0_Init();
LCD_Init();
hour=0;
minute=0;
sec=10;
totelSec=hour*60*60+minute*60+sec;
LCD_ShowString(1,2,"Timer : : ");
LCD_ShowNum(1,8,hour,2);
LCD_ShowNum(1,11,minute,2);
LCD_ShowNum(1,14,sec,2);
while(1);
}
void playBeer(unsigned char times,unsigned char ms){
unsigned char i;
for(i=0;i<times;i++){
BEER=0;
Delay(ms);
BEER=1;
Delay(ms);
}
}
void Timer0_Routine() interrupt 1
{
TH0 = 64535/256; //设置定时初值
TL0 = 64535%256; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
totelSec--;
LCD_ShowNum(1,8,totelSec/(60*60),2);
LCD_ShowNum(1,11,totelSec/60%60,2);
LCD_ShowNum(1,14,totelSec%60,2);
//时间到
if(totelSec==0){
LCD_ShowString(1,1," Timer is over ");
TR0 = 0; //定时器0停止
playBeer(6,200);
LCD_ShowString(1,2,"Timer : : ");
LCD_ShowNum(1,8,totelSec/(60*60),2);
LCD_ShowNum(1,11,totelSec/60%60,2);
LCD_ShowNum(1,14,totelSec%60,2);
}
}
}