【gd32vf103 折腾】基于gcc+make的开发环境配置

GD32VF103 GCC+MAKE环境配置

脱离各种大型IDE的GD32VF103的开发环境。

我常用的代码编辑器是sublime text,除了界面风格符合我的审美外,在安装了python和mingw后,它可以使用快捷键ctrl+b直接运行程序,已经形成肌肉记忆了。

此前使用开发环境为segger的 embedded studio + 官方的DFU下载器,但这个下载器有个致命问题,它会随机触发windows的蓝屏,已经弄崩过一次我的系统,所幸只是系统崩了,资料仍能备份出来。

工具链

1.编译器: riscv-none-elf-gcc

无法翻墙的可以下载官方的ide,在ide中集成了一个riscv-none-embed-gcc

2.make: mingw64

3.烧录器: dfu-util 0.11: dfu-util-0.11.tar.gz

4.固件库: GD32VF103_Demo_Suites_V1.6.0

5.git:git 2.52.0

powershell下运行会存在问题,windows下命令行工具使用 git bash

工程构建

gd32vf103_demo:

bash 复制代码
docs/		# 各类手册及工程资料
src/		# 代码目录
toolchain/	# 编译器及烧录器
tools/		# 解决工程构建过程所需工具

sdk构建

bash 复制代码
为了让整个工程结构精简,我将官方sdk划分为两个部分,一个部分为CMSIS,一个部分为Peripheral
其文件结构是:
src/GD32SDK/CMSIS/		# 外设所需的.c文件和.h文件,其他启动所需的.s文件、.ld文件、.lds文件都放在该文件夹下
src/GD32SDK/Peripheral/	# 仅存放外设所需的.c和.h文件,其下source存放.c文件,include存放.h文件

CMSIS下文件来源
│  gd32vf103.c				
│  gd32vf103.h				# 从Firmware\GD32VF103_standard_peripheral下复制
│  startup_gd32vf103.S		# 从Firmware\GD32VF103_standard_peripheral下复制
│  system_gd32vf103.c		# 从Firmware\GD32VF103_standard_peripheral下复制
│  system_gd32vf103.h		# 从Firmware\GD32VF103_standard_peripheral下复制
│  systick.c				# 从Examples\下随便找一个示例获得
│  systick.h				# 从Examples\下随便找一个示例获得
│
├─drivers		# 从固件库\Firmware\RISCV\drivers复制得到
│      n200_eclic.h
│      n200_func.c
│      n200_func.h
│      n200_timer.h
│      riscv_bits.h
│      riscv_const.h
│      riscv_encoding.h
│
└─env			# 从固件库\Firmware\RISCV\env_Eclipse复制得到
        entry.S
        GD32VF103x4.lds
        GD32VF103x6.lds
        GD32VF103x8.lds
        GD32VF103xB.lds
        handlers.c
        init.c
        start.S

gd32vf103.c:

c 复制代码
#include "gd32vf103.h"

/* 系统初始化 */
void SystemInit(void) {
    /* 默认使用HSI 8MHz */
    RCU->CTL |= (1U << 0);  /* 开启HSI */
    
    /* 等待HSI就绪 */
    while(!(RCU->CTL & (1U << 1)));
    
    /* 配置时钟 */
    RCU->CFG0 = 0;
    RCU->CFG0 |= (0x02 << 4);  /* AHB不分频 */
    RCU->CFG0 |= (0x04 << 8);  /* APB1 2分频 */
    RCU->CFG0 |= (0x00 << 11); /* APB2 1分频 */
    
    /* 选择HSI为系统时钟 */
    RCU->CFG0 &= ~(0x03 << 0);
    RCU->CFG0 |= (0x00 << 0);
    
    /* 更新系统时钟变量 */
    // SystemCoreClock = 8000000;
}

/* 简单延时函数 */
void delay_ms(uint32_t ms) {
    volatile uint32_t i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 8000; j++) {
            __asm__ volatile ("nop");
        }
    }
}

/* 时钟使能函数 */
void rcu_periph_clock_enable(uint32_t periph) {
    if(periph <= 0xFFFF) {
        RCU->APB2EN |= periph;
    } else {
        RCU->APB1EN |= (periph >> 16);
    }
}

/* GPIO初始化 */
void gpio_init(uint32_t gpio_port, uint32_t mode, uint32_t pin) {
    GPIO_Type *gpio = (GPIO_Type *)gpio_port;
    
    for(uint8_t i = 0; i < 16; i++) {
        if(pin & (1U << i)) {
            if(i < 8) {
                /* 低8位配置在CTL0 */
                uint32_t ctl = gpio->CTL0;
                ctl &= ~(0xFU << (i * 4));
                ctl |= (mode << (i * 4));
                gpio->CTL0 = ctl;
            } else {
                /* 高8位配置在CTL1 */
                uint32_t ctl = gpio->CTL1;
                ctl &= ~(0xFU << ((i - 8) * 4));
                ctl |= (mode << ((i - 8) * 4));
                gpio->CTL1 = ctl;
            }
        }
    }
}

