电子说
对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。
一:阻塞式延时
while(1)
{
OFF; =
Delay_ms(500);
ON; =
Delay_ms(500);
}
二:定时器延时
三:非阻塞式延时设计
在之前的文章中有对systick的介绍,比如我要设置其10ms中断一次,如何实现呢?
其定义结构体类型如:
typedef struct
{
uint8_t Tick10Msec;
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 bSystem10Msec gTimer.Status.field.bit0
#define bSystem50Msec gTimer.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 bTemp1Sec gTimer.Status.field.bit7
另外为了后面程序清晰,再定义一状态指示:
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)
{
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){
//do something
}
}
void Task_B_Processing(void)
{
if(TIMER_SET == bSystem100Msec){
//do something
}
}
void Task_C_Processing(void)
{
static uint8_t ticks = 0;
if(TIMER_SET == bSystem100Msec){
ticks ++ ;
}
if(5 == ticks){
ticks = 0;
//do something
}
}
void Task_D_Processing(void)
{
if(TIMER_SET == bSystem1Sec){
//do something
}
}
以上示例四个任务进程,
在主轮询里可进行如下处理:
int main(void)
{
while(1)
{
SysTimer _Process();
Task_A_Processing();
Task_B_Processing();
Task_C_Processing();
Task_D_Processing();
}
}
这样,就可以轻松且清晰实现了多个任务,不同时间内处理不同事件。(但注意,每个任务处理中不要有阻塞延时,也不要处理过多的事情,以致处理时间较长。可设计成状态机来处理不同任务。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !