不少做嵌入式开发的朋友,第一次听说可以用 VSCode 写 STM32 工程、用 GDB 调试的时候,第一反应都是"真的假的?"。
我当时从 Keil 转过来,就是因为在调试一个偶发性的硬件异常时,Keil 的调试器信息太有限,断点命中后看变量值也总觉得隔了一层。换了 VSCode 加 GDB 之后,怎么说呢,属于"一旦用顺了就不想回去"的状态。但这条路上也不是全无坑:STLink 驱动装不上、设备未知、调试器连不上、GDB 一启动就崩……这些我全都踩过。这篇文章就把我从零搭环境到跑通 GDB 调试的完整过程,连同所有踩过的坑、排查思路、最终解法一起整理出来,希望能帮你少走几周弯路。
1. 为什么从Keil转投VSCode:一套环境打通编辑、编译和调试
1.1 Keil的痛点与VSCode的吸引力
先聊一个很现实的问题:Keil 用了这么多年,到底哪里不好?
Keil 的 MDK-ARM 本身是个集成开发环境,编译、下载、调试一把抓,对新手非常友好。但它的代码编辑器停留在上个时代:代码补全聊胜于无,文件跳转时灵时不灵,想看个函数定义经常要手动搜。遇到工程文件多的时候,浏览代码的效率是真的低。
另外还有一个很实际的问题——Keil 对代码格式化的支持很弱,也不方便做代码审查和版本管理。多人协作时,大家用的编码风格、行尾格式都不好统一。
VSCode 的优势则非常明显:免费、跨平台、插件生态极其丰富。装上 C/C++ 插件和 Cortex-Debug 插件之后,编辑体验直接拉满,代码补全、语法高亮、引用跳转、调试变量监视都不在话下。最舒服的是你可以在同一个编辑器里同时写上位机、写固件、写 Python 脚本,不用来回切换工具。
1.2 适用人群与实际场景判断
但我也要泼一盆冷水:这个方案不完全适合所有人。
如果你是纯新手,连 STM32 的时钟树、GPIO 配置、外设初始化都没搞明白,那我建议先用 Keil 或者 STM32CubeIDE 把基本功打扎实。因为 VSCode + 命令行编译的链路里,很多配置需要你自己理解编译过程、链接脚本、启动文件这些底层概念,这些对新手来说负担比较重。
如果你是以下情况,就很适合迁移到 VSCode 这套方案:
- 已经有 STM32 开发基础,受够了 Keil 的编辑器体验
- 需要频繁阅读、修改大型固件工程
- 有命令行编译、自动化构建的需求,比如对接 CI
- 想用 GDB 更深入地分析程序运行状态,比如查看寄存器、反汇编、内存分布
这套方案适用的芯片型号比较广,从 F1 到 F4、H7 都可以,只要 arm-none-eabi-gcc 工具链支持就行。但是 H7 这类较新的芯片,需要注意 CMSIS 库的版本和启动文件的匹配,这个后面会细说。
1.3 工具链选型总览:一台电脑需要装哪些东西
在使用 VSCode 之前,先把整条工具链的依赖理清楚。一个完整的 STM32 开发调试链路,核心组件包括:
| 组件 | 作用 | 选型建议 |
|---|---|---|
| VSCode | 代码编辑与调试前端 | 官方渠道下载最新稳定版即可 |
| STM32CubeMX | 生成初始化代码 | 用 HAL 库时强烈建议 |
| arm-none-eabi-gcc 工具链 | 编译、链接固件 | GNU Arm Embedded Toolchain |
| OpenOCD | 连接调试器与目标芯片 | 0.11.0 及以上版本 |
| ST-Link 驱动 | 让电脑识别 ST-Link 调试器 | 官方驱动包 |
| Cortex-Debug 插件 | VSCode 侧的 GDB 前端 | 配合 OpenOCD 使用 |
这里提醒一点:有人会用 STM32CubeProgrammer 自带的驱动来替代 STLink 官方驱动,虽然大部分情况下能用,但如果你安装完发现设备管理器仍然显示感叹号,建议还是把官方驱动包完整装一遍。
2. STLink驱动安装:从"设备无法识别"到"连接成功"的完整排查链路
2.1 驱动安装的正确顺序与版本细节
先说结论:ST-Link 驱动要装在 STM32CubeProgrammer 和 Keil 之前,或者至少要在连接调试器之前安装完毕。
我第一次装的时候,图省事直接先装了 Keil,然后插上 ST-Link,Windows 弹出"设备安装失败"。后来把官方 ST-Link 驱动包重装了一遍才解决。这是因为 Keil 自带的驱动版本比较旧,不一定能匹配最新固件的 ST-Link。
正确顺序是:
- 去 ST 官网下载 ST-Link 驱动包,或者直接安装 STM32CubeProgrammer(它会自动装驱动)
- 插上 ST-Link 调试器,等待系统识别
- 打开设备管理器,确认端口和 USB 设备列表里出现 ST-Link 相关条目
- 再安装 Keil、VSCode 等上层工具软件
如果设备管理器里显示ST-Link USB 设备或者STMicroelectronics STLink dongle,说明驱动没问题。
另外要注意 32 位和 64 位系统的驱动路径不同,ST 官网的驱动包会自动识别,但如果手动指定路径安装,别选错了。Win10/Win11 系统一般用自带的驱动更新就能装上,Win7 则必须手动装驱动。
2.2 "Unknown Device"和"USB无法识别"的根因排查
这是很多人卡住的地方:设备管理器里显示的是未知 USB 设备(设备描述符请求失败)。这个问题听起来玄学,其实排查链路很清晰,按顺序来。
第一步,检查连接线。ST-Link V2 的 USB 接口是 Mini-USB,很多人随便找了一根只能充电不能传数据的线来用,结果系统完全没反应。这时候换一根确认能传数据的 USB 线,问题直接解决。这种情况我至少见了三次。
第二步,换 USB 口。尽量插在电脑后置的 USB 口上,不要用前置面板或者 USB Hub。ST-Link V2 的电流不大,但劣质 Hub 的供电和信号质量都不行,容易导致枚举失败。
第三步,排查设备管理器里的驱动状态。如果显示"未知设备",右键选择"更新驱动程序"→"浏览我的电脑",手动指向 ST 驱动目录。如果更新驱动时提示"找不到驱动",看看是不是杀毒软件把驱动文件隔离了。
第四步,如果你用的 ST-Link 是山寨版,也就是网上几十块钱的所谓"兼容版",那么它的固件版本可能比较乱,驱动识别后会显示为一个串口设备而不是 ST-Link,这种情况需要先用 STM32CubeProgrammer 里的固件升级工具刷一遍。原装 ST-Link 驱动识别后通常会有两个设备:一个 USB 输入设备和一个 USB 大容量存储设备,山寨版往往只有其中一个。
2.3 硬件层面的坑:引脚定义、供电与连接方式
驱动装好只是第一步,调试器连不上芯片的时候,多半是硬件连接问题。
ST-Link V2 的调试接口通常是 SWD,常用引脚就四个:
| 引脚 | 作用 |
|---|---|
| SWDIO | 数据输入输出 |
| SWCLK | 时钟 |
| GND | 地 |
| 3.3V | 给目标板供电(注意电压匹配) |
网上很多引脚图标注的是 20 针 JTAG 定义,很多人对着 JTAG 图去接 SWD 就会接错。ST-Link V2 的排针上一般有丝印标注,找 SWDIO、SWCLK、GND、3.3V 这四个即可。
连接时有一个非常容易踩的坑:目标板如果已经由外部电源供电,那么 ST-Link 的 3.3V 引脚就不要接到目标板的 3.3V 上,否则两路电源并在一起,轻则电压被拉低,重则烧毁调试器。正确做法是只接 SWDIO、SWCLK、GND,目标板用自己独立的电源。
如果芯片是 5V 供电的逻辑电平,比如某些老款 5V 单片机,那么 ST-Link 的 3.3V 参考电平可能不匹配,导致 SWD 通信不稳定。好在 STM32 基本都是 3.3V 内核,这个坑主要在非 STM32 芯片上才会遇到。
2.4 Keil中STLink调试配置闪退的连带影响
还有一个奇怪但高发的问题:Keil 里配置 ST-Link 调试器时,点开 Settings 直接闪退。这个问题我在两个不同的电脑上都遇到过。
用 ST-Link 驱动、STM32 ST-LINK Utility或STM32CubeProgrammer确认硬件连接和驱动都正常,说明硬件本身没问题,问题出在 Keil 与该驱动版本不兼容。常见的解决办法是升级 Keil 到较新版本,或者在 Keil 安装目录下删除 ST-Link 相关的旧配置文件后重新配置。
但我的建议是:如果 Keil 的调试功能始终不稳定,那就干脆跳过它,直接进入 VSCode 方案。这也是我最终的解决方案。毕竟 GDB + OpenOCD 这套链路在硬件通信层是通用的,STM32CubeProgrammer 能连上,OpenOCD 基本也能连上。
3. VSCode端工程配置:从插件清单到构建系统打通
3.1 必须安装的插件及每个插件的真实用途
VSCode 的插件生态非常庞大,但 STM32 开发真正核心的其实就三个。
第一个是C/C++,微软官方的插件。它负责提供代码补全、语法高亮、IntelliSense 和调试支持。没有它,写代码的体验跟记事本差不多。
第二个是Cortex-Debug,这是整个调试链路最关键的一块。它专门针对 ARM Cortex-M 芯片做了适配,支持通过 OpenOCD 连接 ST-Link,然后在 VSCode 里实现断点、单步、变量监视、寄存器查看等功能。没有这个插件,VSCode 自带的调试器不知道如何与 STM32 通信。
第三个是CMake Tools,如果你用 CMake 来构建工程,这个插件能省很多事。不过如果你沿用 Makefile 或者直接用 STM32CubeMX 生成的工程,也可以不装。
还有一些辅助插件,比如 Cortex-Debug 依赖的Memory Viewer、用来格式化代码的clang-format、管理串口监视的Serial Monitor等,按需安装就行,别一股脑全装,插件多了反而容易互相冲突。
3.2 工具链安装与工程构建方式选择
接下来是编译器。STM32 系列的编译工具链统一用arm-none-eabi-gcc,去 ARM 官网下载 GNU Arm Embedded Toolchain 安装即可。Windows 下安装完成后,把安装目录下的 bin 文件夹加入系统 PATH 环境变量。
验证是否装好,打开终端输入:
arm-none-eabi-gcc --version能正确输出版本信息就说明工具链没问题。
工程构建方面,根据你的项目来源有两条路线:
一条是直接用 STM32CubeMX 生成 Makefile 工程。在 Project Manager 里把 Toolchain 选成 Makefile,生成后 VSCode 里打开工程,终端执行make就能编译。这种方式对新手最友好,因为启动文件、链接脚本、外设初始化代码都是 CubeMX 自动生成的,不需要你手动维护。
另一条是 CMake 路线。适合要写自动化构建脚本、需要灵活配置多个目标(比如 bootloader 和 app)的场景。CMake 的语法在嵌入式里就是用来拼编译参数和源文件列表的,配置好了之后,CMake Tools插件可以一键构建。
我个人的建议是:如果你只是想稳定地写代码和调试,第一条路线就够了。等你发现 Makefile 满足不了你的构建需求了,再迁移到 CMake 也不迟。
3.3 c_cpp_properties.json 配置:让 IntelliSense 不再乱报错
很多人把工程拉进 VSCode 之后,发现满屏红色波浪线,好像程序全是错。其实这是 IntelliSense 不知道头文件在哪里导致的。这时候需要配置.vscode/c_cpp_properties.json。
一个参考配置:
{ "configurations": [ { "name": "STM32", "includePath": [ "${workspaceFolder}/**", "${workspaceFolder}/Core/Inc", "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc", "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include", "${workspaceFolder}/Drivers/CMSIS/Include" ], "defines": [ "STM32F103xB", "USE_HAL_DRIVER" ], "compilerPath": "C:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update/bin/arm-none-eabi-gcc.exe", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "gcc-arm" } ], "version": 4 }这里的defines里的宏,必须和编译命令里的-D参数一致,否则 IntelliSense 看到的代码路径和编译器实际编译的路径可能不同。compilerPath要指向你实际安装的 gcc 路径,填错的话插件会自动降级,很多智能提示就消失了。
如果你发现包含的路径太多导致卡顿,可以在includePath里加${workspaceFolder}/**这样通配路径,但工程特别大的时候会拖慢响应,建议还是用显式路径。
4. GDB调试全流程:launch.json配置与断点调试实操
4.1 调试器选型逻辑:为什么用OpenOCD而不是STM32CubeProgrammer
这里先解决一个很多人困惑的问题:VSCode 的 Cortex-Debug 调试 STM32,背后到底是怎么连接的?
大体链路是这样的:
Cortex-Debug 插件 → GDB 客户端 → GDB Server(OpenOCD 进程) → ST-Link 驱动 → ST-Link 硬件 → 目标芯片Cortex-Debug 插件本身不直接跟 ST-Link 硬件通信,它启动一个 GDB 客户端(也就是 arm-none-eabi-gdb),然后让这个 GDB 连接到 GDB Server。GDB Server 的职责是把 GDB 的调试指令翻译成调试器硬件能理解的命令,再通过 ST-Link 下发到芯片上。
那为什么选 OpenOCD 做 GDB Server?有几点:
- OpenOCD 是开源工具,支持的调试器种类非常多,ST-Link、J-Link、DAP-Link 都可以
- 它对 STM32 的 flash 编程支持很成熟,调试的同时可以直接下载固件
- 社区活跃,遇到问题容易找到解决方案
相比 STM32CubeProgrammer,OpenOCD 的命令行接口更适合作为 GDB Server 被 VSCode 调用。CubeProgrammer 主要面向烧录场景,调试协议兼容性不够好。
4.2 launch.json核心字段逐一拆解
配置调试的核心文件是.vscode/launch.json。第一次配置的时候,我对着网上五花八门的配置复制粘贴,结果各种报错。后来把每个字段搞清楚之后,才算真正理解调试链路的运行逻辑。
一个最小可用的配置:
{ "version": "0.2.0", "configurations": [ { "name": "Cortex Debug", "cwd": "${workspaceFolder}", "executable": "./build/project.elf", "request": "launch", "type": "cortex-debug", "servertype": "openocd", "device": "STM32F103C8", "interface": "swd", "configFiles": [ "interface/stlink.cfg", "target/stm32f1x.cfg" ], "svdFile": "./STM32F103xx.svd", "runToEntryPoint": "main", "armToolchainPath": "C:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update/bin" } ] }逐一说下重点字段:
executable:指向编译出来的 ELF 文件路径。注意不是 hex 或 bin,是 ELF,因为 ELF 里包含调试符号信息,GDB 需要它来关联汇编地址和源代码行。request:选launch而不是attach。launch 模式下 OpenOCD 会先连接目标板,下载固件并复位执行;attach 模式则只连接当前正在运行的程序,不重新下载。device:芯片型号,主要用于 Cortex-Debug 判断芯片架构特性,不同系列之间要填对,不然后面某些调试功能会异常。configFiles:OpenOCD 的配置脚本,这两行相当于告诉 OpenOCD 用 ST-Link 作为调试器,用 STM32F1x 的目标配置。不同芯片对应不同的 target 配置文件,F4 要填stm32f4x.cfg,F0 填stm32f0x.cfg,不要照抄。svdFile:外设寄存器描述文件。填了它之后,调试时 VSCode 的"外设寄存器"视图里就能看到每个寄存器的位域解析,这个对排查硬件问题极其有用。SVD 文件可以从芯片厂商的 SDK 里找,有些第三方也会分享。runToEntryPoint:填main的话,启动调试后程序会自动运行到 main 函数入口处停住。这个字段很方便,省得每次启动后手动打一个 main 的断点。
4.3 一次完整的调试会话:从下载到断点触发
配置好之后,按 F5,观察底部输出面板的 OpenOCD 日志:
Info : STLink V2 JTAG v32 API v2 SWIM v7 VID 0x0483 PID 0x3748 Info : clock speed 1800 kHz Info : SWD DPIDR 0x1ba01477 Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : stm32f1x.cache: flash bank probed Info : listening on port 3333 for gdb connections Info : target state: halted出现这种日志说明一切都通了。如果卡在某一行不出后续,或者报target not found、unable to find a matching MSD device、Error: connect failed,排查方向分别是:
unable to find a matching MSD device:OpenOCD 把 ST-Link 当成大容量存储设备了,说明目标板没正常上电或没有连接 SWDIO 引脚。target not found:芯片型号不匹配,或 SWD 接线不对,重点检查 SWCLK 和 SWDIO 是否接反。connect failed:ST-Link 驱动异常,重新插拔 ST-Link。
调试会话启动后,你就可以在 VSCode 的编辑器行号左侧点一下设置断点,然后按 F5 继续运行,程序会在断点处停下来。左侧调试面板会显示局部变量、监视表达式、调用堆栈,下方还有调试控制台可以直接输入 GDB 命令。
有一点需要提醒:STM32 的 Flash 断点有数量限制。OpenOCD 日志里那行hardware has 6 breakpoints, 4 watchpoints说的就是硬件断点数量。如果断点超过 6 个,GDB 会启用软件断点,而软件断点是在 Flash 中临时改写指令实现的,如果正在从那片 Flash 执行代码,会有冲突,所以遇到奇怪的问题先检查断点数量。
5. GDB 调试实战命令与高频故障应急预案
5.1 GDB常用命令速查与使用场景
平时在 VSCode 里点按钮调试已经很方便,但在某些场景下,直接在调试控制台敲 GDB 命令是最高效的方式,尤其是当图形界面卡顿或者需要查看底层状态的时候。
这里按使用频率整理一份命令表:
| 命令 | 简写 | 作用 | 使用场景 |
|---|---|---|---|
file <elf> | - | 加载调试符号文件 | 手动调试前指定固件 |
target remote :3333 | tar rem | 连接 GDB Server | 手动连接 OpenOCD |
load | - | 下载固件到 Flash | 更新代码到芯片 |
monitor reset halt | mon reset halt | 复位并暂停芯片 | 开始新一轮调试 |
break <函数名/行号> | b | 设置断点 | 指定停住的代码位置 |
info break | i b | 查看所有断点 | 确认断点是否生效 |
delete <编号> | d | 删除断点 | 清理不用的断点 |
continue | c | 继续运行 | 离开断点 |
next | n | 单步跳过 | 跳过函数内部执行 |
step | s | 单步进入 | 进入函数内部 |
finish | - | 执行到当前函数返回 | 跳出函数 |
print <表达式> | p | 打印变量值 | 查看变量内容 |
info registers | i r | 查看全部寄存器 | 排查硬件状态 |
x/<格式> <地址> | x | 查看内存数据 | 检查缓冲区、外设寄存器映射 |
monitor flash write_image | - | 烧录指定镜像 | 固件更新失败时备用 |
一个典型的命令行调试流程:
arm-none-eabi-gdb build/project.elf (gdb) target remote :3333 (gdb) monitor reset halt (gdb) load (gdb) break main (gdb) continue走到断点停住后,用print可以查看局部变量值,用info registers能查看所有寄存器状态。
5.2 硬件断点、变量监视与内存查看的进阶技巧
在实际调试中,有几个技巧非常提升效率。
第一是表达式监视。左侧变量监视面板可以添加表达式,比如myStruct.member或者arr[3],程序每次暂停时都会自动刷新。有些复杂表达式没法在面板里算,就直接在调试控制台敲print。
第二是查看外设寄存器。如果配置了 SVD 文件,Cortex-Debug 会提供一个"外设"视图,里面按寄存器组分类显示所有外设寄存器值。比如你要看某个定时器的 CNT 计数器值,直接在外设视图里展开 TIM2 就能看到,不需要手动算地址,也不用在代码里临时加打印语句。
第三是条件断点。右键一个断点,选择编辑断点,可以设置条件表达式。比如你只在index == 10时停住,断点条件填index == 10即可。这对排查循环内的问题非常有效,不用每轮循环都手动跳过。
第四是内存查看。在调试控制台敲:
x/8wx 0x20000000这表示查看地址0x20000000开始的内存,8 个字节宽的字,十六进制显示。排查数组越界、缓冲区溢出问题时会用到。如果你需要把内存范围和变量对应起来,可以先print &buffer拿到缓冲区地址,再x/32bx查看这块区域的实际内容。
5.3 烧录失败、调试器连接中断等高频问题应急预案
调试环节最让人头疼的问题,我整理几个高发场景和应对方案。
场景一:flash write failed或Error: timeout waiting for flash
这个一般是 Flash 写保护或者芯片正在运行时钟不稳导致的。先用monitor reset halt把芯片停住,再尝试load。如果还不行,用 STM32CubeProgrammer 检查一下 Option Bytes,确认读保护(RDP)等级不是 1 或 2。读保护开启后,调试器无法正常访问 Flash,必须先用串口或其他方式解除保护。
场景二:调试过程中 OpenOCD 报JTAG-DP STICKY ERROR
这个错误多见于 SWD 通信过程中,目标芯片掉电、复位时序不对、或者调试线太长导致信号质量差。简化连线长度,降低 SWD 速度,在 OpenOCD 配置中给 adapter 加一条adapter speed 1000之类的低速设置,一般能解决。
场景三:GDB 启动后无法加载 launch.json,提示找不到arm-none-eabi-gdb
这是环境变量没生效,或者 VSCode 在启动时没有继承新加的 PATH。重启 VSCode,或者在 launch.json 里显式指定"armToolchainPath"字段指向 gcc 的 bin 目录即可。
场景四:OpenOCD 显示Error: open failed打不开端口 3333
这说明 3333 端口被其他进程占用了,通常是一个残留的 OpenOCD 进程。在终端执行taskkill /f /im openocd.exe(Windows)或者pkill openocd(Linux/macOS),然后再启动调试。
场景五:点击调试后 VSCode 进入调试模式但没有下载固件
检查request字段以及runToEntryPoint的配置。如果你用了"request": "attach",并且没有在 attach 模式下配置"loadFiles",那么它确实不会下载固件。想每次调试都自动烧录,就用launch模式。
场景六:断点命中后程序卡死,按单步没有反应
优先检查中断处理函数里的死循环。另外一个常见原因是SysTick或者某个高优先级中断在断点处反复触发,导致调试器根本无法稳定执行单步。解决方法是临时在中断服务函数入口加一个条件断点或者禁用这个中断源。
6. 从工具链到调试习惯:一些进阶配置建议
6.1 让OpenOCD配置文件定制化
默认的interface/stlink.cfg和target/stm32f1x.cfg合起来看,其实是 OpenOCD 加载两个配置文件的串联结果。如果你有特殊需求,比如要调整 SWD 速度、要加载多个目标芯片,可以在项目目录下自己建一个自定义配置:
source [find interface/stlink.cfg] transport select hla_swd adapter speed 4000 source [find target/stm32f1x.cfg] $_TARGETNAME configure -event gdb-attach { halt }然后在 launch.json 里把configFiles改成这个自定义文件,其他不用动。这种做法在调试自制的板卡时非常实用,因为自制板卡的 SWD 布线质量参差不齐,自动速度可能不稳。
6.2 工程文件组织建议
最后给一个工程文件组织的建议。很多人喜欢把.vscode目录里的配置随手一放,结果换一台电脑打开工程就报一堆路径错误。建议把.vscode目录纳入版本管理,但不要把你本机的绝对路径写死。
launch.json 里尽量用${workspaceFolder}代替绝对路径,c_cpp_properties.json 的compilerPath也可以留空让插件自动探测。构建目录统一叫build并加入.gitignore,这样换机器之后只需要重新配置工具链路径,配置文件的改动很少。
6.3 与串口日志配合的调试策略
GDB 不是万能的。在排查启动阶段的问题时,比如时钟初始化失败、外部晶振起振异常,代码都还没跑到 main,断点根本打不上。这时候我会保留一个串口打印函数,在系统启动早期输出关键信息,配合 GDB 的 Flash 烧录功能,先刷一个带打印的固件验证硬件基础,再用 GDB 做精细化调试。
这一步看似土办法,实战中比 GDB 还要可靠。毕竟调试器依赖于 SWD 通信,而 SWD 本身就需要正确的时钟和电源,如果硬件连最基本的起振都做不到,调试器也稳定不下来。
写在最后的一点个人体会
把 VSCode + STM32 这套环境彻底跑通之后,我最大的感触是:工具链本身的坑其实不算多,绝大多数问题都出在驱动版本、接线方式、配置文件细节这些"看起来不起眼"的地方。花时间把每个环节的原理弄明白,比复制粘贴别人的配置要省心得多。
如果你正在搭环境,我建议按照本文的顺序来:先装驱动,再验证 ST-Link 连接,接着配编译器,最后再碰调试配置。一步一步来,每步都确认通过了再往下走。这套方案我第一次搭的时候折腾了差不多两个星期,现在写这篇文章再回头看,很多当时觉得难的问题,根因都特别简单。
最后再分享一个小技巧:遇到调试运行不稳定的时候,先别急着怀疑代码,试着降低 SWD 速度——在 OpenOCD 配置里把adapter speed调低到 1000,很多"玄学"问题直接消失了。这个技巧拯救了我好多个本以为要换线换板的深夜。