main.c

c 复制代码
#include "gd32vf103.h"
#include "systick.h"
#include "gd32vf103_libopt.h"


int main(void) {
    /* 系统初始化 */
    SystemInit();
    
    /* 使能GPIOC时钟 */
    rcu_periph_clock_enable(RCU_GPIOC);
    
    /* 配置PC13为推挽输出 */
    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
    
    while(1) {
        /* 点亮LED(PC13置0,根据电路设计可能是低电平点亮)*/
        gpio_bit_reset(GPIOC, GPIO_PIN_13);
        delay_1ms(500);  // 延时500ms
        
        /* 熄灭LED(PC13置1)*/
        gpio_bit_set(GPIOC, GPIO_PIN_13);
        delay_1ms(500);  // 延时500ms
    }
    
    return 0;
}
bash 复制代码
gd32vf103_libopt.h 从固件库中示例获得,存放于src/inc下

makefile编写

bash 复制代码
# 工具链配置
TOOLDIR = ./toolchain/xpack-riscv-none-elf-gcc-15.2.0-1/bin
PREFIX = $(TOOLDIR)riscv-none-elf-
CC = $(PREFIX)gcc
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
SIZE = $(PREFIX)size

# MCU配置
MCU = -march=rv32imac_zicsr -mabi=ilp32 -mcmodel=medlow
OPT = -O1
DEBUG = -g

# 包含路径
INCLUDES = -I./src/inc -I./src/GD32SDK/CMSIS -I./src/GD32SDK/Peripheral/Include -I./src/GD32SDK/CMSIS/drivers

# 编译选项
CFLAGS = $(MCU) $(OPT) $(DEBUG) $(INCLUDES) \
         -std=gnu11 -ffunction-sections -fdata-sections \
         -Wall -Wextra -Wno-unused-parameter \
         -DGD32VF103R_START  # 添加这个宏避避免报错

# 链接选项
LDFLAGS = $(MCU) $(DEBUG) -Tsrc/GD32SDK/CMSIS/env/GD32VF103xB.lds \
          -nostartfiles -Wl,--gc-sections \
          -Wl,--print-memory-usage \
          -Wl,-Map=$(MAP)  # 使用MAP变量


