STM32F401 移植 μC/OS-II 操作系统

一、移植前准备

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、生成代码


四、μ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

 

专注于matlab/simulink,电子电路,编程