STM32F103移植uC/OS-III实时操作系统

一、实验环境准备

1.1 硬件准备

  • 主控芯片: STM32F103C8T6 (Cortex-M3内核)
  • 开发板: STM32F103C8T6最小系统板
  • 外设:
    • LED灯 x2 (PA0, PA1)
    • USB转串口模块 (PA2, PA3)

1.2 软件准备

  • IDE: STM32CubeIDE 1.8.0
  • RTOS: uC/OS-III v3.08.00
  • HAL库: STM32Cube_FW_F1_V1.8.4

二、uC/OS-III源码获取与准备

2.1 下载uC/OS-III源码

从Micrium官网或GitHub获取uC/OS-III源码,主要包含以下文件:

uC-CPU/
uC-LIB/
uC-OS3/

三、STM32CubeMX工程配置

3.1 创建新工程

  1. 打开STM32CubeMX,选择STM32F103C8Tx
  2. 配置系统时钟:
    • HSE: Crystal/Ceramic Resonator
    • 系统时钟: 72MHz

3.2 GPIO配置

  • LED1: PA0, Output Push-Pull
  • LED2: PA1, Output Push-Pull

3.3 串口配置

  • USART2: Asynchronous Mode
    • Baud Rate: 115200
    • Word Length: 8 Bits
    • Stop Bits: 1
    • Parity: None

3.4 系统配置

  • SYS: Debug: Serial Wire
  • NVIC: 配置PendSV、SysTick优先级

四、uC/OS-III移植步骤

4.1 添加源码到工程

将uC/OS-III源码复制到工程目录,并在IDE中添加文件:

/* 在IDE中创建文件组 */
UCOSIII/CORE
  ├── os_cfg_app.c
  ├── os_core.c
  ├── os_dbg.c
  └── ... (其他核心文件)

UCOSIII/PORT
  ├── os_cpu_c.c
  ├── os_cpu_a.asm
  └── os_cpu.h

UCOSIII/CPU
  ├── cpu_core.c
  └── cpu_def.h

UCOSIII/LIB
  ├── lib_ascii.c
  ├── lib_math.c
  └── ...

4.2 修改os_cfg_app.h配置文件

/* 任务配置 */
#define OS_CFG_PRIO_MAX                  64u
#define OS_CFG_TASK_STK_LIMIT_PCT_EMPTY  10u

/* 系统配置 */
#define OS_CFG_SCHED_ROUND_ROBIN_EN      1u
#define OS_CFG_TICK_EN                   1u
#define OS_CFG_TASK_TICK_EN              1u

/* 时间片轮转调度配置 */
#define OS_CFG_SCHED_ROUND_ROBIN_EN      1u

4.3 修改os_cpu.h

/* 处理器相关定义 */
#define  OS_CPU_CM3_FP_PRESENT          0u
#define  OS_CPU_CM3_FP_USED             0u

/* 临界段保护 */
#define  OS_CRITICAL_METHOD             3u
#define  OS_ENTER_CRITICAL()            {CPU_SR_ALLOC(); CPU_CRITICAL_ENTER();}
#define  OS_EXIT_CRITICAL()             {CPU_CRITICAL_EXIT();}

4.4 修改os_cpu_c.c

/* CPU初始化 */
void OS_CPU_SysTickInit (CPU_INT32U cnts)
{
    CPU_INT32U  prio;
    
    /* 配置SysTick */
    HAL_SYSTICK_Config(cnts);
    
    /* 配置SysTick中断优先级 */
    prio = (1UL << __NVIC_PRIO_BITS) - 1UL;
    HAL_NVIC_SetPriority(SysTick_IRQn, prio, 0U);
}

/* SysTick中断处理 */
void SysTick_Handler(void)
{
    HAL_IncTick();
    if (OSRunning == OS_STATE_OS_RUNNING) {
        OS_TimeTick();
    }
}

4.5 修改启动文件

startup_stm32f103xb.s中添加PendSV和SysTick中断处理:

; 在启动文件的向量表中确保有以下定义
__Vectors
    DCD     __initial_sp
    DCD     Reset_Handler
    DCD     NMI_Handler
    DCD     HardFault_Handler
    DCD     MemManage_Handler
    DCD     BusFault_Handler
    DCD     UsageFault_Handler
    DCD     0
    DCD     0
    DCD     0
    DCD     0
    DCD     SVC_Handler
    DCD     DebugMon_Handler
    DCD     0
    DCD     PendSV_Handler
    DCD     SysTick_Handler

五、创建多任务应用程序

5.1 头文件包含和全局变量定义

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "os.h"
#include "cpu.h"

/* 任务栈大小定义 */
#define TASK_START_STK_SIZE         128
#define TASK_LED1_STK_SIZE          128
#define TASK_LED2_STK_SIZE          128  
#define TASK_UART_STK_SIZE          128

/* 任务控制块 */
OS_TCB TaskStartTCB;
OS_TCB TaskLed1TCB;
OS_TCB TaskLed2TCB;
OS_TCB TaskUartTCB;

/* 任务栈 */
CPU_STK TaskStartStk[TASK_START_STK_SIZE];
CPU_STK TaskLed1Stk[TASK_LED1_STK_SIZE];
CPU_STK TaskLed2Stk[TASK_LED2_STK_SIZE];
CPU_STK TaskUartStk[TASK_UART_STK_SIZE];

