ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GD32F103入门实践:环境配置、工程模板搭建与烧写全流程解析

GD32F103入门实践:环境配置、工程模板搭建与烧写全流程解析 GD32F103入门实践一配置环境搭建工程模板烧写程序先说说我为什么写这个系列。这几年国产MCU用得多GD32F103是绕不开的一颗料。它和STM32F103的引脚、寄存器级兼容价格又低一截很多产品从ST转过来的第一站就是它。但真上手你会发现GD32和ST不至于说是“完全换皮”坑还是有一些的尤其是用习惯ST的标准库之后再切过来环境配置、启动文件、烧写工具链这些环节都会遇到“看着差不多实际对不上”的情况。这篇我把从零到点灯的全过程捋一遍包含每个坑在哪、为什么会有这个坑、怎么绕过去。这个系列适合两类人一类是以前玩51、AVR现在想入门ARM Cortex-M3的新手另一类是常年用STM32、想转国产替代的工程师。不管哪种读完这篇你应该能自己把环境装好、工程建起来、程序烧进去并且理解每一步背后的逻辑而不是照猫画虎点几下鼠标。1. 为什么说第一步不是装Keil而是先搞清楚GD32的“兼容”到底到什么程度很多人拿到GD32F103第一反应是“STM32的工程直接拿来用”。这个思路方向对但细节上坑很多。我见过不少人在这一步卡住的明明工程编译过了烧进去就是不跑或者跑起来串口乱码折腾半天最后发现是用错启动文件。先把这个底摸清了后面所有步骤才有意义。1.1 硬件兼容性的真实边界GD32F103确实做到了和STM32F103的pin-to-pin兼容封装、引脚定义、片上外设分布基本一致。这意味着在硬件设计上大部分情况下你可以直接用ST的封装库画板不用改原理图。但“大部分情况”不等于“所有情况”有几个点必须提前知道首先是主频差异。GD32F103的标准主频是108MHz而STM32F103是72MHz。GD32的时钟树虽然在PLL配置上兼容ST的倍频系数但倍频上限更高。如果你直接用ST的SystemInit代码按72MHz配置GD32会工作在72MHz——这没问题性能打折而已。反过来就麻烦了如果你按108MHz配置但用的还是ST的库文件部分外设的时钟分频可能和实际不符合出现UART波特率偏移、定时器定时不准这类灵异问题。我第一次用GD32把USART波特率配成115200实测却成了115200的1.5倍偏差串口助手收到全是乱码查了半天才发现是时钟树配置用的还是ST的那套PLL倍频数对不上GD32的实际时钟结构。其次是Flash等待周期。GD32F103的Flash加速模块和ST不一样它的零等待访问频率上限比ST高一些。如果用ST的库Flash等待周期在72MHz以上时按ST的规则配在GD32上虽然不会出错但也没充分利用它的性能优势。第三是个别外设的寄存器差异。GD32的USART、SPI这些外设和ST高度兼容但部分外设存在细微差异。最典型的是GD32的I2C模块它和ST的I2C虽然都能用但状态寄存器的某些位定义有区别直接把ST的I2C驱动代码搬过来在GD32上可能出现“卡死在等待标志位”的情况。这也是我后来坚持用GD32官方固件库的原因——省心。1.2 软件兼容性的“看起来一样”陷阱软件层面GD32官方提供了两套东西一是标准外设库GD32F10x_Firmware_Library结构上模仿STM32的标准外设库文件名都类似gd32f10x_gpio.c对应stm32f10x_gpio.c函数命名风格也接近gpio_init对应GPIO_Init。但注意它不是ST库的直接拷贝寄存器和库函数参数有差异不能直接拿ST的工程文件替换头文件路径就完事。二是支持通过Keil的Pack安装安装后Keil会直接支持GD32F103系列的芯片选项包括自带的启动文件、设备头文件和Flash算法。这个方式最省事也是我推荐新手用的方式。但Pack方式和直接下载固件库搭建的工程在文件组织上有区别这点后面详细说。还有一个容易忽略的点CMSIS层。ST使用自己的system_stm32f10x.c来初始化时钟GD32也有自己的system_gd32f10x.c。这两个文件实现的功能类似但内部的时钟配置宏定义完全不同比如GD32的RCU模块对应ST的RCC模块虽然原理一样但寄存器偏移和位定义不同。如果你把ST的system_stm32f10x.c放到GD32工程里编译会报一堆“未定义寄存器”错误或者更糟——编译通过但运行异常因为两者的头文件映射关系对不上。这个阶段我的建议是放弃STM32的工程文件老老实实用GD32的官方库重新建工程。一次性的工作量不大但能省掉后面无数排查的功夫。1.3 新鲜热乎的工具链选型思路2024、2025年这个时间点GD32的开发工具链已经比较成熟了。除了Keil MDK还有几个选择可以横向对比工具链上手难度烧写支持适用人群备注Keil MDK低支持GD-Link、J-Link、DAP新手、绝大多数开发者官方文档最全推荐IAR EWARM中支持J-Link、DAP老工程师、特定项目License费用高GCC CMake较高支持OpenOCD开源爱好者、Linux环境灵活但环境配置繁琐RT-Thread Studio低支持GD-LinkRTOS入门者集成了RT-Thread系统如果你是完全的新手别犹豫直接Keil。不是说Keil最好而是GD32的官方文档、示例代码、网上教程绝大多数都是基于Keil的你遇到问题时能搜到的解决办法最多。如果你是Linux开发环境GCCOpenOCD也是好选择但学习曲线陡一些不适合放在入门第一篇。2. 环境配置实操装的不是软件是一整套依赖链环境配置这件事看起来是“下载安装一个Keil”实际上你装的是Keil主程序、GD32芯片支持包、烧写工具驱动GD-Link驱动、以及可能的固件库文档。这四样缺一不可缺任何一个后面都会卡住。2.1 Keil MDK安装与激活的细节去Keil官网下载MDK-ARM最新版。安装过程没什么特殊一路Next就行。需要注意两个点安装路径尽量不要带中文和空格。很多GD32的自动化脚本、编译中间文件对路径有隐含要求路径里有中文后面可能出现莫名其妙的编译错误排查起来极其痛苦。我建议装到D:\Keil_v5或者C:\Keil_v5这种简单路径。激活方式。正版用户输入License后还需要在Project → Manage → License Management里确认一下芯片支持是否完整。如果你是评估用途Keil有30天评估版功能受限但学习够用。安装完Keil之后先别急着建工程。Keil默认不认识GD32F103你还需要装GD32的芯片支持包Device Pack。打开Keil选择Pack Installer按钮工具栏上的绿色方块图标等它加载包列表在搜索框输入“GD32F10x”找到GigaDevice的包安装对应版本。如果你装的MDK版本比较高Pack Installer可能加载不出来这时候去GigaDevice官网或者Keil官网下载离线Pack文件.pack后缀在Pack Installer里用File → Import功能导入。装完这个Pack之后在新建工程选芯片时就能看到GigaDevice → GD32F10x系列 → GD32F103C8T6或者你的具体型号。同时Pack会自动帮你把启动文件、设备头文件、Flash烧写算法都准备好这就是后面“一键建工程”的基础。2.2 GD-Link驱动“装不上”才是常态GD32官方推荐的调试烧写器是GD-Link类似ST-Link之于STM32。GD-Link的驱动安装是个高频踩坑点。第一次把GD-Link插到电脑上Windows通常会提示“未知设备”或者“设备驱动安装失败”。这不是你的GD-Link坏了而是Windows自动更新找不到匹配的驱动。你需要去GigaDevice官网下载GD-Link驱动安装包手动安装。安装完成后在设备管理器里应该能看到一个GD-Link或者CMSIS-DAP设备而不是黄色的感叹号。这里有一个容易混淆的点GD-Link的调试器核心其实是CMSIS-DAP所以部分版本的驱动装完后显示的设备名是“CMSIS-DAP”不要觉得装错了。判断标准就一个——设备管理器里没有感叹号。还有一种情况某些山寨GD-Link用的是OpenOCD兼容的固件驱动方式不同。如果你买的GD-Link装不上官方驱动试试看是不是需要按住GD-Link上的复位键再插USB进入ISP升级模式重新烧录固件。这种问题很少见但遇到了也不用慌本质就是固件不匹配而已。驱动装好后在Keil的Options for Target → Debug里选CMSIS-DAP Debugger点Settings如果能识别到设备ID说明整个烧写链路已经通了。这个节点的标志性意义是你的电脑已经能和GD32芯片通信了。2.3 电源与接线的基本功烧写之前接线问题也是新手常踩的坑。GD-Link和开发板的连接方式一般是SWD接口四根线SWDIO、SWCLK、GND、3.3V。需要注意的是共地是必须的。烧写器不接GND偶尔能连上但极不稳定经常烧到一半报错。3.3V供电如果开发板已经通过USB自供电可以不接烧写器的VCC如果目标板没供电必须从烧写器取电。但注意GD-Link的输出电流有限如果你的板子有过流问题优先检查电源而不是烧写器。线长不要太夸张。SWD接口对线长和干扰敏感超过20cm的杜邦线在高速模式下面可能通信失败。遇到这个情况把Keil里Debug设置中的Max Clock调低比如降到1MHz以下问题基本能解决。3. 搭建工程模板这个环节决定了你后面三个月是否顺手很多新手喜欢直接打开官方示例工程改改就开始写代码。我的观点是第一篇不要这么做。官方示例工程里集成了一堆你不理解的东西宏定义、头文件夹路径、分散加载文件、启动文件选择……直接改很容易“代码能跑但不知道哪行在起作用”。花一个小时亲手把工程模板建一次你对整个编译链路的理解会上一个台阶。3.1 文件结构规划在开始之前先在本地建一个清晰的文件夹结构我习惯这样组织GD32_Project_Template/ ├── Doc/ // 存放文档、数据手册、笔记 ├── Libraries/ │ ├── CMSIS/ // 内核相关文件core_cm3.h、system_gd32f10x.c/h │ ├── Device/ // 设备头文件和启动文件 │ └── Peripherals/ // GD32标准外设库源码 ├── User/ // 用户代码main.c、中断处理、头文件 ├── Project/ // Keil工程文件.uvprojx所在目录 └── Output/ // 编译输出hex、axf、map文件这个结构不是随便定的它的好处是把“库文件”和“用户代码”分开以后写新功能的时候只改User目录不用动库文件编译产物单独放Output不会和源码混在一堆也方便Git管理。3.2 用Pack自动建工程和纯手工建工程的对比前面提到了装Pack。装好Pack后在Keil里新建工程选好芯片型号Keil会弹出一个“Manage Run-Time Environment”窗口里面列了很多组件选项。对于GD32F103你需要勾选的组件一般是Device → Startup启动文件汇编写的复位向量表、异常向量表Device → System系统时钟初始化代码CMSIS → COREARM CMSIS核心头文件勾选完成后Keil会自动把这几个文件复制到你的工程目录下具体路径在Project → Manage → Project Items → Project的“Manage Run-Time Environment”里可以配置。这个方式的好处是省事缺点是Keil自动复制的文件分散在多个目录新手看着容易懵。我自己的建议是第一篇先纯手工建流程如下新建一个空的Keil工程选好芯片型号这时候会提示是否添加启动文件选择否。从GD32F10x固件库包里手动复制需要的文件到Libraries目录Firmware/CMSIS/core/core_cm3.c、core_cm3.hFirmware/CMSIS/device/system_gd32f10x.c、system_gd32f10x.hFirmware/CMSIS/device/gd32f10x.h这个头文件定义了芯片全部寄存器映射Firmware/CMSIS/device/startup_gd32f10x_hd.s启动文件注意芯片型号后缀Firmware/GD32F10x_standard_peripheral/下的gd32f10x_*.c和gd32f10x_*.h外设库源码在Keil的Project Items里添加分组Group把文件按Libraries/CMSIS、Libraries/Peripherals、User三个组归类。看着麻烦但好处是每个文件在哪、干什么用的你都一清二楚后面排查编译错误时定位速度快很多。另外Keil工程文件.uvprojx保存路径记得选择项目文件夹下的Project目录。这里我遇到过一个坑如果把工程文件直接放在项目根目录而源码在Libraries/User子目录编译时Keil的相对路径配置可能会出问题尤其是换电脑或者换目录后头文件路径全部失效。3.3 启动文件选择这步错了板子直接“跑飞”启动文件是整个嵌入式工程的“入口”。芯片上电后CPU从Flash的0x00000000地址读取栈顶地址从0x00000004地址读取复位中断向量跳转到SystemInit和main。这段逻辑全部由启动文件在main之前完成。GD32F103根据Flash容量不同启动文件也不一样启动文件适用芯片Flash容量startup_gd32f10x_ld.sGD32F103C4/C616/32KBstartup_gd32f10x_md.sGD32F103C8等64/128KBstartup_gd32f10x_hd.sGD32F103CB/VC/VD等128/512KB如果你的芯片是GD32F103C8T6Flash是64KB应该用md版本。如果你选错了启动文件比如用hd版本代码可能也能编译但烧写后程序不执行因为启动文件中向量表的长度和实际Flash不匹配复位后PC指针跳转的位置就不对。这个错误主要看芯片大类的Flash容量和封装是LQFP48还是LQFP100无关。我用过一个项目芯片是GD32F103VET6512KB Flash开发板默认工程用的md启动文件烧进去后LED微亮不闪后来换成hd版本的启动文件就正常了。查这个问题的过程相当痛苦因为是逻辑错误调试器都连得上就是不跑。3.4 编译选项和宏定义一个都不能少手工建模板最容易被忽略的环节就是编译选项。打开Keil的Options for Target需要配置以下几个地方C/C选项卡里的Preprocessor Symbols需要定义宏。对于GD32F103系列一般需要添加两个USE_STDPERIPH_DRIVER表示启用标准外设库的驱动代码GD32F10X_MD或者根据你的芯片选择GD32F10X_HD等表示芯片型号的宏定义这个宏决定了gd32f10x.h里具体使能哪些外设定义Include Paths把你工程里所有包含头文件的目录加进去Libraries\CMSISLibraries\DeviceLibraries\Peripherals\IncludeUser漏掉任何一个编译都会报“找不到头文件”的错误。Keil的编译错误提示有时候不太友好比如“cannot open source input filecore_cm3.h”这种错误90%就是Include Paths没配全。Output选项卡勾选Create HEX File这样编译完才会生成hex文件否则只有axf部分烧写工具需要hex。Debug选项卡选择调试器为CMSIS-DAP Debugger点旁边的Settings在Flash Download页面勾选Reset and Run。这个选项的意思是烧写完成后复位并运行程序如果不勾选烧完程序后芯片停在复位状态看起来就像“程序没烧进去”。这是另一个高频踩坑点。4. 第一个程序从模板到LED闪烁的完整路径模板建好后写一个LED闪烁程序来验证整个链路。这部分我不只讲代码而是把从编写到编译到烧写的完整流程走一遍你会发现其中有不少容易绊倒人的小细节。4.1 main.c代码与时钟初始化讲解新建main.c放在User目录下。代码如下#include gd32f10x.h void delay_cycles(uint32_t count) { while(count--) { __NOP(); } } int main(void) { /* 使能GPIOA时钟 */ rcu_periph_clock_enable(RCU_GPIOA); /* 配置PA1为推挽输出最大速度50MHz */ gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); while(1) { gpio_bit_set(GPIOA, GPIO_PIN_1); // PA1输出高 delay_cycles(1000000); // 延时 gpio_bit_reset(GPIOA, GPIO_PIN_1); // PA1输出低 delay_cycles(1000000); } }这里解释几个关键点rcu_periph_clock_enable对应ST库里的RCC_APB2PeriphClockCmd。GD32系列把RCC换了个名字叫RCUReset and Clock Unit功能完全一样——不打开这个外设的时钟后面配置寄存器的写操作全部无效。gpio_init函数和ST的GPIO_Init在参数类型上很接近但参数内容和顺序有区别。ST的GPIO_Init用的是结构体GD32的gpio_init直接传参数。两者底层逻辑一样配置GPIO引脚的模式输入/输出/复用/模拟、速度2MHz/10MHz/50MHz和引脚编号。gpio_bit_set和gpio_bit_reset对应ST的GPIO_SetBits和GPIO_ResetBits一个是置高一个是清零。这些函数在gd32f10x_gpio.c里实现都是宏展开成寄存器操作效率很高。延时函数用的是简单的NOP空指令循环。这里要提醒这种软件延时不吃定时器也不受中断影响但精度不高而且编译器优化等级不同延时时间也会变。如果要精确延时比如串口时序后面必须换用定时器实现。入门阶段用软件延时完全没问题。4.2 接线与硬件验证我用的板子是GD32F103C8T6核心板板载LED接在PA1引脚。如果你用的板子LED接在其他引脚直接改main.c里的GPIOA、GPIO_PIN_1对应的参数就行。注意确认LED是低电平点亮还是高电平点亮——如果是低电平点亮你把代码里的set/reset对调一下即可。接好GD-Link上电编译点Load按钮烧写。如果一切正常效果是LED以约0.5秒的间隔闪烁。这里说“0.5秒”因为软件延时的具体时长和编译优化有关不是精确值但肉眼看上去就是“不快不慢地闪”。4.3 踩坑实录一次完整的“编译通过但烧录失败”排查过程我在帮朋友调一块GD32F103板子时遇到过一个很有代表性的问题代码编译零错误、零警告烧写器能识别芯片点Load按钮也没报错但程序就是不运行——LED完全不亮。排查链路是这样走的第一步检查电源。万用表量3.3V和GND正常。排除电源问题。第二步检查复位引脚。NRST引脚电压正常但注意这个引脚有个10nF的对地电容如果你手动复位必须通过复位按键拉低再释放不能直接把NRST用杜邦线并到GND——否则芯片一直处于复位状态。排除复位问题。第三步检查烧写选项。在Options for Target → Debug → Flash Download里发现没有勾选Reset and Run。勾上重新烧写还是不行。第四步检查启动文件。手动复制固件库时拿错了一个hd版本的启动文件。芯片是C8T664KB Flash应该用md版本。换掉启动文件重新编译烧写程序正常运行了。这个案例里启动文件错误是最隐蔽的——编译不报错、烧写不报错但程序就是跑不起来。所以在排查“编译烧写都正常但程序不运行”的问题时启动文件型号匹配、Reset and Run是否勾选是最优先检查的两个点。5. 烧写链路详解GD-Link、固件与烧写算法的高频问题烧写这块我单独拎出来写是因为它的坑实在多而且和STM32时代积累的习惯不一样很多人在这里吃了亏。5.1 Keil里烧写相关的配置项逐一说明打开Options for Target → Utilities选项卡里面有几个关键配置Flash Download里的Programming Algorithm显示的是当前工程的Flash烧写算法。对于GD32F103正常情况是一个名为GD32F10x_512KB Flash名字可能因Pack版本略有差异的算法。如果这里显示的是STM32F10x Flash说明你的工程选错芯片型号了——或者你在建工程时直接选了ST的芯片这种问题在一键替换工程时很常见。Erase Full Chip vs Erase Sectors前者擦除整个Flash速度慢后者只擦除需要写入的扇区速度快。日常开发用Erase Sectors即可。如果改了启动文件或者向量表相关的内容建议用Erase Full Chip擦一次全片避免扇区擦除残留的旧向量表干扰。Programming Algorithm的顺序如果同时存在多个算法比如你选了外部Flash需要把备用算法的顺序保证内部Flash算法在前否则烧写器可能优先尝试调用外部算法的FlashLoader导致烧写失败。5.2 HEX、AXF、BIN文件该烧哪个Keil编译完成后Output目录会生成三个文件.axf含有调试信息的完整镜像Keil的“LOAD”按钮烧的是这个文件的扩展版本.hexIntel HEX格式纯代码和数据最常用.bin纯二进制镜像一般不用Keil直接生成需要配置用GD-Link在Keil里烧写直接点Load就行它会自动处理。但如果你后面要用命令行烧写、或者用第三方工具比如FlyMcu烧写一般需要hex文件。因此在Output选项卡里勾选Create HEX File是很有必要的。还有一点要特别说明Keil的Load按钮烧写的是axf文件不依赖hex。有些人不用勾选hex也能烧成功但这只是因为你的环境不需要用hex。如果你以后要批量生产或者用其他工具烧录hex的坑还是会冒出来。5.3 GD-Link固件升级别忽略这个隐藏步骤你买回来的GD-Link内部固件版本可能比较老和Keil新版本的CMSIS-DAP协议不完全兼容表现是“能识别但连接不稳定”或者“擦除Flash成功但写Flash失败”。这种情况需要给GD-Link升级固件。升级方法去GigaDevice官网下载GD-Link Programmer工具把GD-Link插上注意升级过程中不能断开连接切换到固件升级模式选择最新固件文件点击升级。升级完成后重新插拔GD-Link再在Keil里测试连接。这个步骤很容易被忽略因为GD-Link的固件升级失败不会导致硬件损坏但它的驱动状态会异常——表现为设备管理器里设备正常但Keil里就是连不上。如果遇到这种“设备正常但连接失败”的情况先升固件再换线最后才怀疑硬件损坏。这个顺序不能乱。5.4 烧写失败的常见报错对照表报错信息常见原因解决方案“No target connected”接线错误、目标板未上电检查SWD四线连接确保目标板供电“Cannot access target”SWD速度太快、线太长降低Keil Debug里的Max Clock频率“Flash Download failed - Cortex-M3”烧写算法错误、芯片型号选择错误检查Programming Algorithm是否正确“RDDI-DAP Error”GD-Link固件问题、USB接触不良重新安装驱动、升级GD-Link固件“Programming timeout”Flash擦写时序被干扰检查目标板电源稳定性降低SWD速度6. 模板的积累与版本管理一次投入长期受益工程模板建好并验证通过后我强烈建议你做两件事把干净的模板备份成一个独立工程以及纳入Git管理。6.1 为什么需要一个“干净模板”你开发过程中会不断往工程里加功能USART、I2C、SPI、定时器……几个月后你的工程目录会变得一团乱麻。如果没有一个“最小可用模板”每次开新项目都要从零开始或者从旧项目里“剪切粘贴”一堆用不上的外设代码既低效又容易出错。我自己的做法是建一个Template分支或者独立仓库里面是完整的、能编译能烧写的LED闪烁工程文件结构尽量精简。每次开新项目直接复制这个模板目录重命名工程文件然后在上面加功能。这样每个项目都是干净起步不会带入历史遗留的代码垃圾。6.2 Git版本管理与乱删代码的后悔药嵌入式工程的Git管理有几个和纯软件项目不同的点.uvoptx文件是Keil的调试配置里面存了断点、窗口布局等用户相关信息建议加入.gitignore不同开发者共用会互相干扰。.uvprojx是工程配置需要提交到Git但注意这个文件里存储了绝对路径换机器后会失效。一个可行的替代方案是提交一份路径相对化的工程文件开新项目时手动调整一次。库文件Libraries目录建议作为submodule或者vendor目录管理不直接改动因为升级库版本时如果直接在你的项目里修改过合并会非常痛苦。这些经验看起来琐碎但都是代码写完几个月后你在Git log里看到“删了一行代码导致整个功能消失”时最想穿越回去告诉自己的话。7. 下一步这篇之后你该做什么环境、模板、烧写这三个基础打牢之后GD32F103的入门就算是迈过了第一道坎。后续的路线大概是GPIO → 中断 → 定时器 → 串口 → I2C/SPI → ADC/DAC → 然后根据你的项目需要选择具体外设。每一步都在这个模板上添加新功能而不是重新建一套工程。一个很值得做的验证例子是点亮LED的同时用定时器实现精确的1秒翻转替代软件延时——这会迫使你去读定时器寄存器的定义、理解时钟分频和预装载值的计算。做完这个你对GD32F103的掌握就从一个“只会抄代码”的程度进到“能自己算配置参数”的程度了。最后再分享一个小经验这批国产MCU的文档和示例更新频率没有ST那么快而且官网资料偶尔会改版链接失效是常事。所以拿到固件库的第一时间压缩包整个保存一份到本地别指望以后再下载——等你想找的时候可能已经找不到了。
返回列表