05-定时器
05-定时器
背景资料
51
单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。前面介绍的独立按键、led
灯都属于外设。
定时器的作用
- 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作
- 替代长时间的
Delay
,提高CPU
的运行效率和处理速度 - ...
STC89C52RC
定时器资源
- 定时器个数:3个(
T0、T1、T2
),T0
与T1
与传统的51单片机兼容,T2
是此型号单片机增加的资源 - 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,
T0
与T1
的操作方式是所有51单片机所共有的
定时器框图
定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔固定的一段时间,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行
定时器工作模式
STC89C52
的T0
和T1
均有四种工作模式:
- 模式0 :13位定时器/计数器
- 模式1 :16位定时器/计数器(常用)
- 模式2 :8位自动重装模式
- 模式3 :两个8位计数器
工作模式1框图:
每脉冲一次定时器/计数器加一,当16位定时器/计数器0溢出时,对TF0
标志位进行置位,该标志位置位后,就会向中断系统申请中断,然后执行任务
-
SYSclk
:系统时钟,即晶振周期,本开发板上的晶振为12MHz
-
T0 Pin
:对应单片机外部引脚P3.4
,接收外部脉冲 -
\[C/\overline{T} \]
:当其为1时,计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或每6个时钟得到一个计数脉冲,计数值加1;当其为0时,计数脉冲来自单片机外部引脚(
T0
为P3.4
,T1
为P3.5
),则为计数方式,每来一个脉冲加1
中断系统
中断系统是为使CPU
具有对外界紧急事件的实时处理能力而设置的。
当中央处理机CPU
正在处理某件事的时候外界发生了紧急事件请求,要求CPU
暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU
中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU
请求中断,要求为它服务的时候,这就存在CPU
优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU
总是先响应优先级别最高的中断请求。
当CPU
正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU
能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。
- 中断系统是为了使CPU具有对外界紧急事件的实时处理能力
- 多级中断系统中,高优先级的中断可以打断低优先级的中断
中断程序流程
STC89C52RC
中断资源
-
中断源个数:8个(外部中断0/1/2/3,定时器0/1/2中断,串口中断)
-
中断优先级个数:4个
-
中断号:
-
注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等
定时器和中断
定时器相关寄存器
单片机通过配置寄存器来控制内部电路的连接
- 寄存器是连接软硬件的媒介
- 在单片机中寄存器就是一段特殊的
RAM
存储器。一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线(此处是比喻),控制着电路的连接方式 - 寄存器相当于一个复杂机器的“操作按钮”
一、按键控制led
流水灯模式
定时器初始化可以使用STC-ISP
的定时器计算器获得,不过记得再加上允许中断的设置和中断优先级的设置,如下
// 设置定时器0可允许中断
EA = 1;
ET0 = 1;
// 设置定时器0的中断优先级
PT0 = 0;
#include <REGX52.H>
#include <INTRINS.H>
unsigned char chLedDir = 1; // 流水灯方向
void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
/**
* @brief 读取独立按键键码
* @param 无
* @retval chKeyNum 按下按键的键码值
* 如果按键按下不放,程序会停留在此函数,松手返回按键键码。若没有按键按下,返回0
*/
unsigned char Key()
{
unsigned char chKeyNum = 0;
if(P3_1 == 0){Delay(20);while(P3_1 == 0);Delay(20);chKeyNum=1;}
if(P3_0 == 0){Delay(20);while(P3_0 == 0);Delay(20);chKeyNum=2;}
if(P3_2 == 0){Delay(20);while(P3_2 == 0);Delay(20);chKeyNum=3;}
if(P3_3 == 0){Delay(20);while(P3_3 == 0);Delay(20);chKeyNum=4;}
return chKeyNum;
}
/**
* @brief 定时器0初始化 1毫秒@12.000MHz
* @param 无
* @retval 无
*
*/
void Timer0Init()
{
// 设置 T0 定时器的工作模式为16位定时模式
TMOD &= 0xF0; // 把TMOD的低四位清零(T0定时器),高四位保持不变(T1定时器)
TMOD |= 0x01; // 把TMOD的最低位置1(T0定时器),其余高位保持不变
// 设置 T0 定时器的计数单元初值
// 每隔1us计算加1, 定时器最大值为65535
// 64536离定时器溢出差值1000(65535 + 1才溢出),正好使计时时间为1ms
// 中断之后需要在中断服务程序里将定时器计数单元初值
// 重新配置为64535,这样就能做到 每隔1s中断一次的效果
TL0 = 64536 % 256; // TL0 = 0x18
TH0 = 64536 / 256; // TH0 = 0xFC
// 设置定时器0可允许中断
EA = 1;
ET0 = 1;
// 设置定时器0的中断优先级
PT0 = 0;
// 定时器0溢出中断标志清0
TF0 = 0;
// 定时器0开始定时
TR0 = 1;
}
void main()
{
P2 = 0x7F;
Timer0Init();
while(1)
{
unsigned char chKeyNum = Key();
if(chKeyNum)
{
if(chKeyNum == 1)
{
chLedDir = !chLedDir;
}
}
}
}
void Timer0Routine() interrupt 1
{
static unsigned int nT0Cnt = 0;
nT0Cnt++;
// 设置定时初值
TL0 = 64536 % 256;
TH0 = 64536 / 256;
if(nT0Cnt >= 500)
{
nT0Cnt = 0;
if(chLedDir == 0)
{
P2 = _crol_(P2, 1); // 循环左移1位
}
else
{
P2 = _cror_(P2, 1); // 循环右移1位
}
}
}
二、定时器时钟
中断服务程序一般处理短时任务
#include <REGX52.H>
#include "LCD1602.H"
#include <INTRINS.H>
unsigned char chSec = 50, chMin = 59, chHour = 23;
/**
* @brief 定时器0初始化 1毫秒@12.000MHz
* @param 无
* @retval 无
*
*/
void Timer0Init()
{
// 设置 T0 定时器的工作模式为16位定时模式
TMOD &= 0xF0; // 把TMOD的低四位清零(T0定时器),高四位保持不变(T1定时器)
TMOD |= 0x01; // 把TMOD的最低位置1(T0定时器),其余高位保持不变
// 设置 T0 定时器的计数单元初值
// 每隔1us计算加1, 定时器最大值为65535
// 64536离定时器溢出差值1000(65535 + 1才溢出),正好使计时时间为1ms
// 中断之后需要在中断服务程序里将定时器计数单元初值
// 重新配置为64535,这样就能做到 每隔1s中断一次的效果
TL0 = 64536 % 256; // TL0 = 0x18
TH0 = 64536 / 256; // TH0 = 0xFC
// 设置定时器0可允许中断
EA = 1;
ET0 = 1;
// 设置定时器0的中断优先级
PT0 = 0;
// 定时器0溢出中断标志清0
TF0 = 0;
// 定时器0开始定时
TR0 = 1;
}
void main()
{
LCD_Init();
Timer0Init();
LCD_ShowString(1, 1, "Clock:");
LCD_ShowString(2, 3, ": :");
while(1)
{
// 中断服务程序一般处理短时任务
// LCD1602显示会占用较多时间
// 所以放在主函数显示
LCD_ShowNum(2, 1, chHour, 2);
LCD_ShowNum(2, 4, chMin, 2);
LCD_ShowNum(2, 7, chSec, 2);
}
}
void Timer0Routine() interrupt 1
{
static unsigned int nT0Cnt = 0;
nT0Cnt++;
// 设置定时初值
TL0 = 64536 % 256;
TH0 = 64536 / 256;
if(nT0Cnt >= 1000)
{
nT0Cnt = 0;
chSec++;
if(chSec >= 60)
{
chSec = 0;
chMin++;
}
if(chMin >= 60)
{
chMin = 0;
chHour++;
}
if(chHour >= 24)
{
chHour = 0;
}
}
}