前言
上一文章中已经简要地介绍了阻塞延时和非阻塞延时,并给出了相关的编程思路,如果还不了解的伙伴可阅读文章单片机开发思想进阶01:阻塞与非阻塞延时。但由于上一文章中的非阻塞延时的实现很难实现精确的定时,并且随着任务的增加,主函数执行程序时的时基极大可能混乱,导致程序任务的时序混乱。本文开始介绍使用单个定时器实现多路并行非阻塞延时,这个方法克服了上一个方法的缺点。实验的具体任务为:使用51单片机的单个定时器使得LED灯D1、D2、D3并行闪烁,并且三盏灯的闪烁时间(暗-亮一次的时间)分别为:2S、1mS、200mS。
硬件电路
这里使用Proteus仿真软件作图并仿真,电路图如下:
编程思想
使用单片机的定时器定时1ms作为定时的时基,每次定时1ms时间到进入一次中断函数,在中断服务函数中进行计数,计数时间到就打开软件定时开关。比如想要定时1s,可以在中断服务函数中进行1000次计数,计数时间到就将软件定时开关置位,最后通过判断软件定时开关来处理相关的任务即可。基于这种编程思想就可以通过单个定时器来实现多路非阻塞定时的功能。
软件编程
/*基于一个定时器加软件并行实现多个LED闪烁*/
#include "reg52.h"
/*宏定义:D1~D3的闪烁时间*/
#define D1_DELAY_XMS 1000
#define D2_DELAY_XMS 500
#define D3_DELAY_XMS 100
/*********************************/
#define u16 unsigned int
sbit D1 = P2^0;
sbit D2 = P2^1;
sbit D3 = P2^2;
/*位定义:D1~D3延时时间到的标志位*/
bit D1_Delay_ON;
bit D2_Delay_ON;
bit D3_Delay_ON;
/*********************************/
u16 D1DelayCount, D2DelayCount, D3DelayCount; //1ms计数值定义
void Timer0Init(void);
void main(void)
{
Timer0Init();
D1DelayCount = D1_DELAY_XMS;
D2DelayCount = D2_DELAY_XMS;
D3DelayCount = D3_DELAY_XMS; //延时计数的装载初始
while(1)
{
if(D1_Delay_ON) //定时延时到
{
D1 = ~D1;
D1_Delay_ON = 0; //定时到标志位清零,为下一次延时做准备
D1DelayCount = D1_DELAY_XMS; //重载用于延时的计数值,为下次延时准备
}
if(D2_Delay_ON)
{
D2 = ~D2;
D2_Delay_ON = 0;
D2DelayCount = D2_DELAY_XMS;
}
if(D3_Delay_ON)
{
D3 = ~D3;
D3_Delay_ON = 0;
D3DelayCount = D3_DELAY_XMS;
}
}
}
void Timer0Init(void) //1毫秒@12MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //定时模式1
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //开启定时器中断
EA = 1; //开启总中断
}
void time0_isr(void) interrupt 1 //定时中断函数
{
TR0 = 0; //先清零,确保执行完中断函数再开启定时
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
if(D1DelayCount) //D1延时的软件定时器
{
D1DelayCount --;
if(!D1DelayCount)
D1_Delay_ON = 1; //D1的延时时间到
}
if(D2DelayCount)
{
D2DelayCount --;
if(!D2DelayCount)
D2_Delay_ON = 1;
}
if(D3DelayCount)
{
D3DelayCount --;
if(!D3DelayCount)
D3_Delay_ON = 1;
}
TR0 = 1;
}