当前位置: 首页 > >

嵌入式:一种裸机编程多任务切换方法

发布时间:

嵌入式:一种裸机编程多任务切换方法

有时候为了实现一些简单的、对实时性要求不高的任务,采用操作系统不仅增加了程序的复杂性,对低性能单片机的资源占用也是值得考虑的问题。这时候操作系统可能不是必要的,可以通过一种简单的方法,在裸机编程中实现类似“多任务切换”的方法。


比如,在某个应用中,我们需要10ms做一次A/D转换,1s串口发送一次数据,500ms读一次外部IO,并且这些任务都不是对时间要求严格的任务,这时候就可以使用下面的方法实现“多任务”,不仅使程序结构更加清晰,也使我们的编程思路更加清晰



以51单片机为例,通过简单的封装,main.c可以更简短清晰:


#include "common.h"
#include "stdio.h"

void main()
{
Gpio_Init();
Out_Close();
Timer0_Init();
Uart1_Init();

DEBUG_PRINTF(" \ | /
");
DEBUG_PRINTF(" - * -
");
DEBUG_PRINTF(" / | \
");
while(1) {

THREAD(threadTime[0], 10, rain_capture_thread); //10ms 捕获雨量计数据

THREAD(threadTime[1], 1000, msg_thread); //1s 发送雨量值

THREAD(threadTime[2],500,in_capture_thread) //500ms
}
}

实现方法
定义计数器变量

首先实现一个定时器,用于递增计数器变量。定义一个uint32_t类型的timeFlag。这里定时器中断定时为1ms,如果几个任务的执行周期比较长,定义为10ms也是可以的。如果是stm32可以直接打开stm32的SysTick中断用于递增计数器


/**
* @brief 定时器0
* @note timeFlag为计数器,每毫秒递增一次
* @retval None
*/
void Timer0() interrupt 1
{
TF0 = 0;
timeFlag++;
}

任务的时间变量

然后分别定义两个任务的时间变量threadTime1threadTime2,用于与计数器比较,实现方法如下:
基本思路为:用计数器变量减去任务的时间变量,如果时间大于等于执行周期,则执行func(),即我们的任务,否则不执行。然后将最新的计数器值赋值给任务的时间变量用于下一次比较。


uint32_t delay1 = 500; //这里的值与上述中断时间有关 ,执行周期 = delay * 中断时间
uint32_t delay2 = 100;
void main()
{
Gpio_Init();
Out_Close();
Timer0_Init();
Uart1_Init();

while(1) //loop
{
if (timeFlag - threadTime1 >= delay1) //时间变量与计数器变量作比较 500ms
{
threadTime1 = timeFlag; //更新时间变量
func1(); //执行我们的任务
}

if (timeFlag - threadTime2 >= delay2) //时间变量与计数器变量作比较 100ms
{
threadTime2 = timeFlag; //更新时间变量
func2(); //执行我们的任务
}
}
}

简陋的封装一下

将时间变量比较的功能部分提升为宏的形式,这里没有判断参数是否合法。


uint32_t threadTime[4] = {0};//定义四个任务的时间变量
#define THREAD(time, delay, func)

if (timeFlag - time >= delay)
{
time = timeFlag;
func();
}

然后就可以在大循环中这样写,是不是显得很简洁。
这样rain_capture_thread任务将会每10ms执行一次,msg_thread1s执行一次,in_capture_thread500ms执行一次。


while(1)
{
THREAD(threadTime[0], 10, rain_capture_thread);
THREAD(threadTime[1], 1000, msg_thread);
THREAD(threadTime[2],500,in_capture_thread);
}


这样就可以使用一个定时器实现多个对时间要求不高的任务啦。



友情链接: