梁山派GD32F470ZGT6 RT-Thread CMake 模板适配指南

GD32F470ZGT6 RT-Thread CMake 模板适配指南

本文档详细说明如何基于 GD32F470ZGT6 裸机模板,移植 RT-Thread v4.1.1 LTS 实时操作系统。


一、参考标准

参考来源 版本/路径 用途
RT-Thread 内核 v4.1.1 LTS 内核源码、Cortex-M 端口
RT-Thread Cortex-M4 BSP rt-thread/libcpu/arm/cortex-m4/ ARM_CM4 标准端口实现
RT-Thread GD32 BSP rt-thread/bsp/gd32450z-eval/ GD32F4xx 官方参考移植
RT-Thread STM32 BSP rt-thread/bsp/stm32/ STM32F4xx 官方参考移植(HAL 库方案)
GD32F4xx 标准外设库 V3.3.3 GPIO、时钟初始化
RT-Thread 编程规范 RT-Thread 官方文档 代码风格、API 使用规范

二、RT-Thread 官方标准解析

2.1 RT-Thread 启动流程

RT-Thread 采用标准的启动流程,与裸机和 FreeRTOS 有本质区别:

复制代码
Reset_Handler (启动汇编)
    └── entry()                          (components.c)
         └── rtthread_startup()          (components.c)
              ├── rt_hw_interrupt_disable()          /* 关闭全局中断 */
              ├── rt_hw_board_init()                 /* 板级初始化 */
              ├── rt_show_version()                  /* 打印版本信息 */
              ├── rt_system_timer_init()             /* 系统定时器初始化 */
              ├── rt_system_scheduler_init()         /* 调度器初始化 */
              ├── rt_application_init()              /* 创建 main 线程 */
              ├── rt_system_timer_thread_init()      /* 定时器线程初始化 */
              ├── rt_thread_idle_init()              /* 空闲线程初始化 */
              └── rt_system_scheduler_start()        /* 启动调度器 */

关键区别

  • RT-Thread 的程序入口是 entry() 而非 main()
  • main() 是在调度器启动后作为 "main 线程" 被调用的
  • rt_hw_board_init() 是用户必须实现的板级初始化函数

2.2 SysTick 标准实现

参考 GD32 官方 BSPrt-thread/bsp/gd32450z-eval/drivers/board.c):

c 复制代码
/** System Clock Configuration */
void SystemClock_Config(void)
{
    SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    NVIC_SetPriority(SysTick_IRQn, 0);  /* 最高优先级! */
}

/**
 * This is the timer interrupt service routine.
 */
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

关键要点

  • SysTick 在 rt_hw_board_init() 中启用(调度器启动前)
  • SysTick 优先级必须设为 最高 (0),确保 tick 计数不被中断阻塞
  • SysTick_Handler 中调用 rt_tick_increase() 驱动 RT-Thread 调度器

2.3 board.c 标准结构

参考 RT-Thread BSP 标准,board.c 必须包含:

  1. rt_hw_board_init() --- 板级初始化入口
  2. SysTick_Handler() --- SysTick 中断处理
  3. 堆内存初始化 --- rt_system_heap_init()

2.4 rtconfig.h 标准结构

RT-Thread 的内核配置通过 rtconfig.h 管理:

c 复制代码
#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__

/* RT-Thread Kernel */
#define RT_NAME_MAX 8
#define RT_ALIGN_SIZE 4
#define RT_THREAD_PRIORITY_MAX 32
#define RT_TICK_PER_SECOND 1000
/* ... 更多配置 ... */

#endif

三、适配步骤

步骤 1:准备 RT-Thread 源码

将 RT-Thread 源码放置到 SDK 目录:

复制代码
sdk/utilities/Third_Party/RT-Thread/rt-thread/
├── include/           # RT-Thread 内核头文件
├── src/               # 内核源码
│   ├── clock.c        # 系统时钟与 tick 管理
│   ├── components.c   # 组件初始化(entry、rtthread_startup)
│   ├── cpu.c          # CPU 管理
│   ├── device.c       # 设备管理
│   ├── idle.c         # 空闲线程
│   ├── ipc.c          # 进程间通信
│   ├── irq.c          # 中断管理
│   ├── kservice.c     # 内核服务
│   ├── mem.c          # 内存管理
│   ├── mempool.c      # 内存池
│   ├── object.c       # 对象管理
│   ├── scheduler.c    # 调度器
│   ├── thread.c       # 线程管理
│   └── timer.c        # 定时器
└── libcpu/
    └── arm/
        └── cortex-m4/
            ├── cpuport.c      # Cortex-M4 端口(上下文切换、中断管理)
            └── context_gcc.S  # GCC 汇编实现(PendSV、上下文切换)

步骤 2:创建 board.c

myprog/app/board.c 中实现板级初始化:

