☰
RT-Thread CH32F10X 系列 BSP 移植指南:从工程骨架到驱动适配
2026/9/29 2:45:37 网站建设 项目流程
  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

本篇指南基于 RT-Thread 仓库bsp/wch/arm下的 CH32F10x 移植实践,围绕 ch32f10x_port_cn.md 的完整移植流程展开:先理清 BSP 工程结构与已适配驱动清单,再以 ch32f103c8-core 为范例,逐步讲解 MDK 工程模板创建、board/Kconfig配置编写、board.h/board.c接口声明与实现,并结合仓库源码验证驱动使能链路。读完本文,你将掌握为任意 CH32F10x 系列芯片新建 RT-Thread BSP 的完整方法,并理解厂商标准外设库与 RT-Thread 设备框架之间的桥接方式。

1 工程结构:BSP、厂商库与适配驱动三层划分

在 bsp/wch/arm 目录下,CH32F10x 系列的移植代码按职责分为四块:

wch/arm/ch32f103c8-core # 移植到具体芯片的 BSP wch/arm/libraries # 厂商驱动库 + RT-Thread 适配驱动 wch/arm/libraries/ch32_drivers # 适配 RT-Thread 设备框架的驱动 wch/arm/libraries/CH32F10x_StdPeriph_Driver # WCH 厂商标准外设库(CMSIS + StdPeriph_Driver) wch/arm/tools # ENV 环境下 scons --dist 依赖工具(sdk_dist.py)
  • ch32f103c8-core:以具体芯片(CH32F103C8)为对象的板级支持包,包含applications(用户应用与main.c)、board(板级初始化、链接脚本、Kconfig 配置)以及 MDK 工程文件;
  • libraries/CH32F10x_StdPeriph_Driver:WCH 提供的标准外设驱动库,目录内含CMSIS/WCH/CH32F10x(核心头文件、系统初始化与启动文件)以及StdPeriph_Driver/inc、StdPeriph_Driver/src(GPIO、USART、SPI、I2C、TIM、RTC、IWDG 等外设驱动);
  • libraries/ch32_drivers:将厂商库封装为 RT-Thread 设备框架驱动的适配层,例如drv_gpio_ch32f10x.c、drv_uart_ch32f10x.c、drv_spi_ch32f10x.c、drv_hwi2c_ch32f10x.c、drv_pwm_ch32f10x.c、drv_timer_ch32f10x.c、drv_iwdt_ch32f10x.c、drv_rtc_ch32f10x.c等;
  • tools:ENV 环境下执行scons --dist分发 BSP 时所需的依赖工具。

分层设计的好处在于:厂商库和适配驱动(libraries)与具体芯片无关、可跨多个 BSP 复用;每个具体 BSP 只需在board层提供时钟、引脚等板级差异化实现,即可完成移植。

2 已适配 RT-Thread 的驱动(ch32_drivers)

libraries/ch32_drivers目前支持的驱动、对应宏开关以及驱动要求 board 层实现的接口如下表:

驱动功能宏开关外设选择宏board 层需实现的接口
GPIORT_USING_PIN——
UART 1/2/3RT_USING_SERIALBSP_USING_UART1/2/3(父开关BSP_USING_UART)void ch32f1_usart_clock_and_io_init(USART_TypeDef* usartx);
SPI 1/2RT_USING_SPIBSP_USING_SPI1/2(父开关BSP_USING_SPI)void ch32f1_spi_clock_and_io_init(SPI_TypeDef* spix);
rt_uint32_t ch32f1_spi_clock_get(SPI_TypeDef* spix);
I2C(硬件)1/2RT_USING_I2CBSP_USING_HWI2C1/2(父开关BSP_USING_HWI2C)void ch32f1_i2c_clock_and_io_init(I2C_TypeDef* i2cx);
void ch32f1_i2c_config(I2C_TypeDef* i2cx);
WDTRT_USING_WDTBSP_USING_IWDT,另需LSI_VALUE—
RTCRT_USING_RTCBSP_USING_RTC,可选BSP_USING_RTC_LSI(使用 LSI 时钟,否则默认 LSE),另需LSI_VALUE—
CLOCK_TIMER 1/2/3/4RT_USING_CLOCK_TIMEBSP_USING_TIM1/2/3/4_CLOCK_TIMER(父开关BSP_USING_CLOCK_TIMER)void ch32f1_tim_clock_init(TIM_TypeDef *timx);
rt_uint32_t ch32f1_tim_clock_get(TIM_TypeDef *timx);
struct rt_clock_timer_info* ch32f1_clock_timer_info_config_get(TIM_TypeDef *timx);
PWM 4(timer) × 4(channel)RT_USING_PWMBSP_USING_TIM1/2/3/4_PWM_CH1~CH4(父开关BSP_USING_PWM)void ch32f1_pwm_io_init(TIM_TypeDef *timx, rt_uint8_t channel);

