文章目录
1、前言
在前面的文章中介绍了51单片机之矩阵键盘,今天介绍单片机的定时器。
2、定时器
- 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。
- 定时器作用:
- 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。
- 替代长时间的Delay,提高CPU的运行效率和处理速度。
- 操作系统多任务的切换等等(参考STM模块)。
3、STC89C52定时器资源
- 定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源;
- 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的。
3-1、定时器/计数器(T0/T1)
- STC89C52系列单片机的定时器0和定时器1,与传统8051的的定时器完全兼容,当在定时器1做波特率发生器时,定时器0可以当两个8位定时器用。
- STC89C52系列单片机内部设置的两个16位定时器/计数器T0和T1都具有计数方式 和定时方式 两种工作方式。对每个定时器/计数器(T0和T1),在特殊殊功能寄存器TMOD中都有一控制位一C/T来选择T0或T1为定时器还是计数器。定时器/计数器的核心部件是一个加法(也有减法)的计数器,其本质是对脉冲进行计数 。只是计数脉冲来源不同:如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每6个时钟得到一个计数脉冲,计数值加1;如果计数脉冲来自单片机外部引脚(T0为P3.4,T1为P3.5),则为计数方式,每来一个脉冲加1。
- 当定时器/计数器工作在定时模式 时,可在烧录用户程序时在STC-ISP编程器中设置(如下图所示)是系统时钟÷12还是系统时钟÷6后让T0和T1过进行计数。当定时器/计数器工作在计数模式 时,对外部脉冲计数不分频。
Figure1. STC-ISP编程器设置
- 定时器/计数器0有4种工作模式:模式0(13位定时器/计数器),模式1(16位定时器/计数器模式),模式2(8位自动重装模式),模式3(两个8位定时器/计数器)。定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。
3-2、定时器框图
定时器在单片机内部就像一个小闹钟一样,根据时钟的输出言号,每隔"一秒",计数单元的数值就增加一,当计数单元数值增加到"设定的闹钟提醒时间"时,计数单元就会向中断系统发出中断申请 ,产生"响铃提醒",使程序跳转到中断服务函数 中执行。
Figure2. 定时器框图
3-3、定时器/计数器0和1的相关寄存器
Figure3. 定时器/计数器0和1的相关寄存器
3-3-1、定时器/计数器控制寄存器TCON
TCON为定时器/计数器T0、 T1的控制寄存器,同时也锁存T0、 T1溢出中断源和外部请求中断源等, TCON格式如下:
Figure4. 定时器/计数器控制寄存器TCON
3-3-2、定时器/计数器工作模式寄存器TMOD
定时和计数功能由特殊功能寄存器TMOD 的控制位C/\T进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述。
Figure5. 定时器/计数器工作模式寄存器TMOD
3-4、模式1:16位定时器/计数器(常用)
由于STC89C52的T0和T1均有四种工作模式。模式0:13位定时器/计数器;模式1:16位定时器/计数器;模式2:8位自动重装模式;模式3:两个8位计数器。其中模式1最为常用。模式1除了使用了TH0及TL0全部16位外,其他与模式0完全相同。即此模式下定时器/计数器0作为16位定时器/计数器 ,如Figure5. 定时器/计数器0的模式1:16位定时器/计数器所示为工作模式1的框图。
Figure5. 定时器/计数器0的模式1:16位定时器/计数器
- 此模式下,定时器配置为16位定时器/计数器,由TL0的8位和TH0的8位所构成。TL0的8位溢出向TH0进位,TH0计数溢出置位TCON中的溢出标志位TF0。TL最大值是65535,然后就给TF0置位,触发中断操作。
- SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHz。
- 当GATE=0(TMOD.3)时,如TRO=1,则定时器计数。GATE=1时,允许由外部输入INTO控制定时器0,这样可实现脉宽测量。TR0为TCON寄存器内的控制位,TCON寄存器各位的具体功能描述见上节TCON寄存器的介绍。
- 当C/ \T=0时,多路开关连接到系统时钟的分频输出,T0对时钟周期计数,T0工作在定时方式 。当C/ \T=1时,多路开关连接到外部脉冲输入P3.4/T0,即T0工作在计数方式。
- STC89C52系列单片机的定时器有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同;另外一种是6T模式,每6个时钟加1,速度是传统8051单片机的2倍。T0的速率在烧录用户程序时在STC-ISP编程器中设置。
4、中断系统
4-1、概念介绍
- 中断系统是为使CPU具有对外界紧急事件的实时处理能力而可设置的。
- 当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断 。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源 。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。
- 当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高 的中断源请求。如果CPU能够暂停对原来可中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套 。这样的中断系统称为多级中断系统 ,没有中断嵌套功能的中断系统称为单级中断系统。
- STC89C52系列单片机提供了8个中断请求源,它们分别是:外部中断0(\INT0)、定时器0中断、外部中断1(\INTI)、定时器1中断、串口(UART)中断、定时器2中断、外部中断2(\INT2)、外部中断3(\INT3) 。所有的中断都具有4个中断优先级。用户可以用用关总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求,也可以用打开相应的中断允许位来使CPU响应相应的中断申请:每一个中断源可以用软件独立地控制为开中断或关中断状态:每一个中断的优先级别均可用软件设置。高优先级的中断请求可以打断低优先先级的中断,反之,低优先级的中断请求不可以打断高优先级及同优先级的中断。当两个相同优先级的中断同时产生时,将由查询次序来决定系统先响应哪个中断。
Figure6. 中断程序流程图
Table1. 中断查询次序

