嵌入式软件笔记:基于链表的多任务代码架构

        嵌入式软件简单的可以直接写裸机代码,main函数里先初始化硬件然后就跑while死循环,里面放几个函数,比如控制电机,比如读传感器啥的;还可以通过定时器定时执行。

        如果任务多了,比如目前要读10个传感器、控制5个不同的电机设备、切换串口、切换I2C进行通信,最后还有综合的控制算法,这样的逻辑就比较复杂。如果还用裸机的代码架构,逻辑互相耦合的太多,代码写起来容易乱套。

        你也可以直接用别人写好的rtos去管理任务,但是我用的少,我用我自己这套更多些,觉得挺好用。

        前置:你要了解什么是链表,然后看看基于C语言的构建链表的代码。

1、构建一个简单的链表

建立一个C文件和对应的H文件,可以命名为Link.c,Link.h。定义链表结点的添加函数以及链表结点的结构体等。(这里没写删除结点,插入节点。我目前遇到的嵌入式项目中软件的生命周期都是伴随着板子上电,所有任务都开始工作,断电就都结束,用不上删除和插入的功能。)

Link.c
S_Link* Link_Add(S_Link* node_1, S_Link* node_2)
{
	if (node_1 == NULL)//头
	{
		node_1 = node_2;
	}
	else
	{
		S_Link* p = NULL;
		for (p = node_1; (p != node_2) && (p->pNext != NULL); p = p->pNext)
        {
            ;//直到p->pNext为空跳出
        }
		if (node_2 != p)
        {
            p->pNext = node_2;
        }	
	}
	return node_1;
}
分割线///
Link.h
typedef struct Link
{
	struct Link *pNext;
}S_Link;

S_Link* Link_Add(S_Link* node_1, S_Link* node_2);

以上是最基础的链表代码,请根据项目自行添加内容即可。

2、构建任务管理架构

创建Task.c和Task.h,用来写任务架构的代码。可以分为三大类任务,一种是轮询任务,挨个儿执行即可。(比如不依赖时间,而依赖某些标志来执行的函数);二是非中断定时任务,在大概的时间范围内执行即可(一般在这里执行的函数,间隔时间都较长,比如读传感器,1秒1读,就可以放在这里);三是中断定时任务,由定时器的中断决定何时执行(与硬件直接通信的都需要在这里严格定时,保证时序,比如有的ADC你得用SPI通信,有的可编程驱动器得用I2C通信,反正与硬件直接相关的,放这里面就对啦)。

time_ms放在中断里累加即可,用来计时,单位是1毫秒。

Task.c

uint64_t time_ms = 0;
S_LoopTask* loopTask = NULL;
S_TimerTask* timerTask = NULL;
S_TimerTask* timerTask_Irq = NULL;

//遍历
void LoopTask_Traversal(S_LoopTask* task)
{
	while (task != 0)
	{
		if (task->function)
        {
            task->function();
        }
		task = task->pNext;
	}
}

void TimerTask_Traversal(S_TimerTask* task)
{
	while (task != 0)
	{
		if(task->nextRunTime <= time_ms)
		{
			task->function();
			task->nextRunTime += task->interval;
		}
		task = task->pNext;
	}
}

void LoopTask_Add(S_LoopTask* task)
{
    loopTask = (S_LoopTask *)Link_Add((S_Link *)loopTask, (S_Link *)task);
}

void TimerTask_Add(S_TimerTask* task)
{
    timerTask = (S_TimerTask *)Link_Add((S_Link *)timerTask, (S_Link *)task);
}

void IrqTimerTask_Add(S_TimerTask* task)
{
    timerTask_Irq = (S_TimerTask *)Link_Add((S_Link *)timerTask_Irq, (S_Link *)task);
}

Task.h

typedef struct LoopTask
{
	struct LoopTask *pNext;
	void (*function)(void);
}S_LoopTask;


typedef struct TimerTask
{
	struct TimerTask *pNext;
	void (*function)(void);
	uint32_t interval;
    uint64_t nextRunTime;
}S_TimerTask;


以上是简化过的任务结构体,你还可以加入任务开关,控制计数等其他成员。

3、以具体的例子来讲如何使用。

当前我们要使用pwm信号控制一个电机的转速。这里面可能会包含MCU的PWM初始化C文件,电机驱动的C文件,还有电机转速的控制算法、滤波算法等等C文件。

推荐在控制算法的C文件中加入一个全局的静态变量,然后在初始化函数的最后添加任务。

static S_TimerTask timerTask_MotorContorl = {NULL, Motor_Control, 250, 500};//250ms控制一次

void Motor_Control(void)
{
    //具体控制算法
}

void Motor_Init(void)
{
    //一系列初始化
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                
    TimerTask_Add(&timerTask_MotorContorl); 
}

上一步我们已经构建好了定时任务的遍历,放在main函数中执行即可。

main.c
int main(void)
{
    //xxxxxxxxxxx
    while(1)
    {
        LoopTask_Traversal(loopTask);
        if(isAllowTimerTaskRun)
        {
            isAllowTimerTaskRun= false;
            TimerTask_Traversal(timerTask);
        }
    }
}

标志位放在定时器里置true,频率按照你要执行的所有任务的最小公约数来定即可。

4、总结

这样写就非常清爽,想加几个任务就加几个,每个任务都没有耦合,不会写成一大坨代码,调试起来也非常方便。

但要注意,每个任务里不要加入长时间的延时,不然后面就全堵住了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值