3. 开发环境搭建:RISC-V GCC工具链安装、OpenOCD调试器配置、VS Code IDE集成、第一个裸机程序验证

说实话,很多初学者在学RTOS移植时,第一步就栽在了环境搭建上。工具链版本不对、OpenOCD死活连不上、VS Code配置了一堆插件还是报红……这些坑我几乎都踩过。这一章,咱们就把这些基础工作一次性搞定。

我会带着你从零开始,把RISC-V GCC工具链装好,把OpenOCD调通,再把VS Code配成一个顺手的IDE。最后,咱们跑一个裸机程序验证一下——确保整个链路是通的。

本章核心目标:搭建一个可复用的GD32V开发环境,确保能编译、烧录、调试裸机程序。这是后续移植FreeRTOS的基础。

3.1 开发环境全景图

先看看咱们要搭的这套环境长什么样。我习惯画一张图来理清关系,这样心里有底。

GD32V 开发环境架构 主机开发环境(PC) RISC-V GCC 工具链 OpenOCD 调试器 VS Code IDE Make 构建系统 调试接口(JTAG / SWD) GD-Link / J-Link / CMSIS-DAP 目标板(GD32VF103) 裸机程序 FreeRTOS 外设驱动 启动代码(startup)

这套架构其实很清晰:你在PC上写代码,用GCC编译成RISC-V机器码,通过OpenOCD和调试器烧到GD32V芯片里。VS Code负责让你看得舒服、写得顺手。

3.2 安装RISC-V GCC工具链

工具链的选择,我建议直接用官方的RISC-V GNU工具链。GD32V用的是RV32IMAC架构,带硬件乘法和原子操作,这个后面移植FreeRTOS时会用到。

3.2.1 下载与安装

我个人习惯用SiFive提供的预编译包,省去自己编译的麻烦。你想想看,自己编译一遍GCC要一两个小时,何必呢?

  1. 下载地址:SiFive官方工具链页面,选择"GNU Embedded Toolchain for RISC-V"
  2. 版本选择:推荐2024.04.01或更新版本,太老的版本对GD32V支持不好
  3. 解压安装:解压到 /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。

验证清单:

  1. ✅ RISC-V GCC工具链能编译出.elf文件
  2. ✅ OpenOCD能识别芯片并连接
  3. ✅ 能烧录.bin文件到Flash
  4. ✅ 程序运行后LED按预期闪烁
  5. ✅ VS Code调试器能连接并单步执行

全部通过?恭喜,你的开发环境已经就绪,可以开始移植FreeRTOS了。