4. I2C协议基础:I2C地址与读写操作(7位地址、10位地址、读/写位)

在触摸屏调试中,I2C总线是主控与触控IC之间最常用的通信接口。理解I2C地址结构与读写操作,是正确解析总线信号、定位设备枚举失败或通信超时问题的前提。本节从7位地址、10位地址以及读/写位三个核心维度展开。

4.1 7位地址与读/写位

标准I2C总线使用7位从机地址,加上1位读/写控制位(R/W),构成一个完整的8位首字节。主控在发送起始条件后,首先发送这个字节,以选择目标从机并指示操作方向。

位序 7 (MSB) 6 5 4 3 2 1 0 (LSB)
功能 7位从机地址 (A6 – A0) R/W (0=写, 1=读)
示例 0 1 1 1 0 0 0 0 (写)

例如,某触控IC的7位地址为 0x70(二进制 1110000),则写操作首字节为 0xE0,读操作首字节为 0xE1。示波器抓取起始条件后的第一个字节,即可判断主控正在访问哪个设备以及操作方向。

调试要点: 在逻辑分析仪或示波器上观察I2C波形时,首字节的LSB(最低位)直接指示操作方向。若该位为0,表示主控即将写入;若为1,表示主控即将读取。这是快速区分读写序列的第一眼判断依据。
7位地址 + R/W位 首字节结构 A6 A5 A4 A3 A2 A1 A0 R/W 7位从机地址 (MSB优先) 0:写 / 1:读 示例:7位地址 0x70 → 写首字节 0xE0,读首字节 0xE1

4.2 10位地址格式

当系统中从机数量超过127个时,I2C规范定义了10位地址扩展。10位地址的传输需要两个首字节:第一个字节的高5位固定为 11110,接着是10位地址的高2位(A9、A8)和R/W位;第二个字节为10位地址的低8位(A7–A0)。

传输阶段 字节内容 说明
首字节 (SLA+W) 11110 A9 A8 R/W 高5位固定11110,A9/A8为地址高位,R/W=0
第二字节 A7 A6 A5 A4 A3 A2 A1 A0 10位地址的低8位

例如,10位地址 0x2B4(二进制 10 1011 0100)的写操作序列为:首字节 0xF4(11110 10 0),第二字节 0xB4(1011 0100)。读操作时,主控需先发送写方向的首字节+第二字节,然后重新发送起始条件,再发送读方向的首字节(R/W=1)。

常见陷阱: 在触摸屏调试中,绝大多数触控IC使用7位地址(如0x38、0x70、0x5A等)。10位地址多见于高端触控控制器或FPGA扩展场景。如果逻辑分析仪解析出的地址长度异常(如首字节高5位为11110),应检查是否误将10位地址当作7位地址解析,导致设备地址偏移。
10位地址写操作首字节序列 11110 A9 A8 R/W A7 A6 A5 A4 A3 A2 A1 A0 首字节 (SLA+W) 第二字节 (地址低8位) 示例:10位地址 0x2B4 (10 1011 0100) 首字节 = 11110 10 0 = 0xF4,第二字节 = 1011 0100 = 0xB4

4.3 读写操作流程对比

I2C读写操作在时序上的核心差异在于:写操作是主控发送数据到从机,读操作是从机发送数据到主控。主控通过控制R/W位和应答信号来管理数据流向。

  • 写操作流程: 起始条件 → 发送从机地址+R/W=0 → 等待从机应答(ACK) → 发送寄存器地址 → 等待ACK → 发送数据字节 → 等待ACK → ... → 停止条件。
  • 读操作流程: 起始条件 → 发送从机地址+R/W=0 → 等待ACK → 发送寄存器地址 → 等待ACK → 重复起始条件 → 发送从机地址+R/W=1 → 等待ACK → 接收数据字节 → 主控发送NACK → 停止条件。
操作 首字节R/W 数据方向 应答控制方 典型应用
写 (Write) 0 主控 → 从机 从机发送ACK 配置寄存器、发送命令
读 (Read) 1 从机 → 主控 主控发送ACK/NACK 读取触摸坐标、状态寄存器
调试技巧: 在逻辑分析仪上观察读操作时,注意重复起始条件(Sr)的出现。如果主控在发送寄存器地址后没有产生重复起始条件,而是直接发送停止条件,则读操作不会发生。这是驱动代码中常见的逻辑错误——忘记在写地址后重新发送起始条件。
I2C读写操作流程对比 写操作 (R/W=0) 起始 地址+R/W=0 ACK 寄存器地址 ACK 数据字节 停止 读操作 (R/W=1) 起始 地址+R/W=0 ACK 寄存器地址 ACK 重复起始 地址+R/W=1 ACK 数据字节 NACK 停止

4.4 地址冲突与调试案例

在触摸屏调试中,I2C地址冲突是导致设备无法响应的常见原因之一。以下列出几种典型场景及排查方法:

  • 地址重复: 同一I2C总线上挂载了两个相同地址的从机,导致应答冲突。排查方法:逐一断开从机,观察SCL/SDA波形上的应答位是否正常。
  • 地址偏移: 驱动代码中写入的地址未左移1位(7位地址未转换为8位首字节),导致发送的地址错误。例如,7位地址0x38应左移为0x70,若直接发送0x38则无法匹配任何从机。
  • 读/写位错误: 在需要读操作时发送了写方向的首字节,导致从机不返回数据。排查方法:检查逻辑分析仪上首字节的LSB是否为1。
快速自查清单:
  1. 确认触控IC数据手册中的7位地址值(如0x38、0x70)。
  2. 检查驱动代码中地址是否左移1位(7位地址 << 1)。
  3. 在逻辑分析仪上捕获起始条件后的首字节,验证高7位是否匹配目标地址,LSB是否匹配操作方向。
  4. 若使用10位地址,确认首字节高5位是否为11110,并检查第二字节的低8位。
地址冲突排查流程 捕获I2C起始条件 解析首字节高7位 匹配数据手册地址 检查LSB读写方向 通过 若首字节高7位与预期不符,检查地址左移或10位地址格式

4.5 代码示例:Linux内核中I2C地址设置

在嵌入式Linux驱动中,I2C设备地址通常通过设备树或板级文件配置。以下是一个典型的触控IC设备树节点示例:

/* 设备树中I2C设备节点示例 */
&i2c2 {
    status = "okay";
    clock-frequency = <400000>;  /* 400kHz快速模式 */

    touchscreen@38 {
        compatible = "vendor,ts-ic";
        reg = <0x38>;           /* 7位I2C地址,驱动内部左移 */
        interrupt-parent = <&gpio1>;
        interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
    };
};

在驱动代码中,I2C地址的使用方式如下:

/* 驱动中I2C消息构造示例 */
struct i2c_msg msg;
unsigned char addr = 0x38;      /* 7位地址 */
unsigned char reg_addr = 0x00;  /* 寄存器地址 */
unsigned char data;
/* 写操作:设置寄存器地址 */
msg.addr = addr;                /* 驱动框架自动左移1位 */
msg.flags = 0;                  /* 写标志 */
msg.len = 1;
msg.buf = &reg_addr;
i2c_transfer(client->adapter, &msg, 1);
/* 读操作:读取数据 */
msg