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,表示主控即将读取。这是快速区分读写序列的第一眼判断依据。
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位地址解析,导致设备地址偏移。
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)的出现。如果主控在发送寄存器地址后没有产生重复起始条件,而是直接发送停止条件,则读操作不会发生。这是驱动代码中常见的逻辑错误——忘记在写地址后重新发送起始条件。
4.4 地址冲突与调试案例
在触摸屏调试中,I2C地址冲突是导致设备无法响应的常见原因之一。以下列出几种典型场景及排查方法:
- 地址重复: 同一I2C总线上挂载了两个相同地址的从机,导致应答冲突。排查方法:逐一断开从机,观察SCL/SDA波形上的应答位是否正常。
- 地址偏移: 驱动代码中写入的地址未左移1位(7位地址未转换为8位首字节),导致发送的地址错误。例如,7位地址0x38应左移为0x70,若直接发送0x38则无法匹配任何从机。
- 读/写位错误: 在需要读操作时发送了写方向的首字节,导致从机不返回数据。排查方法:检查逻辑分析仪上首字节的LSB是否为1。
快速自查清单:
- 确认触控IC数据手册中的7位地址值(如0x38、0x70)。
- 检查驱动代码中地址是否左移1位(7位地址 << 1)。
- 在逻辑分析仪上捕获起始条件后的首字节,验证高7位是否匹配目标地址,LSB是否匹配操作方向。
- 若使用10位地址,确认首字节高5位是否为11110,并检查第二字节的低8位。
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 = ®_addr;
i2c_transfer(client->adapter, &msg, 1);
/* 读操作:读取数据 */
msg