一、移植前准备
1、硬件环境
| 项目 |
规格 |
| MCU |
STM32F401CCU6 / STM32F401RET6 |
| 内核 |
Cortex-M4 (with FPU) |
| 主频 |
84 MHz |
| Flash |
256 KB / 512 KB |
| RAM |
64 KB / 96 KB |
2、软件环境
| 工具 |
版本 |
| Keil MDK |
V5.x |
| STM32CubeMX |
V6.x |
| μC/OS-II |
V2.92 |
| CMSIS |
V2.x |
二、获取 μC/OS-II 源码
官方源码结构
uCOS-II/
├── Source/ # 内核源码(与CPU无关)
│ ├── os_core.c
│ ├── os_flag.c
│ ├── os_mbox.c
│ ├── os_mem.c
│ ├── os_mutex.c
│ ├── os_q.c
│ ├── os_sem.c
│ ├── os_task.c
│ └── os_time.c
├── Ports/ARM-Cortex-M4/ # 移植代码
│ ├── os_cpu.h
│ ├── os_cpu_c.c
│ └── os_cpu_a.asm
└── Config/
└── os_cfg.h
三、CubeMX 工程配置
1、基础配置
/* System Core */
SYS: Debug = Serial Wire
RCC: HSE = Crystal/Ceramic Resonator
/* Clock Configuration */
PLL Source: HSE
System Clock: 84 MHz
AHB Prescaler: 1
APB1 Prescaler: 2 (42 MHz)
APB2 Prescaler: 1 (84 MHz)
2、SYS 时基配置
SYS -> Timebase Source = TIM1 (不要用 SysTick,留给 OS)
3、生成代码
- Project Name:
STM32F401_uCOSII
- Toolchain: MDK-ARM V5
- 勾选
Generate peripheral initialization as a pair of .c/.h files
四、μC/OS-II 移植核心文件
移植文件清单
Core/
├── Src/
│ ├── os/ # μC/OS-II 内核
│ │ ├── Source/ # 官方源码
│ │ ├── Port/ # 移植代码
│ │ └── Config/ # 配置文件
│ ├── app/ # 应用代码
│ │ ├── app.c
│ │ └── app_cfg.h
│ └── bsp/ # 板级支持包
│ ├── bsp.c
│ └── bsp.h
五、移植关键代码
1、os_cfg.h(配置文件)
#ifndef OS_CFG_H
#define OS_CFG_H
/* ------------------- 内核配置 ------------------- */
#define OS_MAX_TASKS 16u /* 最大任务数 */
#define OS_MAX_EVENTS 10u /* 最大事件数 */
#define OS_MAX_FLAGS 5u /* 最大标志组数 */
#define OS_MAX_MEM_PART 5u /* 最大内存分区数 */
#define OS_MAX_QS 5u /* 最大队列数 */
/* ------------------- 功能开关 ------------------- */
#define OS_FLAG_EN 1u /* 使能事件标志 */
#define OS_MBOX_EN 1u /* 使能消息邮箱 */
#define OS_MEM_EN 1u /* 使能内存管理 */
#define OS_MUTEX_EN 1u /* 使能互斥信号量 */
#define OS_Q_EN 1u /* 使能消息队列 */
#define OS_SEM_EN 1u /* 使能信号量 */
#define OS_TASK_CHANGE_PRIO_EN 1u /* 允许改变任务优先级 */
#define OS_TASK_CREATE_EN 1u /* 允许创建任务 */
#define OS_TASK_DEL_EN 1u /* 允许删除任务 */
#define OS_TASK_SUSPEND_EN 1u /* 允许挂起任务 */
#define OS_TIME_DLY_HMSM_EN 1u /* 允许延时(时:分:秒:毫秒) */
#define OS_TIME_DLY_RESUME_EN 1u /* 允许恢复延时 */
/* ------------------- 滴答时钟 ------------------- */
#define OS_TICKS_PER_SEC 1000u /* 1ms 时基 */
/* ------------------- 钩子函数 ------------------- */
#define OS_APP_HOOKS_EN 1u /* 应用钩子 */
#define OS_CPU_HOOKS_EN 1u /* CPU钩子 */
#endif /* OS_CFG_H */
2、os_cpu.h(CPU相关配置)
#ifndef OS_CPU_H
#define OS_CPU_H
#ifdef __cplusplus
extern "C" {
#endif
/* 数据类型定义 */
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
/* 堆栈增长方向(STM32为向下增长) */
#define OS_STK_GROWTH 1u
/* 临界区保护 */
#define OS_ENTER_CRITICAL() CPU_SR_ALLOC(); CPU_CRITICAL_ENTER()
#define OS_EXIT_CRITICAL() CPU_CRITICAL_EXIT()
/* 任务栈初始化函数声明 */
OS_STK *OSTaskStkInit(void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt);
#ifdef __cplusplus
}
#endif
#endif /* OS_CPU_H */
3、os_cpu_c.c(C语言移植层)
#include "ucos_ii.h"
#include "stm32f4xx_hal.h"
/* 系统滴答定时器初始化 */
void OS_CPU_SysTickInit(INT32U cnts)
{
SysTick->LOAD = cnts - 1u;
SysTick->VAL = 0u;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
}
/* 任务栈初始化 */
OS_STK *OSTaskStkInit(void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
stk = ptos;
/* 模拟异常入栈(Cortex-M4 自动保存的寄存器) */
*(stk) = (INT32U)0x01000000uL; /* xPSR */
*(--stk) = (INT32U)task; /* PC */
*(--stk) = (INT32U)0x14141414uL; /* LR */
*(--stk) = (INT32U)0x12121212uL; /* R12 */
*(--stk) = (INT32U)0x03030303uL; /* R3 */
*(--stk) = (INT32U)0x02020202uL; /* R2 */
*(--stk) = (INT32U)0x01010101uL; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 */
/* 手动保存的寄存器 */
*(--stk) = (INT32U)0x11111111uL; /* R11 */
*(--stk) = (INT32U)0x10101010uL; /* R10 */
*(--stk) = (INT32U)0x09090909uL; /* R9 */
*(--stk) = (INT32U)0x08080808uL; /* R8 */
*(--stk) = (INT32U)0x07070707uL; /* R7 */
*(--stk) = (INT32U)0x06060606uL; /* R6 */
*(--stk) = (INT32U)0x05050505uL; /* R5 */
*(--stk) = (INT32U)0x04040404uL; /* R4 */
return stk;
}
/* 钩子函数(可选) */
void OSInitHookBegin(void) {}
void OSInitHookEnd(void) {}
void OSTaskCreateHook(OS_TCB *ptcb) {}
void OSTaskDelHook(OS_TCB *ptcb) {}
void OSTaskSwHook(void) {}
void OSTimeTickHook(void) {}
4、os_cpu_a.asm(汇编移植层)
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
; 外部引用
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSRunning
IMPORT OSTaskSwHook
; 导出函数
EXPORT OS_CPU_SR_Save
EXPORT OS_CPU_SR_Restore
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT PendSV_Handler
; 临界区保护
OS_CPU_SR_Save
MRS R0, PRIMASK ; 读取 PRIMASK 到 R0
CPSID I ; 关中断
BX LR
OS_CPU_SR_Restore
MSR PRIMASK, R0 ; 恢复 PRIMASK
BX LR
; 启动最高优先级任务
OSStartHighRdy
LDR R0, =OSRunning ; OSRunning = TRUE
MOV R1, #1
STRB R1, [R0]
LDR R0, =OSTCBHighRdy ; 获取最高优先级任务的 TCB
LDR R1, [R0]
LDR SP, [R1] ; 切换到该任务的堆栈
POP {R4-R11} ; 恢复手动保存的寄存器
POP {R0-R3, R12, LR} ; 恢复异常自动保存的寄存器
ADD SP, SP, #4 ; 跳过 xPSR
BX LR ; 返回到任务
; 任务切换(PendSV)
PendSV_Handler
CPSID I ; 关中断
PUSH {R4-R11} ; 保存当前任务寄存器
LDR R0, =OSTCBCur ; 保存当前任务 SP
LDR R1, [R0]
STR SP, [R1]
BL OSTaskSwHook ; 调用钩子函数
LDR R0, =OSPrioCur ; 切换优先级
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; 切换 TCB
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR SP, [R2] ; 切换到新任务堆栈
POP {R4-R11} ; 恢复新任务寄存器
CPSIE I ; 开中断
BX LR ; 返回
; 任务级切换
OSCtxSw
PUSH {R4, R5, LR}
BL PendSV_Handler
POP {R4, R5, LR}
BX LR
; 中断级切换
OSIntCtxSw
BL PendSV_Handler
BX LR
ALIGN
END
六、系统集成(main.c)
1、系统初始化
#include "main.h"
#include "ucos_ii.h"
#include "bsp.h"
/* 任务定义 */
#define APP_TASK_START_PRIO 5
#define APP_TASK_LED_PRIO 6
#define APP_TASK_BUTTON_PRIO 7
#define APP_TASK_START_STK_SIZE 128
#define APP_TASK_LED_STK_SIZE 64
#define APP_TASK_BUTTON_STK_SIZE 64
OS_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
OS_STK AppTaskLedStk[APP_TASK_LED_STK_SIZE];
OS_STK AppTaskButtonStk[APP_TASK_BUTTON_STK_SIZE];
/* 函数声明 */
static void AppTaskStart(void *p_arg);
static void AppTaskLed(void *p_arg);
static void AppTaskButton(void *p_arg);
int main(void)
{
HAL_Init();
SystemClock_Config();
BSP_Init();
OSInit(); /* 初始化 μC/OS-II */
OSTaskCreate(
AppTaskStart,
(void *)0,
&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],
APP_TASK_START_PRIO
);
OSStart(); /* 启动多任务 */
while (1);
}
/* 启动任务 */
static void AppTaskStart(void *p_arg)
{
(void)p_arg;
/* 初始化系统滴答定时器 */
OS_CPU_SysTickInit(SystemCoreClock / OS_TICKS_PER_SEC);
/* 创建其他任务 */
OSTaskCreate(
AppTaskLed,
(void *)0,
&AppTaskLedStk[APP_TASK_LED_STK_SIZE - 1],
APP_TASK_LED_PRIO
);
OSTaskCreate(
AppTaskButton,
(void *)0,
&AppTaskButtonStk[APP_TASK_BUTTON_STK_SIZE - 1],
APP_TASK_BUTTON_PRIO
);
while (1) {
OSTimeDlyHMSM(0, 0, 1, 0); /* 延时1秒 */
}
}
/* LED 闪烁任务 */
static void AppTaskLed(void *p_arg)
{
(void)p_arg;
while (1) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
OSTimeDlyHMSM(0, 0, 0, 500); /* 500ms 闪烁 */
}
}
/* 按键扫描任务 */
static void AppTaskButton(void *p_arg)
{
(void)p_arg;
while (1) {
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {
/* 按键按下处理 */
OSTimeDlyHMSM(0, 0, 0, 50); /* 消抖 */
}
OSTimeDlyHMSM(0, 0, 0, 10); /* 10ms 扫描一次 */
}
}
2、BSP 初始化(bsp.c)
#include "bsp.h"
void BSP_Init(void)
{
/* GPIO 初始化 */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* LED 引脚配置 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* 按键引脚配置 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
参考代码 STM32F401移植UCOS—II操作系统 www.youwenfan.com/contentcnv/72746.html
七、Keil 工程配置
1、Target 配置
Target -> Code Generation
- Use MicroLIB: ✓
- Floating Point Hardware: Single Precision
2、C/C++ 配置
C/C++ -> Preprocessor Symbols
Define: USE_HAL_DRIVER, STM32F401xx
Include Paths:
.\Core\Inc
.\Core\Src\os\Source
.\Core\Src\os\Port
.\Core\Src\os\Config
.\Core\Src\bsp
.\Core\Src\app
3、Linker 配置
Linker -> Misc controls
--diag_suppress=L6304 /* 禁止警告 */
八、调试与验证
1、系统运行检查
/* 在 AppTaskStart 中添加 */
void AppTaskStart(void *p_arg)
{
char info[30];
printf("μC/OS-II Version: %d.%d\n", OS_VERSION / 100, OS_VERSION % 100);
printf("System Clock: %lu Hz\n", SystemCoreClock);
printf("OS Tick Rate: %d Hz\n", OS_TICKS_PER_SEC);
while (1) {
sprintf(info, "Tasks: %d, Free: %d\n",
OSTaskCtr, OSTaskFreeList);
printf("%s", info);
OSTimeDlyHMSM(0, 0, 5, 0);
}
}
2、常见问题排查
| 问题 |
原因 |
解决方法 |
| 系统卡死 |
SysTick 冲突 |
CubeMX 改用 TIM1 作为 HAL 时基 |
| HardFault |
栈溢出 |
增大任务栈大小 |
| 任务不切换 |
PendSV 优先级 |
确保 PendSV 优先级最低 |
| 中断异常 |
中断中使用 OS 函数 |
使用 OSIntEnter() / OSIntExit() |
九、性能优化
1、中断优化
/* 在中断服务程序中 */
void USART1_IRQHandler(void)
{
OSIntEnter(); /* 通知 OS 进入中断 */
/* 中断处理代码 */
HAL_UART_IRQHandler(&huart1);
OSIntExit(); /* 通知 OS 退出中断,可能触发任务切换 */
}
2、内存管理
/* 使用 μC/OS-II 内存分区 */
OS_MEM MemPartition;
INT8U MemBuff[10][128]; /* 10个128字节的内存块 */
void Mem_Init(void)
{
OSMemCreate(
&MemPartition,
"MemPart",
&MemBuff[0][0],
10,
128,
&err
);
}
十、完整工程结构
STM32F401_uCOSII/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── bsp.h
│ │ └── app_cfg.h
│ ├── Src/
│ │ ├── main.c
│ │ ├── bsp.c
│ │ ├── app.c
│ │ └── ucos_ii/
│ │ ├── Source/ # μC/OS-II 内核源码
│ │ ├── Port/ # 移植代码
│ │ │ ├── os_cpu.h
│ │ │ ├── os_cpu_c.c
│ │ │ └── os_cpu_a.asm
│ │ └── Config/
│ │ └── os_cfg.h
├── Drivers/
├── MDK-ARM/
└── README.md