单片机非阻塞延时程序设计

来源:本站
导读:目前正在解读《单片机非阻塞延时程序设计》的相关信息,《单片机非阻塞延时程序设计》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《单片机非阻塞延时程序设计》的详细说明。

对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。

但对于绝大多数人,我们在最最初让灯闪烁起来时大约都会用到阻塞延时实现,会像如下代码的样子:

while(1){LED=OFF;Delay_ms(500);LED=ON;Delay_ms(500);}

然后,在我们接触到定时器,我们会发现,原来用定时中断来处理会更好。比如我们可以500ms中断一次,让灯亮或灭,其余的时间系统还可以做非常之多的事情,效率一下提升了很多。

这时我们就会慢慢意识到,第一种(阻塞延时)方法效率很低,让芯片在那儿空运行几百毫秒,什么也不做,真是莫大的浪费,尤其在芯片频率较高,任务又很多时,这样做就像在平坦宽阔的高速公路上挖了一大坑,出现事故可想而知。

但一个单片机中的定时器毕竟有限,如果我需要几十个或者更多不同时间的定时中断,每一个时间到都完成不同的处理动作,如何去做呢。一般我们会想到在一个定时中断函数中再定义static 变量继续定时,到了所需时间,做不同的动作。而这样又会导致在一个中断里做了很多不同的事情,会抢占主轮询更多时间,有时甚至喧宾夺主,并也不是很如的思维逻辑。

那么有没有更好的方法来实现呢,答案是肯定的。下面介绍我在一个项目中偶遇,一个精妙设计的非阻塞定时延时软件的设计(此设计主要针对于无操作系统的裸机程序)

任务定时用软件是如何设计的呢 ?

且先看其数据结构,这也是精妙所在之处,在此作自顶向下的介绍:

其定义结构体类型如:

typedef struct{ uint8_tTick10Msec;Char_Field Status;}Timer_Struct;

其中Char_Field 为一联合体,设计如下:

typedef union{unsigned char byte;Timer_Bit field;}Char_Field

而它内部的Timer_Bit是一个可按位访问的结构体:

typedef struct{unsigned char bit0:1;unsigned char bit1:1;unsigned char bit2:1;unsigned char bit3:1;unsigned char bit4:1;unsigned char bit5:1;unsigned char bit6:1;unsigned char bit7:1;}Timer_Bit

此联合体的这样设计的目的将在后面的代码中体现出来。

如此结构体的设计就完成了。

然后我们定义的一全局变量,Timer_Struct gTimer;

并在头文件中宏定义如下:

#define bSystem10MsecgTimer.Status.field.bit0#define bSystem50MsecgTimer.Status.field.bit1#define bSystem100Msec gTimer.Status.field.bit2#define bSystem1Sec gTimer.Status.field.bit3#define bTemp10Msec gTimer.Status.field.bit4#define bTemp50Msec gTimer.Status.field.bit5#define bTemp100Msec gTimer.Status.field.bit6#define bTemp1SecgTimer.Status.field.bit

另外为了后面程序清晰,再定义一状态指示:

typedef enum{TIMER_RESET=0,TIMER_SET=1,}TimerStatus;

至此,准备工作就完成了。下面我们就开始大显神通了!

首先,10ms定时中断处理函数如,可以看出,每到达10ms 将把bTemp10Msec置1,每50ms 将把bTemp50Msec 置1,每100ms 将把bTemp100Msec 置1,每1s 将把bTemp1Sec 置1,

void SysTick_Handler(void){bTemp10Msec=TIMER_SET;++gTimer.Tick10Msec;if(0==(gTimer.Tick10Msec%5)){bTemp50Msec=TIMER_SET;}if(0==(gTimer.Tick10Msec%10)){bTemp100Msec=TIMER_SET;}if(100==gTimer.Tick10Msec){gTimer.Tick10Msec=0;bTemp1Sec=TIMER_SET;}}

而这又有什么用呢 ?

这时,我们需在主轮询while(1)内最开始调用一个定时处理函数如下:

void SysTimer _Process(void){gTimer.Status.byte&=0xF0;if(bTemp10Msec){bSystem10Msec=TIMER_SET;}if(bTemp50Msec){bSystem50Msec=TIMER_SET;}if(bTemp100Msec){bSystem100Msec=TIMER_SET;}if(bTemp1Sec){bSystem1Sec=TIMER_SET;}gTimer.Status.byte&=0x0F;}

此函数开头与结尾两句

gTimer.Status.byte&=0xF0;gTimer.Status.byte&=0x0F

就分别巧妙的实现了bSystemXXX (低4位) 和 bTempXXX(高4位)的清零工作,不用再等定时到达后还需手动把计数值清零。此处清零工作用到了联合体中的变量共用一个起始存储空间的特性。

但要保证while(1)轮询时间要远小于10ms,否则将导致定时延时不准确。这样,在每轮询一次,就先把bSystemXXX ,再根据bTempXXX判断是否时间到达,并把对应的bSystemXXX 置1,而后面所有的任务就都可以通过bSystemXXX 来进行定时延时,在最后函数退出时,又会把bTempXXX清零,为下一次时间到达后查询判断作好了准备。

说了这么多,举例说明一下如何应用:

void Task_A_Processing(void){if(TIMER_SET==bSystem50Msec){//dosomething}}void Task_B_Processing(void){if(TIMER_SET==bSystem100Msec){//dosomething}}void Task_C_Processing(void){static uint8_tticks=0;if(TIMER_SET==bSystem100Msec){ticks++;}if(5==ticks){ticks=0;//dosomething}}void Task_D_Processing(void){if(TIMER_SET==bSystem1Sec){//dosomething}}

以上示例四个任务进程,

在主轮询里可进行如下处理:

intmain(void){while(1){SysTimer _Process();Task_A_Processing();Task_B_Processing();Task_C_Processing();Task_D_Processing();}}

这样,就可以轻松且清晰实现了多个任务,不同时间内处理不同事件。(但注意,每个任务处理中不要有阻塞延时,也不要处理过多的事情,以致处理时间较长。可设计成状态机来处理不同任务。)

提醒:《单片机非阻塞延时程序设计》最后刷新时间 2024-03-14 00:52:30,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《单片机非阻塞延时程序设计》该内容的真实性请自行鉴别。