c 复制代码
#include "gd32f4xx.h"
#include "rtthread.h"

/* 定义 64KB 静态堆内存 */
static rt_uint8_t rt_heap[64 * 1024];

/**
 * RT-Thread 板级初始化入口
 * 按照 RT-Thread 官方 BSP 标准实现
 */
void rt_hw_board_init(void)
{
    /* 更新系统时钟 */
    SystemCoreClockUpdate();

    /* 配置 SysTick,每 1ms 一次中断 */
    SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    
    /* 
     * 关键:将 SysTick 优先级设为最高 (0)
     * 参考 GD32 官方 BSP: rt-thread/bsp/gd32450z-eval/drivers/board.c
     */
    NVIC_SetPriority(SysTick_IRQn, 0);

    /* 初始化堆内存 */
    rt_system_heap_init((void *)rt_heap,
                        (void *)((uint32_t)rt_heap + sizeof(rt_heap)));
}

/**
 * SysTick 中断处理函数
 * 按照 RT-Thread 官方标准实现
 * 参考: rt-thread/src/clock.c 中的 rt_tick_increase()
 */
void SysTick_Handler(void)
{
    /* 进入中断上下文 */
    rt_interrupt_enter();

    /* tick 计数增加,驱动 RT-Thread 调度器 */
    rt_tick_increase();

    /* 离开中断上下文 */
    rt_interrupt_leave();
}

标准参考对照

实现 参考来源
rt_hw_board_init() 结构 rt-thread/bsp/gd32450z-eval/drivers/board.c
SysTick_Config() 调用位置 rt-thread/bsp/gd32450z-eval/drivers/board.c:37
NVIC_SetPriority(SysTick_IRQn, 0) rt-thread/bsp/gd32450z-eval/drivers/board.c:38
SysTick_Handler 结构 rt-thread/src/clock.c:91-127 + BSP 实现
rt_system_heap_init() RT-Thread BSP 标准要求

步骤 3:创建 rtconfig.h

myprog/include/rtconfig.h 中创建内核配置:

c 复制代码
#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__

/* RT-Thread Kernel */
#define RT_NAME_MAX 8                      /* 对象名最大长度 */
#define RT_ALIGN_SIZE 4                    /* 内存对齐字节数 */
#define RT_THREAD_PRIORITY_MAX 32          /* 最大优先级数量 */
#define RT_TICK_PER_SECOND 1000            /* tick 频率 (Hz) */
#define RT_USING_OVERFLOW_CHECK            /* 启用栈溢出检测 */
#define RT_USING_HOOK                      /* 启用钩子函数 */
#define RT_USING_IDLE_HOOK                 /* 启用空闲钩子 */
#define RT_IDLE_HOOK_LIST_SIZE 4           /* 空闲钩子列表大小 */
#define IDLE_THREAD_STACK_SIZE 256         /* 空闲线程栈大小 */

/* 内存管理 */
#define RT_USING_MEMPOOL                   /* 启用内存池 */
#define RT_USING_SMALL_MEM                 /* 启用小内存管理 */
#define RT_USING_SMALL_MEM_AS_HEAP         /* 使用小内存作为堆 */
#define RT_USING_HEAP                      /* 启用堆内存 */

/* 线程间通信 */
#define RT_USING_SEMAPHORE                 /* 信号量 */
#define RT_USING_MUTEX                     /* 互斥量 */
#define RT_USING_EVENT                     /* 事件 */
#define RT_USING_MAILBOX                   /* 邮箱 */
#define RT_USING_MESSAGEQUEUE              /* 消息队列 */

/* 控制台 */
#define RT_USING_CONSOLE                   /* 启用控制台 */
#define RT_CONSOLEBUF_SIZE 128             /* 控制台缓冲区大小 */
#define RT_CONSOLE_DEVICE_NAME "uart0"     /* 控制台设备名 */

/* 组件自动初始化 */
#define RT_USING_COMPONENTS_INIT           /* 启用组件自动初始化 */
#define RT_USING_USER_MAIN                 /* 启用用户 main 线程 */
#define RT_MAIN_THREAD_STACK_SIZE 2048     /* main 线程栈大小 */
#define RT_MAIN_THREAD_PRIORITY 10         /* main 线程优先级 */

/* CPU 架构 */
#define ARCH_ARM                           /* ARM 架构 */
#define ARCH_ARM_CORTEX_M                  /* Cortex-M 内核 */
#define ARCH_ARM_CORTEX_M4                 /* Cortex-M4 */

#endif

标准参考 :RT-Thread 官方 BSP 的 rtconfig.hrt-thread/bsp/gd32450z-eval/rtconfig.h)。

步骤 4:配置 CMakeLists.txt

cmake 复制代码
# RT-Thread 源码路径
set(RTTHREAD_ROOT "${SDK_ROOT}/utilities/Third_Party/RT-Thread/rt-thread")

