μC/OS-II(也叫UCOSII)是一个老牌的嵌入式实时操作系统,在国内嵌入式圈子里有二十多年的历史了,很多老工程师的启蒙RTOS就是它。虽然现在FreeRTOS、RT-Thread这些更"轻"的RTOS更流行,但UCOSII在教学和一些对稳定性要求高的工业领域依然有市场。今天就聊聊UCOSII的内核原理和在STM32上的移植。
先说UCOSII是什么。它是一个可剥夺型(Preemptive)的实时内核,由Micrium公司的Jean Labrosse编写,代码全部开源(C语言),最早可以跑到8位单片机上。核心功能包括任务调度、任务通信(信号量、邮箱、消息队列、事件标志组)、内存管理、时间管理。一个UCOSII工程里,用户可以创建任意多个任务(理论上限是255个),每个任务有自己的优先级(0-62,数字越小优先级越高),内核总是运行就绪态中优先级最高的任务。
UCOSII有几个关键概念必须理解。任务有五种状态:休眠态、就绪态、运行态、挂起态和中断态。任务切换的核心是"上下文切换"——保存当前任务的寄存器(压栈),加载新任务的寄存器(出栈),然后跳到新任务继续执行。UCOSII的上下文切换用纯软件实现(PendSV中断或者软中断),跟硬件平台相关,需要在移植时实现几个汇编函数。优先级反转问题是RTOS的经典问题,UCOSII用互斥信号量(Mutex)来解决,比普通信号量多了优先级继承机制。
移植到STM32是很多工程师学习UCOSII的第一关。UCOSII的代码分为三部分:与处理器无关的核心代码(OS_CORE.C、OS_TASK.C等)、与应用相关的配置代码(OS_CFG.H、INCLUDES.H)、与处理器相关的移植代码(OS_CPU.H、OS_CPU_A.ASM、OS_CPU_C.C)。移植的核心就是写最后这部分。STM32的移植相对成熟,可以参考Micrium官方移植例程或者正点原子、野火的教程,关键点包括:实现OS_CPU_SysTickInit(用SysTick做1ms定时中断作为系统心跳)、实现OS_CPU_SysTickHandler(中断服务程序里调用OSTimeTick)、实现OSStartHighRdy(启动第一个任务)。
几个移植中的坑要说一下。第一,任务堆栈大小的设置。STM32每个任务的堆栈是独立分配的(用数组模拟),堆栈太小会导致任务跑飞,太大会浪费RAM。一般建议512字节到2KB起步,跑裸机能用的程序堆栈也够用。第二,PendSV优先级的设置。PendSV是用于任务切换的中断,必须设置为最低优先级,否则高优先级中断无法嵌套,任务切换会出问题。第三,OS_CFG.H里的配置要合理。比如OS_TICKS_PER_SEC设置成1000(1ms一次心跳),不能太大也不能太小。
应用上UCOSII的常见模式是:创建几个固定任务(比如按键扫描任务、显示任务、通信任务、主循环任务),用信号量或者消息队列做任务间通信。比如按键任务检测到按键后发送一个消息给显示任务,显示任务收到消息后切换界面。工业控制领域,UCOSII经常和Modbus、CAN这些通信协议结合,用RTOS的多任务能力处理多个通信链路,比裸机的状态机模式清晰很多。不过要提醒的是,新项目建议优先考虑FreeRTOS或者RT-Thread,社区活跃度更高、文档更多,UCOSII这些年更新慢、商业授权也有些争议。
下一篇:没有了