调试工具箱
硬件调试的核心工具有三件:万用表、示波器、逻辑分析仪。
| 工具 | 用途 | 入门推荐 |
|---|---|---|
| 万用表 | 电压、通断、电阻、电流 | 胜利 VC890C+ |
| 示波器 | 信号波形、时序、噪声 | Rigol DS1054Z |
| 逻辑分析仪 | 数字协议解码(I2C/SPI/UART) | Saleae Clone (24MHz) |
示波器使用基础
基本操作步骤
- 探针地线夹到电路板的 GND 测试点(越短越好)
- 探针尖接触被测信号点
- 调整垂直刻度(Volts/Div)使波形幅度占屏幕 60-80%
- 调整水平刻度(Time/Div)显示几个周期
- 调整触发电平使波形稳定
示波器调试实例
# 用示波器测量 PWM 信号的实际频率
# 假设 MCU 配置了 1kHz PWM,但怀疑不准
# 示波器操作:
# 1. 探针夹在 PWM 输出引脚
# 2. Time/Div -> 1ms
# 3. 测量两个上升沿的时间差 T
# 4. 实际频率 = 1/T
def check_pwm_frequency(pin, expected_hz):
"""伪代码:用示波器验证 PWM 频率"""
actual_us = measure_period_us(pin)
actual_hz = 1_000_000 / actual_us
error = abs(actual_hz - expected_hz) / expected_hz * 100
return f"实际: {actual_hz:.0f}Hz, 误差: {error:.1f}%"
print(check_pwm_frequency("GPIO5", 1000))
常见信号测量
- 电源纹波:AC 耦合模式,20MHz 带宽限制,看 mV 级别的噪声
- 上升时间:测量信号 10%-90% 的时间,太快可能产生 EMI
- 过冲/振铃:方波顶部的尖峰,通常由阻抗不匹配引起
逻辑分析仪
逻辑分析仪只关心信号的高/低电平,不关心具体电压值。它擅长解码数字协议。
使用流程:
- 通道夹连接到 SCL、SDA 等信号线(共用 GND)
- 设置采样率(至少是信号频率的 4 倍以上)
- 设置触发条件(如 I2C Start 条件)
- 抓取波形,使用协议解析器解码
# 逻辑分析仪解码 I2C 数据 (使用 pylibiio 或 saleae 库)
def parse_i2c_byte(scl_samples, sda_samples, sample_rate_hz):
"""解析 I2C 一个字节的数据(简化示意)"""
# 实际应用中 Saleae 或 PulseView 会自动完成
result = []
bit_count = 0
byte_val = 0
for i in range(len(scl_samples) - 1):
# 检测 SCL 上升沿
if scl_samples[i] == 0 and scl_samples[i+1] == 1:
bit = sda_samples[i+1]
byte_val = (byte_val << 1) | bit
bit_count += 1
if bit_count == 8:
result.append(byte_val)
# 之后是 ACK 位
byte_val = 0
bit_count = 0
return result
免费的逻辑分析仪软件:
- PulseView (sigrok) — 支持很多种廉价分析仪
- Logic 2 (Saleae) — 功能强大,免费版有限制
- ESP32 自带逻辑分析仪功能(有限)
串口调试
printf 调试法
嵌入式开发中最常用的调试手段就是串口输出。
// 重定向 printf 到 USART
int __io_putchar(int ch) {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_USART1_UART_Init();
printf("系统启动...\n");
printf("复位原因: %d\n", __HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST));
while (1) {
uint32_t before = HAL_GetTick();
sensor_read();
printf("传感器读取耗时: %lu ms\n", HAL_GetTick() - before);
HAL_Delay(1000);
}
}
调试日志分级:
#define LOG_LEVEL 3
#if LOG_LEVEL >= 3
#define LOG_ERR(fmt, ...) printf("[ERR] " fmt "\n", ##__VA_ARGS__)
#else
#define LOG_ERR(...)
#endif
#if LOG_LEVEL >= 2
#define LOG_WARN(fmt, ...) printf("[WARN] " fmt "\n", ##__VA_ARGS__)
#else
#define LOG_WARN(...)
#endif
#if LOG_LEVEL >= 1
#define LOG_INFO(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__)
#else
#define LOG_INFO(...)
#endif
串口工具推荐
- PuTTY / MobaXterm — Windows 下的串口终端
- minicom / screen — Linux 命令行
- Serial Studio — 带数据可视化功能
常见硬件坑
坑 1: 电源不可靠。 设备随机复位、WiFi 连不上。示波器看 3.3V 电源轨是否出现低于 2.8V 的尖峰。解决方法是增加储能电容,或者换更大电流的 LDO。
坑 2: I2C 总线卡死。 设备运行一段时间后传感器无响应。SDA 被从机持续拉低。软件中实现一个超时恢复函数,给 SCL 发 9 个脉冲释放总线:
void i2c_bus_recover(void) {
gpio_set_direction(SCL_GPIO, GPIO_MODE_OUTPUT_OD);
for (int i = 0; i < 9; i++) {
gpio_set_level(SCL_GPIO, 0);
delay_us(5);
gpio_set_level(SCL_GPIO, 1);
delay_us(5);
}
}
坑 3: GPIO 浮空。 触摸 MCU 引脚时设备异常。原因是未使用的 GPIO 没有配置状态。所有不用的引脚设成输出低电平或内部上拉。
坑 4: ADC 读数跳动。 同样的条件下 ADC 值忽高忽低。参考电压噪声或采样时间不足。延长采样时间,软件做多次采样取平均,使用内部参考电压。
调试流程总结
当硬件工作不正常时的标准化排查步骤:
- 供电正常吗?万用表测各电源轨电压、示波器看纹波
- 晶振起振了吗?示波器看晶振引脚波形
- 复位正常吗?检查复位引脚电压(高电平正常)
- 程序跑起来了吗?LED 闪烁或者串口有输出?
- 外设初始化成功了吗?检查初始化函数的返回值
- 通信正常吗?逻辑分析仪看总线波形
小结
好的调试流程比好的仪器更重要。从电源开始逐个确认,用合适的工具验证每个假设。遇到奇怪的问题时,往往是电源或接地的问题。学会用示波器看关键信号,用逻辑分析仪解码通信协议,能解决 90% 的硬件故障。