# 头文件路径
include_directories(
    ${RTTHREAD_ROOT}/include
    ${RTTHREAD_ROOT}/libcpu/arm/cortex-m4
)

# RT-Thread 内核源码
set(RTTHREAD_SOURCES
    ${RTTHREAD_ROOT}/src/clock.c
    ${RTTHREAD_ROOT}/src/components.c
    ${RTTHREAD_ROOT}/src/cpu.c
    ${RTTHREAD_ROOT}/src/device.c
    ${RTTHREAD_ROOT}/src/idle.c
    ${RTTHREAD_ROOT}/src/ipc.c
    ${RTTHREAD_ROOT}/src/irq.c
    ${RTTHREAD_ROOT}/src/kservice.c
    ${RTTHREAD_ROOT}/src/mem.c
    ${RTTHREAD_ROOT}/src/mempool.c
    ${RTTHREAD_ROOT}/src/object.c
    ${RTTHREAD_ROOT}/src/scheduler.c
    ${RTTHREAD_ROOT}/src/thread.c
    ${RTTHREAD_ROOT}/src/timer.c
    ${RTTHREAD_ROOT}/libcpu/arm/cortex-m4/cpuport.c
    ${RTTHREAD_ROOT}/libcpu/arm/cortex-m4/context_gcc.S
)

# 加入编译
set(PROJECT_SOURCES
    ${PERIPHERAL_SOURCES}
    ${SYSTEM_SOURCES}
    ${STARTUP_SOURCES}
    ${APP_SOURCES}
    ${RTTHREAD_SOURCES}
)

关键文件说明

