什么是I2C死锁
一种I2C通讯停止工作的现象
具体表现为SDA数据线一直处于低电平状态
什么时候会发生I2C死锁
不会发生I2C死锁的情况
SCL时钟线, SDA数据线完全交由主机控制时, 通常不会发生I2C死锁
可能会发生I2C死锁的情况
既然主机操控SDA时不会发生死锁(SCL由主机控制), 那么发生死锁的情况只能是从机导致的
如果从机错误的操纵SDA数据线则可能造成I2C死锁(SDA死锁)
如果从机(特定情况下从机也会操纵SCL时钟线, 详见下文)或主机错误的操纵SCL时钟线也可能造成I2C死锁(SCL死锁)
为什么会发生I2C死锁
SDA死锁
从机拉低SDA数据线有以下两种情况:
- 发送bit0
- 应答ACK
I2C有规定: 在SCL位于高电平期间, SDA是不能进行更改的
如果在SCL位于高电平期间, 从机拉低SDA时, 主机意外复位或断电重启
此时从机识别到SCL时钟线一直为高电平, 就会一直拉低SDA数据线
而主机I2C硬件识别到SDA数据线一直处于低电平状态, 会认为I2C总线一直处于busy状态, 就会一直不拉低SCL时钟线(死等)
因此造成SDA死锁
而如果是SDA位于高电平时主机意外复位或断电重启则不会进入死锁, 只是这一帧的数据会出错(主机都复位或重启了 你不出错谁出错…)
SCL死锁
通常主机和从机通信过程中, 会通过接收数据FIFO来接收数据, 如果该FIFO满时, 则会主动拉低SCL总线(从机也会拉低SCL, 即使该SCl是由主机操控)来通知另一方停止发送数据, 等接收完数据后再重新释放SCL, 一遍另一方继续发送数据 这种方式称为I2C时钟延展
但如果通信双方有其中一方没有处理这种情况则可能会导致SCL死锁

[^STM32F403RCT6与MLX90640进行I2C通信]: D0为SCL时钟线, D1为SDA数据线
可分为四段来看, 前三段推测是一次性通信数据过大造成的死锁情况, 第四段则是正常分两段数据发送的情况(其实是两个两段数据, 共四组数据):
- 这里SDA死锁时位于高电平还是低电平影响因素过多, 这里先理解为随机
- 接收数据FIFO满时 主动拉低SCL, 然后SDA等待下一个SCL高电平(下一个时钟)
这里实测就算模拟I2C的SCL信号也不能使从机继续发送数据, 但是能使SDA数据线重新回到高电平
可以通过主机拉高SCL所以结论是主机的问题, 进一步推测是主机FIFO满导致
死锁时SDA位于高电平时查看主机的I2C_SR1寄存器, 值为0x40(发送寄存器空), I2C_SR2寄存器值为0x3(总线busy, 主机位于发送模式)
死锁时SDA位于低电平时两个寄存器值为0x0, 0x2(总线busy)
具体问题触发原因不明, 欢迎交流
如何避免/解决I2C死锁
SDA死锁
- 当主机识别到I2C死锁时, 直接复位从机
- 选择带有看门狗复位的I2C器件, 自动复位
- 主机识别到I2C死锁后, 将SCL/SDA引脚配置为GPIO模式, SCL输出信号模拟I2C时钟, 一边SDA读取电平, 一旦SDA读取到高电平就控制SCL模拟结束信号, 该方式称为**”最多发送9clock恢复I2C总线”, 常用**(为什么是9clock, 因为8个数据位+一个ack位刚好9位)
SCL死锁
- 唯一且有效的方法就是复位从机