通过设置新增加的特殊功能寄存器IPH 中的相应位,可将中断优先级设为四级,如果只设置IP或XICON,那么中断优先级就只有两级,与传统80511单片机两级中断优先级完全兼容。如果使用C语言编程,中断查询次序号就是中断号,例如:
Table2. 中断查询次序号
void | 中断函数接口 | 中断号 |
---|---|---|
void | Int0_Routine(void) | interrupt 0; |
void | Timer0_Rountine(void) | interrupt 1; |
void | Int1_Routine(void) | interrupt 2; |
void | Timer1_Rountine(void) | interrupt 3; |
void | UART_Routine(void) | interrupt 4; |
void | Timer2_Routine(void) | interrupt 5; |
void | Int2_Routine(void) | interrupt 6; |
void | Int3_Routine(void) | interrupt 7; |
4-2、中断系统结构
Figure7. 中断系统结构
4-3、中断系统寄存器
寄存器是连接软硬件的媒介。在单片机中寄存器就是一段特殊的RAM存储器,一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式 。寄存器相当于一个复杂机器的"操作按钮"。
Figure8. 中断系统寄存器
4-3-1、中断允许寄存器IE
Figure9. 中断允许寄存器IE
4-3-2、中断允许寄存器XICON
Figure10. 中断允许寄存器XICON
4-3-3、中断优先级控制寄存器IP/XICON和IPH
Figure11. 中断优先级控制寄存器IP/XICON和IPH
4-3-4、定时器/计数器0/1控制寄存器TCON
Figure12. 定时器/计数器0/1控制寄存器TCON
4-3-5、串行口控制寄存器SCON
Figure13. 串行口控制寄存器SCON
4-3-6、定时器/计数器2控制寄存器T2CON
Figure14. 定时器/计数器2控制寄存器T2CON
5、工程编码
5-1、点亮一个LED(Timer0)
Figure15. 点亮一个LED
c
#include <REGX52.H>
void Timer0_Init()
{
/* 1.set timer0 as mode1 (16-bit). M1 = 0, M0 = 1. */
TMOD = 0x01;
/* 2.TF0 = 0 */
TF0 = 0;
/* 3.timer0 start running. */
TR0 = 1;
/* 4.0~65535 每隔1us计数加一, 总共定时时间65535us. 64535离计数器溢出差值1000, 所以计时时间为1ms. */
/* 4-1.initial timer0 low byte. */
TH0 = 64535 / 256; // 0xFC17 / 0x100
/* 4-2.initial timer0 high byte. */
TL0 = 64535 % 256; // 0xFC17 % 0x100
/* 5.enable timer0 interrupt. */
ET0 = 1;
/* 6.open global interrupt switch. */
EA = 1;
/* 7. */
PT0 = 0;
}
void main()
{
Timer0_Init();
//loop
while(1)
{
}
}
void Timer0_Routine() interrupt 1
{
P2_0 = 0;
}
5-2、以1s间隔点亮LED(Timer0)
Figure16. 以1s间隔点亮LED
c
#include <REGX52.H>
void Timer0_Init()
{
/* 1.set timer0 as mode1 (16-bit). M1 = 0, M0 = 1. */
TMOD = 0x01;
/* 2.TF0 = 0 */
TF0 = 0;
/* 3.timer0 start running. */
TR0 = 1;
/* 4.0~65535 每隔1us计数加一, 总共定时时间65535us. 64535离计数器溢出差值1000, 所以计时时间为1ms. */
/* 4-1.initial timer0 low byte. */
TH0 = 64535 / 256; // 0xFC17 / 0x100
/* 4-2.initial timer0 high byte. */
TL0 = 64535 % 256; // 0xFC17 % 0x100
/* 5.enable timer0 interrupt. */
ET0 = 1;
/* 6.open global interrupt switch. */
EA = 1;
/* 7. */
PT0 = 0;
}
void main()
{
Timer0_Init();
//loop
while(1)
{
}
}
unsigned int T0Cnt;
void Timer0_Routine() interrupt 1
{
/* Inhibit timer0 count overflow. */
/* 4-1.initial timer0 low byte. */
TH0 = 64535 / 256; // 0xFC17 / 0x100
/* 4-2.initial timer0 high byte. */
TL0 = 64535 % 256; // 0xFC17 % 0x100
T0Cnt++;
if(T0Cnt >= 1000) //1s = 1000ms
{
T0Cnt = 0;
P2_0 = ~P2_0;
}
}
5-3、STC_ISP生成定时器配置代码
Figure17. 以1s间隔点亮LED(Timer0)
c
void Timer0_Isr(void) interrupt 1
{
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
Figure18. STC_ISP生成定时器配置代码
5-4、流水灯LED
Figure19. 流水灯LED
c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main(void)
{
P2 = 0xFE;
Timer0_Init();
//loop
while(1)
{
KeyNum = Key();
if(KeyNum)
{
if(1 == KeyNum)
{
LEDMode++;
if(LEDMode >= 2)
{
LEDMode = 0;
}
}
}
}
}
void Timer0_Isr(void) interrupt 1
{
static unsigned int T0Cnt;
/* Inhibit timer0 count overflow. */
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Cnt++;
if(T0Cnt >= 500) //0.5s = 500ms
{
T0Cnt = 0;
if(LEDMode == 0)
P2 = _crol_(P2,1);
if(LEDMode == 1)
P2 = _cror_(P2,1);
}
}
5-5、实验现象
5-6、时钟显示
Figure20. 时钟显示
c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
#include "LCD1602.h"
unsigned char KeyNum,LEDMode,Sec,Mins,Hour;
void main(void)
{
LCD_Init();
Timer0_Init();
P2 = 0xFE;
LCD_ShowString(1,1,"GalaxyClock:");
LCD_ShowString(2,1,"S:");
LCD_ShowString(2,6,"M:");
LCD_ShowString(2,11,"H:");
//loop
while(1)
{
KeyNum = Key();
if(KeyNum)
{
if(1 == KeyNum)
{
LEDMode++;
if(LEDMode >= 2)
{
LEDMode = 0;
}
}
}
LCD_ShowNum(2,3,Sec,2);
LCD_ShowNum(2,8,Mins,2);
LCD_ShowNum(2,13,Hour,2);
}
}
void Timer0_Isr(void) interrupt 1
{
static unsigned int T0Cnt0,T0Cnt1;
/* Inhibit timer0 count overflow. */
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Cnt0++;
T0Cnt1++;
// if(T0Cnt0 >= 500) //0.5s = 500ms
// {
// T0Cnt0 = 0;
// if(LEDMode == 0)
// P2 = _crol_(P2,1);
// if(LEDMode == 1)
// P2 = _cror_(P2,1);
// }
if(T0Cnt1 >= 1000) //1s = 1000ms
{
T0Cnt1 = 0;
Sec++;
if(Sec >= 60)
{
Sec = 0;
Mins++;
if(Mins >= 60)
{
Mins = 0;
Hour++;
if(Hour >= 24)
{
Hour = 0;
}
}
}
}
}