5.2 任务函数实现

/* LED1任务 - 1秒周期 */
void Task_LED1(void *p_arg)
{
    OS_ERR err;
    
    (void)p_arg;
    
    while(1) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);  // 翻转LED1
        OSTimeDlyHMSM(0, 0, 1, 0, OS_OPT_TIME_HMSM_STRICT, &err);  // 延时1秒
    }
}

/* LED2任务 - 3秒周期 */
void Task_LED2(void *p_arg)
{
    OS_ERR err;
    
    (void)p_arg;
    
    while(1) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);  // 翻转LED2
        OSTimeDlyHMSM(0, 0, 3, 0, OS_OPT_TIME_HMSM_STRICT, &err);  // 延时3秒
    }
}

/* 串口任务 - 2秒周期 */
void Task_UART(void *p_arg)
{
    OS_ERR err;
    char uart_buf[64];
    
    (void)p_arg;
    
    while(1) {
        sprintf(uart_buf, "hello uc/OS! 欢迎来到RTOS多任务环境!\r\n");
        HAL_UART_Transmit(&huart2, (uint8_t*)uart_buf, strlen(uart_buf), 1000);
        
        OSTimeDlyHMSM(0, 0, 2, 0, OS_OPT_TIME_HMSM_STRICT, &err);  // 延时2秒
    }
}

/* 启动任务 */
void Task_Start(void *p_arg)
{
    OS_ERR err;
    
    (void)p_arg;
    
    /* 创建LED1任务 */
    OSTaskCreate(&TaskLed1TCB,
                 "LED1 Task",
                 Task_LED1,
                 (void *)0,
                 2,  // 优先级
                 &TaskLed1Stk[0],
                 TASK_LED1_STK_SIZE / 10,
                 TASK_LED1_STK_SIZE,
                 0,
                 0,
                 (void *)0,
                 OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
                 &err);
    
    /* 创建LED2任务 */
    OSTaskCreate(&TaskLed2TCB,
                 "LED2 Task",
                 Task_LED2,
                 (void *)0,
                 3,  // 优先级
                 &TaskLed2Stk[0],
                 TASK_LED2_STK_SIZE / 10,
                 TASK_LED2_STK_SIZE,
                 0,
                 0,
                 (void *)0,
                 OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
                 &err);
    
    /* 创建串口任务 */
    OSTaskCreate(&TaskUartTCB,
                 "UART Task",
                 Task_UART,
                 (void *)0,
                 4,  // 优先级
                 &TaskUartStk[0],
                 TASK_UART_STK_SIZE / 10,
                 TASK_UART_STK_SIZE,
                 0,
                 0,
                 (void *)0,
                 OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
                 &err);
    
    /* 删除启动任务 */
    OSTaskDel((OS_TCB *)0, &err);
}

5.3 主函数实现

int main(void)
{
    OS_ERR err;
    
    /* HAL库初始化 */
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART2_UART_Init();
    
    /* 初始化LED为关闭状态 */
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    
    /* uC/OS-III初始化 */
    OSInit(&err);
    if (err != OS_ERR_NONE) {
        while(1);  // 初始化失败
    }
    
    /* 创建启动任务 */
    OSTaskCreate(&TaskStartTCB,
                 "Start Task",
                 Task_Start,
                 (void *)0,
                 1,  // 最高优先级
                 &TaskStartStk[0],
                 TASK_START_STK_SIZE / 10,
                 TASK_START_STK_SIZE,
                 0,
                 0,
                 (void *)0,
                 OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
                 &err);
    
    /* 启动多任务调度 */
    OSStart(&err);
    
    /* 不会执行到这里 */
    while(1);
}

六、编译配置和调试

6.1 编译器配置

  • Optimization: -O0 (便于调试)
  • Include Paths: 添加所有uC/OS-III头文件路径
  • Preprocessor Symbols: 添加OS_CFG_APP_HUCOSIII等宏定义

6.2 链接器配置

  • Heap Size: 0x200
  • Stack Size: 0x400

6.3 调试配置

使用ST-Link调试器,在os_cfg_app.c中开启调试功能:

#define OS_CFG_DBG_EN                         1u
#define OS_CFG_TRACE_EN                       1u

七、实验结果验证

7.1 运行现象

  • LED1: 以1秒周期闪烁
  • LED2: 以3秒周期闪烁
  • 串口输出: 每2秒输出"hello uc/OS! 欢迎来到RTOS多任务环境!"
    在这里插入图片描述

7.2 使用串口调试助手观察输出

在这里插入图片描述

八、常见问题及解决方案

8.1 编译错误

问题: 未定义符号错误
解决: 检查所有源文件是否正确添加到工程,头文件路径是否正确配置

8.2 系统无法启动

问题: 卡在OSStart()
解决: 检查PendSV和SysTick中断优先级配置,确保时钟配置正确

8.3 任务无法正常调度

解决: 检查任务栈大小是否足够,优先级配置是否合理

九、总结

通过本次移植实验,我们成功将uC/OS-III实时操作系统移植到STM32F103平台,并创建了三个具有不同周期的任务。实验证明了uC/OS-III在STM32上的稳定运行。

移植要点总结:

  1. 正确配置系统时钟和中断优先级
  2. 合理设置任务栈大小和优先级
  3. 确保所有必要的源文件正确包含
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