简介:这份GD32E10x系列官方例程面向嵌入式硬件与单片机开发者,基于ARM Cortex-M3内核,汇集兆易创新GD32E10x的示例代码,适合工业控制、消费电子、通信设备等场景的学习、验证与二次开发。压缩包共935个文件,约11.16MB,以288个h头文件、261个c源文件为主,并配套37个Keil uvproj工程、36套IAR ewp/ewd工程及说明文档、启动文件、Makefile和少量PDF资料,覆盖外设初始化、中断处理、驱动与应用程序等层次。已有880人学习,说明其在入门与项目参考方面具有一定关注度。内容围绕时钟配置、GPIO、串口、ADC、定时器、CAN、USB等模块展开,提供LED闪烁、通信采样等典型示例,便于读者对照工程结构理解GD32E10x的寄存器与外设库用法,缩短从熟悉芯片到搭建原型的时间,也可作为STM32同类开发迁移时的参考。
1. 从一颗会眨眼的小灯说起:GD32E10x 官方例程到底是什么
很多人第一次听到 GD32E10x 官方例程这个名字,脑子里冒出来的画面是一堆打不开的.c文件和一屏看不懂的寄存器名,还没开始就想关掉网页。说白了一点都不玄:GD32E10x 是一颗基于 Cortex-M4 内核的单片机芯片,而官方例程就是随芯片一起放出来的“现成作业本”——点灯、按键、串口收发、定时器计时、ADC 采样这些最基础的动作,别人已经写好、编译过、在真实板子上跑通过一遍,你下载下来照着跑就行。它解决的是零基础最痛的那个问题:手里有板子、会一点 C 语言,却完全不知道该先敲哪一行代码。这份东西适合刚接触嵌入式硬件、想真正让一颗芯片动起来的新手,不适合急着做产品的人。
2. 为什么新手该先读官方例程:比啃手册快得多的入手路径
刚拿到芯片资料的人,通常会走进同一个死胡同:先打开几百页的数据手册,从第一页的引脚定义读起,读到第三十页就开始犯困,合上电脑什么也没记住。这不是你不够聪明,而是顺序反了。手册天生是给“查”的,不是给“读”的;而例程天生是给“跑”的。先跑通一个能看见现象的例子,再带着问题回去查手册,效率完全是两个量级。这一章把“为什么先看例程”和“具体按什么顺序看”讲清楚,后面几章才好动手。
2.1 手册像字典,例程像课本
你可以把数据手册理解成一本字典:它把每个寄存器的每一位都解释得清清楚楚,但它不会告诉你“一个真实项目该从哪一步开始”。例程恰好补上了这一块——它是一份被拆成小步骤的完整流程。比如同样是配置一个引脚输出高电平,手册告诉你“某寄存器的第 0 到 1 位控制模式”,例程告诉你“先开时钟,再配模式,再写电平”,一共三行代码,顺序还给你排好了。字典查得到字,课本教得会句子,新手缺的是后者。
我第一次接触这类芯片时,犯过一个很典型的错误:抱着手册硬啃了三天,把时钟树的每一路分频都背了下来,结果连一个灯都点不亮。后来是同事把例程里那二十几行main.c甩给我,我照着抄了一遍、改了两个宏,灯就亮了。那种“真的动起来了”的反馈,比看三天手册管用得多。
提示:不要因为例程“看起来太简单”就跳过。简单的东西能跑通,复杂的才有资格谈。
2.2 例程里反复出现的四个动作:时钟、引脚、外设、中断
把官方例程包翻一遍你会发现,不管哪个外设,代码骨架几乎都是同一个套路,来来回回就四个动作。看懂这四个动作,你就看懂了八成例程:
- 开时钟:任何外设在使用前,都要先把它的时钟打开,不开时钟,你后面写的所有配置都会失效,这是新手最常遇到的“代码没错但不生效”的根源。
- 配引脚:芯片的引脚大多身兼数职,同一个引脚可以是普通输入输出,也可以是串口的发送脚、定时器的输出脚。你要明确告诉它“这一次我要你当什么”。
- 初始化外设:设置波特率、工作模式、分频系数这类参数,把外设从默认状态调成你要的状态。
- 使能并处理:最后打开外设,然后在主循环或者中断服务函数里处理数据。
这个套路不是死记硬背的教条,而是一条检查清单。以后你写任何一段新代码,跑不通的时候,按这四步从头捋一遍,绝大多数问题当场就露馅了。
2.3 我建议的阅读顺序:先跑通,再回头读
例程包里的文件夹通常是按外设名字排的,字母顺序看着很整齐,但绝对不是学习顺序。按字母读,你很可能第一个打开的是 ADC,第二个是 CAN,两个都看不懂,直接放弃。入门的话建议按这个顺序推进:
先 GPIO 点灯和按键,这是唯一能立刻看见现象的外设,负责给你信心;再串口,因为串口是你后面调试所有东西的“眼睛”,越早点亮越好;然后是定时器和中断,这两个决定了你能不能写出“不卡死”的程序;最后再碰 ADC、SPI、I2C、DMA 这些需要外部器件配合的外设。至于 USB、以太网这类复杂外设,等你能独立写完一个小项目再回头看,不然只会浪费时间。
这个顺序背后的逻辑很简单:先学能自己验证的,再学需要别人配合的。点灯你自己就能看见,串口你自己就能收到,而 SPI 通信需要另一颗芯片配合,出问题时你根本分不清是发送端错了还是接收端错了。
3. 第一个例程怎么跑:从打开工程到板子上的灯真的亮
前面讲的都是心态和路线,这一章开始动手。整个过程其实只有四件事:认清文件夹、装好工具、打开工程、编译下载。真正卡住新手的往往不是写代码,而是“工程打不开”“编译报一堆错”“下载成功但没反应”这三关。下面把每一关都拆开讲。
3.1 先认清文件夹:官方例程包一般长什么样
解压之后你会看到一堆名字相似的目录,第一次看确实容易懵。它们的典型分工是这样的:
| 目录/文件 | 作用 | 新手要不要动 |
|---|---|---|
| 内核与启动文件目录 | 启动汇编文件、内核相关头文件 | 不要动 |
| 外设固件库目录 | 每个外设一个.c文件,封装了寄存器操作 | 只读不改 |
| 例程目录(按外设分文件夹) | 每个外设一个可编译的小工程 | 主要看这里 |
| 工程模板目录 | 一个空壳工程,方便你新建项目 | 后面自己写代码时用 |
| 说明文档 | 每个例程配套的简短说明 | 跑之前扫一眼 |
新手最容易犯的错,是把“外设固件库目录”当成学习材料去逐个文件读。那些文件是给编译器看的,不是给你看的,几百个函数读下来只会崩溃。你要盯住的是例程目录里那个main.c,通常只有几十行,那才是精华。
3.2 动手前要准备的三样东西
第一样是开发环境。常见的做法是装一个集成开发环境(IDE),再单独安装这颗芯片的器件支持包,两者缺一不可。只装 IDE 不装支持包,打开工程会提示找不到器件,编译按钮直接变灰。这一步装完建议先重启一次软件,很多人卡在这里是因为缓存没刷新。
第二样是例程包本身。下载后解压到一个路径里不带中文和空格的目录,比如D:\work\gd32_demo,不要放在桌面或者“我的文档”下面。这是血泪经验:有些编译工具对中文路径处理得很糟糕,报的错还特别莫名其妙,你在代码里找半天,其实是路径的锅。
第三样是硬件。一块开发板、一根能传数据的 USB 线(有些线只能充电,插上去电脑毫无反应)、以及板载的调试下载器。如果你还准备了 USB 转串口模块,先放着,等第 4 章讲串口时再拿出来。
3.3 第一个能跑的例子:让板子上的小灯闪起来
找到 GPIO 相关的例程目录,打开工程文件,先别急着改代码,直接点编译。如果编译结果是 0 错误,说明环境和工程是匹配的,这一步就成功了一大半。然后点下载,看板子上有没有灯开始闪。
如果例程本身写得比较绕,你也可以在模板工程里自己写一份最小代码,逻辑更清楚:
#include "gd32e10x.h" /* 软件延时,只用于第一个点灯实验,正式项目请换成定时器 */ static void delay_ms(uint32_t ms) { for (uint32_t i = 0; i < ms; i++) { for (uint32_t j = 0; j < 8000; j++) { __NOP(); /* 空操作,防止循环被编译器整体优化掉 */ } } } int main(void) { /* 第一步:打开 GPIOC 的时钟,不开时钟后面所有配置都不生效 */ rcu_periph_clock_enable(RCU_GPIOC); /* 第二步:把 PC13 配成推挽输出,速度 50MHz 对点灯完全够用 */ gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); while (1) { gpio_bit_set(GPIOC, GPIO_PIN_13); /* 输出高电平 */ delay_ms(500); /* 亮 500 毫秒 */ gpio_bit_reset(GPIOC, GPIO_PIN_13); /* 输出低电平 */ delay_ms(500); /* 灭 500 毫秒 */ } }这段代码里几个参数值得说清楚。rcu_periph_clock_enable()的参数是你要开哪一路时钟,这里传RCU_GPIOC表示 C 端口,用哪个端口就换成哪个,写错了不会有任何报错,只是灯永远不亮。gpio_init()的四个参数依次是:端口、模式、速度、引脚号。模式里的GPIO_MODE_OUT_PP是推挽输出,能主动输出高电平和低电平,驱动 LED 就用它;速度参数在点灯场景下随便选,它影响的是引脚翻转的陡峭程度,只有在高频通信里才需要认真挑。引脚号可以按位或,比如GPIO_PIN_13 | GPIO_PIN_14就是同时配置两个脚。
注意:函数名以你手上那份固件库为准,不同版本的库函数命名可能略有差异。编译报“未定义标识符”时,先去库里搜一下同名函数,不要硬改。
还有一点很多人想不到:灯亮还是灯灭,取决于板子上 LED 的接法。如果 LED 一端接电源、一端接引脚(共阳接法),那么输出低电平才是亮;反过来共阴接法,输出高电平才亮。所以你写“高电平点亮,结果灯是灭的”,先别怀疑代码,有可能只是接法相反,把set和reset对调一下试试。
3.4 编译通过但灯不亮,按这个顺序查
第一,看下载是否真的成功。有些 IDE 会弹出“下载完成”,但实际目标芯片根本没连上,尤其是调试器驱动没装好的时候。重新点一次下载,看有没有报错信息。
第二,看程序是否停在启动阶段。可以在main函数第一行下个断点,如果连断点都停不住,说明程序压根没跑起来,问题在启动文件或者工程配置,不在你的代码。
第三,核对引脚号。开发板的原理图上,用户 LED 接的可能是 PC13,也可能是 PA5、PB0,每个板子都不一样。照着例程抄代码之前,先花两分钟看一眼原理图,能省掉半小时的自我怀疑。
第四,确认时钟真的开了。这是最高频的翻车点:配置写得漂漂亮亮,唯独忘了开时钟,寄存器写进去跟写进空气一样,毫无反应。
4. 从点亮到好用:时钟、GPIO 和串口这三件事
灯会闪了,说明工具链通了。但从“会点灯”到“能写点有用的东西”,中间还隔着三块必须迈过去的台阶:时钟、引脚模式、串口打印。这一章把这三块讲透,学完你就能自己写一个“按键控制灯、同时把状态打印到电脑上”的小程序,这在新手阶段已经算一个完整项目了。
4.1 时钟:先让芯片心跳起来,别急着写业务
芯片里所有外设都要吃时钟,就像所有机器都要通电。上电之后,芯片会先跑在一个内部的低速时钟上,这个时钟精度一般、频率也不高,够芯片“活着”,但喂不饱串口和定时器。所以在正式代码里,第一件事通常是把系统时钟切到更高的频率上——这个动作一般由启动阶段的系统初始化函数完成,你不用自己写,但要知道它存在。
为什么必须知道?因为所有跟“时间”相关的参数都建立在时钟频率之上。串口波特率是按时钟算出来的,定时器的计数周期也是按时钟算出来的。如果系统时钟配错了,串口输出的全是乱码,定时器的时间会莫名差好几倍,而代码看起来毫无问题。新手遇到“波特率明明设的 115200,收到一堆乱码”,第一反应应该是去查时钟配置,而不是反复改波特率。
具体怎么查?在系统初始化相关的那份源文件里,找到设置时钟的那段代码,看它选了哪个时钟源、倍频到多少。如果你不确定,就老实沿用官方例程里的配置,别自己改。等你能独立读懂了,再去动它。
4.2 GPIO:一个引脚能有几种身份
引脚是芯片和外界打交道的唯一通道,而它大多数时候不是“一种功能”,而是“一组候选功能”。同一个引脚,可以当普通输入读按键,可以当普通输出点灯,可以交给串口当发送脚,也可以交给定时器输出 PWM 波。你要做的,是在初始化时明确选一个身份。
| 模式 | 典型用途 | 新手常见误用 |
|---|---|---|
| 推挽输出 | 驱动 LED、驱动小电流器件 | 用来读按键,永远读到固定值 |
| 开漏输出 | 多个器件共用一条总线 | 不加外部上拉,什么都读不到 |
| 浮空输入 | 外部电路自己有上下拉时使用 | 直接接按键,读数乱跳 |
| 上拉/下拉输入 | 接按键、接开关 | 上拉下拉选反,按键逻辑颠倒 |
| 复用推挽 | 串口、SPI 这类外设占用引脚 | 忘了配复用,外设完全没输出 |
| 模拟输入 | ADC 采样 | 配成数字输入,采不到真实电压 |
这张表不用背,用的时候回来对一眼就行。真正要建立的意识是:每次用引脚之前,先问自己一句“这次它该当什么”。按键读不到、串口发不出、PWM 没波形,十有八九是这一步没设对。
按键还有一个绕不开的话题叫抖动。机械按键在按下和松开的瞬间,电平会剧烈跳动几毫秒,你的程序可能一瞬间读到十几个“按下”。解决办法要么是加一个小延时再读一次确认,要么用定时器每隔几毫秒扫描一次。新手阶段用简单延时确认就够了,别一上来就搞复杂的状态机。
4.3 串口打印:给自己装一双眼睛
如果说点灯是给板子装了个喇叭,那串口就是给板子装了个显示器。有了它,你能把变量的值、程序的执行流程、出错的位置全部打印到电脑上,调试效率提升的不是一点半点。做法是配置一个串口,然后重定向标准输出函数,让printf走串口发出去:
#include "gd32e10x.h" #include <stdio.h> /* 重定向 printf 的底层发送函数,注意工程选项里要勾选 MicroLIB */ int fputc(int ch, FILE *f) { usart_data_transmit(USART0, (uint8_t)ch); /* 等待发送缓冲区空,不等待会丢字符,输出变成一堆残缺的乱码 */ while (RESET == usart_flag_get(USART0, USART_FLAG_TBE)) { } return ch; } void log_init(void) { rcu_periph_clock_enable(RCU_GPIOA); /* 串口引脚所在端口的时钟 */ rcu_periph_clock_enable(RCU_USART0); /* 串口外设本身的时钟,两个都要开 */ /* PA9 作发送、PA10 作接收,这里按复用推挽配置 */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); usart_baudrate_set(USART0, 115200U); /* 必须和电脑端串口工具一致 */ usart_word_length_set(USART0, USART_WL_8BIT); /* 8 位数据位 */ usart_stop_bit_set(USART0, USART_STB_1BIT); /* 1 位停止位 */ usart_parity_config(USART0, USART_PM_NONE); /* 无校验 */ usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_enable(USART0); /* 最后使能,顺序别调 */ } int main(void) { log_init(); printf("hello, board is running\r\n"); while (1) { } }几个参数解释一下。波特率 115200 是电脑端和板子端必须完全一致的约定,两边差一点都会乱码。数据位 8、停止位 1、无校验,这三项组成的组合俗称“8N1”,是最通用的配置,新手直接用这套就行。fputc里那个等待缓冲区空的循环不能省,省掉之后高速打印时会丢字符,输出的内容断断续续,你会误以为是程序逻辑出了问题。
在电脑这一侧,你需要一个串口工具,选对端口号(设备管理器里能看到),波特率设成 115200,就能看到打印内容了。如果收到的是乱码,按顺序检查三件事:波特率是否一致、系统时钟是否配错、发送脚是否配成了复用模式。
4.4 中断:让芯片学会被打断
轮询的写法是“我一直在问你好了没有”,中断的写法是“你好了主动喊我”。按键、串口接收、定时器到点,这些场景都适合用中断。新手第一次接触中断,最容易在两件事上翻车:一是中断服务函数的名字必须和启动文件里的名字一字不差,写错了不会报错,中断就是进不去;二是中断里不能干重活,打印、延时、死循环都别放进去,否则整个系统像卡住一样。
配置顺序一般是:配好引脚和触发方式(比如下降沿触发),在中断控制器里打开对应的通道并设置优先级,最后在中断服务函数里做最少量的处理,比如只置一个标志位,真正的活留给主循环干。这个“中断只置标志、主循环做处理”的习惯,越早养成越好,后面写多任务时才不会乱。
5. 新手避坑:五个最容易翻车的瞬间
这一章专门写给“代码看着没问题,就是不工作”的人。下面五条是新手阶段出现频率最高的坑,每条都按“现象、原因、解决”写清楚,中招了直接对照查。
5.1 工程与环境相关的两个坑
坑一:打开工程就报一堆找不到文件的错。现象是编译输出里几十条“cannot open source input file”,红色一片。原因通常有两个:例程包解压路径带了中文或空格,或者工程里用的是相对路径,而你只复制了工程文件、没复制同级目录下的库文件。解决办法是把整个例程包完整解压到纯英文短路径下,再打开工程;如果还是报错,检查工程设置里的头文件包含路径,确认每一条都指向真实存在的目录。
坑二:编译零错误,下载也提示成功,但板子毫无反应。现象是灯不闪、串口没输出,怎么改代码都没用。原因多半是下载器没真正连上目标芯片,或者工程里选的芯片型号和板上实际型号不一致。解决方法是先点一次“连接/读取芯片 ID”,能读到才说明链路是通的;再核对工程配置里的器件型号,型号选错时编译能过,但下载进去的机器码跑不起来。
5.2 代码与硬件相关的三个坑
坑三:串口打印全是乱码。现象是电脑上收到一堆看不懂的符号或者方块。原因按概率排序:波特率两边不一致、系统时钟配置和预期不符、发送引脚没配成复用模式。解决方法是先把两边波特率都设成 115200 再试;仍然乱码就去核对时钟配置;最后检查引脚模式,普通输出模式是发不出串口波形的。
坑四:按键读到的值乱跳,按一次触发好几次。现象是打印出来的按键状态忽高忽低。原因是机械按键的抖动,加上引脚配成了浮空输入,电平没有确定参考。解决办法是把引脚改成上拉或下拉输入(取决于按键另一端接电源还是接地),再在检测到变化后加几毫秒延时重新确认一次。这一步做完,抖动能压掉九成。
坑五:中断进不去,或者进去一次就再也不出来了。现象是打了断点却从不命中,或者程序跑飞。原因通常是中断服务函数名和启动文件里的名字不匹配,或者中断里忘了清除中断标志位,导致芯片一直以为中断还没处理完。解决办法是去启动文件里复制那个函数名,一个字都别自己拼;再确认中断服务函数里做了标志位清除动作。
提示:这五条坑的共同点是“代码看起来完全正确”。所以排查时不要盯着代码反复读,要按“环境、配置、硬件、代码”的顺序一层层往下剥,先把不可能的排除掉。
6. 下一步学什么:从跟着例程跑到写出自己的第一个驱动
跑通点灯和串口之后,很多人会陷入第二个迷茫期:例程都会跑了,然后呢?我的建议是别再继续“顺着目录一个个跑”,而是挑一个小目标,把它完整做出来。目标可以很小,比如“按键按一下灯亮、再按一下灯灭,同时把按键次数打印到串口”。这个目标同时用到了引脚输入输出、中断或轮询、串口打印,是一个完整的闭环。做完它,你对这套东西的掌握程度会明显超过跑十个例程。
再往后,按这个路径推进比较稳:
| 阶段 | 学什么 | 判断自己过关的标准 |
|---|---|---|
| 第一阶 | GPIO 输入输出、软件延时 | 能不看例程独立写出点灯和按键 |
| 第二阶 | 串口收发、printf重定向 | 能自己打印变量并定位一个逻辑错误 |
| 第三阶 | 定时器、中断、中断优先级 | 能写出不卡死主循环的定时任务 |
| 第四阶 | ADC 采样、PWM 输出 | 能读一个电位器、调一个灯的亮度 |
| 第五阶 | SPI/I2C 接外部器件、DMA | 能读懂外部器件的时序图并对着写驱动 |
每一阶都有个共同要求:先自己写,写不出来再看例程。顺序反过来,你永远只能抄。我见过太多人例程跑得飞快,一到自己写就大脑空白,原因就是从没经历过“卡住、想不通、最后自己解决”这个过程,而这个过程中的挫败感恰恰是最有价值的部分。
还有个小技巧值得单独说:给自己准备一个“最小工程模板”。把时钟配置、串口打印、延时函数这些每个项目都要用的东西放在一个干净的空工程里,以后开新项目直接复制它,能省掉大量重复劳动和重复踩坑。这个模板不用一次做完,每次踩了新坑就补一条进去,半年后它会变成你最顺手的工具。
从那以后我每次开新项目,都强制先把最小工程跑一遍:灯闪不闪、串口打不打得出来、时钟配置对不对。这三件事任何一件不过关,我就绝不动业务代码——因为在一个地基没打好的工程上debug,浪费的时间足够你把地基重新打三遍。希望帮到你。
本文还有配套的精品资源,点击获取