其中 PWM 与 CLOCK_TIMER 共用 TIM1~TIM4 定时器资源,二者是同一定时器的两种工作模式(详见 ch32f103c8-core/board/Kconfig 中的choice配置),因此 PWM 驱动还依赖定时器相关接口:

void ch32f1_tim_clock_init(TIM_TypeDef *timx); rt_uint32_t ch32f1_tim_clock_get(TIM_TypeDef *timx);

从源码看,这些接口正是适配驱动与 board 层之间的“契约”:适配驱动只负责调用 RT-Thread 设备框架的注册与读写逻辑,而具体的外设时钟使能、GPIO 复用引脚配置等芯片相关细节,全部下沉到 board 层实现。以串口为例,board.c 中ch32f1_usart_clock_and_io_init完成了每个 USART 的时钟与引脚初始化:

  • USART1:TX→PA9(复用推挽)、RX→PA10(浮空输入),时钟源为RCC_APB2Periph_USART1;
  • USART2:TX→PA2、RX→PA3,时钟源为RCC_APB1Periph_USART2;
  • USART3:TX→PC10、RX→PC11,并调用GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE)进行部分重映射。

同样,SPI 的ch32f1_spi_clock_get依据外设所在总线返回对应时钟:SPI1 挂载在 APB2(返回PCLK2_Frequency),SPI2 挂载在 APB1(返回PCLK1_Frequency);而 TIM1~TIM4 均位于 HCLK,ch32f1_tim_clock_get统一返回HCLK_Frequency。这些返回值会被适配驱动用于计算波特率、SPI 分频与定时器计数频率。

3 必须实现的接口

无论启用哪些驱动,以下接口都必须由 board 层实现,它是整个系统时钟配置的基础:

rt_uint32_t ch32_get_sysclock_frequency(void);

在 board.c 中,该接口通过厂商库的RCC_GetClocksFreq获取当前 SYSCLK 频率:

rt_uint32_t ch32_get_sysclock_frequency(void) { RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); return RCC_Clocks.SYSCLK_Frequency; }

它被 drv_common.c 中的SystemClock_Config()使用:SysTick_Config(ch32_get_sysclock_frequency() / RT_TICK_PER_SECOND)据此计算出 SysTick 重装载值,再配合SysTick_Handler中调用rt_tick_increase()驱动 RT-Thread 内核节拍。也就是说,只要板级时钟树配置正确,系统节拍便自动就绪。

4 构建具体芯片 BSP:所需文件清单

以 ch32f103c8-core 为例,在 ENV 环境下使用 MDK 开发,至少需要准备以下文件:

类型路径
BSP 根目录ch32f103c8-core/
应用层ch32f103c8-core/applications/SConscript、ch32f103c8-core/applications/main.c
链接脚本ch32f103c8-core/board/linker_scripts/link.sct(GCC 对应link.lds)
板级源码ch32f103c8-core/board/board.c、ch32f103c8-core/board/board.h
芯片配置ch32f103c8-core/board/ch32f10x_conf.h、ch32f103c8-core/board/system_ch32f10x.c
Kconfigch32f103c8-core/board/Kconfig、ch32f103c8-core/Kconfig
构建脚本ch32f103c8-core/board/SConscript、ch32f103c8-core/SConscript、ch32f103c8-core/SConstruct、ch32f103c8-core/rtconfig.py
MDK 工程模板ch32f103c8-core/template.uvprojx

对照仓库中真实的 ch32f103c8-core 目录,文件一一对应,可作为新建其他 CH32F10x 芯片 BSP 时的目录模板。

顶层 Kconfig 负责串联整个配置体系:RTT_DIR := ../../..指向 RT-Thread 内核,source "$(RTT_DIR)/Kconfig"引入内核配置,rsource "../libraries/Kconfig"引入芯片系列定义,rsource "board/Kconfig"引入板级外设配置。这意味着板级 Kconfig 是用户通过 menuconfig 选择外设驱动的唯一入口。

5 创建 template.uvprojx(MDK 工程模板)

  1. 在 Keil MDK 中新建一个名为template的工程;
  2. 删除多余的默认子目录target1,将目录名改为rtthread-ch32f103x;
  3. 在 Options(魔术棒)的 Device 选项中选择目标芯片型号(如 CH32F103C8);
  4. 在 User 选项中配置编译后动作,例如输出二进制镜像:
