1. 概述
本文档记录将 RT-Thread Nano 3.1+ 移植到 STM32U3CZI 开发板的过程,基于 STM32CubeMX 生成的项目模板,使用 CMake 构建。
硬件平台
- MCU : STM32U3CZI (Cortex-M33)
- 开发板 : NUCLEO-U3CZI
软件环境
- IDE : VS Code + CMake Tools
- HAL : STM32CubeU3 HAL
- RTOS : RT-Thread Nano 3.1+
- 构建工具 : CMake + GCC ARM Embedded
2. 移植修改的文件
2.1 启动文件 startup_stm32u3c5xx.s
文件位置 : startup_stm32u3c5xx.s
修改内容 : 将启动入口从 main 改为 rtthread_startup
// 修改前 (第96行)
bl main
// 修改后
bl rtthread_startup
原因 : RT-Thread Nano 的标准入口是 rtthread_startup(),它负责:
- 调用
rt_hw_board_init() 初始化板级硬件
- 初始化定时器系统
- 初始化调度器
- 创建主线程
- 启动调度器
调度器启动后,才会调用用户的 main() 函数。这样确保 RT-Thread 内核在用户代码运行前已经正常工作。
2.2 主文件 Core/Src/main.c
文件位置 : Core/Src/main.c
2.2.1 添加 RT-Thread 头文件
/* USER CODE BEGIN Includes */
#include <rtthread.h>
/* RT-Thread entry - will be called by rtthread_startup() before main */
extern int rtthread_startup(void);
/* USER CODE END Includes */
2.2.2 主函数结构
main() 函数不需要 调用:
HAL_Init() — 已在 rt_hw_board_init() 中调用
SystemClock_Config() — 已在 rt_hw_board_init() 中调用
rt_hw_board_init() — 已在 rtthread_startup() 中调用
rt_components_init() — 已在 rtthread_startup() 中调用
int main(void)
{
// 初始化外设
MX_GPIO_Init();
MX_ICACHE_Init();
// 初始化 LED
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_BLUE);
// 初始化按钮
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
// 初始化串口
BspCOMInit.BaudRate = 115200;
// ...
// 主循环
while (1)
{
BSP_LED_Toggle(LED_GREEN);
rt_thread_mdelay(500); // RT-Thread 延时函数
BSP_LED_Toggle(LED_RED);
rt_thread_mdelay(500);
BSP_LED_Toggle(LED_BLUE);
rt_thread_mdelay(500);
}
}
2.3 RT-Thread 配置文件 RT-Thread/rtconfig.h
文件位置 : RT-Thread/rtconfig.h
关键配置项说明:
// 线程优先级数量
#define RT_THREAD_PRIORITY_MAX 32
// OS tick 频率 (1ms)
#define RT_TICK_PER_SECOND 1000
// 启用组件初始化
#define RT_USING_COMPONENTS_INIT
// 启用用户 main 线程
#define RT_USING_USER_MAIN
// 主线程栈大小
#define RT_MAIN_THREAD_STACK_SIZE 1024
// 启用 RT-Thread Heap (动态内存)
#define RT_USING_HEAP
// 启用控制台
#define RT_USING_CONSOLE
#define RT_CONSOLEBUF_SIZE 128
// 启用 FinSH Shell
#define RT_USING_FINSH
#define FINSH_THREAD_STACK_SIZE 1024
2.4 板级初始化文件 board.c
文件位置 : Middlewares/Third_Party/RealThread_RTOS_RT-Thread/bsp/_template/cubemx_config/board.c
此文件由 RT-Thread 提供,CubeMX 模板已包含,一般不需要修改。
关键函数说明
rt_hw_board_init() — 板级初始化
- 调用
HAL_Init()
- 调用
SystemClock_Config()
- 配置 SysTick 中断 (
HAL_SYSTICK_Config)
- 初始化堆内存
- 调用组件板级初始化回调
void rt_hw_board_init(void)
{
HAL_Init();
SystemClock_Config();
SystemCoreClockUpdate();
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/RT_TICK_PER_SECOND);
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
SysTick_Handler() — 系统时钟中断
void SysTick_Handler(void)
{
rt_interrupt_enter();
rt_tick_increase();
rt_interrupt_leave();
}
3. RT-Thread Nano 启动流程
CPU Reset
│
▼
Reset_Handler (startup_stm32u3c5xx.s)
│
▼
rtthread_startup() ◄── 启动文件跳转到这里
│
├─→ rt_hw_board_init() // 板级初始化 (HAL, Clock, Heap)
│
├─→ rt_show_version() // 打印 RT-Thread 版本
│
├─→ rt_system_timer_init() // 定时器系统初始化
│
├─→ rt_system_scheduler_init() // 调度器初始化
│
├─→ rt_application_init() // 创建主线程
│ │
│ └─→ main_thread_entry()
│ └─→ main() // 用户的 main() 函数
│
├─→ rt_system_timer_thread_init() // 定时器线程
│
├─→ rt_thread_idle_init() // 空闲线程
│
└─→ rt_system_scheduler_start() // 启动调度器
│
▼
(永不返回)
4. 常见问题
Q1: 编译报错 implicit declaration of function 'rt_hw_board_init'
原因 : rt_hw_board_init() 只在 components.c 中声明,未暴露在头文件。
解决 : 确认启动文件跳转到 rtthread_startup() 而不是 main()。如果需要在 main.c 中调用板级初始化函数,需添加 extern 声明。
Q2: 程序进入 HardFault_Handler
原因 : 在 RT-Thread 未初始化前就调用了 rt_thread_mdelay() 等内核函数。
解决 :
- 确认启动文件
startup_*.s 跳转到 rtthread_startup()
main() 中不要 重复调用 HAL_Init()、SystemClock_Config()、rt_hw_board_init()
Q3: rt_thread_mdelay() 和 HAL_Delay() 哪个更好?
| 函数 |
特点 |
HAL_Delay() |
裸机延时,阻塞式,不释放 CPU |
rt_thread_mdelay() |
RTOS 延时,触发线程调度,CPU 可执行其他任务 |
在 RT-Thread 环境下,推荐使用 rt_thread_mdelay() 。
Q4: 如何调整 SysTick 中断频率?
修改 RT-Thread/rtconfig.h 中的 RT_TICK_PER_SECOND:
#define RT_TICK_PER_SECOND 1000 // 1ms 一次 tick
// #define RT_TICK_PER_SECOND 100 // 10ms 一次 tick
5. 验证测试
LED 闪烁测试
// 在 main() 的主循环中
BSP_LED_Toggle(LED_GREEN);
rt_thread_mdelay(500); // 延时 500ms
三个 LED(绿、红、蓝)依次循环亮起,间隔 500ms。
串口输出测试
RT-Thread 启动时会自动打印版本信息:
RT-Thread
version 3.1.x
build: May 31 2026
可在代码中使用 rt_kprintf() 进行调试输出:
rt_kprintf("LED Toggle!\n");
6. 项目结构
mythtread_pro/
├── Core/
│ └── Src/
│ └── main.c # 用户主程序
├── Middlewares/
│ └── Third_Party/
│ └── RealThread_RTOS_RT-Thread/
│ ├── bsp/
│ │ └── _template/
│ │ └── cubemx_config/
│ │ └── board.c # 板级初始化
│ ├── libcpu/
│ │ └── arm/cortex-m33/
│ │ └── cpuport.c # CPU 端口相关
│ └── src/
│ └── components.c # RT-Thread 组件 & 入口
├── RT-Thread/
│ └── rtconfig.h # RT-Thread 配置文件
├── startup_stm32u3c5xx.s # 启动文件 (已修改)
└── CMakeLists.txt
7. 附录:关键源码
启动文件修改点
; startup_stm32u3c5xx.s 第93-96行
; Call static constructors
bl __libc_init_array
/* Call the application's entry point.*/
bl rtthread_startup ; 原来是 bl main
main.c 完整结构
#include "main.h"
#include <rtthread.h>
extern int rtthread_startup(void);
int main(void)
{
MX_GPIO_Init();
MX_ICACHE_Init();
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_BLUE);
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
BspCOMInit.BaudRate = 115200;
// ... 串口初始化
while (1)
{
BSP_LED_Toggle(LED_GREEN);
rt_thread_mdelay(500);
BSP_LED_Toggle(LED_RED);
rt_thread_mdelay(500);
BSP_LED_Toggle(LED_BLUE);
rt_thread_mdelay(500);
}
}