上节我们讲了中断的外部中断,基本的了解了一下中断,这一节我们继续来学习中断系统的定时器中断基本原理,本节很重要无论是在比赛中还是在单片机、嵌入式等的学习上都有着很重要的地位。如对本作者有兴趣可以去我主页看其他章节Do My Best的博客_CSDN博客-蓝桥杯单片机比赛学习,sim800c模块领域博主。
定时器中断的基本概念
定时器中断就是根据系统时钟频率自行设置中断时间(多长时间进一次定时器中断),在中断服务函数里写自己想做的程序。定时器有着定时、计数、精准延时、小车速度调节等重要的作用,而且在模拟生成PWM时很方便可以更好的调节小车的速度,而且定时器中断节省了CPU资源。比如,如果要想使LED灯以1s为间隔闪烁,原始思想我们可以加个1s延迟程序使LED闪烁,这样会消耗很多CPU资源;但是现在有了定时器就可以节约CPU负载,我们可以根据系统频率调节好定时器中断时间,这样就可以在不打断CPU执行其他程序时也可以实现LED灯的闪烁,这样就节约了CPU资源。因为定时器有着这样的好处,所以在单片机领域有着重要的地位。
定时器的使用
如下图,我们使用烧写软件STC-ISP生成时钟12T模式、定时长度为1ms的定时器。我们可以看到在初始化定时器的时候我们使用到了AUXR、TMOD、TL/TH、TCON(TF、TR)、IE寄存器,这几个寄存器在初始化的时候很重要,在这里我们务必熟练的掌握。下面我们也会讲其内部结构和使用原理。
定时器时钟模式:
12T模式(将系统时钟进行12分频)是指12个时钟周期执行一条语句。
1T模式(将系统时钟进行1分频)是指1个时钟周期执行一条语句。
定时器定时长度:
定时长度由加法计数器( TH 和 TL)控制,给TH 和 TL不同的值定时器的定时长度不同。这里我们不要求掌握,因为在考试中我们可以借助烧写软件STC-ISP生成任意定时长度的定时器。
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //允许定时器0中断
EA = 1; //允许系统总中断
}
定时器内部结构及其寄存器
如下图,IAP15F2K61S2 单片机内部集成了 3 个 16 位的定时/计数器 T0、 T1 和 T2,其核心部件是一个加法计数器( TH 和 TL),对输入脉冲进行计数,当发生溢出时进入中断。若计数脉冲来自系统时钟,则为定时器方式;若计数脉冲来自 T0( P34)、 T1( P35)或 T2( P31)引脚,则为计数器方式。
1、定时器/计数器0/1的控制寄存器TCON
TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON格式如下:
TF1:定时器T1溢出中断标志。T1被允许计数以后,从初值开始加1计数。当产生溢出时TF1置 “1”,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”(也可由软件清“0”)。
TR1:定时器T1的运行控制位。该位由软件置位和清零。当GATE=0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE=1,TR1=1且INT1输入高电平时,才允许T1计数。
TF0、TR0和TF1、TR1类似,这里就不再解释。
IE1:外部中断1请求源(INT1/P3.3)标志。IE1=1,外部中断向CPU请求中断,当CPU响应该中断时由硬件使IE1清“0”。
IT1:外部中断源1触发控制位。IT1=0,外部中断1 上升沿或下降沿均可触发。IT1=1,外部中断1为下降沿触发。
IE0、IT0和IE1、IT1类似,这里就不再解释。
2、定时器/计数器的工作模式寄存器TMOD
定时/计数器 T0 和 T1 具有四种工作方式,由特殊功能寄存器 TMOD 中的 M1 和 M0 位决定,
如下图1所示。我们这里只讲定时器0,定时器1的工作模式与定时器0的类似,这里就不再讲定时器1了。要看定时器0首先我们先看低4位
![](https://img-blog.csdnimg.cn/4a391ec5a29042f4b1b051e463f50b25.png)
第3位 GATE:当GATE=0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE=1,TR1=1且INT1输入高电平时,才允许T1计数。我们一般默认为“0”
第2位 C/T:当C/T=0,为定时模式。当C/T=1,为计数模式。
第1位和第0位我们合并在一起来表示4种工作方式:M1 M0如下图2所示
![](https://img-blog.csdnimg.cn/8ece4b932d0d445397137892ffe4ffcc.png)
这里的工作方式我们要注意:定时器0/1有上述4种工作方式,但是定时器2只有16 位自动重装初值这1种工作方式。
3、辅助寄存器AUXR
由于STC15系列的的单片机都是1T的8051单片机,为了更好的兼容传统12T的51单片机设置了此寄存器。此寄存器可以通过分频(12分频和1分频)设置单片机的时钟频率。如下图所示,这里我们只讲图中框出的几位。
T0x12:定时器的速度控制位。T0x12=0,定时器采用12分频;T0x12=1,定时器采用1分频。
其它两位也是类似。
4、中断允许寄存器 IE
EA:控制系统总中断。EA=1,打开总中断;EA=0,关闭总中断。
ET1:控制定时器1的中断。ET1=1,打开定时器1中断;ET1=0,关闭定时器1中断。
ET0:控制定时器0的中断。ET0=1,打开定时器0中断;ET0=0,关闭定时器0中断。
以上就是使用定时器时所要了解的全部寄存器。下面我们来详细说一下在比赛中使用定时器的步骤。
使用定时器的步骤:
(1)确定工作方式(这里只能设置定时器0/1的工作方式,定时器2有固定的的工作方式),给工作模式寄存器 TMOD 写入控制字。
例:TMOD=0x42,我们对照上述寄存器,可以看到此寄存器为8位寄存器,高4位设置定时器1的工作模式,低4位设置定时器0的工作模式。我们将16进制的0x42化为2进制的0100 0010,就很容易看出寄存器写入的值,进而推出定时器的工作方式。这里为了让大家更好的理解我画了草图,如下图
(2)通过设置AUXR的值 确定是采用 1T 还是采用 12T 方式,计算定时/计数器的初值,并将初值写入寄存器 TH和 TL。
例:我们在比赛中一般题目都是要求单片机内部振荡器频率设定为 12MHz。如下图3所示
AUXR &= 0x7F; 这里就是设置定时器时钟12T模式,(这里的 “&=” 如有不理解请去(1条消息) 蓝桥杯单片机比赛学习:2、蜂鸣器和继电器的使用和基本原理_Do My Best的博客-CSDN博客有详细解释)这里将16进制0x7F化为2进制0111 1111就可以看出定时器0为12T(12分频),分频后的频率为1MHz,则加法计数器每加1都需要耗时1us,此时我们通过设置TH、TL的初始值就可以精确的设置每进一次中断的时间,详细解释如图4所示。
![](https://img-blog.csdnimg.cn/8500eb083ad848678eeea5711c7f8fa3.png)
![](https://img-blog.csdnimg.cn/1d1b8b6277e14b7fab6a3d2f45747f32.png)
(3)根据需要设置中断控制寄存器 IE 和 IE2 的初值,决定是否开放定时器中断(ET0/ET1/EA=1打开中断)。
(4)将控制寄存器 TCON 中的 TR0、 TR1 和 TR2 位置 1 ,启动定时/计数器。
代码实现:
运用定时器中断,L1以500ms为间隔闪烁,L8以1s为间隔闪烁。
#include "stc15f2k60s2.h"
#include "intrins.h"
sbit L1=P0^0;
/* 选择通道并且输入数据 */
void slect_138_573(unsigned char channel, unsigned char dat)
{
P0 = 0x00;
P0 = dat; /* 这里用P0而不用其他端口是因为我们操作LED、数码管、继电器、蜂鸣器都是对P0进行操作 */
switch(channel) /* 选择通道 */
{
case 4: /* 使Y4输出低电平0,也就是操作LED */
P2 = (P2 & 0x1f) | 0x80; /* (P2 & 0x1f)就是将前3位清0,再操作前3位。将0x80转为2进制1000 0000,这里只看前3位100就是Y4 */
break;
case 5: /* 使Y5输出低电平0,也就是操作继电器和蜂鸣器 */
P2 = (P2 & 0x1f) | 0xa0;
break;
case 6: /* 使Y6输出低电平0,也就是操作8个数码管 */
P2 = (P2 & 0x1f) | 0xc0;
break;
case 7: /* 使Y7输出低电平0,也就是操作8个数码管中的1个数码管显示值 */
P2 = (P2 & 0x1f) | 0xe0;
break;
case 0: /* 关闭所有通道 */
P2 = (P2 & 0x1f) | 0x00;
break;
}
P2 = (P2 & 0x1f) | 0x00; /* 使用后,关闭所有通道 */
}
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //定时器0中断使能
EA=1; //使能总中断
}
unsigned int count=0;
unsigned char led_stat=0xff;
void Timer0Service(void) interrupt 1
{
count++;
if(count%500==0) /* 500ms为间隔 */
{
if((led_stat&0x01)==0x01) /* 检查第0位是否为1 */
led_stat &= ~(0x01); /* 第0位为1,则清0(0x01取反为0xfe,0xfe再与上0x01,将第0位清0)*/
else
led_stat |= (0x01); /* 第0位为0,则置1 */
slect_138_573(4,led_stat);
}
if(count%1000==0)
{
count=0;
if((led_stat&0x80)==0x80)
led_stat &= ~(0x80);
else
led_stat |= (0x80);
slect_138_573(4,led_stat);
}
}
void System_init(void)
{
slect_138_573(4,0xff);
slect_138_573(5,0x00);
Timer0Init();
}
void main(void)
{
System_init();
while(1)
{
}
}