协程OSFrame@AI8H,最简代码实现多任务阻塞,替代 switch 状态机
前言:本帖的前身是 “裸机编程框架, 可以显示CPU使用率”.
近日在看到 王同学的 【协程方式】实现【多任务调度 / 多线程】 帖时.
突然想到裸机编程也可以利用OS的阻塞原理并用简单代码实现.
于是就出现了这个 协程OSFrame@AI8H
特点:
* 这是一个协程OS,但完全继承了祼机的简洁,每个任务仅占用 4ByteRAM,code 仅占用 478byte
* 符合OS的编程习惯,每个任务中一个while(1)循环。
* 因为是协程,没有RTOS中的重入问题,当然也没有信号量这一块。这也是代码为何这么简单的原因
* 用 TaskTickDly() 替代 switch 状态机,使代码看起来就像是顺序执行,编程逻辑变简单了。
* 已实现了CPU使用率统计功能。利于开发阶段调试。
* 效率极高,任务切换就像发生了一次return,几乎没有额外的CPU损耗。
用法详细介绍:先看看任务函数 TaskA:
代码中用两个 TaskTickDly() 函数来点亮LED闪烁,
演示了下次运行时,是从TaskTickDly() 函数的下一句开始执行的。即断点续继执行
每个任务都是一个while(1) 死循环,看起来并不需要返回,是不是有OS的感觉了.
实际是上 TaskTickDly()会跳出任务。
void TaskA(void)
{
TaskStart(); //任务开始, 此句不能省略
while(1)
{
LED = 1;
TaskTickDly(500); //阻塞延时500个时钟滴答,演示断点继续功能
LED = 0;
TaskTickDly(500); //阻塞延时500个时钟滴答,演示断点继续功能
}
}
OSFrame系统服务仅两个 ,分别为:
TaskStart() //这是一个宏,此宏定义了任务所需的静态变量并初始化。
TaskTickDly()//也是一个宏,实现阻塞功能。
范例中的另外两个任务:
/*---------------------------------------------------------
TaskB 扫描串口,接收数据
----------------------------------------------------------*/
void TaskB(void)
{
TaskStart(); //任务开始, 此句不能省略
while(1)
{
Uart_Polling(); //扫描串口,接收数据
TaskTickDly(20); //阻塞延时20个时钟滴答
}
}
/*---------------------------------------------------------
TaskC打印CPU使用率
----------------------------------------------------------*/
void TaskC(void)
{
TaskStart(); //任务开始, 此句不能省略
while(1)
{
#if USE_STATISTICAL
printf("CPU使用率为: %u.",CPUUsage/10U); printf("%u%%\r\n",CPUUsage%10U);
#endif
TaskTickDly(1000); //阻塞延时1000个时钟滴答
}
}
调度器:即创建任务
/*---------------------------------------------------------
调度器顶端, TICK_hook() 每滴答被调用一次
---------------------------------------------------------*/
void TICK_hook(void)
{
TaskA(); //每个任务函数都在此调用
TaskB();
TaskC();
WDT_CONTR = 0X37; //启用看门狗
}
另外,代码中还包含了两个可选项。适应多种场景,
Main_Polling(),启用后,只要CPU有空闲时间,就不断的重复调用 Main_Polling()
Timer0_Ihook() ,启用后,每滴答调用一次 Timer0_Ihook()这个是中断调用
我认为这个框架可以替代大部分祼机编程,作为项目编程的一个起点
协程OSFrame@AI8H
补充说明:
* TaskStart() TaskTickDly()目前只支持在任务函数中调用
* 任务函数中如果有局部变量,要加static, 这是因为 KEIL C51存在变量分析覆盖,
也就是说任务函数中只能定义静态局部变量。以避免 TaskTickDly() 阻塞 任务时,其它任务改变了函数体中的局部变量值。
TaskTickDly() 阻塞 任务时,编译器并不知道你已经退出了函数。
要改进这两个限制也不难,不过先就这样吧,这东西也是随兴而起,哪天自己用到了,再折腾也不迟
协程OSFrame@AI8H的设计理念确实为裸机编程提供了一种简洁且高效的多任务处理方案。以下是对该框架的进一步分析和优化建议:
1. 协程与裸机编程的结合
协程(Coroutine)是一种轻量级的线程,能够在执行过程中暂停和恢复。与传统的RTOS相比,协程不需要复杂的上下文切换和资源管理,因此在资源受限的嵌入式系统中具有显著优势。协程OSFrame@AI8H通过协程机制实现了多任务调度,同时保持了裸机编程的简洁性。
2. 任务管理与阻塞机制
在协程OSFrame@AI8H中,每个任务通过TaskTickDly()函数实现阻塞延时。该函数不仅实现了任务的暂停,还确保了任务在恢复时从上次暂停的位置继续执行。这种机制替代了传统的switch状态机,使得代码逻辑更加清晰,易于维护。
3. 资源占用与性能优化
每个任务仅占用4字节的RAM,代码占用478字节,这在资源受限的嵌入式系统中是非常高效的。然而,为了进一步提升性能,可以考虑以下优化措施:
任务优先级:引入简单的任务优先级机制,确保高优先级任务能够及时响应。
事件驱动:结合事件驱动机制,减少不必要的任务轮询,降低CPU使用率。
内存管理:优化内存分配策略,减少内存碎片,提高系统稳定性。
4. CPU使用率统计
CPU使用率统计功能对于系统调试和性能优化至关重要。协程OSFrame@AI8H已经实现了该功能,但可以进一步扩展:
任务级统计:统计每个任务的CPU使用率,帮助开发者识别性能瓶颈。
历史数据:记录历史CPU使用率数据,便于分析系统负载变化。
5. 代码示例与优化
以下是一个优化后的任务函数示例,展示了如何利用协程OSFrame@AI8H实现多任务处理:
c
void TaskA(void)
{
TaskStart(); // 任务开始, 此句不能省略
while(1)
{
LED = 1;
TaskTickDly(500); // 阻塞延时500个时钟滴答,演示断点继续功能
LED = 0;
TaskTickDly(500); // 阻塞延时500个时钟滴答
}
}
void TaskB(void)
{
TaskStart(); // 任务开始, 此句不能省略
while(1)
{
// 执行其他任务逻辑
TaskTickDly(100); // 阻塞延时100个时钟滴答
}
}
6. 总结
协程OSFrame@AI8H通过协程机制实现了高效的多任务处理,同时保持了裸机编程的简洁性。通过进一步优化任务管理、资源占用和CPU使用率统计功能,可以进一步提升系统的性能和稳定性。该框架为嵌入式系统开发者提供了一种新的思路,值得在实际项目中推广应用。
希望以上分析和建议能够对协程OSFrame@AI8H的进一步发展和优化提供参考。
[本文内容由国芯人工智能辅助生成,仅供参考] 不错,很简洁,有个详细文档也许更容易理解 您好,老师,这个系统我加入了USBCDC串口后,电脑也可以显示出(COM5)USB-CDC,CDC端口,但是发送不了消息。然后把Frame.c文件内的一部分屏蔽后就可以通讯了。想请教一些USBCDC该怎么在这个OS内使用。 一束光的旅程 发表于 2025-5-14 09:59
您好,老师,这个系统我加入了USBCDC串口后,电脑也可以显示出(COM5)USB-CDC,CDC端口,但是发送不了消息 ...
初看了一下代码,你用的是USB_CDC库, 我一般是引用原码, 库很少用,
以我对这个USB_CDC库的初浅理解, 这个库已包包含了, USB 和 UART 的初始化和相关操作.
他会自动把USB_CDC映射到串口收发, 主要问题应该就出在这了, 我代码里面也初始化了串口,
并使用了prntf() , 两者硬件冲突.
所以, 你只要把我原有的和串口相关的代码删除,应该就可以正常用了. 包括printf()
tzz1983 发表于 2025-5-14 10:34
初看了一下代码,你用的是USB_CDC库, 我一般是引用原码, 库很少用,
以我对这个USB_CDC库的初浅理解, 这个 ...
我测试了一下,确实是这样。谢谢老师指导 tzz1983 发表于 2025-5-14 10:34
初看了一下代码,你用的是USB_CDC库, 我一般是引用原码, 库很少用,
以我对这个USB_CDC库的初浅理解, 这个 ...
又仔细看了usb的头文件,上面有关于串口和usbcdc的说明。
老师您好,我使用这个框架系统时发现几个问题:1.CPU使用率问题:串口时,无论是否打开Main_Polling沟通,CPU使用率都是0.0%,而使用USBCDC串口使用率是有区别的,开Mainpolling使用率时24.4%,不开时是0.4%,2.当使用串口时,打开定时器钩子,出现printf乱码,LED指示灯也不再闪烁,类似死机情况,而使用USBCDC打印时没有这个情况,CPU使用率增加0.1%。
针对上面的情况,串口输出的cpu使用率和cdc串口输出的使用率哪个一个是真实的。
想请教老师这个该怎么解决。这个框架系统大部分代码能看懂,使用也很简单,无奈自己对mcu的理解是皮毛,想请教老师学习学习。谢谢了
这个是两种模式下的程序,方便老师快速打开 自己设计的一款类似于arduino nano的小板子,之前在论坛上上传过原理图,现在打板了,原理图有些变动