1. I2C总线基础

I2C(Inter-Integrated Circuit)总线由 Philips 公司(现 NXP)于 1980 年代推出,是一种同步、多主从、串行通信总线。在 Android 设备中,I2C 广泛用于连接传感器、触控芯片、电源管理 IC、音频编解码器等低速外设。理解 I2C 物理层与协议是驱动开发的第一道门槛。

1.1 物理层与电气特性

I2C 总线仅使用两条双向信号线:SCL(串行时钟线)和 SDA(串行数据线)。所有设备通过开漏输出(Open-Drain)连接到这两条线上,并通过上拉电阻接至 VDD(通常为 1.8V、3.3V 或 5V)。

参数 标准模式(100 kHz) 快速模式(400 kHz) 高速模式(3.4 MHz)
最大时钟频率 100 kHz 400 kHz 3.4 MHz
总线最大电容 400 pF 400 pF 100 pF
上拉电阻典型值 4.7 kΩ 2.2 kΩ 1.0 kΩ
逻辑低电平(VIL) ≤ 0.3 × VDD ≤ 0.3 × VDD ≤ 0.3 × VDD
逻辑高电平(VIH) ≥ 0.7 × VDD ≥ 0.7 × VDD ≥ 0.7 × VDD
驱动开发提示:在 Android 内核 DTS(设备树)中配置 I2C 控制器时,clock-frequency 属性必须与硬件上拉电阻匹配。例如,400 kHz 总线若使用 4.7 kΩ 上拉电阻,可能导致上升沿过缓,造成通信失败。

1.2 起始条件(START)与停止条件(STOP)

I2C 总线在空闲时,SCL 和 SDA 均为高电平(由上拉电阻拉高)。总线上所有数据传输由主设备产生的起始条件和停止条件界定:

  • 起始条件(S):SCL 为高电平时,SDA 从高电平切换到低电平。
  • 停止条件(P):SCL 为高电平时,SDA 从低电平切换到高电平。

起始和停止条件由主设备独占产生,从设备只能检测这些条件。每次传输必须以起始条件开始,以停止条件结束,或者以重复起始条件(Sr)开始下一笔传输而不释放总线。

关键点:重复起始条件(Repeated START)允许主设备在未产生停止条件的情况下,直接发起新的传输方向(例如从写切换为读),这是 I2C 协议的重要特性。
I2C 起始条件与停止条件时序 SCL SDA 起始条件 (S) 停止条件 (P) 数据字节传输(8 位数据 + 1 位应答)

1.3 数据有效性

I2C 协议规定数据采样的严格时序:

  • 数据稳定:在 SCL 时钟的高电平期间,SDA 上的数据必须保持稳定。
  • 数据变化:仅在 SCL 为低电平时,SDA 上的数据才允许发生变化(起始条件和停止条件除外)。

每个数据字节传输 8 位(MSB 先发),之后跟随一个应答位(ACK/NACK)。主设备在 SCL 的第 9 个时钟周期释放 SDA,由从设备拉低 SDA 表示应答(ACK),或保持高电平表示非应答(NACK)。

常见驱动 Bug:在用户态通过 i2c-dev 发送数据时,若未正确处理 SCL 时钟拉伸(Clock Stretching)——即从设备在接收数据后拉低 SCL 以延长时钟周期——可能导致内核 I2C 控制器超时。Android 内核中 i2c-imx、i2c-omap 等控制器驱动默认超时时间为 1 秒。

1.4 应答机制(ACK/NACK)

应答机制是 I2C 总线可靠传输的核心。每次传输完 8 位数据后,接收方必须给出应答:

应答类型 SDA 电平(第 9 个 SCL 周期) 含义 典型场景
ACK(应答) 低电平 接收成功,可以继续发送下一字节 从设备正常接收数据或主设备正常读取数据
NACK(非应答) 高电平 接收失败或传输结束 从设备地址不匹配、寄存器不存在、主设备读操作结束

在 I2C 读操作中,主设备在读取最后一个字节后发送 NACK,以通知从设备停止发送,随后产生停止条件。

I2C 单字节写传输:地址 + 数据 + 应答 主设备(Master) 从设备(Slave) 传输序列(SCL 时钟周期 1~9) S 7-bit 地址 + W ACK 8-bit 数据 ACK P 从设备在地址匹配后发送 ACK;数据字节接收完成后再次发送 ACK

1.5 完整传输流程示例

以下是一个典型的 I2C 写寄存器操作流程(主设备向从设备写入 1 字节数据):

  1. 主设备产生起始条件(S)。
  2. 主设备发送7 位从设备地址 + 写位(0)(共 8 位)。
  3. 从设备在第 9 个 SCL 周期拉低 SDA 发送 ACK。
  4. 主设备发送寄存器地址(8 位)。
  5. 从设备发送 ACK。
  6. 主设备发送数据字节(8 位)。
  7. 从设备发送 ACK。
  8. 主设备产生停止条件(P)。
用户态驱动设计关联:在 Android 用户态通过 i2c-dev 驱动访问 I2C 设备时,上述流程由内核 I2C 核心层自动完成。用户态程序只需构造 struct i2c_msg 数组并调用 ioctl I2C_RDWR。但理解底层协议对调试通信异常(如 ACK 丢失、时钟拉伸超时)至关重要。

1.6 总线仲裁与多主设备

I2C 总线支持多主设备共享同一总线。当多个主设备同时尝试驱动总线时,通过线与(Wired-AND)特性进行仲裁:

  • 如果某个主设备试图将 SDA 拉高,但检测到 SDA 实际为低电平(因为另一个主设备正在拉低),则该主设备失去仲裁并立即转为从模式。
  • 仲裁过程不会丢失数据,也不会破坏通信。

在 Android 设备中,多主设备场景较少见,但部分电源管理芯片(如 PMIC)可能作为主设备主动上报中断,此时驱动需处理总线仲裁。

I2C 总线仲裁过程 主设备 A 主设备 B SDA 总线(线与逻辑) A 输出高,B 输出低 → 总线为低,A 失去仲裁 仲裁获胜者继续传输

1.7 本章小结

知识点 核心要点 驱动开发关注点
电气特性 开漏输出 + 上拉电阻;SCL/SDA 双线 DTS 中 clock-frequency 与上拉电阻匹配
起始/停止条件 S 为 SCL 高时 SDA 下降沿;P 为 SCL 高时 SDA 上升沿 重复起始条件用于读写组合操作
数据有效性 SCL 高电平时数据稳定;SCL 低电平时数据变化 时钟拉伸可能导致超时,需配置 adapter->timeout
应答机制 ACK = SDA 低;NACK = SDA 高 NACK 通常表示地址错误或设备离线
总线仲裁 多主设备通过线与逻辑仲裁 Android 中较少见,但 PMIC 主设备场景需注意