源码文件 功能
components.c 启动入口(entry()rtthread_startup()
scheduler.c 调度器实现
thread.c 线程管理(创建、销毁、挂起、恢复)
clock.c tick 管理(rt_tick_increase()
cpuport.c Cortex-M 端口(上下文切换、中断禁用/使能)
context_gcc.S GCC 汇编(PendSV 处理、上下文切换汇编)

步骤 5:修改启动文件

确保启动文件中 FPU 配置正确:

assembly 复制代码
.cpu cortex-m4
.fpu fpv4-sp-d16          /* Cortex-M4F 硬件浮点 */
.thumb

.section .isr_vector,"a",%progbits
g_pfnVectors:
    .word   _estack
    .word   Reset_Handler
    /* ... 其他中断向量 ... */
    .word   SysTick_Handler  /* SysTick 中断向量 */

关键点

  • .fpu fpv4-sp-d16 必须与 CMake 中的 -mfpu=fpv4-sp-d16 一致
  • 向量表中必须包含 SysTick_Handler,指向 board.c 中的实现

步骤 6:实现 main.c

c 复制代码
#include "gd32f4xx.h"
#include "rtthread.h"

#define LED1_PIN    GPIO_PIN_3
#define LED1_PORT   GPIOE
/* ... 其他 LED 定义 ... */

/**
 * LED 跑马灯线程入口
 */
static void led_thread_entry(void* parameter)
{
    while(1)
    {
        gpio_bit_write(LED1_PORT, LED1_PIN, 1);
        gpio_bit_write(LED2_PORT, LED2_PIN, 0);
        gpio_bit_write(LED3_PORT, LED3_PIN, 0);
        gpio_bit_write(LED4_PORT, LED4_PIN, 0);
        rt_thread_mdelay(1000);
        
        /* 切换下一个 LED ... */
    }
}

/**
 * main 函数(在 RT-Thread 调度器启动后作为 main 线程执行)
 */
int main(void)
{
    /* 使能 GPIO 时钟 */
    rcu_periph_clock_enable(RCU_GPIOE);
    rcu_periph_clock_enable(RCU_GPIOD);
    rcu_periph_clock_enable(RCU_GPIOG);
    rcu_periph_clock_enable(RCU_GPIOA);
    
    /* 配置 LED 引脚 */
    gpio_mode_set(LED1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LED1_PIN);
    gpio_output_options_set(LED1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED1_PIN);
    /* ... 其他 LED 配置 ... */
    
    /* 创建 LED 线程 */
    rt_thread_t led_thread = rt_thread_create("led",
        led_thread_entry, RT_NULL,   /* 线程名、入口、参数 */
        512, 10, 20);                /* 栈大小、优先级、时间片 */
    
    if (led_thread != RT_NULL)
    {
        rt_thread_startup(led_thread);
    }
    
    /* main 线程空循环 */
    while(1)
    {
        rt_thread_delay(RT_TICK_PER_SECOND);
    }
}

关键说明

  • main() 由 RT-Thread 调度器自动调用,不是程序入口
  • 程序入口是 components.c 中的 entry()rtthread_startup()
  • rt_thread_create() 参数:名称、入口函数、参数、栈大小、优先级、时间片
  • rt_thread_mdelay(1000) 使当前线程挂起 1000ms,期间调度器运行其他线程

四、关键差异对比

项目 裸机模板 FreeRTOS 模板 RT-Thread 模板
程序入口 main() main()vTaskStartScheduler() entry()rtthread_startup()
SysTick 处理 用户手动实现 port.c 自动处理 用户实现 SysTick_Handler + rt_tick_increase()
延时方式 delay_ms() 阻塞 vTaskDelay() 非阻塞 rt_thread_mdelay() 非阻塞
线程创建 xTaskCreate() rt_thread_create()
堆内存 heap_4.c 自动管理 rt_system_heap_init() 手动初始化
板级初始化 main() 中直接调用 main() 中直接调用 rt_hw_board_init() 标准接口
配置文件 FreeRTOSConfig.h rtconfig.h
启动文件 Reset_Handler → main Reset_Handler → main Reset_Handler → entry

五、关键修复说明

SysTick 中断优先级修复

问题SysTick_Config() 默认将 SysTick 中断优先级设为最低(0xFF)。

后果:当其他中断正在执行时,SysTick 中断会被延迟处理,导致:

  • rt_tick 计数不准确
  • rt_thread_mdelay() 定时器无法按时触发
  • 线程被挂起后无法被唤醒,系统卡死

修复

c 复制代码
NVIC_SetPriority(SysTick_IRQn, 0);  /* 最高优先级 */

参考标准

  • RT-Thread GD32 BSP:rt-thread/bsp/gd32450z-eval/drivers/board.c:38
  • 该配置将 SysTick 优先级设为最高(0),确保 tick 中断不被任何中断阻塞

六、编译与烧录

6.1 编译

bash 复制代码
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug
make -j8

6.2 烧录

bash 复制代码
sudo ./flash/flash.sh

6.3 验证

4 个 LED 每 1 秒依次切换,形成跑马灯效果。


七、关键配置汇总

项目 说明
RT-Thread 版本 v4.1.1 LTS 长期支持版
CPU 端口 cortex-m4 Cortex-M4 标准端口
堆大小 64KB rt_heap[64 * 1024]
Tick 频率 1000Hz (1ms) RT_TICK_PER_SECOND
优先级数量 32 RT_THREAD_PRIORITY_MAX
main 线程栈 2048 字节 RT_MAIN_THREAD_STACK_SIZE
main 线程优先级 10 RT_MAIN_THREAD_PRIORITY
SysTick 优先级 0 (最高) NVIC_SetPriority(SysTick_IRQn, 0)
调度方式 基于优先级的抢占式 RT-Thread 标准调度

八、注意事项

  1. 入口函数 :RT-Thread 使用 entry() 作为入口,不是 main()。启动文件中的 Reset_Handler 调用 SystemInit() 后跳转到 entry()
  2. 堆内存初始化 :必须在 rt_hw_board_init() 中调用 rt_system_heap_init(),否则 rt_thread_create() 等动态分配函数会失败
  3. SysTick 优先级:必须设为最高(0),参考 GD32 官方 BSP 标准
  4. rtconfig.h 必须包含ARCH_ARMARCH_ARM_CORTEX_MARCH_ARM_CORTEX_M4 三个宏,否则 RT-Thread 无法正确编译 Cortex-M 端口代码
  5. 组件自动初始化 :启用 RT_USING_COMPONENTS_INIT 后,使用 INIT_COMPONENT_EXPORT() 宏声明的函数会被自动调用
  6. 中断上下文SysTick_Handler 中必须调用 rt_interrupt_enter()rt_interrupt_leave(),否则 RT-Thread 无法正确管理中断嵌套
相关推荐
W.W.H.8 天前
STM32实现LED闪烁和串口打印案例
stm32·单片机·嵌入式硬件·usart·gd32·dap-link
qq_3707730922 天前
GD32F470ZGT6梁山派 CMake 模板工程创建
gd32·梁山派·工程模板
追兮兮24 天前
基于 GD32 与 LwIP 的 TCP OTA 固件升级实现
网络·网络协议·tcp/ip·tcp·gd32·ota
番茄灭世神1 个月前
MCU开发常见软件BUG总结(持续更新)
c语言·stm32·单片机·嵌入式·gd32
记录无知岁月1 个月前
【GD32】(二) 基本外设使用
单片机·can·iic·gd32·dwt
漠落1 个月前
GD32替换STM32导致ADC卡顿问题解析
stm32·单片机·gd32
番茄灭世神2 个月前
空气质量检测仪项目笔记——硬件介绍
stm32·单片机·嵌入式·gd32·国产芯片
qq_341581173 个月前
GD32E103CBT6+freeRTOS
freertos·gd32
番茄灭世神4 个月前
基于VScode搭建GD32开发环境
arm开发·vscode·单片机·cmake·gd32