摘要:I2C 总线突然挂死,SDA 线被拉低,无论怎么复位 MCU 都没用?这不是芯片坏了,而是 I2C 从机的“时钟延展”机制卡死了。本文给出 3 种硬件级的自救方案。


一、问题描述(现象)

**系统运行几天后,I2C 通讯突然中断;

示波器一看,SDA 一直为低电平,SCL 为高;

复位 MCU、重新上电都没用,只有拔掉从机电源才行。**

很多工程师的第一反应是:

  1. 从机芯片坏了?

  2. 程序跑飞了?

  3. 换一块板子试试?


二、原理分析(为什么会卡死?)

1. 物理模型

I2C 从机在接收数据时,如果处理不过来,会主动拉低 SCL(时钟延展)。

正常: SCL ___|‾‾‾|___|‾‾‾|___
卡死: SCL ___|‾‾‾|________ (一直等 ACK)
       SDA ____________________ (被从机拉低)

2. 核心参数

  • Clock Stretching(时钟延展):从机拉低 SCL 等待主机。

  • Bus Free Time:总线空闲时间。

3. 反直觉真相

死锁通常发生在“异常终止”时。

比如:

  • MCU 在发送过程中断电。

  • 程序跑飞,I2C 状态机停在中间。

  • 从机正在等 ACK,主机却没了。

结果:​ 从机还在等时钟,主机已经不发了,SDA 被永久拉低。


三、工程级解决方案(按推荐顺序)

方案 1:硬件自愈(最推荐,量产必加)

在 SDA/SCL 线上加一个复位电路

电路设计:

SDA -> [100Ω] -> [MOSFET Gate]
SCL -> [100Ω] -> [MOSFET Gate]
         |
        [N-MOS]
         |
        GND

控制逻辑:

  • 检测到 I2C 超时(如 100ms 无响应)。

  • MCU 拉高 MOS 管 Gate,强制将 SDA/SCL 拉低。

  • 延时 10us,释放。

  • 重新初始化 I2C 外设。

方案 2:GPIO 位拆(软件急救)

如果硬件改不了,用 GPIO 手动解锁。

// 1. 关闭 I2C 外设
__HAL_I2C_DISABLE(&hi2c1);

// 2. 配置 SCL/SDA 为 GPIO 输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Pin = SCL_PIN | SDA_PIN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

// 3. 模拟 9 个时钟脉冲(I2C 规范推荐)
for (int i = 0; i < 9; i++) {
    HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_PIN, GPIO_PIN_RESET);
    delay_us(5);
    HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_PIN, GPIO_PIN_SET);
    delay_us(5);
}

// 4. 发送 STOP 条件
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_PIN, GPIO_PIN_SET);
delay_us(5);
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_PIN, GPIO_PIN_SET);

// 5. 恢复 I2C 外设
HAL_I2C_Init(&hi2c1);

方案 3:外部复位(最稳)

直接从机 RESET 引脚。

  • 如果 I2C 从机有 RESET 引脚,直接拉低 10us。

  • 这是物理层面的强制复位,100% 有效。


四、选型避坑建议

  1. 不要迷信软件复位HAL_I2C_MspInit()在 SDA 被拉低时通常无效。

  2. 上拉电阻:死锁后,上拉电阻无法把 SDA 拉高,必须从机释放。

  3. 超时机制:软件必须检测 I2C 超时,不能无限等待。


五、总结 Checklist

  • [ ] 是否遇到过“拔电才能恢复”的 I2C 故障?

  • [ ] 硬件上是否预留了强制拉低 SDA/SCL 的 MOS 管?

  • [ ] 软件是否有 I2C 超时检测与 GPIO 位拆逻辑?

  • [ ] 从机是否有 RESET 引脚可用?


六、写在最后(关注我,少走弯路)

我是 gqqsherry,一个拒绝调包、专注底层逻辑的嵌入式工程师。

I2C 死锁是工业现场最常见的“幽灵故障”,往往几个月才出现一次,一旦出问题就是致命的。

关注我的专栏《嵌入式底层避坑指南》,我会持续更新 UART、SPI、I2C 等外设的真实调试案例量产级解决方案

👉 下一篇预告:《I2C 地址扫描不到?别只怪从机,看看这 2 个隐藏地址位》


References

  • NXP UM10204 – I2C-bus specification and user manual

  • TI Application Report SLVA704 – I2C Bus Fault Recovery


如果你在项目中遇到过 I2C 死锁,欢迎在评论区分享你的解决经验。

原创文章,转载请注明出处。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