如果你是从零开始学STM32大概率会经历这么一个阶段照着网上教程一步步装软件桌面上莫名其妙多了四个图标却说不清每个软件到底管什么。有人让你装STM32CubeMX有人催你装Keil MDK还让你装ST-Link驱动、ST-Link Utility最后再来一个串口助手。装完之后你盯着屏幕心里只有一个念头这四个东西是不是都要用能不能删掉几个我太熟悉这种状态了。带过不少新人几乎每个人第一周都会问我同样的问题。这篇系列文章已经到第4篇前面几篇我们把STM32是什么、嵌入式C和普通上位机开发的差异聊了个大概这篇文章干脆把工具链这层窗户纸捅破那四个让你懵圈的软件分别是在给嵌入式开发的哪一段“打工”。它们不是四个互相竞争的工具而是一条流水线上的四个工位少一个你都很难把程序真正跑在芯片上。1. 先看整体那四个软件不是四座山是流水线上的四个工位1.1 嵌入式开发本质上是一条“把代码送进芯片”的流水线理解这四个软件最重要的不是背功能清单而是先建立一个整体画面你写的代码最终要变成芯片能执行的东西中间要经过好几个独立环节。你可以把STM32芯片想象成一家“只听得懂机器指令”的工厂。你的C源码、HAL库调用、引脚配置本质上都是写给人类看的“需求说明书”。工厂不认说明书它只认最底层的那种机器码文件。所以开发过程必须有这么几步先把芯片用成什么样规划出来再把逻辑写成代码然后编译成机器码接着把机器码送进芯片的Flash里最后让芯片跑起来并告诉你跑得对不对。这四个环节刚好对应你装的那几个软件。它们不是重复的也不是可选项而是分别把守着这条流水线的一环。很多新手之所以困惑是因为教程只告诉你“点下一步点完成”从来不告诉你“这一步到底在干什么”。于是你装完了四个软件却始终觉得自己只是在陪跑。1.2 四件套与开发环节的对应关系我用一个老土但特别准确的类比来帮你记住开一间餐厅你需要四个岗位——设计菜单的人、掌勺的厨师、端菜的服务员、站在门口听顾客反馈的店长。这四个角色各干各的但缺一个餐厅就没法正常转。放到STM32开发里最常见的“四件套”是这么分工的软件岗位类比实际职责输出物STM32CubeMX设计菜单的人图形化配置芯片引脚、时钟、外设生成初始化代码一个初始化完成的MDK工程骨架Keil MDK掌勺的厨师写代码、编译C/C源码、调试.hex或.axf文件机器码ST-Link Utility / STM32CubeProgrammer端菜的服务员通过ST-Link调试器把编译好的固件下载到芯片Flash里芯片里能稳定运行的固件串口调试助手站在门口听反馈的店长接收芯片通过串口打印出来的运行信息芯片的运行日志、变量值、错误码注意我的用词这四件套是“最常见的组合”不是唯一组合。有些软件可以合并比如STM32CubeIDE就把前面三个活全包了Keil本身也能烧录所以第三件有时候你不用管。但你至少要把“配置—写码—编译—烧录—观察”这两三件事想明白后面不管用什么工具都换汤不换药。2. 逐个“验明正身”每个装完最好别急着删的软件到底是干嘛的2.1 一号选手STM32CubeMX它是“芯片外包配置员”如果你让一个老工程师纯手写寄存器配置他也能把STM32跑起来但效率极低。STM32芯片的外设多到让人头皮发麻定时器有好几个串口有三五个GPIO引脚动不动一百多个还有ADC、DAC、PWM、DMA、I2C、SPI……你记得住几个但你绝对不想每次都用寄存器手动初始化一遍。STM32CubeMX解决的就是这件事。它是一个图形化配置工具你只需要在界面里选好芯片型号然后像画连线图一样把某个引脚设置成UART的TX把某个引脚设置成LED输出再把时钟树拉到想要的频率它会自动帮你算好分频、倍频系数最后一键生成一份完整、规范的初始化C代码。我打个比方它就像装修时的“水电设计师”。你不需要自己去墙上开槽、布管线你只需要告诉设计师这里要放冰箱、那里要装洗手台、阳台要留洗衣机。设计师就能帮你画出完整的水电走向图施工队照着做就行。没有它你开发STM32会一直在配置各种寄存器上耗费大量时间。用CubeMX还有一个巨大好处它生成的是HAL库代码。HAL库把芯片操作封装成了比较统一的接口比如HAL_GPIO_WritePin、HAL_UART_Transmit。你写业务逻辑时只需要调用这些API不用跟寄存器位定义死磕。对于初学嵌入式C的人来说这样可以把精力集中在算法和逻辑抽象上而不是陷在底层寄存器的海洋里。但有一个坑必须提前说CubeMX生成代码时会在main.c里画好一对对的USER CODE BEGIN/END注释区间。你自己的业务代码必须写在这个区间内部。如果写在区间外面下次你在CubeMX里改配置再重新生成代码你的代码会被覆盖得渣都不剩。我见过太多人辛辛苦苦写了几百行一重新生成全被冲掉了然后跑到群里问为什么代码不见了。2.2 二号选手Keil MDK真正的“代码工坊”第二个软件Keil MDK准确地说是一个集成开发环境IDE。在它里面你要写代码、要编译、要调试、要设置下载参数。它装的时候还会自带你写代码用到的编译器比如ARMCC或者AC6。说实话Keil的界面谈不上漂亮但它在中国嵌入式圈子里普及率最高几乎所有STM32例程和教程都能直接在Keil里打开这也是大家都让你装它的原因。Keil在流水线里的作用是“翻译”。它把你写的C/C代码翻译成ARM芯片能理解的机器指令。这里多说一句很多新手以为自己写的代码就是芯片看到的代码其实不是。你写的是给人看的高级语言芯片不认。你之所以需要编译这一步就是让编译器做这个翻译工作最后生成一个.hex后缀的文件或者Keil内部的.axf文件。用Keil打开CubeMX生成的工程之后你会看到左侧Project窗口里有一堆文件夹和文件比如Application/User、Core、Drivers。刚开始你可能会觉得乱其实这些文件各司其职Drivers里面放的是HAL库Core里放着系统核心文件你真正要改的地方往往只有main.c以及后续自己新建的模块文件。Keil里有一个经常被新手忽略的东西——“魔术棒”也就是那个长得像魔法棒的图标点进去是Target Options。这里面藏着很多关键设置芯片型号、Flash下载算法、调试器选择、C/C选项、宏定义。如果芯片型号选错或者Flash算法不对百分百下载失败。很多你从网上搜到的“Keil报错”最后都能在这个设置界面里找到根源。顺带提一点这个软件不是用来“看效果”的它是用来“生产成果物”的。你在里面把代码编译好、下载到板子上才算完成流水线的第二和第三段。我的建议是第一次用就不要想太多花哨功能先把“打开工程—修改main.c—编译—下载”这四个动作练熟其他的插值断点、单步调试等跑通一次再说。2.3 三号选手ST-Link Utility / STM32CubeProgrammer负责“把固件怼进芯片”很多新手看到Keil已经能下载了就会觉得ST-Link Utility是个多余的软件。这个想法不算错但你们会慢慢发现它不只是“下载工具”这么简单。ST-Link Utility和它新一代的替代者STM32CubeProgrammer本质上是“烧录器”。它们通过你手上的ST-Link调试器跟芯片建立起连接然后把Keil编译出来的.hex/.bin文件刷写进STM32的Flash里。相当于餐厅里那个端菜的服务员厨房里做好了菜还得有人把菜从厨房端到顾客桌上。Keil虽然也能做这个动作但它的烧录功能是“附带功能”而专用烧录工具有很多你自己手写下载时没有的额外本事。举个例子芯片一旦开启了读保护或者你之前的代码把Systick、JTAG占用了Keil可能就连接不上芯片了。这时候专用烧录工具能通过更底层的方式全片擦除、解除读保护把芯片“救活”。再比如你做了一个产品总不能抱着一台装了Keil的电脑去现场烧程序吧专用烧录工具往往支持独立的图形界面和命令行拿到现场就能直观地批量下载。再比如你想查看Flash里的原始内容、想对比固件版本、想单独擦除某块区域ST-Link Utility用起来比Keil顺手得多。在接线方面ST-Link和STM32板子之间常见的是SWD四线SWDIO、SWCLK、GND、3.3V有些板子不用3.3V但大多数小板要。这个环节很容易出低级错误SWDIO和SWCLK接反了或者没共地电脑就会一直提示找不到设备。插反了不要慌把线重新排一下问题大多能解决。我建议新手上路时这样用你日常写代码就只在Keil里点下载等某一天Keil连不上芯片了或者有同学跟你说“芯片锁死了”再打开烧录工具执行擦除、解除读保护、重新下载。你会感谢这个“多余”的软件。2.4 四号选手串口调试助手芯片的“传声筒”第四个软件串口调试助手这一类的工具很多XCOM、SSCOM、MobaXterm、PuTTY都可以用。它干的活简单说就是取代电脑上的超级终端接收STM32从串口发出来的文字信息。STM32不像你的电脑有屏幕它怎么告诉你当前在干什么呢最常用、最简单的方法就是通过串口把数据发出来。你在代码里写一行HAL_UART_Transmit或者重定向printf函数芯片就会把字符一个接一个地从串口TX引脚送出来。电脑想要看见这些字符要么用一个USB转TTL的工具要么依赖ST-Link的虚拟串口功能把串口信号转成USB最终在你电脑上映射出一个COM口。串口调试助手的作用就是打开这个COM口把收到的数据翻译成人能看懂的字符滚动显示在屏幕上。这个软件的坑比前几个都多也是新手最容易崩溃的地方。首先接线要对芯片的TX要接USB转TTL模块的RX芯片的RX要接模块的TX名字里的Tx接Rx、Rx接Tx很多新手的第一次下面没有反应就是因为拿TX跟TX接了一块。其次GND必须和电脑这边共地不共地通信大概率是乱码或者完全没反应。再者波特率要一致代码里用115200串口助手左下角也得选115200而且常用的数据位、停止位、校验位基本固定是8-N-1。最后还容易忽略一件事先接好硬件再打开串口助手或者关闭串口助手后重新打开否则电脑可能认不出你刚插入的串口设备。串口助手在调试里的地位怎么强调都不过分。你后面做的一切传感器读取、状态上报、异常日志、PID参数调参几乎都要靠它来“听”芯片说话。毫不夸张地说如果你只学了四个软件第四个可能是陪伴你时间最长的。不过它也最容易被新手忽视因为大家都以为它是“可选附件”其实它才是名副其实的调试主力。3. 完整跑一次“点灯串口打印”让四个软件在一条战壕里干一次活3.1 第一步在CubeMX里把芯片“调教”成你想要的样子理论说再多不如亲手走一遍。下面我以一块很常见的STM32F103C8T6开发板就是大家俗称的蓝Pill为例把四个软件串起来跑一遍。整个流程你照着做一遍之前那种“装了四个软都不知道干嘛”的感觉会瞬间消失。打开CubeMX新建Project在芯片搜索框里输入STM32F103C8T6双击选中。进入配置界面之后先做三件事第一件在System Core里找到SYS把Debug设置为Serial Wire。这一步极其重要很多人没有做程序下载到一半第二次就再也连不上板子了。原因在于如果你不启用Serial Wire模式SWDIO和SWCLK这两个引脚会被释放出来当普通IO用调试器就没有通道访问芯片了。这个坑不知道让多少新手重新换了一块板子我先给你排掉。第二件配置RCC的HSE选Crystal/Ceramic Resonator。然后到Clock Configuration界面把系统时钟拉高到72MHz。CubeMX会自动帮你安排PLL分频倍频路径不需要你手算。第三件配置一个GPIO口来控制板载LED。找到PC13引脚把它设为GPIO_Output这就是很多开发板上那颗LED所在的位置。再配置一个串口用于调试在Connectivity里找到USART1Mode选Asynchronous波特率设115200此时CubeMX会自动把PA9和PA10分配为TX和RX这两个复用功能。最后到Project Manager界面Project Name起个名字比如demo_led_uartToolchain/IDE选择MDK-ARM勾选Generate Under Root然后点右上角GENERATE CODE。等它把工程生成出来你会得到一个文件夹里面就是Keil能直接打开的工程。注意这整个过程你还没写一行业务代码但芯片的“骨架”已经搭好了。3.2 第二步Keil里写代码顺手放一个C类进去打开刚才生成文件夹里的demo_led_uart.uvprojxKeil会自动加载整个工程。你先不要急着改点一下编译按钮确认工程能零错误零警告地编译通过。这一步是通过说明CubeMX生成的初始化代码和你的编译器是兼容的。然后开始写业务逻辑。我建议你别一上来就写复杂功能先实现两件事让LED以1秒的间隔翻转同时通过串口打印一行字。找到main.c里的main函数在USER CODE BEGIN WHILE这个区间里加你循环业务while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); printf(hello from stm32\r\n); HAL_Delay(1000); /* USER CODE END WHILE */ }printf在嵌入式环境里不是默认就能用的你还需要把标准库的字符输出重定向到串口。最简单的方法是重写fputc函数然后在Keil的Target Options里勾选Use MicroLIB。重定向的代码写在哪都行但务必放在USER CODE BEGIN PFP和USER CODE END PFP两个注释之间这样CubeMX重新生成代码时不会把它抹掉int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }到这里为止整个工程基本是C代码逻辑。但既然我们的系列名字挂着C我可以给你一个很轻量的切入点在Keil里新建一个Led.cpp文件写一个最简单的类比如构造函数负责初始化引脚或者提供一个Toggle方法。然后在main.c里用extern声明一个函数在.cpp里用extern C把这个函数暴露给C文件调用。这一篇我们只做“会跑”具体C封装技巧、虚函数开销、静态对象初始化顺序等内容我后面会单独写文章深入展开。你先知道Keil能编译C而且和CubeMX生成的C代码可以混合编译就够了。3.3 第三步编译、下载再用串口助手看输出代码写好后点Keil的魔术棒在Debug选项卡里把Use调试器设置为ST-Link Debugger再点旁边的Settings确认能识别到一个SW Device。如果这边一直空白说明ST-Link驱动没装好或者接线有问题先回上一节把硬件搞定。然后在Utilities选项卡里把下载算法选对一般CubeMX生成的工程会自动带好。点击Download按钮看到Flash Download Finished的提示恭喜你代码已经下到板子里了。此时LED应该在闪烁但你想看的串口输出还看不见。打开设备管理器看在端口COM和LPT下面有没有新的COM号。很多用ST-Link的板子还会多出一个虚拟串口COM号记录下来。打开串口调试助手选择对应的COM口波特率设为115200数据位8、停止位1、无校验点击打开串口。如果一切顺利你会每隔一秒看到一行“hello from stm32”。到这里你已经完整地走了一遍四个软件各司其职的流程CubeMX配置、Keil编译下载、烧录工具备用、串口助手查看输出。此时你再回头想“这四个软件到底干嘛的”心里应该非常清楚了。如果串口里显示莫名奇妙的乱码别急着怀疑代码先看波特率是不是一致如果完全没数据先检查TX接RX、RX接TX的交叉规则再接GND共地然后用万用表或者示波器确认引脚上是否有数据波形。这些几乎覆盖了九成的串口问题。4. 那些“能不能只装一个软件”的问号为什么有人一个IDE就搞定一切4.1 “为什么有人只用STM32CubeIDE现在我也能懂”装完四个软件之后新手很容易发现一个现象视频里的老工程师有时候桌面就开一个STM32CubeIDE写代码、编译、下载、看串口全在这一里完成瞬间觉得自己装四个软件是被坑了。其实不是。STM32CubeIDE是ST官方出的集成开发环境它把CubeMX、编译器、调试器、烧录工具全部打包在一起相当于把流水线的几个工位合并到一个大车间里。你能在一个软件里完成刚才说的全部事情。那为什么很多教程仍然坚持让你分开装因为在很多课程、教材、比赛例程里大家已经习惯用Keil MDK工程直接发过来就能打开而且企业里有大量历史代码是基于Keil维护的换了IDE就可能动工程结构。同时STM32CubeIDE基于Eclipse有些人觉得它启动慢、占用内存高老电脑跑起来卡所以老工程师也不会轻易推别人换。我说这些是想让你明白软件之间不是“谁更高级”而是“谁更适合当前场景”。单独装四个软件的好处是每个环节边界清晰哪一步出了问题你能准确猜到是哪层出事了。等你把整个流程走熟了再去尝试一体化工具那是锦上添花的事。4.2 编译器、调试器、烧录器、固件包这些词谁是谁还有一个容易让大家晕头转向的点就是嵌入式开发里有一堆类似名词IDE、编译器、调试器、烧录器、HAL库固件包。这四个软件的学习过程中这些词频繁出现但没几个人愿意停下来给你解释清楚。我在这里直接把它们拆开名词常见实现具体干什么你什么时候要接触它IDEKeil MDK、STM32CubeIDE、VS Code提供一个地方让你编辑代码、组织工程、触发编译和下载每次写代码都离不开编译器ARMCC、AC6、arm-none-eabi-gcc把C/C源码翻译成机器指令点Build/编译时它在后台干活链接器跟随编译器一起把所有编译好的目标文件和HAL库合并成可执行固件编译的最后阶段调试器ST-Link、J-Link、CMSIS-DAP通过SWD/JTAG口访问芯片内部用于下载和在线调试接线、驱动、下载烧录器/编程器ST-Link Utility、STM32CubeProgrammer专门把固件文件写入Flash或擦除、读取芯片下载固件、救砖、批量生产固件包STM32CubeF1、HAL库、标准外设库芯片厂商提供的底层驱动代码CubeMX生成工程时需要调用这个表你不需要背但建议保存下来碰到分不清的名词就回来看一眼。你会发现这四个软件基本对应了表中后几行CubeMX负责生成调用库的工程Keil负责IDE编译器链接器ST-Link Utility属于烧录器串口助手则是你的调试观测端。4.3 引人注意的一点C代码在哪个环节哪个环节要注意什么既然你是奔着“嵌入式C编程之旅”来的最后有必要提一个非常现实的问题C在这个四件套流水线里究竟在哪一个环节发挥作用答案是在Keil这个环节。CubeMX生成的是C代码而且是你日后经常用作“基础设施”的初始化代码你在Keil里新建的.cpp文件、你写的类、你定义的C命名空间都发生在Keil的编辑和编译阶段。编译器必须支持C语法Keil MDK的AC6编译器对C11的支持还算可以所以写起来没有太大的阻碍。但要注意CubeMX生成的main.c是C文件你要在C文件里调用C模块时必须在.cpp文件里用extern C导出接口否则会在链接阶段报一堆莫名奇妙的符号未定义错误。另外嵌入式的C和PC端的C有些微妙的差异。比如在Keil工程里很多人会追求用写法更现代的C但是在没有操作系统的环境下我们要尽量避免用new动态申请内存因为堆空间不够大也不够可控还要尽量少依赖异常处理因为ARMCC默认情况下异常支持的代码体积很大。这些都是后面要展开内容这一篇先让你在工具链层面建立起认识C不是不能在STM32上写而是你要知道它在每个环节里怎么接进来。5. 环境安装顺序与避坑清单照着做能少熬两天夜5.1 推荐的安装顺序很多教程给的安装顺序是三四个软件按名字排序一通装但你如果踩过坑就会收到一个反直觉的建议先装驱动类/烧录类软件再装开发环境。我推荐的安装顺序是这样的第一步装ST-Link驱动确保在你插上调试器后Windows能正常把它识别为设备。驱动不到位后面所有“连接不上芯片”的问题都会让你疑神疑鬼。第二步装STM32CubeProgrammer它是新一代官方烧录工具能顺便把ST-Link的驱动也处理干净。ST-Link Utility虽然界面比较老但如果你用的是旧例程里的操作截图也可以装两者共存不冲突日常用新的CubeProgrammer就行。第三步装STM32CubeMX这时候电脑上已经有Java运行环境的基础了CubeMX 6.x版本一般会自带所需运行时安装过程比较省心。第四步装Keil MDK以及对应的器件支持包Device Pack。注意CubeMX生成MDK工程时会帮你把固件包打包进工程但Keil本身需要安装STM32F1系列的Device Family Pack否则编译时可能提示缺少文件。第五步串口驱动和串口助手随时装用到再说。掐顺序的原因很简单越底层、越接近硬件的工具越先装越上层、越大而全的IDE越后装。这样即使后面IDE装失败也不至于连重新识别硬件都搞不定。5.2 我踩过的坑按出现频率排序第一坑Keil工程路径带中文。我见过最夸张的一个案例同事把工程放在“桌面/学习/STM32项目”下编译时报警了一长串文件找不到。Keil对中文路径的支持一直很糟糕防御办法只有一个所有工程目录用纯英文最好连用户名都别带中文如果系统用户名是中文可以把工程直接放到D盘根目录之类的英文路径下。第二坑下载时报Flash Timeout。这种情况大概率是芯片型号对应的Flash算法选错了。在魔术棒Utilities选项卡里看下载算法是否选了STM32F10x Flash如果选了别的型号下载时根本找不到对应的存储区域。另外板子供电不足也会导致下载时电压跌落接一个外接5V电源再试。第三坑第一次下载成功后第二次连不上芯片。大概率就是我在前面反复强调的SYS Debug没选Serial Wire导致SWD引脚被释放成了普通GPIO。解决办法是用烧录工具按住复位在连接瞬间操作或者直接全片擦除。越早看到这条你越不用受苦。第四坑串口乱码或者没有数据。乱码优先查波特率其次查两边是否RX接TX、TX接RX没有数据先查GND是否共地再查是否选对了COM口。一般情况下串口问题九成出在这四个检查项剩下的才是你代码里真的没发数。第五坑买到劣质ST-Link。市面上几十块的“迷你ST-Link V2”大量存在有些用几天就掉固件或者连接速度稍微一快就断开。如果你发现驱动器識别不稳定换一根数据线往往能救回一半如果还不行那大概率是硬件本身的锅。这时候除了换一个口碑好的正品或高质量兼容设备没有更好的办法。5.3 ST-Link Utility / CubeProgrammer会在什么时候变成“救命稻草”还有一个经验要告诉你前三个软件你平时可能都在用但第四个烧录软件很多新手真的只有在出意外时才想得起它。而它往往会成为你救砖的关键。打个比方有一天你心血来潮想把诱导开发板送变砖的代码烧上去——比如你在主循环里把Flash的某段地址给擦了或者开启了读保护第二天上电Keil连不上芯片了你觉得板子废了。实际上只要ST-Link还能连线打开CubeProgrammer先Try Connect如果连接正常直接选择Full chip erase把Flash擦成默认状态再把读保护等级调低或去除。等你看到Successful擦除板子基本就活了。这个救命经验是很多初学者刷几十遍教程也不会讲到的。所以我一直觉得这四个软件的“第四件”不是给日常开发用的是给关键时刻保命用的。你可以平时不用它但你必须知道它放在那里并且明白“Keil连不上时可以切换到专用烧录工具去连接”。这种不被表面问题卡住的能力比多背几个API重要得多。最后说点实在的我个人见过太多人倒在学习门槛的第一道坎上装了四个软件却因为不懂它们是在干什么而觉得自己不适合做嵌入式甚至直接把整个工具链卸载放弃了。太可惜了。这几个软件没有任何一个需要你“彻底搞懂原理”才能开始用。你需要的只是照着上面第3部分的流程完整地把一个LED程序从CubeMX一路跑到串口。跑通那一次所有的困惑都会迎刃而解。就我自己而言当年第一次跑通串口输出“hello”的时候那四个软件的图标在我眼里才真正变成了四个分工明确的工位而不是四堆看不懂的按钮。下一次我们继续沿着这条嵌入式C之旅往下走聊聊在Keil里怎么用C把HAL库包得更好用让业务代码更耐看。