一、简介
定时器实际上就是Soc当中的一个内部外设。定时器常与计数器扯到一起,计数器也是Soc当中的一个内部外设,计数器顾名思义是用来计数的,就和我们的秒表一样,秒表实际上就是一个计数器,每隔一个单位走一个格(就是计一个数),因为计数器的计数时间周期是固定的,因此到了一定时间只要用计数值*计数时间周期,就能得到一个时间段,这个时间段就是我们定的时间(这就是定时器了)。计数器和定时器其实是一回事。
二、定时器的原理
定时器计时其实是通过计数来实现的。定时器内部有一个计数器,每隔一个时钟周期,计数器就计数一次,定时器的时间就是计数器计数值x时钟周期。
- 振荡周期:为单片机提供定时信号的振荡源周期。
- 状态周期:2个振荡周期为一个状态周期。
- 机器周期:一个机器周期包含六个状态周期,十二个振荡周期。
- 指令周期:完成一条指令所占用的全部时间
三、51单片机的定时器
STC89C52有三个16位定时器,T0、T1、T2,单片机内的定时器和CPU是相互独立的,互不影响,所以它们的执行不需要CPU的参与,而软件延迟利用若干次空循环实现,占用CPU。
四、内部结构
定时/计数器的实质是加一计数器(16位),由高8位和低八位两个寄存器TH和TL组成,TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0,T1的启动和停止及设置溢出标志位。
五、定时器/计数器0和1的相关寄存器
1、定时器/计数器控制寄存器TCON
TCON为定时器/计数器TO、T1的控制寄存器,同时也锁存TO、T1溢出中断源和外部请求中断源等,TCON格式如下:
TF1:定时器/计数器T1溢出标志。T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置“1”TF1,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”TF1 (TF1也可由程序查询清“0”)。
TR1:定时器T1的运行控制位。该位由软件置位和清零。当GATE(TMOD.7) =O,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE (TMOD.7)=1, TR1=1且INT1输入高电平时,才允许T1计数。
TFO:定时器/计数器TO溢出中断标志。TO被允许计数以后,从初值开始加1计数,当最高位产生溢出时,由硬件置“1”TFO,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清“0”TFO (TFO也可由程序查询清“O”)。
TRO:定时器TO的运行控制位。该位由软件置位和清零。当GATE(TMOD.3)=O,TRO=1时就允许TO开始计数,TRO=0时禁止TO计数。当GATE (TMOD.3)=1,TRO=1且INTO输入高电平时,才允许TO计数。
IE1:外部中断1请求源(INT1/P3.3)标志。IE1=1,外部中断向CPU请求中断,当CPU响应该中断时由硬件清“0”IE1。
IT1:外部中断1触发方式控制位。IT1=0时,外部中断1为低电平触发方式,当INT1(P3.3)输入低电平时,置位IE1。采用低电平触发方式时,外部中断源(输入到INT1)必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.3要变高),否则将产生另一次中断。当IT1=1时,则外部中断1(INT1)端口由“1”→“0”下降沿跳变,激活中断请求标志位IE1﹐向主机请求中断处理。
IEO:外部中断O请求源(INTO/P3.2)标志。IEO=1外部中断0向CPU请求中断,当CPU响应外部中断时,由硬件清“0”IEO(边沿触发方式)。
ITO:外部中断0触发方式控制位。ITO=0时,外部中断0为低电平触发方式,当INTO (P3.2)输入低电平时,置位IEO。采用低电平触发方式时,外部中断源(输入到INTO)必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.2要变高),否则将产生另一次中断。当ITO=1时,则外部中断0(INTO)端口由“1”→“0”下降沿跳变,激活中断请求标志位IEO ,向主机请求中断处理。
2.定时器/计数器工作模式寄存器TMOD
定时和计数功能由特殊功能寄存器TMOD的控制位CT进行选,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和MO选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述。
六、两个重要概念
1、TH和TL用来干嘛?
TH和TL是计数器的初始计数值,计数器会从这个值开始向上计数。
2、什么时候触发中断?
配置成模式1时,当计数值计数到65536时,溢出中断触发,进入中断服务函数。
七、配置定时器/计数器
1、标准定时器(以T0为例)
/****
*******定时器0初使化函数
*****/
void Timer0_Init(void)
{
TMOD &= 0xF0; //清除 T0 的工作模式
TMOD |= 0x01; //配置 T0 的GATE=0; C/T=0; M1=0; M0=1;(模式1(16位))
TH0 = (65536-921)/256; //配置 T0 高8位计数初始值
TL0 = (65536-921)%256; //配置 T0 低8位计数初始值
TR0 = 1; //启动 T0
ET0 = 1; //启动 T0 中断
EA = 1; //打开总中断
}
/****
*******T0计时中断函数
1s的定时次数= 1/(12/11059200)=921600,16位计数器最大可计数65536次,所以通常定时ms为单位,比如定时1ms,则循环1000次为1s,则计数次数为921600/1000=921,则初值为
TH0 = (65536-921)/256;
TL0 = (65536-921)%256;
*****/
void Timer0_Handler(void) interrupt 1 //0:外部中断1的中断号; 1:定时器0的中断号; 2 外部中断2的中断号; 3:为定时器1的中断号; 4 串口中断的中断号;
{
static uint timer0_count;
TH0 = (65536-921)/256; //重新赋初值
TL0 = (65536-921)%256;
if(flag_timer_begin == 1) //开始计时
{
timer0_count++;
if(timer0_count >= 1000) //定时1s
{
timer0_count = 0;
flag_1s = 1;
}
}
else //停止计时,清除标志位
{
timer0_count = 0;
flag_1s = 0;
}
}
2、触发定时器(以T0为例,引脚INT0必须为高电平才开启定时器)
/****
*******定时器0初使化函数
*****/
void Timer0_Init(void)
{
TMOD &= 0xF0; //清除 T0 的工作模式
TMOD |= 0x09; //配置 T0 的GATE=1; C/T=0; M1=0; M0=1;(模式1(16位))
TH0 = (65536-921)/256; //配置 T0 高8位计数初始值
TL0 = (65536-921)%256; //配置 T0 低8位计数初始值
TR0 = 1; //启动 T0
ET0 = 1; //启动 T0 中断
EA = 1; //打开总中断
}
/****
*******T0计时中断函数
1s的定时次数= 1/(12/11059200)=921600,16位计数器最大可计数65536次,所以通常定时ms为单位,比如定时1ms,则循环1000次为1s,则计数次数为921600/1000=921,则初值为
TH0 = (65536-921)/256;
TL0 = (65536-921)%256;
*****/
void Timer0_Handler(void) interrupt 1 //0:外部中断1的中断号; 1:定时器0的中断号; 2 外部中断2的中断号; 3:为定时器1的中断号; 4 串口中断的中断号;
{
static uint timer0_count;
TH0 = (65536-921)/256; //重新赋初值
TL0 = (65536-921)%256;
timer0_count++;
if(timer0_count >= 1000) //定时1s
{
timer0_count = 0;
flag_1s = 1;
}
}
3、计数器(以T0为例,P3.4引脚由高电平到低电平跳变,计数值+1)
/****
*******定时器0初使化函数
*****/
void Timer0_Init(void)
{
TMOD &= 0xF0; //清除 T0 的工作模式
TMOD |= 0x05; //配置 T0 的GATE=0; C/T=1; M1=0; M0=1;(模式1(16位))
TH0 = (65536-10)/256; //配置 T0 高8位计数值
TL0 = (65536-10)%256; //配置 T0 低8位计数值
TR0 = 1; //启动 T0
ET0 = 1; //启动 T0 中断
EA = 1; //打开总中断
}
/****
*******T0计时中断函数
1s的定时次数= 1/(12/11059200)=921600,16位计数器最大可计数65536次,所以通常定时ms为单位,比如定时1ms,则循环1000次为1s,则计数次数为921600/1000=921,则初值为
TH0 = (65536-921)/256;
TL0 = (65536-921)%256;
*****/
void Timer0_Handler(void) interrupt 1 //0:外部中断1的中断号; 1:定时器0的中断号; 2 外部中断2的中断号; 3:为定时器1的中断号; 4 串口中断的中断号;
{
flag_finish = 1;
}
八、流程设计
首先清除定时器工作模式,然后配置定时器工作模式为模式1,接着配置定时器高八位和低八位计数初始值,接着启动定时器、启动定时器中断、打开总中断。当计数值溢出时,触发中断,进入中断服务函数,在中断服务函数中重新赋值计数初始值,进行计时处理。