4. I2C数据传输:主写从读、主读从写、复合传输、重复起始条件

数据传输是I2C协议的核心。说白了,总线上的所有操作,最终都归结为「谁写谁读」的问题。我刚开始接触I2C时,总觉得协议很绕——又是起始条件,又是应答,还要区分读写方向。但后来发现,只要理清四种基本传输模式,剩下的都是套路。

这一节,我们就把这四种模式掰开揉碎。你想想看,主设备要跟从设备说话,无非就这几种情况:

  • 主写从读:主设备发数据,从设备收数据
  • 主读从写:主设备收数据,从设备发数据
  • 复合传输:一次传输里既有写又有读
  • 重复起始条件:不释放总线,直接开始下一轮传输

嗯,咱们一个一个来看。

4.1 主写从读:最基础的传输

这是I2C最常用的模式。主设备发送数据给从设备,比如配置传感器寄存器、写入显示缓冲区等。

流程其实很简单:

  1. 主设备发送起始条件(S)
  2. 发送从设备地址 + 写位(0)
  3. 等待从设备应答(ACK)
  4. 发送数据字节,每字节后等待应答
  5. 发送停止条件(P)

我在项目中遇到过一个问题:某个温度传感器,写入配置寄存器后死活不响应。后来发现,从设备在收到每个字节后需要一点时间处理,而我连续发送太快了。解决办法是在每字节后加一个微秒级的延时,或者检查SCL时钟延展。

关键点:主写模式下,数据方向是从主设备到从设备。从设备通过拉低SDA来应答,表示「我收到了」。

4.2 主读从写:读取从设备数据

这个模式用于主设备从从设备读取数据,比如读取传感器数值、读取存储器内容。

流程稍微有点不同:

  1. 主设备发送起始条件(S)
  2. 发送从设备地址 + 读位(1)
  3. 等待从设备应答(ACK)
  4. 主设备接收数据字节,每字节后发送应答
  5. 最后一个字节,主设备发送非应答(NACK)
  6. 发送停止条件(P)

这里有个坑:最后一个字节必须发NACK。为什么?因为从设备看到NACK就知道「主设备不想再要数据了」,然后释放SDA总线。如果你发了ACK,从设备会继续发数据,总线就乱套了。

我曾经在调试一个加速度计驱动时,忘了在最后一个字节发NACK。结果从设备一直发数据,主设备一直收,最后总线挂死。查了两天才找到原因——嗯,从那以后我写驱动都会在接收循环里单独处理最后一个字节。

4.3 复合传输:一次传输搞定读写

很多时候,我们需要先写再读。比如读取某个寄存器:先写入寄存器地址,再读取寄存器值。

复合传输就是干这个的。它把写和读合并到一次传输里,中间用重复起始条件(Sr)连接,不释放总线。

流程是这样的:

  1. 起始条件(S)
  2. 从设备地址 + 写位(0)
  3. 发送寄存器地址(写操作)
  4. 重复起始条件(Sr)
  5. 从设备地址 + 读位(1)
  6. 读取寄存器值(读操作)
  7. 停止条件(P)

你看,中间没有停止条件,总线一直被主设备占用。这样做的好处是:原子性。其他主设备无法在中间插入操作,保证了数据传输的完整性。

我个人习惯:在RTOS驱动中,复合传输一定要用临界区保护。因为如果被任务切换打断,重复起始条件可能发不完整,总线状态就乱了。

4.4 重复起始条件:不释放总线的艺术

重复起始条件(Repeated Start,简称Sr)是I2C协议里一个很巧妙的设计。它允许主设备在不发送停止条件的情况下,直接开始新一轮传输。

为什么要这么做?

  • 防止总线被抢占:如果发了停止条件,其他主设备可能抢占总线
  • 提高效率:省去了停止和重新开始的时间
  • 保证原子性:多步操作不会被中断

时序上,重复起始条件和普通起始条件完全一样:SCL高电平时,SDA从高变低。区别只在于它前面没有停止条件。

我记得在调试一个多主设备系统时,两个主设备同时访问同一个从设备。如果不使用重复起始条件,一个主设备发了停止条件后,另一个主设备立刻抢占了总线,导致数据错乱。改用重复起始条件后,问题就解决了。

4.5 四种模式对比

为了方便记忆,我整理了一个对比表:

传输模式 数据方向 起始条件 停止条件 典型应用
主写从读 主→从 1次 1次 配置寄存器、写存储器
主读从写 从→主 1次 1次 读取传感器、读存储器
复合传输 先主→从,后从→主 1次起始 + 1次重复起始 1次 先写地址再读数据
重复起始 任意 多次(含Sr) 最后1次 多步原子操作

4.6 知识结构图

下面这张图展示了四种传输模式的关系和流程:

I2C四种传输模式流程图 I2C四种传输模式 主写从读 S + 地址(W) + 数据 + P 主读从写 S + 地址(R) + 数据 + NACK + P 复合传输 S + 地址(W) + 数据 + Sr + 地址(R) + 数据 + P 重复起始条件 ...数据 + Sr + 地址... S = 起始条件 | Sr = 重复起始条件 | P = 停止条件 | W = 写位(0) | R = 读位(1) 四种模式核心区别 主写从读:主设备主动发送,从设备被动接收 主读从写:主设备主动接收,从设备被动发送 复合传输:写+读合并,中间用Sr连接,不释放总线 重复起始:不发送停止条件,直接开始新一轮传输 注意:复合传输和重复起始条件经常一起使用,但概念不同。复合传输是「做什么」,重复起始是「怎么做」。

4.7 RTOS驱动中的实现要点

在RTOS环境下实现这些传输模式,有几个地方要特别注意:

  • 超时处理:从设备可能因为忙而拉低SCL(时钟延展),驱动必须设置超时,防止死等
  • 中断与轮询:传输过程中,建议用中断方式处理每字节的收发,避免阻塞任务调度
  • 错误恢复:如果从设备无应答,驱动应该能发送停止条件复位总线,而不是卡死
  • 缓冲区管理:复合传输涉及多次数据交换,缓冲区要提前规划好,避免溢出

我建议:在驱动层封装一个统一的传输接口,把四种模式抽象成参数。上层应用只需要告诉驱动「我要做什么」,不用关心底层时序。这样代码复用率高,也容易维护。

好了,四种传输模式就讲到这里。理解了这些,I2C协议的基本操作你就掌握了八成。剩下的两成,是各种细节和坑——我们后面章节慢慢聊。