从点亮LED开始入门STM32,可能是最经典的一条路线。很多新手教程一上来就让你手写寄存器操作,看到一串像0x40021000这样的地址就觉得头大,还没点亮灯先把热情浇灭了。用STM32CubeMX配合HAL库,思路就完全不一样:图形界面里鼠标点点点,时钟和引脚配置自动生成代码,你真正要写的只有几行业务逻辑。这篇文章就是给你这棵“小白菜”写的,我会把从新建工程到LED亮起来中间所有容易卡住的细节都过一遍。全程不跳过原理,也不堆砌术语,每一步都告诉你“为什么这么做”,读完你不光能点亮人生第一颗LED,还能把这套流程平移到任何一个GPIO应用上。
1. 项目概述:为什么第一颗LED是嵌入式必修课
1.1 点亮LED的本质:控制一个IO引脚的电平
LED这玩意儿干了这么多年,本质就是一个二极管,给它正向电压就导通发光。在STM32上点亮它,说白了就是让某个GPIO引脚输出高电平或低电平,配合外部电路形成回路,让电流流过LED。
这里面牵扯出的第一个概念是GPIO,全称General Purpose Input Output,通用输入输出。你可以把它理解成一排带开关的接线柱:输出模式下,CPU能控制它对外输出高电平(一般是3.3V)或者低电平(0V);输入模式下,它能读取外部电平是高还是低。点亮LED用的是输出功能,这是嵌入式开发的“第一步输出”。
电路上有个细节必须提前交代:LED不能直接怼在引脚和地之间,要串一个限流电阻。红色LED正向压降大约1.8V,STM32引脚输出3.3V,如果不限流,电流可能冲到几十毫安甚至更多,LED会很快烧掉。串个220Ω到1kΩ的电阻,典型选330Ω,把电流限制在几毫安到十几毫安,亮度均匀还保护引脚。
另一个容易被坑的点是LED接法。常见的两种:
- 低电平点亮:LED阳极接3.3V,阴极经电阻接GPIO。引脚输出低电平时,电流从3.3V经LED和电阻流到引脚,灯亮。这种接法在很多开发板上都能看到。
- 高电平点亮:LED阳极经电阻接GPIO,阴极接地。引脚输出高电平时灯亮。
不先看原理图就接反极性,大概率是灯不亮。这是新手第一个必须养成的习惯:拿到一块板子,第一件事是找它的原理图和引脚标注,搞清楚板载LED到底连在哪个引脚、是哪种接法。
1.2 STM32CubeMX到底帮你干了什么
早年间写STM32工程的姿势是这样的:新建工程、添加启动文件、手动配置时钟树、计算PLL倍频系数、写外设初始化、还要记得开启外设时钟,漏一步调试半天。时钟配置尤其劝退新人——HSE是多少、PLL要几倍频、总线分频多少、APB1最大能跑多少,全要自己算,算错了系统时钟就乱套,症状五花八门。
STM32CubeMX就是芯片原厂出的图形化初始化代码生成工具,解决的就是这套繁琐的底层配置问题。你在图形界面里选中一颗芯片,在引脚图上点一下把某个脚设为输出,在时钟树页面填上你外部晶振的频率、选一个目标主频,然后点生成代码,CubeMX会把完整的初始化代码给你写好:时钟配置、GPIO初始化、外设初始化,全部生成在一个清晰的工程目录里。
底层用的库叫HAL库,也就是硬件抽象层(Hardware Abstraction Layer)。HAL库把寄存器操作封装成了函数,比如想给引脚输出高电平,不用再折腾寄存器位操作,直接调用HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET),代码的可读性比一堆位操作强太多了。
这套方案适合谁?适合新手快速建立整体认知,适合做原型项目时快速迭代,也适合在不同芯片型号之间迁移工程——换颗芯片重新生成一下就行。它的短板也很明确:对底层寄存器做极致优化、在资源极小的芯片上抠性能的场景,HAL库偏重,生成的初始化代码也比较“啰嗦”。但那是后话,入门阶段用HAL库可以让你把有限的精力放在理解逻辑上,而不是和寄存器配置死磕。
1.3 硬件与软件准备清单
动手之前先把东西备齐:
- 一块带板载LED的STM32开发板,常见的是基于F103系列中低端型号的板子,板载LED通常在PC13或者PB0、PB1、PB2这几个引脚上,具体看原理图。你完全可以用面包板加一颗STM32最小系统板自己搭电路,但第一次学,建议直接用带LED的板子,省去接线环节,降低变量。
- 一个调试器,最常见的是ST-Link,SWD接口四根线:3.3V、SWDIO、SWCLK、GND。有些板子自带ST-Link,用一根USB线就能下载调试,最省事。
- 如果自己搭电路:面包板、两颗LED、330Ω电阻若干、杜邦线。
- 软件三件套:STM32CubeMX(图形配置工具)、HAL固件包(CubeMX里可以自动下载)、一个IDE或工具链(CubeMX生成工程时要选择目标工具链,常见有MDK-ARM和STM32CubeIDE,前者是老牌商业IDE,后者是官方免费IDE,两个都行)。
这套清单覆盖了从软件配置到硬件下载的完整链路。把环境一次搭好,后面学定时器、串口、I2C都用得上。
2. 新建工程与芯片选型细节
2.1 安装与首次启动的几个坑
STM32CubeMX安装本身没太多可说,但要提醒两个坑。第一个,老版本CubeMX依赖Java运行环境,装完CubeMX才发现打不开,基本是JRE没装或版本不匹配。新版CubeMX已经把Java运行时打包进去了,建议直接下载最新版,少折腾。
第二个坑是首次使用时会提示下载固件包。CubeMX生成代码需要对应芯片系列的固件包,比如F1系列对应的固件包,体积不小。在菜单栏的固件包管理里能看到下载进度。国内网络环境下有时候下载慢,卡在那儿不动是常事。解决办法是手动下载固件包压缩包,然后在CubeMX的设置里指定本地路径,让它从本地解压安装。
首次启动后建议花两分钟把界面过一遍:左边是快速开始对话框,可以新建工程、打开已有工程;主界面分几个区域,最核心的是引脚视图区和配置区。后面操作都在这个界面里完成。
2.2 用MCU选型视图挑一颗芯片
新建工程时有两个入口:
- Access to Board Selector:从官方开发板列表选择,适合用官方评估板的人。
- Access to MCU Selector:从芯片型号选择,适合用自己手上的核心板、最小系统板的人。
我建议小白菜直接走第二入口,因为以后你换板子、换型号是常态,学会按MCU型号找工程起点才是通用能力。在MCU Selector的搜索框里输入你板子主控的型号,比如F103系列的常见型号如STM32F103C8T6,搜索结果里能看到这颗芯片的资源概况。
看懂型号命名能帮你少犯错。以STM32F103C8T6为例,拆开看:STM32是系列名,F指通用类型,103是性能线(主流型号,72MHz主频),C表示引脚数大概是48脚,8表示Flash容量是64KB,T表示LQFP封装,6表示工作温度范围。不同型号引脚数和资源不同,选型和你的实际板子对上就行。
如果搜索不到芯片,多半是固件包没装全,回到固件包管理里把对应系列的包装上。在MCU Selector里双击选中的芯片,就进入了工程配置界面。
2.3 时钟树:真正搞懂主频从哪里来
进入工程配置界面后,第一个值得花时间的地方是Clock Configuration标签页。新人在这一步最容易直接跳过,结果后面所有外设的时序都不对。
先解释几个名词。HSE是外部高速晶振,也就是板子上的那颗无源晶振,常见频率8MHz。HSI是内部RC振荡器,8MHz,精度不如外部晶振,但芯片上电后没配置外部时钟时会先用它。PLL是锁相环倍频器,可以把输入频率成倍放大。主时钟的路径一般是:外部8MHz晶振经过PLL倍频到目标主频,作为系统时钟,再经过AHB和APB分频器提供给各个外设总线。
以F103系列典型配置为例,你可以在软件里配置:
- HSE选择Crystal/Ceramic Resonator,即8MHz外部时钟。
- PLL Source选择HSE,倍频系数设为9,得到72MHz。
- 系统时钟源选择PLLCLK,主频就是72MHz。
- AHB Prescaler设为1,AHB即72MHz。
- APB1 Prescaler设为2,APB1取36MHz。这是因为APB1外设总线的最高允许频率是36MHz,超过这个值外设可能工作异常。APB2不用分频,保留72MHz。
这个“为什么APB1要分频”值得展开说。STM32内部有两套外设总线,APB2接的是高速外设(ADC、高级定时器等),APB1接的是低速外设(通用定时器、串口、I2C、SPI等)。APB1最高36MHz是硬件设计上限,硬往上拉高的结果是外设时序错乱,表现可能是串口波特率不准、定时器计数时间不对,而这类问题埋得深,很难排查。所以配置时钟树时,上面那个框图必须仔细看,设置完要确认各部分没有红色超限提示。
同样的逻辑,后面做任何用到时间基准的应用,包括本文的闪烁延时,都和时钟配置直接相关。时钟树配不对,后面全是疑难杂症。
3. GPIO配置的每一个细节
3.1 在引脚视图上找到你的LED
进入主配置界面后,中间是一张芯片引脚分布图。把鼠标移到某个引脚上,左键点击,会弹出一个功能选择菜单,里面是这颗芯片上该引脚所有可用的复用功能。我们要把连接LED的引脚配置成GPIO_Output,也就是普通输出模式。
这里有个关键动作:不要凭空选引脚,一定先看板子原理图。板载LED连的是哪个引脚、是高电平点亮还是低电平点亮,都在原理图里。绝大多数开发板会把板载LED印在引脚旁。假如原理图显示LED连接PC13,那你就在引脚图上点PC13对应的引脚,选GPIO_Output;如果连接的是PB0,就去配置PB0。选错引脚,代码逻辑再正确灯也不会亮。
配置完引脚,在配置区的GPIO设置页面能看到该引脚的状态:它是输出模式、初始电平是多少、最大速度是多少。这里可以顺便给引脚起个名字,也就是User Label,比如LED_0。设置User Label之后,CubeMX生成的宏定义会变成#define LED_0_Pin GPIO_PIN_13这样的形式,代码里直接写LED_0_Pin,可读性比盯着GPIO_PIN_13强太多了。工程大了以后几百个引脚,没有标签根本没法维护。
3.2 GPIO模式参数怎么选
在GPIO配置页里,点开那行GPIO_Output的配置项,会看到四个关键参数,分别是GPIO output level、GPIO mode、GPIO Pull-up/Pull-down、Maximum output speed。逐个说一下。
GPIO mode有两个选项:Output Push Pull(推挽输出)和Output Open Drain(开漏输出)。点亮LED用推挽输出就对了。推挽输出可以主动输出高电平或低电平,电流驱动能力强。开漏输出只能主动拉低,输出高电平要靠外部上拉电阻,常用于I2C这类需要线与逻辑的场合。初学者记住结论:驱动LED、数码管这类器件,推挽;做通信总线、需要电平匹配的,才用开漏。
GPIO Pull-up/Pull-down在输出模式下一般选No pull-up and no pull-down。上下拉的作用是给引脚一个确定默认电平,防止浮空,但推挽输出本身已经在驱动电平,再加上下拉是多余的。
Maximum output speed有三个档位:Low、Medium、High。输出速度不是越大越好,速度越高,边沿越陡峭,信号完整性问题也越多。LED点亮这种低频应用,选Low最稳妥,还能降低功耗和电磁干扰。高速信号(SPI、SDIO这类)才需要High。
初始输出电平这里的设置,取决于你的LED接法。低电平点亮的,初始电平设High,也就是说上电瞬间灯其实是灭的,执行到代码里让它拉低才亮;高电平点亮的则相反。设为合适的初始电平,可以避免程序一上电LED就乱闪。
3.3 生成代码前的工程设置
把引脚配置完,先别急着生成。切换到Project Manager标签页,这里面有三类设置要确认。
Project部分:填工程名、选择工程保存路径。注意路径里别带中文和空格,否则工具链容易出各种奇怪错误,这是老生常谈但还是有人踩。
Toolchain部分:选择生成哪个工具链的工程。选MDK-ARM就生成Keil项目文件,选STM32CubeIDE就生成对应的项目工程。另外还要选一下固件包版本,用默认推荐的就行。左下角有个Minimum Firmware Package Version,一般保持默认。
Code Generator部分:几个选项主要控制生成的代码风格。建议勾选Generate peripheral initialization as a pair of .c/.h files per peripheral,意思是每个外设的初始化独立成c文件和h文件,工程结构更清晰。还有一点,勾选不会影响入门,但建议别勾“仅拷贝所需库文件”之类的增强选项,默认即可。
设置完成后点右上角GENERATE CODE,会先提示你保存工程文件,保存之后开始生成代码。生成完成的标志是一个新的工程文件夹出现在你指定的目录里。
4. 解读生成的代码:从复位到main
4.1 上电之后程序怎么跑到while(1)
代码生成后,CubeMX会为你创建完整的工程。很多新手看到一长串文件就发怵,其实第一阶段你只需要盯住一个文件:Core/Src/main.c。这个文件里包含全部关键流程。
STM32上电后,先是复位向量从Flash里取出启动代码,做一堆底层准备,然后跳转到main函数。在main函数的开头,你会看到依次调用了几个初始化函数:
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { } }HAL_Init()负责初始化HAL库内部的东西,最重要的是配置SysTick。SysTick是Cortex-M内核里的一个简单定时器,HAL库的HAL_Delay()延时函数就靠它跑。SystemClock_Config()就是你在时钟树页面配置的结果,会被翻译成一段寄存器配置代码,最终把主频设成你选的72MHz。MX_GPIO_Init()是GPIO初始化函数。最后程序进入一个空的while(1)循环,你写的业务逻辑就放在这里。
有个值得一提的细节:如果把SystemClock_Config的生成代码展开,里面那些PLL配置寄存器位、等待标志位的操作,正是十年前让新人口干舌燥的内容。现在你不用逐行背它,但至少要知道它存在、它控制着什么。后面万一时钟出问题,至少能回看这段代码找线索。
4.2 MX_GPIO_Init逐行拆解
打开Core/Src/gpio.c,能看到MX_GPIO_Init()的实现,典型代码如下:
static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }第一行先声明一个GPIO初始化结构体并清零。这个结构体就是你在图形界面配置的那几个参数的家,Pin、Mode、Pull、Speed,对应你刚才在CubeMX页面上的每个下拉框。
第二行值得重点解释:__HAL_RCC_GPIOB_CLK_ENABLE()——打开GPIOB这个端口的外设时钟。这是STM32和单片机入门时最容易忽略的一步。STM32为了省电,几乎所有外设时钟默认都是关闭状态,你不打开它,即使寄存器配置完全正确,引脚也不干活。这在HAL库生成的代码里是自动处理的,但你要是哪天去移植裸机代码,忘了开这个时钟,灯怎么都不亮。
后面先写了一次初始电平,然后填充结构体,调用HAL_GPIO_Init()完成初始化。如果你想初始状态是低电平,把这行的GPIO_PIN_RESET改成GPIO_PIN_SET,方向相反。
4.3 while(1)里写点亮和闪烁代码
初始化都就绪后,在main.c的while(1)循环里就是你的主场。先写最简单的点亮:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin是HAL库最常用的GPIO输出函数,三个参数:端口、引脚、电平。这里GPIO_PIN_0是引脚标号,GPIO_PIN_RESET表示输出低电平。如果你的LED是低电平点亮,这一句下去灯就亮了。高电平点亮的板子,把它换成GPIO_PIN_SET。
想让LED闪烁,就要在点亮和熄灭之间加延时,否则人眼根本分辨不出变化。最简单的闪烁:
while (1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_Delay(500); }也可以用HAL库提供的翻转函数HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0),每调用一次引脚电平翻转一次,代码更简洁。注意HAL_Delay里的单位是毫秒,500就是半秒。它依赖SysTick,所以前面说HAL_Init那一套一个都不能少。
想玩点花活,还可以加个PWM呼吸灯效果的伏笔:GPIO的翻转速度如果通过定时器控制,就能做脉宽调制。不过那已经是后面系列的范畴,本篇先把GPIO这块地基打好。
5. 编译、下载与实测记录
5.1 打开工程与工程目录识别
CubeMX生成代码之后,你会得到一个完整的工程文件夹,目录结构大致是:
- Core/:核心代码目录,Src里是全部生成的源文件,Inc里是头文件。
- MDK-ARM/:如果你选的是MDK-ARM工具链,这里就是Keil工程文件所在目录,双击后缀为.uvprojx的项目文件打开。
- 如果用STM32CubeIDE,则在工程目录里直接打开对应项目文件。
打开工程后第一件事,确认编译器版本设置。MDK界面里Options for Target里有个Device页,要确保选中的芯片型号和你手上的实物一致;C/C++页里要检查宏定义,HAL库工程一般需要定义USE_HAL_DRIVER和STM32F103xB这类芯片宏,CubeMX生成时会自动带好,但如果你自己改了工程配置导致宏丢了,编译就会报一堆找不到头文件的错误。
5.2 编译选项的几个细节
编译前把优化等级调低。MDK的C/C++优化建议选-O0,也就是不优化。原因很直接:入门阶段大概率要仿真调试,优化开高了可能导致断点不触发、变量值被优化掉,调试体验很差。等代码稳定了再考虑优化。
ARM编译器版本选5还是选6,以前是个麻烦事。虽然酷炫的新工程大多默认用6,但编译器6对C99语法要求更严格,老代码可能编译不过。遇到编译报错先看看是不是编译器版本问题,切回ARM Compiler 5基本能救回来。
第一次编译如果顺利,会看到Program Size里分出Code、RO-data、RW-data等几个区域,这是程序大小和内存占用统计。F103这种级别的芯片Flash大几十KB,点亮LED这点代码只是九牛一毛。程序自己会放在Flash里,上电后由启动代码取出来执行。
5.3 烧录方式和接线确认
下载程序到板子,最简单的方式是用ST-Link的SWD接口,接四根线:3.3V(或5V,看板子标识)、SWDIO、SWCLK、GND。具体接线图在板子说明书和调试器说明里都有。如果板子自带ST-Link,直接USB线接电脑,驱动装好后在调试器的设置里就能识别到设备。
烧录前还有一个经常拦住新人的点:BOOT0跳线。STM32有两个启动模式引脚,BOOT0接0(低电平)时从主Flash启动,正常跑我们的程序;BOOT0接1(高电平)时会从系统存储器启动,进入芯片出厂自带的Bootloader,你下载的时候程序就“跑飞”了,表现是下载成功但灯不亮。很多新人Program成功但程序不跑,就是因为BOOT0没在正确位置。
点击下载按钮后,下载器界面会显示烧写进度,成功后会提示Verification OK之后程序开始运行。此时LED应该按照你代码里的逻辑点亮或者闪烁。
5.4 实测现象记录
我这边的实测场景:一块F103核心板,板载LED接在PB0,低电平点亮,CubeMX里生成工程后,在while(1)里写翻转和500毫秒延时,编译下载后LED以大约每秒一次的频率闪烁。
用逻辑分析仪探针夹在PB0引脚上,抓到的波形是方波,高电平持续约500毫秒、低电平持续约500毫秒,周期约1秒。说明GPIO的翻转没问题,HAL_Delay的计时也够准。如果用示波器测,还能看到从高到低跳变的边沿速度比较缓,这就是我们选了Low输出速度的结果,在LED应用里完全够用。
延时误差属于正常范围。HAL_Delay基于SysTick中断计数,受其他中断影响会有少量偏差,但毫秒量级延时并不敏感。如果你做精确定时的需求,比如电机控制,那就不能靠HAL_Delay,要改用定时器,这一步的认知先记在这里。
6. 常见问题与排查实录
6.1 灯不亮:先别怀疑代码,按顺序排查
代码写对了还不行,LED不亮是新手第一大坑。我的排查顺序是固定的:
第一,确认硬件接法。LED极性方向对不对,限流电阻有没有断开或者阻值过大,面包板接触不良的情况太常见了,杜邦线换个孔插。第二,确认引脚配对了没有。很多板子板载LED不止一个,比如三个指示灯分别接在不同引脚,你初始化了PB0但灯接在PC13上,那当然不亮。第三,确认是高电平点亮还是低电平点亮。这一点我反复强调,因为方向错了,代码执行再正确灯也不亮。
第四,确认BOOT0的状态和供电。BOOT0拉高时程序不会从Flash启动,下载成功也没反应。供电方面,有的核心板带板载LDO,用USB供电就行,但外接LED、传感器都要算电流,USD口提供不了大电流的时候,电压会被拉垮,整个板子重启也是可能的。
还有一类问题是代码逻辑本身卡住了。比如你在MX_GPIO_Init()之前就操作GPIO,或者循环里写了个无法退出的等待,导致后续代码执行不到。这时候用仿真调试器打断点比肉眼盯代码更高效。在IDE里设几个断点,看程序跑到哪里就不动了,定位非常快。
6.2 编译报错:常见问题速查
编译报错也有一套高频套路,整理成表格方便对照:
| 报错现象 | 原因 | 处理方式 |
|---|---|---|
| 找不到头文件,比如stm32f1xx_hal.h | 宏定义缺失或头文件引用路径不对 | 检查C/C++标签里是否定义了对应芯片系列宏,确认Include路径包含Core/Inc |
| 链接时提示找不启动文件 | 这个芯片系列的启动文件没加进工程 | CubeMX生成的工程默认已带启动文件,多半是手动改工程后弄丢了,恢复原始生成目录 |
| MDK编译器6报语法错误 | 编译器6的C99检查更严格 | 切换回ARM Compiler 5,或按报错提示改语法 |
| main.c里找不到HAL_GPIO_WritePin | gpio.c没有被包含进编译 | 检查gpio.c是否在工程文件树里,CubeMX自动生成时应该都有 |
| 报Undefined symbol SystemInit | 启动文件缺失 | 重新用CubeMX生成工程并完整覆盖 |
新手遇到报错先别慌,看第一个报错,通常是关键路径。另外提醒一下,别同时改多处代码再一口气编译,一次只改一个点,编译验证一次,出了问题定位容易得多。
6.3 下载失败:从接口到驱动的逐个检查
下载失败是另一个高频卡点。症状可能是“No target connected”或者“无法连接SWD”。
先说接线。SWD的四根线看着简单,其实很容易接错。举例:有些调试器的3.3V脚输出电流有限,如果板和调试器各自供电还共地,又会引入电平不一致的问题。SWDIO和SWCLK两根线互相接反也是常见的低级错误。正确的排查顺序是先检查接线,用万用表量通断。
再说引脚占用。SWD使用的SWDIO、SWCLK两个引脚,如果你初始化时把它们配成了普通GPIO甚至复用功能,调试器就连不上芯片了。这种问题CubeMX配置时不会默认出现,但你自己扩展功能时容易踩到。解决办法是如果还想用SWD调试,就不能乱占用这两个引脚。
供电和驱动的问题也常见。板子供电不足时SWD协议通信不稳定;调试器驱动没装好,电脑设备管理器里会出现带感叹号的未知设备。把驱动重新装一遍,或者换一根质量好一点的USB线,都可能解决。
6.4 给你留的避坑技巧和几个进阶方向
最后分享两条经验,是我当年踩坑踩出来的。
一是修改引脚后务必回CubeMX重新生成。很多人直接在生成的代码里改引脚编号、改GPIO模式,改完编译运行发现行为不对。正确流程是:在CubeMX里改好配置再重新生成代码,让工具自己把初始化同步好。直接手改生成的代码,下次重新生成时会被覆盖掉,这也是为什么业务逻辑要写在用户代码区。CubeMX会用注释标记User Code Section,你写的代码放在/* USER CODE BEGIN */和/* USER CODE END */之间,重新生成时才能被保留下来,写在区域外面就会被清掉——这个坑新手必踩,务必记住。
二是每学一个新功能,都要回到CubeMX里把对应配置打开再研究生成的代码。比如看了这篇文章,你完全可以再做一个小实验:把LED引脚从PB0换到另一个空闲引脚,重新生成固件,观察移植过程是不是完全不需要手改代码。能把“配置—生成—验证”的闭环跑通,比一口气背十篇文章都管用。
这个项目做完,你可以沿着三条路继续往前走:第一条,按键输入,采集按键状态控制LED亮灭,把GPIO的输出学完再学输入;第二条,定时器PWM,让LED实现呼吸灯效果,从数字控制进入模拟控制;第三条,串口打印,用USART把调试信息输出到电脑,从此有了“眼睛”。这三件事分别对应GPIO输入、定时器和通信接口,是继续学下去的三大支柱。我在实际带人过程中的体会是:第一颗LED点亮的这半天,是整个嵌入式学习里最有成就感的一段时间——它是你第一次真正让代码变成了物理世界的动作。之后的路还很长,但这条链路你跑通了,后面所有外设学习都是一个套路:CubeMX配置、生成代码、理解初始化、写业务逻辑、编译下载。把今晚这第一步踩实,后面就不难了。