# 源文件
PERIPHERAL_SRCS = $(wildcard src/GD32SDK/Peripheral/Source/*.c)
SRCS = src/src/main.c\
		src/GD32SDK/CMSIS/system_gd32vf103.c \
		src/GD32SDK/CMSIS/systick.c\
		src/GD32SDK/CMSIS/drivers/n200_func.c\
		src/GD32SDK/CMSIS/env/handlers.c \
		src/GD32SDK/CMSIS/env/init.c\
		$(PERIPHERAL_SRCS)
ASMSRCS =	src/GD32SDK/CMSIS/env/entry.S \
			src/GD32SDK/CMSIS/env/start.S 
			#src/GD32SDK/CMSIS/startup_gd32vf103.S\

# 目标文件 - 将所有.o文件放在obj目录下
C_OBJS = $(patsubst %.c,$(OBJ_DIR)/%.o,$(SRCS))
ASM_OBJS = $(patsubst %.S,$(OBJ_DIR)/%.o,$(ASMSRCS))
OBJS = $(C_OBJS) $(ASM_OBJS)

# 输出目录
BUILD_DIR = src/build
OBJ_DIR = $(BUILD_DIR)/obj

# 输出文件
TARGET = src/build/GD32_DEMO
ELF = $(TARGET).elf
BIN = $(TARGET).bin
HEX = $(TARGET).hex
MAP = $(TARGET).map

# 构建规则
all: $(ELF) $(MAP) $(BIN) $(HEX) 

$(ELF): $(OBJS) | $(BUILD_DIR) $(OBJ_DIR)
	$(CC) $(LDFLAGS) -o $@ $(OBJS)

# 创建构建目录
$(BUILD_DIR) $(OBJ_DIR):
	mkdir -p "$@"

# C文件编译规则 - 自动创建子目录
$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR)
	@mkdir -p "$(dir $@)"
	$(CC) $(CFLAGS) -c -o $@ $<

# 汇编文件编译规则
$(OBJ_DIR)/%.o: %.S | $(OBJ_DIR)
	@mkdir -p "$(dir $@)"
	$(CC) $(CFLAGS) -c -o $@ $<

$(HEX): $(ELF)
	$(OBJCOPY) -O ihex $< $@	

# 生成正确的BIN文件
$(BIN): $(ELF) $(MAP)
	$(OBJCOPY) -O binary \
		-j .init \
		-j .text \
		-j .rodata \
		-j .data \
		-j .init_array \
		-j .fini_array \
		--gap-fill=0xFF \
		$< $@

elf:	$(ELF)

bin:	$(BIN)

hex:	$(HEX)

# 反汇编用于调试
disasm: $(ELF)
	$(OBJDUMP) -d $< > $(TARGET).txt

clean:
	rm -f $(OBJS) $(ELF) $(BIN) $(HEX) $(TARGET).txt $(MAP)
	rm -r $(OBJ_DIR)

flash: $(BIN)
	dfu-util --alt 0  -D $(BIn) -s 0x8000000 
	dfu-util -i 0 -s 0x8000000:leave

# 调试目标
debug:
	# 未配备
	# openocd -f interface/cmsis-dap.cfg -f target/gd32vf103.cfg

.PHONY: all clean flash debug  disasm
bash 复制代码
该文件代码掺和了deepseek编写的内容

修复usb驱动

从官方下载的dfu驱动仅官方的DFU工具可用,使用dfu-util会报错。折腾了我很长一段时间,期间我试过重编译dfu-util,试过安装低版本dfu-util,后查资料时,在https://github.com/riscv-mcu/gd32-dfu-utils/issues/2获得解决方案
libuse-win32:libusb-win32-bin-1.2.6.0.zip

bash 复制代码
运行bin/x86/install-filter-win.exe
找到对应的gd32,安装驱动后就能正常使用dfu-util下载了
make flash 集成了烧录后跳转到运行代码

若整个过程没有错的话,会跟我实机上的红灯闪烁,我手上的是很久之前的longan开发版,它上面的是一块gd32vf103cbt6,有一个3色灯,其中红色的gpio是pc13.

macos下可以通过brew安装dfu-util,不会出现报驱动出错的问题,然后需要下载对应版本riscv-none-elf-gcc,需要给工具授权,否则在运行过程中,要在安全选项中给所涉及的程序点仍然允许

其他踩过的坑

bash 复制代码
1.elf转bin
makefile中可以看到,我尝试过使用map来让objcopy生成正确的bin文件,deepseek写了一部分参数,虽然能够生成合理大小的bin文件,但烧录后不能正常运行.
最初一般的objcopy生成的bin文件是空的,后面生成的bin文件是384mb的,原因是flash的初始地址,跟sram之间的相隔384mb.最后经trae完善,能够生成正确的bin文件了。

2.ai编程的应用
不建议新手,特别没有编程基础的新手,一下生成大量的代码。
现阶段,ai只能辅助编程,你对整个工程认识越清晰,对如何实现它认识越清晰,你获得的最终产品才会越趋向正确。
在大型的工程辅助开发中,尽量将项目拆分成多个模块,然后一个模块一个模块实现,然后review代码。

在我使用ai来编程的过程中,一次性生成完整代码,在项目规模超出一个学生管理系统的规模后,容易出现无法控制的错误,在我尝试多次让ai自己修复后,我追踪过一些bug,在定位到产生bug的代码后,多次无语,ai严格按照我的要求生成了代码,但实际上我对功能的描述有时候是不准确的,或者我认识的某些概念,与ai认识的是不一样的。

所以,有必要在ai写代码前,让它用语言描述一遍需求。
这种经验,在其他实践中也是通用的,比如在企业运作过程中,需要安排或者接受安排的时候,被安排者有必要以他自己的语言描述一遍任务,以便消除认识上的不同产生的错误
相关推荐
振南的单片机世界11 小时前
2MHz/50MHz玄机:GPIO速度档选错,信号直接“烂尾”
arm开发·stm32·单片机·嵌入式硬件
Run_Teenage12 小时前
嵌入式:理解STM32文档和社区生态,工具链的选择与安装
stm32·单片机·嵌入式硬件
ACP广源盛1392462567312 小时前
DeepSeek‑V4‑Flash 公测@ACP#昇腾 950 国产算力组合落地,国产 PCIe 交换芯片 IX9104 有哪些硬件机会
大数据·数据库·人工智能·分布式·单片机·嵌入式硬件·microsoft
铅笔小新z12 小时前
【stm32】RCC 与时钟树
stm32·单片机·嵌入式硬件
什么都干的派森12 小时前
小智MCP智能家居(红外设备篇)
硬件工程·智能家居·小智ai·小智mcp·红外控制
时空自由民.13 小时前
STM32 IIC HAL库,LL库
stm32·单片机·嵌入式硬件
txh050713 小时前
不用变压器也能把220V交流电变为5V直流电?
单片机·嵌入式硬件
辰哥单片机设计13 小时前
STM32智能门禁系统(语音版)
stm32·单片机·嵌入式硬件
txh050714 小时前
防过压电路
硬件工程
小帽子_12314 小时前
构网型PCS惯量模拟实现方式及其支撑电网频率稳定的原理
硬件工程