news 2026/5/11 12:35:31

小小调度器:轻量任务调度的应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小小调度器:轻量任务调度的应用

参考:

  • http://www.51hei.com/bbs/dpj-132959-1.html
  • https://www.armbbs.cn/forum.php?mod=viewthread&tid=110648
  • https://bbs.eeworld.com.cn/thread-501913-1-1.html

仓库:

  • https://github.com/smset028/xxddq
  • https://github.com/fxyc87/xxddq(改版)

前面我们从理论层面详细介绍了“小小调度器”的设计思路和实现原理;现在,到了把它真正用起来的阶段;在嵌入式开发的世界里,资源永远都是最宝贵的,尤其是在一些低端或者老旧的单片机上,内存和处理能力都非常有限;此时,一个轻量、高效且易用的任务调度方案尤为重要;

“小小调度器”恰恰在这方面表现得非常出色;它的设计极为精简,每个任务只需要保存两个状态变量:一个 unsigned short 用作定时器计数器,和一个 unsigned char 用来记录任务当前执行的代码行位置;换句话说,一个任务的 RAM 占用最小仅为 3 个字节,这在嵌入式系统中实在是太“香”了,不仅节省了宝贵的内存空间,也让系统能够同时管理更多任务;

这种极简设计,使得“小小调度器”在资源受限的环境下,依然能够稳定、可靠地执行多任务调度,满足绝大多数实际需求;无论是传感器采样、串口通信,还是屏幕刷新,都可以轻松实现任务之间的有序切换,既避免了复杂的状态机设计,也避免了繁重的 RTOS 资源开销;

准备工作

将 xxddq 文件添加到 Keil 工程的包含路径中;

因为调度器使用状态机宏的方式,大部分任务实际上是死循环(但函数声明仍有返回值);如果不想看到烦人的告警,可以这样处理,在魔术棒 -> C/C++ 编译器 -> Misc Controls 添加:

--diag_suppress=128,111

任务结构定义

每个任务都需要定义一个包含 C_task 成员的结构体;例如:

Class(LED0Task){C_task task;// 必须包含uint8_ti;// 用户自定义变量}led0={.i=0};

说明:

  • C_task task; 是必须包含的,调度器会根据这个成员来管理任务;
  • 用户自定义变量可以任意添加,但注意不要与调度器内部使用的变量重名;

任务函数定义

任务函数使用 TaskFun 和 EndFun 包围,如下:

TaskFun(LED0Task){while(1){if(me.i==0){GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_RESET);me.i=1;}elseif(me.i==1){GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_SET);me.i=0;}WaitX(500);// 必须手动主动释放CPU,否则该任务永远占用CPU}}EndFun

如果任务不需要延时,也需要使用 WaitX(0); 来释放 CPU 控制权;

任务调度与更新机制

外设中任务时间更新

考虑到每个任务变量是文件内定义的,我们采用“曲线救国”的办法:在每个外设对应的 .c 文件中定义更新函数,如:

voidGPIOTask_Update(void){UpdateTimer(led0);UpdateTimer(key0);}

外设中任务运行函数

同样,为了更好的模块化,每个外设文件中也定义运行函数:

voidGPIOTask_Run(void){RunTask(LED0Task,led0);RunTask(KEY0Task,key0);}

更新函数在 SysTick 中调用

voidSysTick_Handler(void){Ticks++;// 可选全局计时器GPIOTask_Update();// 定时更新所有任务计时器}

在主循环中执行任务

intmain(void){// 初始化代码...while(1){GPIOTask_Run();// 执行各任务逻辑}}

总结

“小小调度器”使用极简的宏和状态机思想,实现了对多个任务的低成本管理,非常适合内存紧张的 MCU 项目;通过合理组织代码结构,使用更新和执行函数对任务状态进行控制,你可以在不引入 RTOS 的情况下,实现类似多任务调度的效果;

这套方法虽然看似“原始”,但在资源受限系统中却非常实用;尤其适用于裸机开发场景中,让 MCU 稳定地跑多个任务而不会陷入状态机地狱或时间片混乱;

注意:由于“小小调度器”本身内部的任务调度机制依赖于 C 语言的 switch-case 结构进行任务状态切换,因此在任务函数内部不要再嵌套使用 switch-case;这可能会导致状态跳转逻辑冲突,甚至出现难以排查的行为错误;如果确实需要实现复杂的分支逻辑,建议改用 if-else 或将该任务拆分为多个子任务分别管理,以保持调度系统的稳定性与可维护性;

阅读原文:小小调度器:轻量任务调度的应用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 12:33:31

进程(2):环境变量与进程地址空间

命令行参数 命令行参数是用户在命令行界面执行可执行程序 / 系统命令时,紧跟在程序名之后输入的字符串序列。 C语言程序想要接收命令行参数,必须使用 main 函数的完整标准原型: int main(int argc, char *argv[])参数名 全称 含义 argc argument count 命令行参数的总个数…

作者头像 李华
网站建设 2026/5/11 12:29:35

金仓数据库 V9R4C19 安全加固实战:禁用 root 部署 + hashbytes 单向哈希

文章目录引言:两个看似平常的操作,暗藏安全隐患安全能力一:禁止 root 用户执行数据库部署为什么不能用 root?金仓的具体实现正确的部署方式给运维团队的建议安全能力二:hashbytes 单向哈希替代可逆加密可逆加密的致命弱…

作者头像 李华
网站建设 2026/5/11 12:28:50

从代码到图表:Mermaid Live Editor如何重塑技术文档可视化范式

从代码到图表:Mermaid Live Editor如何重塑技术文档可视化范式 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-liv…

作者头像 李华
网站建设 2026/5/11 12:28:32

【算法】小白也能懂 · 第 3 节:链表反转(迭代法、递归法)

链表反转是面试中出镜率最高的算法题之一,也是理解链表操作的最佳练习。这一节我们会用两种方法来实现:迭代法和递归法,并配合图解帮你真正搞懂每一步在干什么。 1. 什么是链表 在正式开始之前,先快速回顾一下链表的结构。 链表由一系列"节点"组成,每个节点包…

作者头像 李华
网站建设 2026/5/11 12:28:13

QGC界面切换背后的秘密:拆解MainToolBar.qml如何通过信号槽驱动五大视图

QGC界面切换背后的秘密:拆解MainToolBar.qml如何通过信号槽驱动五大视图 当你在QGroundControl(QGC)中点击底部工具栏的按钮时,整个界面会流畅地切换到对应的功能视图。这看似简单的交互背后,隐藏着QML框架精妙的信号槽机制和组件化设计思想…

作者头像 李华