一、原理部分
1、什么是定时/计数器
定时/计数器,是一种能够对内部时钟信号或外部输入信号进行计数,当计数值达到设定要求时,向CPU提出中断处理请求,从而实现定时或者计数功能的外设。定时/计数器的最基本工作原理是进行计数。作为定时器时,计数信号的来源选择周期性的内部时钟脉冲;用作计数器时,计数信号的来源选择非周期性的外部输入信号。
2、蓝桥杯单片机定时器资源
该单片机有两个定时/计数器T0和T1,均为16位加法计数器,由低8位TLx和高8位THx两个寄存器组成,最大计数值为65535个计数脉冲。
该加1计数器的计数脉冲来源有2个:
①系统时钟振荡器输出的12分频。
②T0或T1引脚输入的外部脉冲信号。每接收到一个计数脉冲,计数器就会加1,当计数值达到峰值时,再输入一个计数脉冲,计数器便会溢出回零,并且将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+1) / 256;
TL0 = 0xef; 或者 TL0 = (65535 - 10000+1) % 256;
3、配置定时器
配置前需要了解一下定时器相关寄存器
(1)TCON:定时器/计数器控制寄存器
TF1: 定时器/计数器T1溢出标志。
T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置“1”TF1,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”TF1 (TF1也可由程序查询清“0”)。
TR1:定时器T1的运行控制位。该位由软件置位和清零。
当GATE (TMOD.7) =0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。
当GATE (TMOD.7) =1,TR1=1且INT1输入高电平时,才允许T1计数。
TF0:定时器/计数器TO溢出中断标志。T0被允许计数以后,从初值开始加1计数,当最高位产生溢出时,由硬件置“1”TF0,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清“0”TF0 ( TF0也可由程序查询清“0”)。
TR0:定时器T0的运行控制位。该位由软件置位和清零。
当GATE (TMOD.3) =0,TR0=1时就允许T0开始计数,TR0=0时禁止T0计数。
当GATE (TMOD.3) =1,TR1=0且INT0输入高电平时,才允许T0计数。
若使用定时器0,则TCON应该设置为:
TR0=1;开启定时器
(2)TMOD:定时器/计数器工作模式寄存器
GATE:GATE=0时,由TR0和TR1控制定时器,GATE=1时,由INT0和INT1控制定时器
C/T:C/T=0时,为定时功能,C/T=1时,为计数功能
M1、M0:定时器定时器/计数器1模式选择
①M1=0,M0=0->16位自动重装定时器,当溢出时将RL_TH1和RL_TL1存放的值自动重装入TH1和TL1中。
②M1=0,M0=1->16位不可重装载模式,TL1、TH1全用,计数溢出后需要手动设置计数值
③M1=1,M0=0->8位自动重装载定时器,当溢出时将TH1存放的值自动重装入TL1
④M1=1,M0=1->定时器/计数器1此时无效(停止计数) 。
一般选择16位自动重装定时器,不用手动设置计数值。
若使用定时器0,此时TMOD设置为:
TMOD&=0xf0;
将低四位置0,即GATE、C/T、M1、M0均为0,设置为16位自动重装定时器
(3)AUXR辅助寄存器
这个寄存器主要用于设置定时器分频,定时器0、定时器1,和定时器2复位后是传统8051的速度,即12分频,这是为了兼容传统8051。但也可不进行12分频,通过设置新增加的特殊功能寄存器AUXR,将T0,T1,设置为1T。
(4)IE和IP:定时器T0和T1的中断控制寄存器
IE:中断允许寄存器 (可位寻址)
EA:CPU的总中断介许控制位
EA=1,CPU开放中断
EA=0,CPU屏两所有的中新申请
EA的作用是使中断允许形成多级控制,即各中断源首先受EA控制;其次还受各中断源自己的中断允许控制位控制。
ET1:定时/计数器T1的溢出中断允许位ET1=1,允许T1中断
ET1=0,禁止T1中断
ET0: T0的溢出中断允许位
ET0=1允许T0中断
ET0=0禁止T0中断
IP:中断优先级控制寄存器 (可位寻址)
PT1:定时器1中断优先级控制位。
当PT1=0时,定时器1中断为最低优先级中断(优先级0)
当PT1=1时,定时器1中断为最高优先级中断(优先级1)
PT0:定时器0中断优先级控制位。
当PT0=0时,定时器0中断为最低优先级中断(优先级0)当PT0=1时,定时器0中断为最高优先级中断(优先级1)
若使用定时器0,此时IE和IP设置为:
ET0=1;
EA=1;
4、定时器框架编写
定时器设计使用中,一般需要编写两个函数:初始化函数和中断服务函数。
(1)初始化函数:
①配置工作模式,即对TMOD寄存器编程。
②计算计数初值,即对THx和TLx寄存器进行赋值。
③使能定时/计数器中断,即ET0或ET1置1。
④打开总中断,即EA =1。
⑤启动定时器,即TR0或TR1置1。(2)中断服务函数(记住中断号!!):
①如果不是自动重装模式,需要对THx和TLx重新赋值。
②一定时间间隔后执行相应逻辑。
5、STC-ISP生成定时器初始化函数
使用STC-ISP中的定时器计算器可以生成定时器初始化函数。
二、程序案例
例子:使用定时器T0和数码管制作能够显示分、秒、0.05秒的秒表,按键S5秒表清零,按键S4用于秒表的暂停与启动
#include "reg52.h"
sbit S4=P3^3;//定义按键引脚
sbit S5=P3^2;
unsigned char t_m=0;//用于记录分钟
unsigned char t_s=0;//用于记录秒
unsigned char t_005s=0;//用于记录0.05秒
unsigned char code SMG_duanma[18]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};//数码管段码表
/*****************************************************************
*@Function: SelectHC573 //
*@Description: 选择锁存器 //
*@Input:channel:选择通道 //
*@Output:无 //
*@Return:无 //
*@Others:无 //
/*****************************************************************/
void SelectHC573(unsigned char channel)
{
switch(channel)
{
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;
}
}
/*****************************************************************
*@Function: DelaySMG //
*@Description: 用于数码管延时 //
*@Input:t:延时长短 //
*@Output:无 //
*@Return: 无//
*@Others: 无//
/*****************************************************************/
void DelaySMG(unsigned int t)
{
while(t--);
}
/*****************************************************************
*@Function: DisplaySMG_Bit //
*@Description:单位数码管显示 //
*@Input: value:显示的内容
* pos:显示的位置//
*@Output: 无//
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void DisplaySMG_Bit(unsigned char value,unsigned char pos)
{
SelectHC573(6);
P0=0x01<<pos;
SelectHC573(7);
P0=value;
}
/*****************************************************************
*@Function: DisplayTime //
*@Description:显示时间 //
*@Input: 无//
*@Output: 无//
*@Return: 无//
*@Others: 无 //
/*****************************************************************/
void DisplayTime()
{
DisplaySMG_Bit(SMG_duanma[t_005s%10],7);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[t_005s/10],6);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[16],5);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[t_s%10],4);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[t_s/10],3);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[16],2);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[t_m%10],1);
DelaySMG(500);
DisplaySMG_Bit(SMG_duanma[t_m/10],0);
DelaySMG(500);
}
/*****************************************************************
*@Function: InitTimer0 //
*@Description:初始化定时器0 //
*@Input:无 //
*@Output: 无//
*@Return: 无//
*@Others: 无 //
/*****************************************************************/
void InitTimer0()
{
TMOD=0x01;//设置定时器模式
TH0=(65535-50000+1)/256;//设置计数初始值
TL0=(65535-50000+1)%256;
ET0=1;//使能定时器T0中断
EA=1;//使能总中断
TR0=1;//开启T0时钟
}
/*****************************************************************
*@Function: ServiceTimer0 //
*@Description:定时器T0中断函数 //
*@Input: 无//
*@Output: 无//
*@Return: 无 //
*@Others:无 //
/*****************************************************************/
void ServiceTimer0 () interrupt 1
{
TH0=(65535-50000+1)/256;
TL0=(65535-50000+1)%256;
t_005s++;
if(t_005s==20)//如果满足20个0.05秒,则t_005s清零,t_s加1,即秒数加1
{
t_s++;
t_005s=0;
if(t_s==60)//如果满60秒,t_s清零,t_m加1,即分钟加1
{
t_m++;
t_s=0;
}
if(t_m==99)//如果分钟满99分,则清零
{
t_m=0;
}
}
}
/*****************************************************************
*@Function: DelayK //
*@Description:用于按键消抖延时 //
*@Input:无 //
*@Output: 无//
*@Return: 无//
*@Others: 无 //
/*****************************************************************/
void DelayK(unsigned char t)
{
while(t--);
}
/*****************************************************************
*@Function: ScanKeys //
*@Description: 按键S4,S5扫描 //
*@Input:无 //
*@Output: 无//
*@Return: 无 //
*@Others: 无 //
/*****************************************************************/
void ScanKeys()
{
if(S4==0) //秒表启动与暂停
{
DelayK(100);
if(S4==0)
{
TR0=~TR0;
while(S4==0)
{
DisplayTime();
}
}
}
if(S5==0) //秒表清零
{
DelayK(100);
if(S5==0)
{
t_005s=0;
t_s=0;
t_m=0;
while(S5==0)
{
DisplayTime();
}
}
}
}
void main()
{
InitTimer0();
while(1)
{
DisplayTime();
ScanKeys();
}
}
感谢大家的观看
欢迎大家提出问题并指正~