1. 调试基础与环境搭建
调试,说白了就是给嵌入式系统看病。没有趁手的工具,你连症状都摸不清。这一章我们先把地基打牢——GDB 基础命令、交叉编译链、OpenOCD/JLink 驱动,最后用 VSCode + Cortex-Debug 把它们串起来。
1.1 GDB 基础命令回顾
GDB 是调试界的瑞士军刀。我见过不少工程师只会用 next 和 continue,遇到复杂 bug 就抓瞎。其实掌握十几个命令就够了。
| 命令 | 简写 | 作用 |
|---|---|---|
break |
b |
设置断点(行号/函数/条件) |
continue |
c |
继续运行到下一个断点 |
next |
n |
单步跳过(不进入函数) |
step |
s |
单步进入函数内部 |
print |
p |
打印变量或表达式值 |
backtrace |
bt |
查看函数调用栈 |
info registers |
i r |
查看所有寄存器 |
display |
disp |
每次停下时自动打印某个变量 |
bt 看调用链,再用 info registers sp 看栈指针位置。十有八九能定位到问题。
举个例子,假设你有一个 uart_send() 函数卡死了:
(gdb) b uart_send
(gdb) c
(gdb) bt
#0 uart_send (data=0x55) at uart.c:42
#1 0x08000234 in main () at main.c:28
(gdb) p/x data
$1 = 0x55
(gdb) info registers r0
r0: 0x55
嗯,这里要注意:p/x 是以十六进制打印,看外设寄存器时特别有用。
1.2 交叉编译工具链配置
嵌入式调试和桌面调试最大的区别——你得用交叉编译链。你的 PC 是 x86,目标芯片是 ARM,编译器必须能生成 ARM 指令。
我个人推荐 ARM GNU Toolchain(gcc-arm-none-eabi)。为什么?开源、稳定、社区活跃。我在项目中踩过不少坑,最典型的就是版本不匹配——GDB 版本太老,不认识新芯片的调试特性。
| 组件 | 说明 | 版本建议 |
|---|---|---|
arm-none-eabi-gcc |
C/C++ 编译器 | ≥ 10.3.1 |
arm-none-eabi-gdb |
调试器 | ≥ 10.2 |
arm-none-eabi-objcopy |
生成 .hex/.bin | 随工具链自带 |
Makefile / CMakeLists.txt |
构建脚本 | 按项目需求 |
developer.arm.com 下载。
安装后验证一下:
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10) 10.3.1 20210824
$ arm-none-eabi-gdb --version
GNU gdb (GNU Arm Embedded Toolchain 10.3-2021.10) 10.2.90.20210621
看到版本号,说明工具链就绪了。
1.3 OpenOCD 与 JLink 驱动安装
调试器是 PC 和芯片之间的翻译官。OpenOCD 开源免费,JLink 商业但稳定。我两个都用,看项目需求切换。
OpenOCD
OpenOCD 支持 ST-Link、CMSIS-DAP、FTDI 等调试器。安装方式:
# Ubuntu / Debian
sudo apt install openocd
# macOS (Homebrew)
brew install openocd
# Windows
# 从 https://openocd.org 下载预编译包
启动命令示例(以 STM32F4 + ST-Link 为例):
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
你会看到类似输出:
Info : Listening on port 3333 for gdb connections
端口 3333 就是 GDB 要连接的调试通道。
JLink
JLink 驱动从 Segger 官网下载。安装后有两个关键工具:
- JLinkGDBServer:启动 GDB 服务器
- JLinkExe:命令行调试工具
启动 GDB 服务器:
JLinkGDBServer -device STM32F407VG -if SWD -speed 4000
1.4 VSCode + Cortex-Debug 插件集成
命令行调试虽然强大,但效率不如 IDE。VSCode 配上 Cortex-Debug 插件,能让你在代码里直接设断点、看变量、查寄存器。
安装步骤:
- 安装 VSCode
- 安装 Cortex-Debug 插件(作者:Marcin Borowczak)
- 安装 Arm Assembly 插件(语法高亮)
关键配置在 .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"type": "cortex-debug",
"request": "launch",
"servertype": "openocd",
"cwd": "${workspaceRoot}",
"executable": "./build/firmware.elf",
"device": "STM32F407VG",
"configFiles": [
"interface/stlink.cfg",
"target/stm32f4x.cfg"
],
"svdFile": "./STM32F407.svd"
}
]
}
配置好之后,按 F5 启动调试。你会看到:
- 代码左侧出现断点红点
- 变量窗口实时更新
- 寄存器窗口显示外设状态
- 调用栈窗口显示函数调用链
下面这张图展示了整个调试环境的架构关系:
netstat -an | grep 3333 确认 OpenOCD 是否在监听。我遇到过好几次防火墙把端口挡了。
调试环境搭好之后,你可以试试:在 main 函数第一行设断点,按 F5 启动。如果能停在断点,说明整个链路通了。如果不行,按这个顺序排查:
- 硬件连接:SWD 线序对不对?GND 接了没?
- 驱动:OpenOCD 或 JLink 能识别到芯片吗?
- 配置:launch.json 里的 device 和芯片型号一致吗?
- 固件:.elf 文件编译时带
-g选项了吗?
monitor reset halt 让芯片停在复位向量表。这样能确认调试器确实控制了芯片,而不是在跑飞的状态下乱停。