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)等,不同速率模式下有严格规范。
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)。数据帧格式遵循以下规则:
- 地址帧: 起始条件后,主设备发送 7 位或 10 位从设备地址,后跟 1 位读写标志(R/W)。R/W = 0 表示写操作,R/W = 1 表示读操作。
- 应答位: 每个字节(包括地址字节)传输完成后,接收方在第 9 个 SCL 周期拉低 SDA 表示应答(ACK),或保持高电平表示非应答(NACK)。
- 数据帧: 地址帧应答后,主设备(写模式)或从设备(读模式)发送数据字节,每个字节后跟应答位。
- 传输结束: 主设备产生停止条件,或通过重复起始条件开始新的传输。
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 位地址模式下,主设备发送地址帧分为两个阶段:
- 首字节: 格式为
11110 A9 A8 R/W,其中 A9 和 A8 为 10 位地址的最高两位,R/W 为读写标志。首字节后跟从设备应答。 - 次字节: 发送地址的低 8 位(A7 ~ A0),从设备再次应答。
- 数据传输: 后续数据字节与 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 标志的传输请求。