1. 项目背景与目标
最近在为一个工业控制项目选型MCU时,发现了沁恒微电子的CH32V307VCT6这颗RISC-V内核芯片。它144MHz主频、内置480Mbps USB PHY、双CAN和10M以太网MAC的外设配置,在性价比方面确实很有吸引力。不过官方SDK基于裸机开发,而我们团队更习惯使用RT-Thread这样的实时操作系统来提升开发效率。
于是萌生了一个想法:能不能在Ubuntu开发环境下,为CH32V307VCT6移植RT-Thread并制作完整的BSP包?这样既能发挥RT-Thread丰富的软件包生态,又能利用Linux环境下更强大的开发工具链。经过两周的摸索,终于完成了这个移植项目,过程中踩了不少坑,也积累了一些值得分享的经验。
2. 环境准备与工具链配置
2.1 开发环境搭建
选择Ubuntu 20.04 LTS作为基础开发环境,主要考虑其长期支持特性和工具链稳定性。需要安装的核心组件包括:
bash复制sudo apt install git wget make python3 gcc-arm-none-eabi
特别要注意的是RISC-V工具链的安装。由于Ubuntu官方源的版本较旧,我们直接从SiFive获取预编译工具链:
bash复制wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2020.04.0-x86_64-linux-ubuntu14.tar.gz
tar -xzf riscv64-unknown-elf-gcc-*.tar.gz
export PATH=$PATH:~/riscv64-unknown-elf-gcc/bin
2.2 源码获取与目录结构
RT-Thread的源码管理非常规范,我们采用官方推荐的仓库结构:
bash复制mkdir ch32v307_bsp && cd ch32v307_bsp
git clone --recursive https://github.com/RT-Thread/rt-thread.git
git clone https://github.com/openwch/ch32v307.git
最终的BSP目录结构设计如下:
code复制ch32v307v-r0/
├── applications
├── board
│ ├── Kconfig
│ ├── board.c
│ ├── drv_gpio.c
│ └── linker_scripts
├── libraries
│ ├── CH32V307VCT6
│ └── rt_drv
└── rt-thread
3. 内核移植关键步骤
3.1 启动文件适配
CH32V307VCT6的启动流程与常规ARM Cortex-M芯片有所不同,需要特别注意以下几点:
- 修改汇编启动文件(startup_ch32v30x_D8C.S)中的堆栈初始化
- 重定向中断向量表到RAM区
- 添加RT-Thread需要的异常处理钩子
关键修改片段:
assembly复制/* 在Reset_Handler中添加 */
la a0, __StackTop
mv sp, a0
call SystemInit
call entry /* RT-Thread入口 */
3.2 链接脚本优化
针对CH32V307VCT6的320KB Flash和64KB RAM配置,设计了专用链接脚本:
ld复制MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 320K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
/* 确保RT-Thread的heap和stack区域足够 */
_stack_size = 0x1000;
_heap_size = 0x2000;
3.3 时钟树配置
CH32V307的时钟系统相当灵活,我们选择外部8MHz晶振作为时钟源,通过PLL倍频到144MHz:
c复制void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL18;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
}
4. 外设驱动开发
4.1 GPIO驱动实现
RT-Thread的设备驱动框架要求实现标准的ops结构体:
c复制static const struct rt_pin_ops _ch32_pin_ops = {
.pin_mode = ch32_pin_mode,
.pin_write = ch32_pin_write,
.pin_read = ch32_pin_read,
.pin_attach_irq = ch32_pin_attach_irq,
.pin_detach_irq = ch32_pin_detach_irq,
.pin_irq_enable = ch32_pin_irq_enable,
};
int rt_hw_pin_init(void)
{
return rt_device_pin_register("pin", &_ch32_pin_ops, RT_NULL);
}
INIT_BOARD_EXPORT(rt_hw_pin_init);
4.2 USB驱动适配
CH32V307内置USB PHY是个亮点,我们为其实现了CDC设备类:
c复制static struct udcd_ops ch32_udc_ops = {
.set_address = ch32_udc_set_address,
.ep_set_stall = ch32_udc_ep_set_stall,
.ep_clear_stall = ch32_udc_ep_clear_stall,
.ep_write = ch32_udc_ep_write,
.ep_read = ch32_udc_ep_read,
};
static rt_err_t ch32_udc_init(rt_device_t device)
{
/* USB时钟和GPIO配置 */
RCC_USBCLK48MConfig(RCC_USBCLK48MCLKSource_PLLCLK_Div3);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
return RT_EOK;
}
5. 系统优化与调试
5.1 内存管理策略
针对64KB RAM的限制,我们采用了以下优化措施:
- 将RT-Thread内核对象池大小调整为2KB
- 使用SLAB算法替代原生内存管理
- 为网络协议栈单独划分16KB空间
c复制#define RT_HEAP_SIZE (32*1024)
static rt_uint8_t rt_heap[RT_HEAP_SIZE];
void rt_system_heap_init(void *begin_addr, void *end_addr)
{
rt_slab_init(&_heap, begin_addr, RT_HEAP_SIZE);
}
5.2 调试技巧
在Ubuntu环境下,我们使用OpenOCD进行调试:
bash复制openocd -f interface/wch-link.cfg -f target/wch-riscv.cfg
配合GDB调试时,需要特别注意RISC-V的特殊寄存器:
gdb复制# 查看mstatus寄存器
info register mstatus
# 设置硬件断点
hb rt_thread_mdelay
6. 常见问题解决
6.1 中断响应异常
症状:系统随机死机,特别是USB中断触发时
原因:RISC-V的CLIC中断控制器需要特殊配置
解决方案:
c复制void rt_hw_interrupt_init(void)
{
/* 启用CLIC的向量中断模式 */
__RV_CSR_SET(CSR_MINTTHRESH, 0);
__RV_CSR_SET(CSR_MSTATUS, MSTATUS_MIE);
}
6.2 线程栈溢出
症状:线程莫名其妙崩溃
排查方法:
- 在board.h中开启RT_USING_OVERFLOW_CHECK
- 使用rt_thread_self()->stack_size检查使用量
优化建议:
- 为关键线程增加128字节安全余量
- 使用rt_memcheck工具定期扫描
7. 项目成果与测试
最终实现的BSP包支持以下功能:
- 多线程调度(实测切换时间<5us)
- 完整的设备驱动框架
- FinSH控制台(通过UART1)
- USB虚拟串口
- 以太网LwIP协议栈
性能测试结果:
code复制msh />list_thread
thread pri status sp stack size max used left tick error
-------- --- ------- ---------- ---------- ------ ---------- ---
tshell 20 running 0x00000060 0x00001000 15% 0x0000000a 000
usbd 16 suspend 0x00000080 0x00000800 22% 0x00000005 000
tidle0 31 ready 0x00000040 0x00000400 09% 0x00000010 000
8. 移植心得
- RISC-V的中断处理机制与ARM有很大不同,特别是CLIC控制器需要仔细阅读《RISC-V特权架构手册》
- CH32V307的HAL库存在一些寄存器操作顺序问题,建议关键外设直接操作寄存器
- RT-Thread的构建系统对RISC-V支持良好,但需要手动修改rtconfig.py中的工具链路径
- 在资源受限环境下,合理配置RT_USING_XXX宏可以显著减小固件体积
这个BSP现在已经提交到RT-Thread官方仓库,后续计划增加更多驱动支持和性能优化。对于想在RISC-V芯片上使用RT-Thread的开发者,希望这个项目能提供有价值的参考。