3. 开发环境搭建:RISC-V GCC工具链安装、OpenOCD调试器配置、VS Code IDE集成、第一个裸机程序验证
说实话,很多初学者在学RTOS移植时,第一步就栽在了环境搭建上。工具链版本不对、OpenOCD死活连不上、VS Code配置了一堆插件还是报红……这些坑我几乎都踩过。这一章,咱们就把这些基础工作一次性搞定。
我会带着你从零开始,把RISC-V GCC工具链装好,把OpenOCD调通,再把VS Code配成一个顺手的IDE。最后,咱们跑一个裸机程序验证一下——确保整个链路是通的。
本章核心目标:搭建一个可复用的GD32V开发环境,确保能编译、烧录、调试裸机程序。这是后续移植FreeRTOS的基础。
3.1 开发环境全景图
先看看咱们要搭的这套环境长什么样。我习惯画一张图来理清关系,这样心里有底。
这套架构其实很清晰:你在PC上写代码,用GCC编译成RISC-V机器码,通过OpenOCD和调试器烧到GD32V芯片里。VS Code负责让你看得舒服、写得顺手。
3.2 安装RISC-V GCC工具链
工具链的选择,我建议直接用官方的RISC-V GNU工具链。GD32V用的是RV32IMAC架构,带硬件乘法和原子操作,这个后面移植FreeRTOS时会用到。
3.2.1 下载与安装
我个人习惯用SiFive提供的预编译包,省去自己编译的麻烦。你想想看,自己编译一遍GCC要一两个小时,何必呢?
- 下载地址:SiFive官方工具链页面,选择"GNU Embedded Toolchain for RISC-V"
- 版本选择:推荐2024.04.01或更新版本,太老的版本对GD32V支持不好
- 解压安装:解压到
/opt/riscv或C:\riscv,路径不要有中文和空格
我的经验:Linux用户直接解压到 /opt/riscv 后,把 /opt/riscv/bin 加到PATH里。Windows用户我建议用MSYS2环境,比Cygwin省心很多。
3.2.2 验证安装
装完之后,打开终端跑一下这个命令:
riscv-none-embed-gcc --version
如果看到版本信息,说明工具链装好了。我遇到过有人装完发现命令是 riscv64-unknown-elf-gcc,其实也能用,但后面Makefile里的前缀要对应改。
| 工具链前缀 | 适用场景 | 备注 |
|---|---|---|
riscv-none-embed- |
嵌入式裸机开发 | 推荐,SiFive官方包 |
riscv64-unknown-elf- |
通用RISC-V开发 | 也可用,需调整Makefile |
riscv32-unknown-elf- |
32位RISC-V专用 | 较少见,不推荐 |
3.3 配置OpenOCD调试器
OpenOCD是连接PC和开发板的桥梁。GD32V系列通常板载GD-Link,其实就是CMSIS-DAP的变种。我刚开始用的时候,总搞不清配置文件怎么写,后来发现其实有模板可以直接用。
3.3.1 安装OpenOCD
我建议从源码编译,因为官方发行版对GD32V的支持可能不够新。不过如果你嫌麻烦,也可以直接用xpack的预编译包。
# 从源码编译(Linux/macOS)
git clone https://github.com/riscv-collab/riscv-openocd.git
cd riscv-openocd
./bootstrap
./configure --enable-ftdi --enable-cmsis-dap
make -j4
sudo make install
注意:Windows用户建议直接用xpack的预编译包,省去编译的麻烦。下载后解压,把 openocd.exe 所在目录加到PATH里。
3.3.2 编写GD32V的OpenOCD配置文件
这是关键一步。我踩过最大的坑就是配置文件写错了,导致OpenOCD一直报"JTAG scan failed"。后来发现是复位时序不对。
下面是我常用的配置文件,适用于GD32VF103系列:
# gd32vf103.cfg
source [find interface/cmsis-dap.cfg]
transport select jtag
adapter speed 1000
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
$_TARGETNAME configure -work-area-phys 0x20000000
$_TARGETNAME configure -work-area-size 0x10000
flash bank spi0 gd32v_flash 0x08000000 0 0 0 $_TARGETNAME
init
reset halt
嗯,这里要注意:-expected-id 的值要根据你的芯片实际ID修改。GD32VF103一般是 0x1000563d,但不同批次可能有差异。你可以先不加这个参数,让OpenOCD自动检测。
3.3.3 启动OpenOCD并连接
把开发板用USB连到电脑,然后执行:
openocd -f gd32vf103.cfg
如果看到 Info : Listening on port 3333 for gdb connections,说明连接成功了。我曾经有一次怎么都连不上,最后发现是USB线的问题——有些线只能充电不能传数据。
3.4 VS Code IDE集成
VS Code配置得好,开发效率能翻倍。我习惯把它打造成一个轻量级的IDE,既不像Keil那么臃肿,又比纯命令行友好。
3.4.1 必备插件
| 插件名称 | 用途 | 是否必须 |
|---|---|---|
| C/C++ (ms-vscode.cpptools) | 代码补全、语法高亮、调试 | 是 |
| RISC-V Support (zhwu95.riscv) | RISC-V汇编语法高亮 | 推荐 |
| Makefile Tools (ms-vscode.makefile-tools) | Makefile集成 | 推荐 |
| Serial Monitor (ms-vscode.vscode-serial-monitor) | 串口监视器 | 推荐 |
| Cortex-Debug (marus25.cortex-debug) | 调试支持 | 是 |
3.4.2 配置调试任务
在 .vscode/launch.json 里配置调试器:
{
"version": "0.2.0",
"configurations": [
{
"name": "GD32V Debug",
"type": "cortex-debug",
"request": "launch",
"servertype": "openocd",
"cwd": "${workspaceRoot}",
"executable": "./build/demo.elf",
"configFiles": [
"gd32vf103.cfg"
],
"svdFile": "./GD32VF103.svd",
"runToMain": true,
"preLaunchTask": "build"
}
]
}
我的习惯:svdFile 一定要配上,这样调试时能看到外设寄存器的值,比裸看内存方便太多了。SVD文件可以从GD32官方SDK里找到。
3.5 第一个裸机程序验证
环境搭好了,咱们跑个最简单的程序——点灯。别小看这个,它能验证工具链、烧录、调试整个链路是否正常。
3.5.1 项目结构
我习惯这样组织项目:
gd32v-demo/
├── startup/
│ ├── startup_gd32vf103.S # 启动文件
│ └── system_gd32vf103.c # 系统初始化
├── src/
│ └── main.c # 主程序
├── ld/
│ └── gd32vf103.lds # 链接脚本
├── Makefile # 构建文件
└── gd32vf103.cfg # OpenOCD配置
3.5.2 主程序代码
这个程序让板载LED以500ms间隔闪烁:
// main.c
#include "gd32vf103.h"
void delay_volatile(uint32_t count) {
while (count--) {
__asm__ volatile("nop");
}
}
int main(void) {
// 使能GPIOC时钟
rcu_periph_clock_enable(RCU_GPIOC);
// 配置PC13为推挽输出
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
while (1) {
gpio_bit_set(GPIOC, GPIO_PIN_13); // LED灭(低电平有效)
delay_volatile(0x1FFFFF);
gpio_bit_reset(GPIOC, GPIO_PIN_13); // LED亮
delay_volatile(0x1FFFFF);
}
}
3.5.3 Makefile构建
Makefile是核心,我直接给一个能用的版本:
TOOLCHAIN = riscv-none-embed-
CC = $(TOOLCHAIN)gcc
OBJCOPY = $(TOOLCHAIN)objcopy
SIZE = $(TOOLCHAIN)size
CFLAGS = -march=rv32imac -mabi=ilp32 -Os -ffreestanding -nostdlib
LDFLAGS = -T ld/gd32vf103.lds -nostartfiles
SRCS = src/main.c startup/startup_gd32vf103.S startup/system_gd32vf103.c
OBJS = $(SRCS:.c=.o)
OBJS := $(OBJS:.S=.o)
all: build/demo.elf build/demo.bin
build/demo.elf: $(OBJS) | build
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
$(SIZE) $@
build/demo.bin: build/demo.elf
$(OBJCOPY) -O binary $< $@
build:
mkdir -p build
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.S
$(CC) $(CFLAGS) -c -o $@ $<
flash: build/demo.bin
openocd -f gd32vf103.cfg -c "program build/demo.bin 0x08000000 verify reset exit"
clean:
rm -rf build $(OBJS)
3.5.4 编译与烧录
在终端执行:
make clean
make
make flash
如果一切顺利,你会看到LED开始闪烁。我第一次跑通的时候,那个小灯闪起来的感觉,比后来调通FreeRTOS还激动。你想想看,从零开始搭环境,到最终看到硬件响应,这就是嵌入式开发的魅力。
常见问题:
- 编译报错"undefined reference to `__start'": 启动文件没加对,检查startup.S是否在编译列表里
- 烧录时报"Error: flash write failed": 检查OpenOCD配置里的flash地址,GD32VF103的Flash起始地址是0x08000000
- LED不亮: 先确认板子供电正常,再用万用表量一下LED引脚电平。我遇到过板子LED是反的——高电平亮,结果代码写成了低电平亮
3.6 调试验证
程序跑起来了,但咱们还得验证调试功能是否正常。在VS Code里按F5启动调试,你应该能看到:
- 代码停在main函数入口
- 可以单步执行
- 可以查看变量值
- 可以设置断点
如果调试器连不上,多半是OpenOCD配置问题。我建议先用命令行启动OpenOCD,看输出日志。最常见的错误是 JTAG scan failed,这时候检查一下接线和芯片ID。
验证清单:
- ✅ RISC-V GCC工具链能编译出.elf文件
- ✅ OpenOCD能识别芯片并连接
- ✅ 能烧录.bin文件到Flash
- ✅ 程序运行后LED按预期闪烁
- ✅ VS Code调试器能连接并单步执行
全部通过?恭喜,你的开发环境已经就绪,可以开始移植FreeRTOS了。