fromelf --bin !L --output rtthread.bin

这样每次编译完成后自动生成rtthread.bin,便于烧录与分发。模板工程的作用是让scons --dist分发 BSP 时能够以template.uvprojx为蓝本生成用户可用的project.uvprojx。

6 编写 board/Kconfig:芯片定义与外设使能

6.1 芯片 SOC 定义

在板级board/Kconfig中首先定义本 BSP 对应的芯片型号:

menu "Hardware Drivers Config" config SOC_CH32F103C8 bool select SOC_ARM_SERIES_CH32F103 select RT_USING_COMPONENTS_INIT select RT_USING_USER_MAIN default y
  • SOC_CH32F103C8是本 BSP 的芯片标识;
  • SOC_ARM_SERIES_CH32F103在 libraries/Kconfig 中定义,它负责select ARCH_ARM_CORTEX_M3并归属到SOC_ARM_FAMILY_CH32家族,从而自动拉起内核架构配置;
  • RT_USING_COMPONENTS_INIT、RT_USING_USER_MAIN为 RT-Thread 设备框架默认选项,保证自动初始化和 main 线程机制生效。

6.2 外设驱动使能模板

启用适配驱动只需按“BSP_USING_XXX选择RT_USING_XXX,外设细分宏逐项使能”的规则编写即可。以串口驱动为例:

menu "On-chip Peripheral Drivers" config BSP_USING_UART bool "using onchip usart" select RT_USING_SERIAL default n if BSP_USING_UART config BSP_USING_UART1 bool "using uart1" default n config BSP_USING_UART2 bool "using uart2" default n config BSP_USING_UART3 bool "using uart3" default n endif

当用户开启BSP_USING_UART时,Kconfig 自动select RT_USING_SERIAL,把 RT-Thread 串口设备框架拉入编译;再通过BSP_USING_UART1/2/3精确选择具体外设。SPI、硬件 I2C 的写法完全一致(见 ch32f103c8-core/board/Kconfig 中BSP_USING_SPI、BSP_USING_HWI2C段)。

WDT 与 RTC 稍有特殊,它们依赖内部低速时钟参数:

config BSP_USING_IWDT bool "using onchip iwdt" select RT_USING_WDT select LSI_VALUE default n config BSP_USING_RTC bool "using onchip rtc" select RT_USING_RTC default n if BSP_USING_RTC config BSP_USING_RTC_LSI bool "using LSI clock for rtc, if not, LSE default" select LSI_VALUE default n endif config LSI_VALUE int default 40000

LSI_VALUE默认 40000(即 40 kHz 内部低速时钟),IWDG 与选择 LSI 作为时钟源的 RTC 都会用到它。

6.3 定时器与 PWM 的特殊宏开关

定时器(CLOCK_TIMER)与 PWM 共享 TIM1~TIM4,因此额外定义了一层工作模式选择宏,详见 ch32f103c8-core/board/Kconfig 的BSP_USING_TIM段:

config BSP_USING_TIM bool "using TIMx" default n if BSP_USING_TIM config BSP_USING_CLOCK_TIMER bool select RT_USING_CLOCK_TIME default n config BSP_USING_PWM bool select RT_USING_PWM default n ... endif

整体宏开关层级可归纳为:

  • BSP_USING_TIM:定时器总开关;
  • BSP_USING_TIM1/2/3/4:具体定时器;
  • 每个定时器内通过choice互斥选择:
    • BSP_USING_TIMx_CLOCK_TIMER:作为系统时钟定时器(select BSP_USING_CLOCK_TIMER,进而select RT_USING_CLOCK_TIME);
    • BSP_USING_TIMx_PWM:作为 PWM 输出(select BSP_USING_PWM,进而select RT_USING_PWM),其下再细分BSP_USING_TIMx_PWM_CH1~CH4四个通道。

因此“PWM 4(timer) × 4(channel)”最多可同时使用 4 个定时器、共 16 路 PWM 通道;但同一定时器在同一时间只能被选择为时钟定时器或 PWM 中的一种模式。

6.4 配置生成结果

在 ENV 下执行menuconfig完成上述选择后,配置会写入rtconfig.h。仓库中现成的 ch32f103c8-core/rtconfig.h 展示了启用 UART2 时的生成结果:RT_USING_SERIAL、BSP_USING_UART、BSP_USING_UART2、LSI_VALUE 40000等宏均已定义,并设置控制台设备为RT_CONSOLE_DEVICE_NAME "uart2"。

