目录

硬件 I2C 调试技巧:逻辑分析仪抓包实战

搞嵌入式开发的朋友都知道,I2C 这协议说起来简单,就两根线(SDA 和 SCL),真用起来能把你折腾得怀疑人生。设备不响应、数据读不对、时序有问题……今天我就用逻辑分析仪实战演示一下,怎么把这些奇奇怪怪的 I2C 问题一个个揪出来。

需要准备什么?

物品 型号/规格 价格
逻辑分析仪 Saleae Logic 8 / 国产克隆版 ¥80-300
I2C 设备 MPU6050 加速度计 ¥15
开发板 CH559 开发板 ¥25
杜邦线 公对母 10cm ¥5
面包板 迷你型 ¥8
**总计** **¥133-253**

逻辑分析仪不一定要买原版 Saleae,国产克隆版几十块钱就能搞定,日常调试完全够用。我手上这台是 8 通道的,测 I2C 绰绰有余。

步骤 1:搭建测试环境

先把硬件连起来。I2C 接线很简单:

CH559          MPU6050
─────          ───────
3.3V    →      VCC
GND     →      GND
P1.4(SCL) →    SCL
P1.5(SDA) →    SDA

注意事项: ⚠️

  • I2C 需要上拉电阻!大多数模块已经内置了 4.7kΩ上拉,如果没有,记得在 SDA 和 SCL 上各接一个 4.7kΩ电阻到 VCC
  • 电压要匹配!CH559 是 3.3V 逻辑,MPU6050 也是 3.3V,如果接 5V 设备需要电平转换
  • 逻辑分析仪的地线一定要和被测电路共地,不然抓不到信号

接下来安装逻辑分析仪软件。国产克隆版通常用 Sigrok/PulseView:

# Ubuntu/Debian
sudo apt-get install pulseview

# Fedora
sudo dnf install pulseview

# 或者下载 Saleae 官方软件(原版用户)
# https://www.saleae.com/downloads

步骤 2:编写 I2C 测试代码

我们用 CH559 写个简单的 I2C 读写程序。CH559 的硬件 I2C 在 P1.4 和 P1.5 引脚:

#include 
#include 

// I2C 引脚定义
sbit I2C_SDA = P1^5;
sbit I2C_SCL = P1^4;

// MPU6050 地址(7 位地址,0x68)
#define MPU6050_ADDR 0x68
#define WHO_AM_I_REG 0x75

// I2C 延时(根据实际时钟调整)
void I2C_Delay() {
    _nop_();
    _nop_();
}

// I2C 起始信号
void I2C_Start() {
    I2C_SDA = 1;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 0;
    I2C_Delay();
    I2C_SCL = 0;
}

// I2C 停止信号
void I2C_Stop() {
    I2C_SDA = 0;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 1;
    I2C_Delay();
}

// I2C 发送一个字节
uint8_t I2C_SendByte(uint8_t dat) {
    uint8_t i, ack;
    for (i = 0; i < 8; i++) {
        I2C_SDA = (dat >> 7) & 0x01;
        dat <<= 1;
        I2C_SCL = 1;
        I2C_Delay();
        I2C_SCL = 0;
        I2C_Delay();
    }
    // 读取应答
    I2C_SDA = 1;
    I2C_SCL = 1;
    I2C_Delay();
    ack = I2C_SDA;
    I2C_SCL = 0;
    I2C_Delay();
    return ack;
}

// I2C 接收一个字节
uint8_t I2C_RecvByte(uint8_t ack) {
    uint8_t i, dat = 0;
    I2C_SDA = 1;
    for (i = 0; i < 8; i++) {
        dat <<= 1;
        I2C_SCL = 1;
        I2C_Delay();
        dat |= I2C_SDA;
        I2C_SCL = 0;
        I2C_Delay();
    }
    // 发送应答
    I2C_SDA = ack ? 1 : 0;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SCL = 0;
    I2C_Delay();
    I2C_SDA = 1;
    return dat;
}

// I2C 写寄存器
void I2C_WriteReg(uint8_t reg, uint8_t dat) {
    I2C_Start();
    I2C_SendByte(MPU6050_ADDR << 1);  // 写地址
    I2C_SendByte(reg);
    I2C_SendByte(dat);
    I2C_Stop();
}

// I2C 读寄存器
uint8_t I2C_ReadReg(uint8_t reg) {
    uint8_t dat;
    I2C_Start();
    I2C_SendByte(MPU6050_ADDR << 1);  // 写地址
    I2C_SendByte(reg);
    I2C_Start();                       // 重复起始
    I2C_SendByte((MPU6050_ADDR << 1) | 0x01);  // 读地址
    dat = I2C_RecvByte(1);  // NACK
    I2C_Stop();
    return dat;
}

