定时器中断延时程序
(1)该类型的delay()函数采用的延时是通过对变量进行递减或递增实现的,很难计算精确的延时时间。
(2)由于跑马灯的状态函数和延时函数是和主任务耦合到一起的,因此主任务的执行时间要对延时的时间产生影响。如图6-12所示,假设一个跑马灯的延时函数的延时时间是1s,但是由于主函数可能存在不同的分支,因此当主函数执行不同分支程序时,实际的跑马灯延时时间是不一样的。
图6-12 程序分支对delay()函数的影响
因此,对于某些对定时精度有要求的场合,例如某项工程需要跑马灯1s更换一个状态(最典型的例子就是时钟指示),就不能采用变量递减或递增的延时方法,而要采用更加精确的中断定时方法。
中断的概念在前面的章节已经有过详细讲解,这里不再赘述,与采用递增递减延时函数相比,采用中断来进行跑马灯的状态更换有以下几个优点。
(1)采用中断函数进行延时,可以精确保证延时的精度,即有效地保证跑马灯状态更换的频率;
(2)采用中断函数进行跑马灯状态的更换,在进行延时期间,并不占用单片机资源,单片机可以执行其他的任务。
采用中断延时函数来进行跑马灯状态更换的软件结构如图6-13所示,主程序和跑马灯状态变换程序是独立分开的,当中断发生时,主程序被打断,进行跑马灯状态的变换。
AT89S51单片机里有两个独立的计时器T0和T1,为了得到精确的定时中断,在这里采用T0的模式0来产生定时中断。如图6-14所示为T0工作于模式0时的结构图。
图6-13 采用时间中断函数的程序结构
图6-14 计时器T0的工作模式0
当T0工作于模式0时,相关需要配置的寄存器如下:
(1)TMOD寄存器:TMOD寄存器是管理计时器T0和T1工作模式和相关配置的寄存器,寄存器内各位如图6-15所示,需要配置的功能位如下所示。
?M10-M00:M10-M00用于选择T0的工作模式,工作于模式0时,T0是一个13位的定时器/计数器,如图6-14所示,THx和TLx分别为8bits和5bits长度,共为13位计数器。
?C/T0#:如图6-14所示,C/T0#决定T0的工作方式是计时器还是计数器,当配置为0时,T0工作方式是计时器,T0的计数器由晶振脉冲时钟的6分频进行触发;而当配置为1时,T0工作方式是计数器,T0的计数器由来自T0引脚的外部脉冲进行触发,可以用于
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
外部输入脉冲数。此处应将C/T0#配置为0。
?GATE0:如图6-14所示,GATE0位决定T0的启动方式,当配置为0时,由TR0启动计时器;当配置为1时,由外部中断启动计时器。此处应将GATE0配置为0。
图6-15 TMOD寄存器
(2)TCON寄存器:TCON寄存器是对T0和T1进行控制的寄存器,寄存器各位如图6-16所示,需要配置和使用的功能位如下:
? TR0:如图6-14所示,TR0控制计数器THx和TLx的停止与启动,该位由软件进行置位与复位,在模式0时,TR0置1为启动T0计数;TR0清零时为停止T0计数。
?TF0:如图6-14所示,当T0计数器溢出时,TF0置1,并向CPU请求中断,当CPU响应时,硬件自动对TF0清零,同时TF0也可以由程序查询或清零。
图6-16 TCON寄存器
(3)TH0和TL0:TH0和TL0在模式0下是一个13位长度的计数器,TH0为高8位,TLx为低5位。TH0和TL0在单片机复位时的初值都是0,当计数器累加溢出时产生中断,因此,为了得到指定频率的中断,必须要计算计数器的装载的初值,下面以产生2ms的中断为例。
如图6-14所示,单片机的外部晶振脉冲在6分频以后对计数器T0进行触发,如使用的是24MHz的外部晶振,则触发的频率如下所示。
f = 24MHz/6 = 4MHz
则2ms共需要的触发数或计数值如下:
2ms * 4MHz = 8000 = 0x1F40
因此可知,需要计数8000后T0溢出产生中断,则T0的初始装载值如下:
2^13 – 8000 = 192 = 0x00C0 = 0000000011000000B;
因此T0计数器的初始装载值为0x00C0,需要注意的是,T0是由THx和TLx组成的,因此0x00C0要按照如图6-17所示的方式装载到THx和TLx中去,其中TH0的初始值为0x06,TL0的初始值为0x00。
图6-17 T0计数器初始值的装载
除了对计时器T0相关寄存器进行配置以外,为了使T0能够产生中断,还需要对单片机的中断寄存器IE进行配置,IE寄存器的结构如图6-18所示,需要配置的功能位如下:
?EA:EA位对AT89S51的所有中断进行管理,当EA置1时,单片机开放中断资源,当EA清零时,单片机屏蔽所有的中断请求。因此在完成必要的初始化后,需要产生中断时,EA位必须置1。
?ET0:ET0位对T0产生的中断进行管理,当ET0置1时,T0溢出时,TF0置1将产生中断,当ET0清零时,T0产生的中断被屏蔽。
图6-18 IE寄存器
由前面的分析,可以得到采用T0计时器中断进行延时的跑马灯状态控制程序如下,该段代码采用龙舞花样作为示例。
/**********************************************
* File: T0IntLEDsExample.c
* Description: LEDs T0 Int Triggle
* Created Date: 2007-09-14
* Last Modified: 2007-09-14
* Author: Jeffrey - Schicksal@126.com
* Notes: None
**********************************************/
#include
#define TH0_VALUE 0x06
#define TL0_VALUE 0x00
unsigned int timer_tick;
unsigned int timer_tick_1s;
void LEDs_Move();
void LEDs_Error();
void LEDs_Gragon();
void TIMER_Init();
void TIMER_Start();
#ifndef true
#define true 1
#endif
#define TASK_1 1
#define TASK_2 2
/**********************************************
* Function: main()
* Input Variables: None
* Return Variables: None
* Usage: Program Entry
*********************************************/
void main()
{
unsigned char System_Status = true; unsigned char System_Task = TASK_2;
EA = 1; //开全局中断
TIMER_Init(); //初始化T0 TIMER_Start(); //启动T0 while(1)
{
// 程序主任务区
// ............
// 程序主任务区
if(System_Status != true) //当系统发生错误 {
EA = 0; //关中断
LEDs_Error(); // 跑马灯指示错误 EA = 1; // 开中断
}
}
}
/**********************************************
* Function: LEDs_Move
* Input Variables: None
* Return Variables: None
* Usage: System Normal Status Report *********************************************/
void LEDs_Move()
{
static unsigned char LEDs = 0x55; // 静态变量用于存储LEDs发光状态
P0 = LEDs; // LED间隔亮灭并移位
delay(LED_FLASH_T); // 延时
LEDs = ~LEDs; // 状态改变
}
/********************************************** * Function: LEDs_Error
* Input Variables: None
* Return Variables: None
* Usage: System Error Status Report *********************************************/ void LEDs_Error()
{
static unsigned char LEDs = 0x00; // 静态变量用于存储LEDs发光状态
P0 = LEDs; // LED警告报警亮灭
delay(LED_FLASH_T); // 延时
LEDs = ~LEDs; // 状态改变
}
/********************************************** * Function: LEDs_Dragon
* Input Variables: None
* Return Variables: None
* Usage: System Dragon LED Animation *********************************************/ void LEDs_Dragon()
{
static unsigned char Direction = 1; // 静态变量用于存储龙舞方向
static unsigned char LED_status = 0x0F; // 静态变量用于存储LEDs发光状态
if(Direction==1)
{
if(LED_status>=0x0F)
LED_status=LED_status<<1;
else if(LED_status==0x07)
LED_status=0x0F;
else if(LED_status==0x03)
LED_status=0x07;
else
LED_status=0x03;
if(LED_status==0xC0)
Direction=0;
}
else
{
if(LED_status==0xE0)
LED_status=0xF0;
if(LED_status==0xC0)
LED_status=0xE0;
else if(LED_status<=0xF0) LED_status=LED_status>>1; if(LED_status==0x03)
Direction=1;
}
P0=~LED_status;
}
/**********************************************
* Function: TIMER_Init
* Input Variables: None
* Return Variables: None * Usage: T0 Initialization *********************************************/
void TIMER_Init(void)
{
ET0 = 0; // 关闭T0的中断 TMOD = 0x00; // T0工作在模式0 TCON = 0x00; // 暂时未启动T0 TL0 = TL0_VALUE;
TH0 = TH0_VALUE; // 产生2ms中断 |24 MHz 晶振
ET0 = 1; // 打开T2的中断
timer_tick = 0;
timer_tick_1s = 0;
}
/**********************************************
* Function: timer0_interrupt * Input Variables: None
* Return Variables: None * Usage: TIMER_Interrupt Service Routine
*********************************************/
void timer0_interrupt(void) interrupt 5 using 1
{
EA = 0; // 关全局中断
TF0 = 0; // 清中断标志
timer_tick++; // 2ms
500, if ( timer_tick == 500 ) // 1s = 2ms
{
timer_tick_1s += 1; // 秒计数增1
timer_tick = 0; // 2ms计数清零
void LEDs_Dragon(); // 龙舞花样状态变换
}
TL0 = TL0_VALUE; // T0初值装载
TH0 = TH0_VALUE; // 产生2ms中断 |24 MHz 晶振
EA = 1; // 开中断
}
/**********************************************
* Function: TIMER_Start
* Input Variables: None
* Return Variables: None
* Usage: Start T0
*********************************************/
void TIMER_Start()
{
TR0 = 1; // 启动T0
}
注意上面的代码有以下几个特点。
(1)跑马灯龙舞花样状态的变换由中断每一秒进行一次变换,在主程序结构中已经找
不到龙舞花样的相关代码了,这和如图6-13所示结构是一致的。 (2)要使用计时器来进行延时操作,初始化操作是不可少的,例如初始化计时器T0
的工作模式、预装载计数器TH0和TL0的值、确定由软件启动还是由外部中断启动等,都
是在函数void TIMER_Init(void)中完成的。
注意:如果没有正确初始化计时器~计时器并不能够正常工作: (3)要使用计时器中断,除了要将计时器中断ET0进行使能配置以外,全局中断使能
位EA也必须配置为使能,否则所有的中断都无法产生。