基于链表管理的单片机轮询程序框架

💡 原文中文,约7300字,阅读约需18分钟。
📝

内容提要

本文介绍了单片机裸机程序框架的重要性,以及如何使用时间片轮询框架来实现该框架的完全解耦。通过示例代码展示了如何创建任务并加入到时间片轮询的框架中。文章还介绍了底层侵入式双向链表的使用。该框架使用方便,可以快速创建任务并加入到时间片轮询中。

🔎

延伸解读

解耦设计:业务代码与框架分离

文章强调时间片轮询框架通过接口函数注册任务,用户只需包含头文件,无需修改库文件。这种设计借鉴了实时操作系统的低耦合思路,将任务对象与调度逻辑分离。实际开发中,这意味着新增或删除任务只需调用timeslice_task_add/del,不必改动框架源码,降低了维护成本,也便于团队协作。

侵入式链表的效率与代价

框架底层采用侵入式双向链表管理任务,参考RT-Thread实现。链表节点内嵌在任务结构体中,避免了额外内存分配,遍历和增删操作效率高。但这也要求任务对象必须包含ListObj成员,且链表操作需谨慎处理指针,否则易引发内存错误。适合资源受限的单片机环境,但需开发者对链表原理有基本理解。

时间片调度的运行机制

timeslice_tick递减每个任务的timer,归零时置is_run为TASK_RUN并重置timer;timeslice_exec遍历链表,执行is_run为TASK_RUN的任务后将其置为TASK_STOP。这种设计确保任务按设定周期运行,且每次tick只触发一次。示例中通过两个线程模拟定时器中断和主循环调度,展示了裸机环境下时间片轮询的基本工作流程。

适用场景与注意事项

该框架适合裸机单片机中需要多任务周期调度的场景,接口简单,可快速创建任务。但文章未涉及任务优先级、阻塞或抢占,所有任务按链表顺序轮询执行,长任务可能影响其他任务实时性。此外,示例代码在PC上模拟,移植到单片机需适配定时器和主循环。开发者应评估任务执行时间,避免因单任务过长导致调度延迟。

❓

Q&A

单片机裸机程序为什么需要重视程序框架设计?

良好的代码架构设计可以提高编写效率和调试效果。初学者常一点一点写代码,缺乏整体规划,导致后期代码混乱、bug增多,增加新功能时浪费大量时间甚至崩溃。项目开始时花时间设计架构非常必要。

时间片轮询框架如何实现与业务代码的完全解耦?

借鉴实时操作系统的思维方式,用户只需包含头文件,通过接口函数注册业务代码,无需修改库文件。框架使用结构体作为对象,通过侵入式双向链表管理任务,实现完全解耦。

如何使用时间片轮询框架创建并添加任务?

首先定义TimesilceTaskObj类型的任务对象,然后调用timeslice_task_init初始化任务(指定任务函数、ID和时间片长度),再调用timeslice_task_add将任务加入调度链表。示例中创建了5个任务并依次添加。

时间片轮询框架中的任务调度是如何工作的?

框架通过timeslice_tick在定时器中断中递减每个任务的timer,当timer减到0时设置任务为运行状态并重置timer;timeslice_exec在主循环中遍历任务链表,执行处于运行状态的任务,执行后将其状态置为停止。

框架底层使用的侵入式双向链表有什么特点?

该链表参考RT-Thread实时操作系统的实现,在Linux内核中广泛使用。它通过ListObj结构体嵌入到任务对象中,提供初始化、插入、删除、判空、长度计算等操作,使用container_of宏从链表节点获取宿主结构体,高效且节省资源。

时间片轮询框架提供了哪些接口函数来管理任务?

主要接口包括:timeslice_task_init(初始化任务)、timeslice_task_add(添加任务)、timeslice_task_del(删除任务)、timeslice_get_task_num(获取任务数量)、timeslice_get_task_timeslice_len(获取任务时间片长度)、timeslice_task_isexist(判断任务是否存在),以及timeslice_exec和timeslice_tick用于调度。

🏷️

标签

➡️

继续阅读