文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 本文目标](#1.2 本文目标)
- [1.3 适合读者](#1.3 适合读者)
- 二、环境准备
-
- [2.1 uC/OS-III特性概览](#2.1 uC/OS-III特性概览)
- [2.2 源码获取](#2.2 源码获取)
- [2.3 硬件清单](#2.3 硬件清单)
- 三、uC/OS-III移植
-
- [3.1 项目结构](#3.1 项目结构)
- [3.2 配置文件](#3.2 配置文件)
- [3.3 移植层实现](#3.3 移植层实现)
- [3.4 主程序](#3.4 主程序)
- 四、系统架构
- 五、测试验证
-
- [5.1 串口输出示例](#5.1 串口输出示例)
- 六、故障排查
-
- [6.1 任务不运行](#6.1 任务不运行)
- [6.2 系统崩溃](#6.2 系统崩溃)
- 七、总结
-
- [7.1 核心知识点](#7.1 核心知识点)
- [7.2 扩展方向](#7.2 扩展方向)
一、前言
1.1 技术背景
在嵌入式实时操作系统领域,uC/OS-III(Micro-Controller Operating Systems Version 3)是一款广泛应用于商业和工业场景的RTOS内核。相比FreeRTOS,uC/OS-III提供了更丰富的内核服务,包括时间片轮转调度 、任务内嵌信号量 、任务消息队列 、事件标志组等高级特性。
uC/OS-III由Micrium公司开发(现被Silicon Labs收购),通过了严格的工业标准认证(如DO-178B、IEC 61508),适用于对可靠性要求极高的应用场景,如航空航天、医疗设备、工业控制等。
1.2 本文目标
通过本教程,你将学会:
- 理解uC/OS-III核心架构和特性
- 掌握uC/OS-III在STM32F103上的移植方法
- 实现多任务应用开发
- 使用uC/OS-III的高级同步机制
- 构建完整的嵌入式应用系统
技术栈:
- 硬件平台:STM32F103C8T6(Blue Pill)
- 操作系统:uC/OS-III V3.08.00
- 开发环境:STM32CubeIDE + uC/OS-III源码
- 调试工具:ST-Link V2、串口助手
1.3 适合读者
本教程适合中级进阶级读者,假设你已具备:
- 扎实的C语言编程能力
- STM32 HAL库开发经验
- 操作系统基础概念(任务、中断、同步)
二、环境准备
2.1 uC/OS-III特性概览
| 特性 | 说明 |
|---|---|
| 抢占式调度 | 256个优先级,支持同优先级时间片轮转 |
| 任务数量 | 无限制(受限于RAM) |
| 同步机制 | 信号量、互斥锁、事件标志、消息队列 |
| 内存管理 | 支持内存分区管理 |
| 任务服务 | 任务内嵌信号量、任务消息队列 |
| 定时器 | 软件定时器 |
| 中断延迟 | 确定性的中断响应时间 |
| 代码规范 | 符合MISRA C标准 |
2.2 源码获取
步骤1:下载uC/OS-III源码
访问Silicon Labs官网或GitHub获取源码:
bash
git clone https://github.com/weston-embedded/uC-OS3.git
步骤2:源码结构
uC-OS3/
├── Source/ # 内核源码
│ ├── os.h # 主头文件
│ ├── os_core.c # 核心功能
│ ├── os_task.c # 任务管理
│ ├── os_sem.c # 信号量
│ ├── os_mutex.c # 互斥锁
│ ├── os_q.c # 消息队列
│ ├── os_flag.c # 事件标志
│ ├── os_mem.c # 内存管理
│ ├── os_tmr.c # 定时器
│ └── ...
├── Cfg/Template/ # 配置文件模板
│ ├── os_cfg.h # 内核配置
│ └── os_cfg_app.h # 应用配置
└── Port/ # 移植层
└── ...
2.3 硬件清单
| 组件 | 型号/规格 | 数量 | 说明 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | 1片 | 主控制器 |
| USB转TTL | CH340/CP2102 | 1个 | 串口调试 |
| ST-Link | V2 | 1个 | 程序下载调试 |
| LED | 3mm各色 | 3个 | 任务指示 |
三、uC/OS-III移植
3.1 项目结构
uCOSIII_Project/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ └── stm32f1xx_hal_conf.h
│ └── Src/
│ ├── main.c
│ ├── stm32f1xx_hal_msp.c
│ └── system_stm32f1xx.c
├── uC-OS3/
│ ├── Source/ # 内核源码
│ ├── Cfg/ # 配置文件
│ └── Port/ # 移植文件
├── Middlewares/
│ └── uCOS-III/ # 中间件接口
└── README.md
3.2 配置文件
📄 创建文件:
uC-OS3/Cfg/os_cfg.h
c
/**
* @file os_cfg.h
* @brief uC/OS-III 内核配置文件
* @version 1.0
*/
#ifndef OS_CFG_H
#define OS_CFG_H
/* ==================== 系统配置 ==================== */
// 系统节拍频率(Hz)
#define OS_CFG_TICK_RATE_HZ 1000u
// 最高任务优先级(数字越小优先级越高)
#define OS_CFG_PRIO_MAX 64u
// 是否启用统计任务
#define OS_CFG_STAT_TASK_EN 1u
#define OS_CFG_STAT_TASK_STK_SIZE 128u
#define OS_CFG_STAT_TASK_PRIO (OS_CFG_PRIO_MAX - 2u)
// 是否启用空闲任务钩子
#define OS_CFG_TASK_IDLE_EN 1u
#define OS_CFG_TASK_IDLE_STK_SIZE 128u
// 是否启用滴答钩子
#define OS_CFG_TIME_DLY_HMSM_EN 1u
// 是否启用软件定时器
#define OS_CFG_TMR_EN 1u
#define OS_CFG_TMR_TASK_PRIO (OS_CFG_PRIO_MAX - 3u)
#define OS_CFG_TMR_TASK_STK_SIZE 128u
#define OS_CFG_TMR_RATE_HZ 10u
/* ==================== 任务配置 ==================== */
// 是否启用任务创建/删除
#define OS_CFG_TASK_CHANGE_PRIO_EN 1u
#define OS_CFG_TASK_DEL_EN 1u
#define OS_CFG_TASK_Q_EN 1u
#define OS_CFG_TASK_Q_PEND_ABORT_EN 1u
#define OS_CFG_TASK_SEM_EN 1u
#define OS_CFG_TASK_SEM_PEND_ABORT_EN 1u
#define OS_CFG_TASK_SUSPEND_EN 1u
/* ==================== 同步机制配置 ==================== */
// 事件标志
#define OS_CFG_FLAG_EN 1u
#define OS_CFG_FLAG_DEL_EN 1u
#define OS_CFG_FLAG_MODE_CLR_EN 1u
// 互斥锁
#define OS_CFG_MUTEX_EN 1u
#define OS_CFG_MUTEX_DEL_EN 1u
#define OS_CFG_MUTEX_PEND_ABORT_EN 1u
// 信号量
#define OS_CFG_SEM_EN 1u
#define OS_CFG_SEM_DEL_EN 1u
#define OS_CFG_SEM_PEND_ABORT_EN 1u
#define OS_CFG_SEM_SET_EN 1u
// 消息队列
#define OS_CFG_Q_EN 1u
#define OS_CFG_Q_DEL_EN 1u
#define OS_CFG_Q_FLUSH_EN 1u
#define OS_CFG_Q_PEND_ABORT_EN 1u
// 内存管理
#define OS_CFG_MEM_EN 1u
#endif /* OS_CFG_H */
📄 创建文件:
uC-OS3/Cfg/os_cfg_app.h
c
/**
* @file os_cfg_app.h
* @brief uC/OS-III 应用配置文件
* @version 1.0
*/
#ifndef OS_CFG_APP_H
#define OS_CFG_APP_H
#include "os_cfg.h"
// 空闲任务堆栈大小
#define OS_CFG_IDLE_TASK_STK_SIZE 128u
// 统计任务堆栈大小
#define OS_CFG_STAT_TASK_STK_SIZE 128u
// 定时器任务堆栈大小
#define OS_CFG_TMR_TASK_STK_SIZE 128u
// ISR堆栈大小
#define OS_CFG_ISR_STK_SIZE 128u
// 中断嵌套管理
#define OS_CFG_INT_Q_SIZE 10u
#define OS_CFG_INT_Q_TASK_STK_SIZE 128u
#endif /* OS_CFG_APP_H */
3.3 移植层实现
📄 创建文件:
uC-OS3/Port/os_cpu.h
c
/**
* @file os_cpu.h
* @brief uC/OS-III 移植层头文件 (Cortex-M3)
* @version 1.0
*/
#ifndef OS_CPU_H
#define OS_CPU_H
#include <stdint.h>
// 数据类型定义
typedef uint8_t CPU_INT08U;
typedef int8_t CPU_INT08S;
typedef uint16_t CPU_INT16U;
typedef int16_t CPU_INT16S;
typedef uint32_t CPU_INT32U;
typedef int32_t CPU_INT32S;
typedef uint64_t CPU_INT64U;
typedef int64_t CPU_INT64S;
// 布尔类型
typedef uint8_t CPU_BOOLEAN;
#define DEF_TRUE 1u
#define DEF_FALSE 0u
// 状态类型
typedef uint8_t OS_STATUS;
#define OS_STATUS_OK 0u
#define OS_STATUS_ERR 1u
// 临界区管理(使用PRIMASK)
#define OS_CRITICAL_ENTER() __disable_irq()
#define OS_CRITICAL_EXIT() __enable_irq()
// 任务切换触发(使用PendSV)
#define OS_TASK_SW() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
// 系统节拍中断(使用SysTick)
#define OS_TICK_HANDLER SysTick_Handler
// 函数声明
void OSStartHighRdy(void);
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSTickISR(void);
#endif /* OS_CPU_H */
📄 创建文件:
uC-OS3/Port/os_cpu_c.c
c
/**
* @file os_cpu_c.c
* @brief uC/OS-III 移植层C代码 (Cortex-M3)
* @version 1.0
*/
#include "os_cpu.h"
#include "os.h"
// 外部定义的启动函数
extern void OSStartHighRdy(void);
/**
* @brief 系统节拍初始化
* @param ticks 每个节拍的SysTick计数
*/
void OS_CPU_SysTickInit(CPU_INT32U ticks)
{
// 配置SysTick
SysTick->LOAD = ticks - 1u; // 设置重载值
SysTick->VAL = 0u; // 清零计数器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | // 使用处理器时钟
SysTick_CTRL_TICKINT_Msk | // 使能中断
SysTick_CTRL_ENABLE_Msk; // 使能SysTick
}
/**
* @brief 任务级上下文切换
*/
void OSCtxSw(void)
{
// 触发PendSV异常进行上下文切换
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
/**
* @brief 中断级上下文切换
*/
void OSIntCtxSw(void)
{
// 触发PendSV异常进行上下文切换
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
📄 创建文件:
uC-OS3/Port/os_cpu_a.asm
asm
;******************************************************************************
; @file os_cpu_a.asm
; @brief uC/OS-III 移植层汇编代码 (Cortex-M3)
; @version 1.0
;******************************************************************************
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OSTickISR
EXPORT PendSV_Handler
IMPORT OSTCBCurPtr
IMPORT OSTCBHighRdyPtr
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTaskSwHook
IMPORT OSIntEnter
IMPORT OSIntExit
;******************************************************************************
; 启动最高优先级任务
;******************************************************************************
OSStartHighRdy
; 调用任务切换钩子
BL OSTaskSwHook
; 设置运行标志
MOVS R0, #1
LDR R1, =OSRunning
STRB R0, [R1]
; 设置PSP为0,表示第一次任务切换
MOVS R0, #0
MSR PSP, R0
; 触发PendSV进行第一次任务切换
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
; 等待PendSV完成
CPSIE I
OSStartHang
B OSStartHang
;******************************************************************************
; 任务级上下文切换 (通过SVC或触发PendSV)
;******************************************************************************
OSCtxSw
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;******************************************************************************
; 中断级上下文切换
;******************************************************************************
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;******************************************************************************
; 系统节拍中断处理
;******************************************************************************
OSTickISR
PUSH {R4-R11, LR}
BL OSIntEnter
BL OSTimeTick
BL OSIntExit
POP {R4-R11, LR}
BX LR
;******************************************************************************
; PendSV异常处理 - 上下文切换核心
;******************************************************************************
PendSV_Handler
CPSID I ; 禁用中断
; 检查PSP是否为0(第一次任务切换)
MRS R0, PSP
CBZ R0, PendSVHandler_NoSave
; 保存当前任务的上下文
SUBS R0, R0, #0x20 ; 为R4-R11预留空间
STM R0!, {R4-R11}
; 保存当前任务的PSP
LDR R1, =OSTCBCurPtr
LDR R1, [R1]
STR R0, [R1]
PendSVHandler_NoSave
; 调用任务切换钩子
PUSH {LR}
BL OSTaskSwHook
POP {LR}
; 更新当前任务为最高优先级任务
LDR R0, =OSTCBCurPtr
LDR R1, =OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R0]
; 更新当前优先级
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
; 恢复新任务的上下文
LDR R0, [R2] ; R0 = 新任务的PSP
LDM R0!, {R4-R11} ; 恢复R4-R11
MSR PSP, R0 ; 更新PSP
; 异常返回
CPSIE I
BX LR
;******************************************************************************
; 常量定义
;******************************************************************************
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
ALIGN
END
3.4 主程序
📄 创建文件:
Core/Src/main.c
c
/**
* @file main.c
* @brief uC/OS-III 多任务应用主程序
* @version 1.0
*/
#include "main.h"
#include "os.h"
#include <stdio.h>
#include <string.h>
// 任务堆栈
static CPU_STK AppTaskStartStk[128];
static CPU_STK AppTaskLed1Stk[128];
static CPU_STK AppTaskLed2Stk[128];
static CPU_STK AppTaskPrintStk[256];
// 任务控制块
static OS_TCB AppTaskStartTCB;
static OS_TCB AppTaskLed1TCB;
static OS_TCB AppTaskLed2TCB;
static OS_TCB AppTaskPrintTCB;
// 信号量
static OS_SEM LedSem;
// 互斥锁
static OS_MUTEX PrintMutex;
// 消息队列
static OS_Q MsgQ;
static void *MsgQStorage[10];
// 事件标志组
static OS_FLAG_GRP EventFlags;
// 外设句柄
UART_HandleTypeDef huart1;
/**
* @brief 重定向printf到串口
*/
int _write(int file, char *ptr, int len)
{
OS_ERR err;
// 使用互斥锁保护串口输出
OSMutexPend(&PrintMutex, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
OSMutexPost(&PrintMutex, OS_OPT_POST_NONE, &err);
return len;
}
/**
* @brief 启动任务
*/
static void AppTaskStart(void *p_arg)
{
OS_ERR err;
(void)p_arg;
printf("\r\n========================================\r\n");
printf(" uC/OS-III on STM32F103\r\n");
printf(" Multi-Task Demo\r\n");
printf("========================================\r\n\r\n");
// 初始化HAL
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
// 初始化系统节拍
OS_CPU_SysTickInit(SystemCoreClock / OS_CFG_TICK_RATE_HZ);
// 创建同步对象
OSSemCreate(&LedSem, "LedSem", 0, &err);
OSMutexCreate(&PrintMutex, "PrintMutex", &err);
OSQCreate(&MsgQ, "MsgQ", 10, &err);
OSFlagCreate(&EventFlags, "EventFlags", 0, &err);
// 创建应用任务
OSTaskCreate(&AppTaskLed1TCB,
"Led1Task",
AppTaskLed1,
0,
5,
&AppTaskLed1Stk[0],
128 / 10,
128,
0,
0,
0,
OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
&err);
OSTaskCreate(&AppTaskLed2TCB,
"Led2Task",
AppTaskLed2,
0,
6,
&AppTaskLed2Stk[0],
128 / 10,
128,
0,
0,
0,
OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
&err);
OSTaskCreate(&AppTaskPrintTCB,
"PrintTask",
AppTaskPrint,
0,
4,
&AppTaskPrintStk[0],
256 / 10,
256,
0,
0,
0,
OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
&err);
printf("[StartTask] All tasks created\r\n");
// 启动任务删除自身
OSTaskDel(&AppTaskStartTCB, &err);
}
/**
* @brief LED1任务 - 闪烁LED并发送信号量
*/
static void AppTaskLed1(void *p_arg)
{
OS_ERR err;
uint32_t counter = 0;
(void)p_arg;
printf("[Led1Task] Started\r\n");
while (1) {
// 点亮LED
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 200, OS_OPT_TIME_HMSM_STRICT, &err);
// 熄灭LED
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 200, OS_OPT_TIME_HMSM_STRICT, &err);
counter++;
// 每5次闪烁发送信号量
if (counter % 5 == 0) {
OSSemPost(&LedSem, OS_OPT_POST_1, &err);
// 设置事件标志
OSFlagPost(&EventFlags, 0x01, OS_OPT_POST_FLAG_SET, &err);
// 发送消息到队列
char *msg = "Hello from Led1Task";
OSQPost(&MsgQ, (void *)msg, strlen(msg), OS_OPT_POST_FIFO, &err);
}
}
}
/**
* @brief LED2任务 - 等待信号量后闪烁
*/
static void AppTaskLed2(void *p_arg)
{
OS_ERR err;
(void)p_arg;
printf("[Led2Task] Started\r\n");
while (1) {
// 等待信号量
OSSemPend(&LedSem, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
printf("[Led2Task] Semaphore received!\r\n");
// 快速闪烁3次
for (int i = 0; i < 3; i++) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 0, 100, OS_OPT_TIME_HMSM_STRICT, &err);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 0, 100, OS_OPT_TIME_HMSM_STRICT, &err);
}
}
}
/**
* @brief 打印任务 - 接收消息并打印
*/
static void AppTaskPrint(void *p_arg)
{
OS_ERR err;
OS_MSG_SIZE msgSize;
char *msg;
OS_FLAGS flags;
(void)p_arg;
printf("[PrintTask] Started\r\n");
while (1) {
// 等待事件标志
flags = OSFlagPend(&EventFlags,
0x01,
0,
OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_BLOCKING,
NULL,
&err);
if (err == OS_ERR_NONE) {
// 清除事件标志
OSFlagPost(&EventFlags, 0x01, OS_OPT_POST_FLAG_CLR, &err);
// 接收消息
msg = (char *)OSQPend(&MsgQ, 100, OS_OPT_PEND_NON_BLOCKING, &msgSize, NULL, &err);
if (err == OS_ERR_NONE && msg != NULL) {
printf("[PrintTask] Received: %s\r\n", msg);
}
// 打印系统统计
printf("[PrintTask] CPU Usage: %d%%\r\n", OSCPUUsage);
printf("[PrintTask] Context Switches: %lu\r\n", OSTaskCtxSwCtr);
}
OSTimeDlyHMSM(0, 0, 1, 0, OS_OPT_TIME_HMSM_STRICT, &err);
}
}
/**
* @brief 系统入口
*/
int main(void)
{
OS_ERR err;
// 禁用中断
CPU_IntDis();
// 初始化uC/OS-III
OSInit(&err);
if (err != OS_ERR_NONE) {
while (1);
}
// 创建启动任务
OSTaskCreate(&AppTaskStartTCB,
"StartTask",
AppTaskStart,
0,
3,
&AppTaskStartStk[0],
128 / 10,
128,
0,
0,
0,
OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
&err);
if (err != OS_ERR_NONE) {
while (1);
}
// 启动多任务调度
OSStart(&err);
// 不应该到达这里
while (1);
}
// HAL初始化函数...
// SystemClock_Config(), MX_GPIO_Init(), MX_USART1_UART_Init()
四、系统架构
🔌 Hardware
🔄 uC/OS-III Kernel
Synchronization
Tasks
Post
Post
Set
Pend
Pend
Pend
Toggle
Toggle
Print
创建
内核核心
调度/中断/时钟
StartTask
Priority:3
Led1Task
Priority:5
Led2Task
Priority:6
PrintTask
Priority:4
信号量
LedSem
互斥锁
PrintMutex
消息队列
MsgQ
事件标志
EventFlags
LED1
PB12
LED2
PB13
UART1
Debug
五、测试验证
5.1 串口输出示例
========================================
uC/OS-III on STM32F103
Multi-Task Demo
========================================
[StartTask] All tasks created
[Led1Task] Started
[Led2Task] Started
[PrintTask] Started
[Led2Task] Semaphore received!
[PrintTask] Received: Hello from Led1Task
[PrintTask] CPU Usage: 5%
[PrintTask] Context Switches: 1234
[Led2Task] Semaphore received!
...
六、故障排查
6.1 任务不运行
- 检查任务堆栈大小
- 检查优先级设置
- 使用OSStatTask查看任务状态
6.2 系统崩溃
- 检查堆栈溢出
- 检查中断嵌套
- 使用OSDebug功能
七、总结
7.1 核心知识点
- uC/OS-III架构:内核对象、任务管理
- 移植要点:时钟、中断、上下文切换
- 同步机制:信号量、互斥锁、队列、事件标志
- 调试技巧:统计任务、堆栈检查
7.2 扩展方向
- 内存管理:使用内存分区
- 定时器:软件定时器应用
- 低功耗:空闲任务进入睡眠
- 安全认证:符合工业标准
💡 提示:uC/OS-III相比FreeRTOS功能更丰富,但学习曲线更陡峭。建议先掌握FreeRTOS再迁移到uC/OS-III。