1. I2C 总线协议基础

I2C(Inter-Integrated Circuit)总线由 Philips 公司(现 NXP)于 1980 年代推出,是一种同步、多主从、串行通信总线。它仅需两根信号线(SDA 和 SCL)即可完成设备间的数据交换,是嵌入式系统中传感器、EEPROM、RTC、ADC/DAC 等外设最常用的通信接口之一。理解 I2C 物理层特性与信号时序,是编写正确、高效的 Linux I2C 适配器驱动与从设备驱动的基石。

1.1 物理层特性

特性 描述
信号线 SDA(串行数据线)和 SCL(串行时钟线),均为双向开漏(Open-Drain)结构
上拉电阻 两根线必须通过外部上拉电阻接至 VDD(典型值 1.8V / 3.3V / 5V)
逻辑电平 低电平 = 0V,高电平 = VDD;由主设备驱动时钟,从设备可拉低 SCL 实现时钟拉伸
最大总线电容 标准模式 400pF,快速模式 400pF,高速模式 100pF
最大从设备数 受限于 7 位地址(最多 112 个)或 10 位地址(最多 1024 个)及总线电容
工作模式 标准模式(100 kbit/s)、快速模式(400 kbit/s)、快速模式+(1 Mbit/s)、高速模式(3.4 Mbit/s)
开漏与线与逻辑: 开漏输出允许任一设备将总线拉低,多个设备同时拉低不会造成短路。这种“线与”机制是 I2C 仲裁和时钟拉伸的物理基础。

1.2 SDA/SCL 信号时序

I2C 总线在空闲状态时,SDA 和 SCL 均为高电平(由上拉电阻拉高)。数据传输过程中,SCL 提供时钟同步,SDA 在 SCL 低电平时改变数据,在 SCL 高电平时被主设备或从设备采样。时序参数包括上升时间(tr)、下降时间(tf)、数据建立时间(tSU;DAT)、数据保持时间(tHD;DAT)等,不同速率模式下有严格规范。

I2C 总线信号时序图(标准模式) SCL SDA 起始条件 停止条件 采样 采样 采样 数据变化 数据变化 数据变化

1.3 起始条件与停止条件

I2C 总线上每一次数据传输都以起始条件(START)开始,以停止条件(STOP)结束。这两个条件由主设备产生,是总线状态转换的关键标志。

  • 起始条件(S): SCL 为高电平时,SDA 从高电平切换到低电平。表示总线从空闲状态进入忙碌状态,开始一次新的传输。
  • 停止条件(P): SCL 为高电平时,SDA 从低电平切换到高电平。表示传输结束,总线恢复空闲状态。
  • 重复起始条件(Sr): 在一次传输过程中,不产生停止条件,直接再次产生起始条件。用于改变数据传输方向(读/写切换)或切换从设备地址,避免总线释放后被其他主设备抢占。
驱动开发要点: 在 Linux I2C 适配器驱动中,master_xfer() 回调函数需要正确处理起始条件、重复起始条件和停止条件的时序生成。对于不支持硬件自动产生这些条件的控制器,必须通过 GPIO 模拟或寄存器位操作精确控制 SDA/SCL 电平变化。

1.4 数据帧格式

I2C 数据传输以字节(8 位)为单位,每个字节后跟随一个应答位(ACK/NACK)。数据帧格式遵循以下规则:

  1. 地址帧: 起始条件后,主设备发送 7 位或 10 位从设备地址,后跟 1 位读写标志(R/W)。R/W = 0 表示写操作,R/W = 1 表示读操作。
  2. 应答位: 每个字节(包括地址字节)传输完成后,接收方在第 9 个 SCL 周期拉低 SDA 表示应答(ACK),或保持高电平表示非应答(NACK)。
  3. 数据帧: 地址帧应答后,主设备(写模式)或从设备(读模式)发送数据字节,每个字节后跟应答位。
  4. 传输结束: 主设备产生停止条件,或通过重复起始条件开始新的传输。
I2C 数据帧格式(7 位地址 + 写操作) S 7位从地址 R/W ACK 数据字节1 ACK 数据字节N ACK/NACK P 说明:S = 起始条件;P = 停止条件;R/W = 0(写)/ 1(读);ACK = 从设备应答(拉低 SDA);NACK = 从设备非应答(保持高电平) 数据字节传输顺序为 MSB(最高位)优先,每个字节后必须跟一个应答位。 起始/停止条件 地址 读写标志 应答位 数据字节

1.5 7 位与 10 位地址模式

I2C 总线支持两种从设备地址模式,驱动开发中需要根据硬件手册正确配置地址格式。

特性 7 位地址模式 10 位地址模式
地址范围 0x00 ~ 0x7F(保留地址:0x00 ~ 0x07,0x78 ~ 0x7F) 0x000 ~ 0x3FF
最大设备数 112 个(扣除保留地址) 1024 个
地址帧格式 1 字节:7 位地址 + 1 位 R/W 2 字节:首字节 11110XX + R/W,次字节 8 位地址低 8 位
兼容性 所有 I2C 设备均支持 需要主设备和从设备都支持 10 位地址模式
Linux 内核表示 addr & 0x7F addr | I2C_TENBIT(标志位 0x1000)
典型应用 大多数传感器、EEPROM、RTC 多路复用器、复杂 PMIC、地址冲突场景
地址冲突与保留地址: 7 位地址模式中,0x00(通用呼叫地址)、0x01(起始字节)、0x02 ~ 0x07(保留)、0x78 ~ 0x7F(10 位地址前缀)不可用于普通从设备。设计驱动时,应通过设备树或板级数据检查地址合法性,避免与保留地址冲突。

1.6 10 位地址通信流程

10 位地址模式下,主设备发送地址帧分为两个阶段:

  1. 首字节: 格式为 11110 A9 A8 R/W,其中 A9 和 A8 为 10 位地址的最高两位,R/W 为读写标志。首字节后跟从设备应答。
  2. 次字节: 发送地址的低 8 位(A7 ~ A0),从设备再次应答。
  3. 数据传输: 后续数据字节与 7 位模式相同,每个字节后跟应答位。

在 Linux 内核中,10 位地址通过 i2c_msg.addr 字段携带 I2C_TENBIT 标志(值为 0x1000)来标识。适配器驱动在 master_xfer() 中需要检查该标志,并生成对应的两字节地址帧。

/* Linux 内核中 10 位地址标志定义 */
#define I2C_TENBIT      0x1000

/* 构造 10 位地址消息示例 */
struct i2c_msg msg;
msg.addr  = 0x01F0 | I2C_TENBIT;  /* 10 位地址 0x1F0 */
msg.flags = 0;                     /* 写操作 */
msg.len   = 2;
msg.buf   = data_buffer;
驱动开发提示: 在实现适配器驱动的 functionality() 回调时,应通过 I2C_FUNC_10BIT_ADDR 标志声明对 10 位地址的支持。如果控制器硬件不支持 10 位地址模式,内核将自动拒绝使用 I2C_TENBIT 标志的传输请求。