7 在 board/board.h 声明接口、在 board/board.c 实现

7.1 board.h 中的接口声明

根据启用的驱动宏,在 board/board.h 中声明对应接口,例如串口驱动:

#ifdef BSP_USING_UART void ch32f1_usart_clock_and_io_init(USART_TypeDef* usartx); #endif

定时器与 PWM 驱动使用额外定义的宏开关,其完整声明块如下:

#ifdef BSP_USING_TIM void ch32f1_tim_clock_init(TIM_TypeDef *timx); rt_uint32_t ch32f1_tim_clock_get(TIM_TypeDef *timx); #ifdef BSP_USING_CLOCK_TIMER struct rt_clock_timer_info* ch32f1_clock_timer_info_config_get(TIM_TypeDef *timx); #endif #ifdef BSP_USING_PWM void ch32f1_pwm_io_init(TIM_TypeDef *timx, rt_uint8_t channel); #endif #endif

board.h还同时定义了内存布局宏:CH32_FLASH_START_ADRESS(0x08000000)、FLASH_PAGE_SIZE(64)、HEAP_BEGIN(根据编译器取 RW 区 ZI 段结束地址或__bss_end)、HEAP_END(SRAM 末尾)。这些宏配合 drv_common.c 的rt_hw_board_init()完成堆初始化(rt_system_heap_init(HEAP_BEGIN, HEAP_END))、Pin/串口驱动初始化与控制台绑定。

7.2 board.c 中的接口实现

在 board/board.c 中逐一实现这些接口。以定时器时钟为例:

#ifdef BSP_USING_TIM void ch32f1_tim_clock_init(TIM_TypeDef *timx) { if (timx == TIM1) RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); if (timx == TIM2) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); if (timx == TIM3) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); if (timx == TIM4) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); } #endif

注意 TIM1 挂载在 APB2,TIM2/3/4 挂载在 APB1,时钟源总线不同。PWM 的ch32f1_pwm_io_init则按定时器与通道配置复用引脚(如 TIM1_CH1→PA8、TIM2_CH1→PA0、TIM3_CH1→PA6、TIM4_CH1→PB6),这些引脚关系与 ch32f103c8-core/README.md 中列出的 IO 映射表一致。

7.3 驱动注册链路

适配驱动通过INIT_BOARD_EXPORT/INIT_DEVICE_EXPORT等自动初始化机制注册到 RT-Thread 设备框架,rt_hw_board_init()中的rt_components_board_init()会触发这些初始化函数;而ch32_drivers内各驱动源文件会在其初始化过程中回调上面声明的 board 层接口完成时钟与引脚配置,随后调用rt_hw_xxx_register()注册设备。这就是“board 层提供硬件差异、适配层提供设备框架逻辑”的完整调用链。

8 编译验证与运行

  • 在 ENV 环境下进入 BSP 目录执行scons即可完成编译(MDK 工程亦可直接在 Keil 中打开 project.uvprojx 编译下载);
  • 连接开发板串口到 PC,终端设置为 115200-8-1-N,复位后可看到 RT-Thread 启动 banner 与msh>命令行提示符;
  • 若需将 BSP 分发给他人,可使用 tools/sdk_dist.py 执行scons --dist,生成独立的 BSP 包。

9 小结:CH32F10x 系列 BSP 移植四步法

  1. 搭建骨架:复制ch32f103c8-core目录结构,准备applications、board、SConscript/SConstruct、template.uvprojx等文件;
  2. 定义芯片:在board/Kconfig声明SOC_CH32Fxxxx,select到 libraries/Kconfig 中已有的SOC_ARM_SERIES_CH32F103;
  3. 使能驱动:按BSP_USING_XXX → select RT_USING_XXX → BSP_USING_XXXn的层级编写外设 Kconfig,定时器/PWM 另加模式选择;
  4. 补齐接口:在board.h声明、board.c实现ch32_get_sysclock_frequency()与各驱动的时钟/IO 初始化接口,保证 drv_common.c 中的系统节拍与设备初始化链路完整运行。

按照这套流程,即可快速将同一系列的其他 CH32F10x 芯片接入 RT-Thread,复用libraries/ch32_drivers的全部适配驱动。

  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

相关推荐

上一篇:Power Apps组件框架深度解析:从IncrementControl到CodeInterpreterControl
下一篇:TakePhoto无障碍标签:为图片处理界面添加内容描述

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询