1. 嵌入式调试基础:从“灯不亮”到“系统稳”
做嵌入式这么多年,我最大的体会就是:调试能力决定了你能走多远。写代码谁都会,但出了问题能快速定位,这才是真本事。这一章,咱们就把调试的底子打好。
1.1 嵌入式系统调试到底在调什么?
说白了,调试就是让硬件和软件“对上话”。你想想看,一个单片机跑着你的代码,它到底在执行哪条指令?某个寄存器值对不对?外设有没有按预期工作?这些信息,调试工具就是帮你“看”到的。
我个人习惯把调试场景分成三类:
| 调试类型 | 典型问题 | 常用工具 |
|---|---|---|
| 逻辑/时序 | GPIO 电平跳变不对、SPI 时钟相位错 | 逻辑分析仪、示波器 |
| 程序执行 | 跑飞、死循环、变量值异常 | JTAG/SWD 调试器 |
| 通信/协议 | I²C 无应答、UART 乱码 | 逻辑分析仪 + 串口助手 |
核心原则:先确认硬件没问题,再怀疑软件。我在项目中遇到过太多次,折腾半天代码,最后发现是虚焊。
1.2 三大调试工具,你得会用
1.2.1 逻辑分析仪——看“数字信号”的眼睛
逻辑分析仪只关心电平高低,不关心具体电压值。它最适合看时序关系。比如 SPI 的 CS、SCLK、MOSI 之间的先后顺序对不对。
- 采样率:至少是信号频率的 4 倍。测 1MHz 的 SPI,采样率要 4MHz 以上。
- 通道数:8 通道起步,16 通道更实用。我常用的就是 8 通道,够抓 I²C + UART 同时跑。
- 触发条件:设置上升沿或下降沿触发,能帮你精准抓到异常时刻。
我的习惯:调试 I²C 时,我会同时抓 SCL 和 SDA,再设一个触发条件为“SCL 连续 9 个脉冲后 SDA 没拉低”。这样能快速定位从设备无应答的问题。
1.2.2 示波器——看“模拟信号”的利器
示波器能看电压波形,还能测噪声、过冲、振铃。这些逻辑分析仪干不了。
- 带宽:测普通数字信号,100MHz 够用。测高速信号(比如 USB、以太网),至少 200MHz。
- 采样率:1GS/s 是主流,抓单次信号够用。
- 探头:10x 探头比 1x 探头更准,尤其测高频时。
注意:示波器探头的地线夹子要尽量短。地线太长会引入噪声,测出来的波形全是毛刺。我曾经因为这个,误判了一个电源纹波问题,折腾了两天。
1.2.3 JTAG/SWD 调试器——控制程序执行的“遥控器”
JTAG 和 SWD 是两种调试接口。JTAG 用 5 根线,SWD 只用 2 根(SWDIO + SWCLK)。现在主流 MCU 都支持 SWD,省引脚。
| 特性 | JTAG | SWD |
|---|---|---|
| 引脚数 | 5(TMS, TCK, TDI, TDO, nTRST) | 2(SWDIO, SWCLK) |
| 速度 | 最高可达 50MHz | 通常 4-10MHz |
| 调试能力 | 支持多核、边界扫描 | 单核调试,够用 |
| 适用场景 | FPGA、复杂 SoC | ARM Cortex-M 系列 MCU |
我个人建议:能用 SWD 就别用 JTAG。省下来的 3 个 GPIO,够你接一个按键加一个 LED 了。
1.3 调试环境搭建——别让工具拖后腿
环境搭不好,调试效率直接减半。我见过太多人,花半小时连不上调试器,最后发现是驱动没装。
1.3.1 硬件连接
- 调试器选型:J-Link、ST-Link、DAP-Link 都行。J-Link 最稳,但贵。ST-Link 便宜,够用。
- 接线:SWD 只需要 4 根线:SWDIO、SWCLK、GND、VCC(可选)。VCC 用来检测目标板电压,不是供电。
- 上拉/下拉:SWDIO 需要上拉(10kΩ 到 3.3V),SWCLK 需要下拉(10kΩ 到 GND)。很多开发板已经集成,但自己做板子时别忘了。
避坑指南:我曾经因为 SWD 线太长(超过 20cm),导致调试器经常连不上。后来换成 10cm 的排线,问题解决。SWD 信号频率高,线越短越好。
1.3.2 软件配置
- IDE:Keil、IAR、STM32CubeIDE 都行。我个人习惯用 VS Code + Cortex-Debug 插件,轻量又灵活。
- 驱动:调试器需要装驱动。J-Link 用 Segger 官方驱动,ST-Link 用 ST 官方驱动。别用 Windows 自动更新的,容易出问题。
- 调试配置:设置目标芯片型号、调试接口(SWD/JTAG)、时钟频率。时钟频率别设太高,先设 1MHz,稳定后再往上调。
1.3.3 验证环境
搭好环境后,先跑一个最简单的程序:点灯。能正常进入调试、单步执行、查看变量,说明环境没问题。
// 点灯测试程序
#include "stm32f1xx_hal.h"
int main(void) {
HAL_Init();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
while (1) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500); // 断点打在这里,看能不能停住
}
}
小技巧:第一次连接调试器时,如果提示“No target connected”,先检查接线,再检查供电。很多开发板需要外部供电,调试器不供电。
1.4 本章知识体系
下面这张图,帮你把本章的核心逻辑串起来:
调试不是玄学,是方法论。工具只是手段,关键是思路。这一章的内容,就是帮你把调试的“骨架”搭起来。后面几章,咱们会在这个骨架上填肉——具体怎么用逻辑分析仪抓 I²C、怎么用示波器测 PWM、怎么用调试器看堆栈……