3. I2C 寻址与数据传输:7位/10位地址模式、读写操作流程、多主机仲裁机制

好,咱们今天聊点硬核的。I2C 总线上的设备怎么找到彼此?数据怎么传?多个主机抢总线怎么办?这些搞不清楚,写驱动就是瞎蒙。我刚开始调 I2C 的时候,就吃过地址不对的亏,折腾了两天才发现是 7 位和 8 位地址搞混了。

3.1 7位地址模式:最常用的寻址方式

I2C 总线上的每个从设备都有一个唯一地址。7 位地址模式,说白了就是地址范围从 0x00 到 0x7F,一共 128 个地址。但实际能用的没那么多,0x00 是通用呼叫地址,0x01 到 0x07 保留,0x78 到 0x7F 也是保留地址。真正给设备用的,大概 112 个。

关键点:7 位地址在发送时会被左移 1 位,最低位用来表示读写方向(0 = 写,1 = 读)。所以你在数据手册里看到的设备地址,通常是 7 位值,但实际发送的是 8 位。

举个例子。瑞芯微平台上常见的传感器,比如温度传感器 LM75,它的 7 位地址是 0x48。发送时变成 0x90(写)或 0x91(读)。我见过不少新手直接拿 0x90 当设备地址去配寄存器,结果死活不通。

我的习惯:在代码里统一用 7 位地址定义宏,然后在发送函数里左移。这样可读性强,也不容易出错。

3.2 10位地址模式:什么时候用?怎么用?

7 位地址不够用怎么办?I2C 规范定义了 10 位地址模式。它用两个字节来传输地址:第一个字节的前 5 位是 11110,后 2 位是地址的高 2 位;第二个字节是地址的低 8 位。

地址模式 地址范围 传输字节数 适用场景
7 位地址 0x00 - 0x7F 1 字节 大多数传感器、EEPROM
10 位地址 0x000 - 0x3FF 2 字节 地址冲突、多设备复杂系统

10 位地址模式在瑞芯微平台上用得不多,但也不是没有。我记得有一次调试一个多路复用器,它用了 10 位地址,我一开始没注意,按 7 位去配,结果总线一直报 NACK。后来查了数据手册才发现问题。

注意:10 位地址模式下,从设备必须支持该模式。不是所有 I2C 设备都支持 10 位地址。瑞芯微的 I2C 控制器支持 10 位地址,但需要在寄存器里显式配置。

3.3 读写操作流程:从 START 到 STOP

I2C 的数据传输,说白了就是一套固定的流程。我把它拆成几步,你照着做就行。

  1. START 条件:SCL 高电平时,SDA 从高变低。表示总线开始传输。
  2. 发送从设备地址:主机发送 7 位或 10 位地址 + 读写位。
  3. 等待 ACK:从设备在第 9 个时钟周期拉低 SDA,表示收到。
  4. 数据传输:每 8 位数据后跟一个 ACK/NACK。主机或从机发送数据,取决于读写方向。
  5. STOP 条件:SCL 高电平时,SDA 从低变高。表示传输结束。

写操作流程:主机发地址(写方向),然后发寄存器地址,再发数据。读操作流程:主机先发地址(写方向)选寄存器,然后重启 START,再发地址(读方向),从机开始发数据。

避坑指南:我曾经在瑞芯微 RK3399 上调试一个音频编解码器,读操作时忘了发重启条件,结果从机一直不回数据。后来加上重启,问题解决。记住:读操作需要两次地址传输,中间用重启隔开。

3.4 多主机仲裁机制:谁抢到算谁的

I2C 总线允许多个主机。但同一时间只能有一个主机控制总线。如果两个主机同时发数据,怎么办?仲裁机制来解决。

仲裁发生在 SDA 线上。多个主机同时发送数据时,谁先拉低 SDA,谁就赢得仲裁。因为 I2C 是开漏输出,低电平会覆盖高电平。输掉仲裁的主机必须释放总线,转为从机模式。

我的经验:多主机场景在瑞芯微平台上不常见,但也不是没有。比如系统里有两个处理器都要访问同一个传感器。这时候仲裁机制就很重要。我建议你在设计硬件时,尽量避免多主机,除非你有充分的理由。

仲裁的规则很简单:

  • 每个主机在发送数据时,同时监测 SDA 电平。
  • 如果发送的电平与监测到的电平不一致,说明仲裁失败。
  • 失败的主机立即停止发送,释放总线。
  • 赢得仲裁的主机继续传输,不受影响。

你想想看,如果两个主机同时发 0x90 和 0x88,第一个字节的高 4 位都是 0x9,不会冲突。但到了第 5 位,0x90 是 0,0x88 是 1,SDA 被拉低,发 0x88 的主机就输了。

3.5 知识结构图:I2C 寻址与数据传输

下面这张图把本章的核心逻辑串起来了。从地址模式到读写流程,再到仲裁机制,一目了然。

I2C 寻址与数据传输知识结构图 I2C 寻址与数据传输核心逻辑 7 位地址模式 10 位地址模式 多主机仲裁 读写操作流程 START → 地址 + 读写位 → ACK → 数据 → ACK/NACK → STOP 仲裁规则 谁先拉低 SDA 谁赢得仲裁 失败者释放总线 关键提醒 7 位地址左移 1 位 + 读写位 = 8 位发送地址 10 位地址需要 2 字节传输 读操作需要重启条件

3.6 实战要点总结

嗯,到这里,I2C 寻址和数据传输的核心内容就讲完了。我最后给你列几个实战中一定要记住的点:

  • 地址确认:拿到数据手册,先确认设备地址是 7 位还是 10 位。7 位地址在代码里左移 1 位再发。
  • 读写流程:写操作一次地址传输,读操作两次地址传输(中间加重启)。
  • 仲裁机制:多主机场景下,谁先拉低 SDA 谁赢。失败的主机自动释放总线。
  • 调试技巧:用逻辑分析仪抓波形,看 START、地址、ACK、数据、STOP 是否完整。我每次调 I2C 都先抓波形,省时间。

最后提醒:瑞芯微的 I2C 控制器寄存器里,有个地址模式配置位。如果你用 10 位地址,记得配这个位。不然控制器会按 7 位地址解析,数据全乱套。我曾经在这个坑里爬了整整一个下午。