F FisherHub Docs

硬件调试技巧

调试工具箱

硬件调试的核心工具有三件:万用表、示波器、逻辑分析仪。

工具用途入门推荐
万用表电压、通断、电阻、电流胜利 VC890C+
示波器信号波形、时序、噪声Rigol DS1054Z
逻辑分析仪数字协议解码(I2C/SPI/UART)Saleae Clone (24MHz)

示波器使用基础

基本操作步骤

  1. 探针地线夹到电路板的 GND 测试点(越短越好)
  2. 探针尖接触被测信号点
  3. 调整垂直刻度(Volts/Div)使波形幅度占屏幕 60-80%
  4. 调整水平刻度(Time/Div)显示几个周期
  5. 调整触发电平使波形稳定

示波器调试实例

# 用示波器测量 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
  • 过冲/振铃:方波顶部的尖峰,通常由阻抗不匹配引起

逻辑分析仪

逻辑分析仪只关心信号的高/低电平,不关心具体电压值。它擅长解码数字协议。

使用流程

  1. 通道夹连接到 SCL、SDA 等信号线(共用 GND)
  2. 设置采样率(至少是信号频率的 4 倍以上)
  3. 设置触发条件(如 I2C Start 条件)
  4. 抓取波形,使用协议解析器解码
# 逻辑分析仪解码 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 值忽高忽低。参考电压噪声或采样时间不足。延长采样时间,软件做多次采样取平均,使用内部参考电压。

调试流程总结

当硬件工作不正常时的标准化排查步骤:

  1. 供电正常吗?万用表测各电源轨电压、示波器看纹波
  2. 晶振起振了吗?示波器看晶振引脚波形
  3. 复位正常吗?检查复位引脚电压(高电平正常)
  4. 程序跑起来了吗?LED 闪烁或者串口有输出?
  5. 外设初始化成功了吗?检查初始化函数的返回值
  6. 通信正常吗?逻辑分析仪看总线波形

小结

好的调试流程比好的仪器更重要。从电源开始逐个确认,用合适的工具验证每个假设。遇到奇怪的问题时,往往是电源或接地的问题。学会用示波器看关键信号,用逻辑分析仪解码通信协议,能解决 90% 的硬件故障。