【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写代码前,让它用语言描述一遍需求。
这种经验,在其他实践中也是通用的,比如在企业运作过程中,需要安排或者接受安排的时候,被安排者有必要以他自己的语言描述一遍任务,以便消除认识上的不同产生的错误
相关推荐
项目題供诗4 小时前
51单片机入门(二)
单片机·嵌入式硬件·51单片机
恶魔泡泡糖4 小时前
51单片机步进电机
单片机·嵌入式硬件·51单片机
tobias.b5 小时前
408真题解析-2010-12-计组-程序执行时间
单片机·嵌入式硬件·fpga开发·计算机考研·408真题解析
List<String> error_P5 小时前
独立看门狗IWDG原理详解
stm32·单片机·嵌入式硬件·定时器
shansz20205 小时前
CDC虚拟串口与硬件串口传输速度的对比测试
单片机·嵌入式硬件
一个云朵6 小时前
【江协科技】KEIL如何下载离线支持包
单片机·嵌入式硬件
云山工作室6 小时前
基于单片机的居家智能音箱系统(论文+源码)
单片机·嵌入式硬件·毕业设计·毕设·智能音箱
单片机系统设计7 小时前
基于STM32的智能防摔报警系统
stm32·单片机·嵌入式硬件·毕业设计·防摔报警·短信报警·号码设置
人间不清醒ab8 小时前
FREERTOS检测任务栈内存情况
c语言·单片机