4. I2C 地址与读写操作:7位地址与10位地址、读/写位、多字节传输格式

I2C 总线协议的核心在于其独特的寻址机制与读写控制方式。每个从设备都有一个唯一的地址,主设备通过地址来选择通信对象,并通过读/写位(R/W)决定数据传输方向。本章将深入解析 7 位与 10 位地址结构、读写位的作用以及多字节传输的完整格式,为 GPIO 模拟 I2C 驱动开发奠定协议基础。

4.1 7 位地址结构

标准 I2C 总线使用 7 位地址,最多可挂载 112 个从设备(保留 16 个地址用于特殊用途)。地址在起始条件(START)后由主设备发送,占用第一个字节的高 7 位,最低位为读写控制位(R/W)。

关键点: 7 位地址范围是 0x00 ~ 0x7F,其中 0x00 为通用呼叫地址,0x01 ~ 0x03 为保留地址,0x04 ~ 0x77 为用户可用地址,0x78 ~ 0x7F 为 10 位地址预留。
7 位地址帧格式(主设备发送) S A6 A5 A4 A3 A2 A1 A0 R/W ACK 数据字节(8 位) P S = 起始条件(SCL 高电平时 SDA 下降沿) P = 停止条件(SCL 高电平时 SDA 上升沿) | ACK = 从设备应答(SDA 低电平)

4.2 10 位地址结构

当系统需要挂载超过 112 个从设备时,I2C 规范定义了 10 位地址模式。10 位地址使用两个字节传输:第一个字节的高 5 位固定为 11110,后跟地址的高 2 位(A9、A8)和 R/W 位;第二个字节为地址的低 8 位(A7 ~ A0)。

驱动开发提示: 在 GPIO 模拟 I2C 驱动中,10 位地址需要发送两个字节的地址帧,且第一个字节的 R/W 位必须为 0(写方向),第二个字节后跟 ACK。如果使用 7 位地址模式,则只需发送一个地址字节。务必在驱动初始化时确认从设备地址类型。

4.3 读/写位(R/W)

R/W 位是地址字节的最低位,用于指示数据传输方向:

  • R/W = 0(写操作): 主设备向从设备发送数据。地址字节后,主设备继续发送数据字节,从设备每接收一个字节后回复 ACK。
  • R/W = 1(读操作): 主设备从从设备读取数据。地址字节后,从设备发送数据字节,主设备每接收一个字节后回复 ACK(最后一个字节回复 NACK 以结束传输)。
常见误区: 许多初学者误以为 R/W 位是地址的一部分。实际上,R/W 位独立于地址,它决定的是总线上的数据流方向。在驱动代码中,地址左移 1 位后,最低位写入 R/W 值:addr << 1 | rw_flag。
读/写操作对比 写操作(R/W = 0) 主设备 → 从设备:地址 + 数据字节 主设备控制 SDA,从设备每字节回复 ACK 读操作(R/W = 1) 主设备 → 从设备:地址;从设备 → 主设备:数据 从设备控制 SDA,主设备每字节回复 ACK/NACK 注意:读操作通常需要先发送写地址(R/W=0)指定寄存器,再发送重复起始条件(Sr)切换为读地址(R/W=1)。 这种“写地址 + 重复起始 + 读地址”模式称为组合传输(Combined Transfer)。

4.4 多字节传输格式

I2C 支持在一个传输事务中连续发送或接收多个字节。多字节传输格式分为 写多字节 和 读多字节 两种场景。

4.4.1 写多字节传输

主设备发送起始条件后,依次发送从设备地址(R/W=0)、寄存器地址(可选)、多个数据字节,最后发送停止条件。每个字节后从设备回复 ACK。

// 写多字节传输时序(伪代码)
START();
send_byte(device_addr << 1 | 0);  // 写地址
wait_ack();
send_byte(reg_addr);               // 寄存器地址
wait_ack();
send_byte(data[0]);                // 数据字节 1
wait_ack();
send_byte(data[1]);                // 数据字节 2
wait_ack();
// ... 更多数据字节 ...
send_byte(data[N-1]);              // 数据字节 N
wait_ack();
STOP();

4.4.2 读多字节传输

读多字节通常采用组合传输:先写寄存器地址,再重复起始条件切换为读方向,连续读取多个字节。主设备在最后一个字节回复 NACK 以通知从设备停止发送。

// 读多字节传输时序(伪代码)
START();
send_byte(device_addr << 1 | 0);  // 写地址(指定寄存器)
wait_ack();
send_byte(reg_addr);               // 寄存器地址
wait_ack();
REPEATED_START();                  // 重复起始条件
send_byte(device_addr << 1 | 1);  // 读地址
wait_ack();
data[0] = read_byte();            // 读取字节 1
send_ack();                        // 回复 ACK
data[1] = read_byte();            // 读取字节 2
send_ack();                        // 回复 ACK
// ... 更多数据字节 ...
data[N-1] = read_byte();          // 读取字节 N
send_nack();                       // 回复 NACK(结束)
STOP();
驱动实现注意事项: 在 GPIO 模拟 I2C 驱动中,多字节传输的时序必须严格遵循 SCL 时钟周期。每个字节的 8 个数据位和 1 个应答位都需要精确的延时控制。特别是重复起始条件(Sr)的时序:在 SCL 低电平时释放 SDA,然后在 SCL 高电平时将 SDA 拉低,与普通起始条件相同。

4.5 地址与读写操作总结

字节序号 位 7 位 6 位 5 位 4 位 3 位 2 位 1 位 0 说明
第 1 字节 1 1 1 1 0 A9 A8 R/W 10 位地址标识 + 高 2 位地址
第 2 字节 A7 A6 A5 A4 A3 A2 A1 A0 低 8 位地址
特性 7 位地址 10 位地址
地址字节数 1 字节(高 7 位为地址) 2 字节(第 1 字节标识 + 高 2 位,第 2 字节低 8 位)
最大设备数 112 个 1024 个
R/W 位位置 地址字节的最低位 第 1 字节的最低位
兼容性 所有 I2C 设备均支持 仅支持 10 位地址模式的设备
驱动复杂度 低 中(需发送两个地址字节)