嵌入式:一种裸机编程多任务切换方法
有时候为了实现一些简单的、对实时性要求不高的任务,采用操作系统不仅增加了程序的复杂性,对低性能单片机的资源占用也是值得考虑的问题。这时候操作系统可能不是必要的,可以通过一种简单的方法,在裸机编程中实现类似“多任务切换”的方法。
比如,在某个应用中,我们需要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();
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++;
}
任务的时间变量
然后分别定义两个任务的时间变量threadTime1
,threadTime2
,用于与计数器比较,实现方法如下:
基本思路为:用计数器变量减去任务的时间变量,如果时间大于等于执行周期,则执行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) \
do \
{
\
if (timeFlag - time >= delay) \
{
\
time = timeFlag; \
func(); \
} \
} \
while(0);
然后就可以在大循环中这样写,是不是显得很简洁。
这样rain_capture_thread
任务将会每10ms执行一次,msg_thread
1s执行一次,in_capture_thread
500ms执行一次。
while(1)
{
THREAD(threadTime[0], 10, rain_capture_thread);
THREAD(threadTime[1], 1000, msg_thread);
THREAD(threadTime[2],500,in_capture_thread);
}
这样就可以使用一个定时器实现多个对时间要求不高的任务啦。