void main() {
    uint8_t who_am_i;

    // 配置时钟(12MHz)
    CCLK_CFG = 0x00;

    // 配置 GPIO(开漏输出)
    P1_MOD_OC |= 0x30;  // P1.4/P1.5 开漏
    P1_DIR_PU |= 0x30;  // P1.4/P1.5 输入

    // 初始化串口(用于调试输出)
    mInitSTDIO();

    printf("I2C Test Starting...\n");

    while (1) {
        // 读取 WHO_AM_I 寄存器(应该是 0x68)
        who_am_i = I2C_ReadReg(WHO_AM_I_REG);
        printf("WHO_AM_I: 0x%02X\n", who_am_i);

        if (who_am_i == 0x68) {
            printf("MPU6050 detected!\n");
        } else {
            printf("Device not found!\n");
        }

        DelayMs(1000);
    }
}

编译烧录:

# 安装 SDCC
sudo apt-get install sdcc

# 编译
sdcc --code-loc 0x0000 --xram-loc 0x0000 main.c

# 烧录(使用 wchisp)
wchisp flash main.bin

步骤 3:逻辑分析仪抓包

硬件和代码都准备好了,现在重头戏来了——抓包分析!

  1. 连接探头:把逻辑分析仪的通道 0 接 SCL,通道 1 接 SDA,GND 接电路地线

  2. 配置采样率:I2C 标准模式是 100kHz,快速模式 400kHz。采样率至少是信号频率的 10 倍,我一般设 1MHz 或更高

  3. 设置触发:选"下降沿触发",通道 0(SCL),这样能从起始信号开始抓

  4. 开始捕获:运行程序,等几秒后停止捕获

在 PulseView 里你会看到这样的波形:

SCL: ──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──
       └──┘  └──┘  └──┘  └──┘  └──┘  └──┘  └──┘

SDA: ──┐      ┌────────────────────────────┐  ┌──
   START  └──┘  地址 + 读写位 + 应答 + 数据  └──┘ STOP

PulseView 有 I2C 协议解码器,加载后会自动解析出:

  • 起始/停止条件
  • 设备地址(0x68)
  • 读写方向
  • 寄存器地址
  • 数据内容
  • ACK/NACK 状态

步骤 4:分析波形找问题

正常波形特征

正常的 I2C 通信应该满足:

  • SCL 高电平和低电平时间基本对称(50% 占空比)
  • SDA 只在 SCL 低电平时变化(除了起始/停止条件)
  • 每个字节后有 ACK(SDA 被拉低)
  • 起始条件:SCL 高电平时 SDA 下降沿
  • 停止条件:SCL 高电平时 SDA 上升沿

常见问题波形

问题 1:没有 ACK

波形表现:第 9 个时钟周期 SDA 保持高电平(没有被拉低)

可能原因:

  • 设备地址错误(MPU6050 是 0x68,不是 0x69)
  • 设备没供电或损坏
  • 上拉电阻缺失或阻值太大
  • 接线松动

问题 2:数据读回来全是 0xFF 或 0x00

波形表现:SDA 一直高或一直低

可能原因:

  • 上拉电阻太大/太小
  • 总线电容太大(线太长)
  • 设备处于错误状态,需要复位

问题 3:时序错乱

波形表现:SDA 在 SCL 高电平时变化

可能原因:

  • 代码延时不对
  • 中断干扰了 I2C 时序
  • 使用了错误的 GPIO 模式(应该用开漏)

常见问题排查

问题 1:设备地址不对,一直 NACK

  • 原因: I2C 地址有 7 位和 8 位两种表示法。MPU6050 的 7 位地址是 0x68,但有些库用 8 位地址(左移一位 + 读写位)
  • 解决: 确认你的代码用的是 7 位地址。写操作时左移一位(0xD0),读操作时左移一位 +1(0xD1)

问题 2:波形毛刺很多

  • 原因: 接地不良、线太长、附近有干扰源
  • 解决: 缩短杜邦线、确保共地、远离电机/继电器等干扰源

问题 3:逻辑分析仪抓不到信号

  • 原因: 探头没接好、触发电平设置不对、采样率太低
  • 解决: 检查 GND 连接、调整阈值电压(3.3V 逻辑设 1.5V 左右)、提高采样率

问题 4:偶尔能读到数据,偶尔读不到

  • 原因: 时序余量不足、电源不稳定
  • 解决: 增加 I2C 延时、加去耦电容、检查电源质量

总结

I2C 调试的核心就是"看到"信号。没有逻辑分析仪的时候,我们只能猜:"是不是地址错了?""是不是时序有问题?"有了逻辑分析仪,一切都在眼前,问题一目了然。

几点经验总结:

  1. 先抓波形,再改代码——不要盲目改代码,先看波形对不对
  2. 从简单开始——先读 WHO_AM_I 这种固定值的寄存器,确认通信正常再读其他
  3. 注意上拉电阻——这是最容易被忽视的问题
  4. 保存波形截图——遇到问题截图保存,方便对比分析

最后提醒一句,逻辑分析仪不一定要买贵的。淘宝上几十块的国产克隆版,日常调试 I2C/SPI/UART 完全够用。让它发热吧,别发光就好——稳定工作比什么都重要。

希望这篇博客文章对您有所帮助!


相关资源:

更多关于 的文章
关注创客出手公众号

关注创客出手