Jasonangel

V1

2022/12/10阅读:17主题:默认主题

I2C 死锁及恢复方法

最近在项目中遇到了 I2C 死锁问题,发出来给大家看看。

I2C 是常用的串行通信协议,常见于主控和一些外设的通信中,比如主控和外部 EEPROM、传感器等的通信都会用到 I2C 通信。

在实际使用过程中,I2C 比较容易出现的一个问题就是死锁,今天就来介绍一下 I2C 死锁产生的原因以及如何解决死锁问题。

I2C 的通信协议

I2C 由串行数据线 SDA 和串行时钟线 SCL 组成,SDA 和 SCL 在空闲时都处于高电平状态。SCL 由主设备来产生,SDA 上既可以传输主设备发给从设备的数据,也可以传输从设备发给主设备的数据。从设备在接收完一个字节的数据后,需要在第九个 clock 回复给主设备一个 ACK 信号,此时从设备会把 SDA 拉低,等待主设备控制 SCL 从高到低取走 ACK 位。

死锁的产生常见的有两种情况:

  1. 从设备在回复 ACK 时主设备异常复位;
  2. 从设备在回复数据位是 0 的时候主设备异常复位。

两种情况的相同点都是主设备异常复位时 SDA 处于被从设备拉低状态,而主设备复位后 SCL 处于高电平状态(空闲状态)。此时从设备会等待主设备拉低 SCL 取走 ACK 或者数据位,而主设备会等待从设备释放 SDA 线。主设备和从设备互相等待,隔空对望,进入死锁状态。 解决死锁问题的几种常见方法:

  1. 主设备在检测到 SDA 被拉低超过一段时间后,主动复位从设备从而使之释放 SDA。这种方法的前提是从设备有复位引脚,主控可以控制从设备的复位引脚使之复位。
  2. 主设备在检测到 SDA 被拉低超过一段时间后,推送 9 个 Clock 到时钟总线上,取走从设备的 ACK 位从而使从设备释放 SDA 为高电平。
  3. 在主从设备之间串联一个 I2C 缓冲器,该缓冲器可以自动检测死锁状态。当检测到死锁时会主动断开与主设备的连接,并发送 9 个 Clock 给从设备,等从设备释放 SDA 线后从新与主设备建立连接。

I2C 的死锁问题无法从根本上避免,除了主控的异常复位导致 I2C 死锁,从设备在正常通信过程中也有可能异常拉低 SDA 导致死锁。所以软件在设计时要考虑当死锁发生时要能够从死锁中恢复,使得 I2C 通信可以继续进行。

博主遇到的问题

我们平台 I2C 默认都是用 DMA 操作 FIFO 进行传输(不管几个字节);SPI 则是传输长度超过 32Bytes 才会启用 DMA,否则是 FIFO。

问题现象:主机在发送八个 clock 后,主机的 DMA 异常复位,导致 I2C 和 DMA 之间的握手断掉,I2C 发不出数据,这样主机没有发出第九个 clock。而此时从机拉低 SDA 线准备回 ACK 给主机,但是一直等不到第九个 clock,因此从机一直没有释放 SDA 线,后面 i2c 总线一直卡住,一直报 i2c timeout。

解决方案:修改了 i2c 控制器驱动,在每次发起 i2c 传输之前,检测 DMA 或者 i2c 控制器是否异常复位,若 DMA 有异常复位,则重新初始化一遍 i2c 硬件,让两者重新握手。

后续排查,怀疑是某个时刻设备进入了一个比较深的 low power state,系统对 DMA 进行了掉电复位。

note:这题是概率性问题,一般压测两天才会出现,因此抓波形也抓了很久才抓到。整个问题搞了一周才分析出根因。

分类:

移动端开发

标签:

移动端开发

作者介绍

Jasonangel
V1