3. 移植RT-Thread Nano到STM32
任务目标
将RT-Thread Nano内核源码集成到STM32裸机工程中,完成基础时钟与SysTick中断配置,创建两个不同优先级的线程循环打印日志,通过串口输出验证RTOS调度器已正常启动并执行线程切换。
环境与依赖
- 硬件:STM32F103C8T6最小系统板(蓝丸),USART1(PA9/PA10)接USB转串口模块
- 软件:Keil MDK 5.x 或 STM32CubeIDE,STM32标准外设库或HAL库均可
- RT-Thread Nano源码包(rt-thread-4.0.3\nano目录),包含
rtthread.h、board.c、rtconfig.h等核心文件
移植步骤与关键配置
将Nano源码中的src、libcpu/arm/cortex-m3、include目录加入工程,并添加board.c与rtconfig.h。核心配置如下:
- 时钟:SysTick中断频率设为1000Hz(即
RT_TICK_PER_SECOND = 1000),作为RTOS系统节拍 - 中断:在
board.c中实现SysTick_Handler,调用rt_tick_increase();PendSV与SVCall中断由Nano自动接管 - 内存:在
rtconfig.h中定义RT_HEAP_SIZE为2KB,用于动态内存管理
完整实现代码
// rtconfig.h 关键配置
#define RT_THREAD_PRIORITY_MAX 8
#define RT_TICK_PER_SECOND 1000
#define RT_ALIGN_SIZE 4
#define RT_HEAP_SIZE 2048
#define RT_USING_COMPONENTS_INIT
#define RT_USING_CONSOLE
#define RT_CONSOLEBUF_SIZE 128
// board.c 关键实现(时钟与中断绑定)
#include "stm32f10x.h"
#include "rtthread.h"
void SysTick_Handler(void)
{
rt_tick_increase();
}
void rt_hw_board_init(void)
{
// 系统时钟72MHz,SysTick 1ms中断
SystemInit();
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
// 串口1初始化(PA9=TX, PA10=RX, 115200-8-N-1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef gpio = {GPIO_Pin_9, GPIO_Speed_50MHz, GPIO_Mode_AF_PP};
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_10;
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &gpio);
USART_InitTypeDef usart = {115200, USART_WordLength_8b, USART_StopBits_1,
USART_Parity_No, USART_Mode_TX | USART_Mode_RX};
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
rt_console_set_device("uart1"); // 注册控制台设备
}
// 串口输出底层函数(rt_console 需要)
void rt_hw_console_output(const char *str)
{
while (*str) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, *str++);
}
}
// main.c 应用入口
#include "rtthread.h"
static void thread1_entry(void *param)
{
while (1) {
rt_kprintf("[Thread1] running, count: %d\n", (int)param);
rt_thread_mdelay(500); // 延时500ms
}
}
static void thread2_entry(void *param)
{
while (1) {
rt_kprintf("[Thread2] running, count: %d\n", (int)param);
rt_thread_mdelay(1000); // 延时1000ms
}
}
int main(void)
{
rt_thread_t t1 = rt_thread_create("t1", thread1_entry, (void*)1,
512, 5, 20);
rt_thread_t t2 = rt_thread_create("t2", thread2_entry, (void*)2,
512, 3, 20);
if (t1) rt_thread_startup(t1);
if (t2) rt_thread_startup(t2);
return 0;
}
运行与验证
- 将上述文件加入Keil工程,勾选微库(MicroLIB)以避免半主机模式
- 编译下载后,打开串口助手(115200, 8N1),复位开发板
- 通过标准:串口交替输出
[Thread1] running, count: 1与[Thread2] running, count: 2,且Thread1输出频率约为Thread2的两倍(500ms与1000ms间隔),说明两个线程独立运行且优先级调度正常 - 若仅输出一次或卡死,检查SysTick中断是否触发(在
SysTick_Handler设断点)、rt_hw_console_output是否被正确调用
常见问题
- 编译报错
Undefined symbol SysTick_Handler:确认启动文件(startup_stm32f10x_md.s)中未定义同名中断,或删除启动文件中的弱定义 - 串口无输出:检查GPIO复用时钟是否开启,以及
rt_console_set_device是否在输出前调用 - 线程不切换:确认
RT_TICK_PER_SECOND与SysTick配置一致,且PendSV_Handler未在启动文件中被占用