2. I2C总线协议基础
I2C(Inter-Integrated Circuit)总线是由Philips公司(现NXP)开发的一种同步、多主从、串行通信总线。在嵌入式Linux设备驱动模型中,I2C总线驱动框架直接构建于该协议物理层与链路层之上。理解I2C协议的底层机制,是分析I2C核心层(i2c-core)与适配器(adapter)驱动的基础。
2.1 I2C物理层特性
I2C总线仅使用两条双向开漏(Open-Drain)信号线:串行数据线(SDA)和串行时钟线(SCL)。所有设备通过这两条线并联在总线上,通过线与逻辑实现仲裁与时钟同步。
| 特性 | 说明 |
|---|---|
| 信号线数量 | 2根(SDA + SCL) |
| 电气特性 | 开漏输出,需外部上拉电阻至Vdd |
| 逻辑电平 | 低电平=0(驱动拉低),高电平=1(释放,由上拉电阻拉高) |
| 总线容量 | 标准模式100 kbit/s,快速模式400 kbit/s,高速模式3.4 Mbit/s |
| 最大设备数 | 受限于总线电容(通常400 pF),理论最多128个从设备 |
| 主从关系 | 多主能力,通过仲裁决定总线控制权 |
驱动开发要点: 在Linux I2C适配器驱动中,
i2c_adapter 结构体中的 algo 回调函数(如 master_xfer)必须严格按照物理层时序操作SDA和SCL。开漏结构决定了总线空闲时SDA和SCL均为高电平,这是判断起始/停止条件的基础。
2.2 起始条件(START Condition)与停止条件(STOP Condition)
I2C总线上数据传输的起始和结束由主设备控制,通过SDA和SCL的特定电平跳变来定义。这两个条件是所有I2C通信的帧同步标志。
- 起始条件(S): SCL为高电平时,SDA从高电平切换到低电平。表示总线从空闲状态进入忙碌状态,开始一次新的传输。
- 停止条件(P): SCL为高电平时,SDA从低电平切换到高电平。表示传输结束,总线释放为空闲状态。
- 重复起始条件(Sr): 在一次传输中,不发送停止条件,直接再次发送起始条件。用于改变数据传输方向(如从写切换到读)或访问不同从设备,避免总线释放后被其他主设备抢占。
驱动开发陷阱: 在实现
master_xfer 回调时,起始条件和停止条件的时序必须严格满足I2C规范。SCL高电平期间SDA的跳变必须干净,避免毛刺导致从设备误判。Linux内核中的 i2c_msg 标志位 I2C_M_NOSTART 和 I2C_M_STOP 用于控制是否发送起始/停止条件,驱动开发者需正确解析这些标志。
2.3 数据有效性(Data Validity)
I2C协议规定,SDA线上的数据必须在SCL时钟的高电平期间保持稳定。数据变化只能在SCL低电平期间进行。这一规则保证了数据采样的可靠性。
- 数据采样: 接收方在SCL的上升沿或高电平期间采样SDA电平。
- 数据变化: SDA仅在SCL为低电平时允许变化(起始和停止条件除外)。
- 字节格式: 每次传输8位数据(一个字节),高位(MSB)在前。
- 应答位(ACK/NACK): 每发送完一个字节后,接收方在第9个SCL时钟周期拉低SDA表示应答(ACK),或保持高电平表示非应答(NACK)。
2.4 地址与读写位(Address & R/W Bit)
每次I2C传输以起始条件开始,随后主设备发送一个字节的从设备地址和读写控制位。地址用于选择总线上的目标从设备,读写位决定数据传输方向。
| 字段 | 位宽 | 说明 |
|---|---|---|
| 从设备地址 | 7位(标准)或10位(扩展) | 唯一标识总线上的从设备。7位地址范围:0x00~0x7F,其中0x00为通用呼叫地址,0x04~0x77为用户可用地址。 |
| 读写位(R/W) | 1位 | 0 = 写(主设备发送数据到从设备),1 = 读(从设备发送数据到主设备)。 |
| 应答位(ACK) | 1位 | 从设备在地址字节后必须发送ACK,表示该设备存在且准备好通信。 |
地址字节格式: 主设备发送的第一个字节由7位地址和1位读写位组成。例如,地址0x50(7位)加上写位(0)组成字节0xA0;加上读位(1)组成字节0xA1。
内核地址映射: 在Linux I2C子系统中,
i2c_client 结构体的 addr 字段存储7位地址(左对齐)。驱动开发者在设备树中配置 reg = <0x50> 时,内核会自动处理地址与读写位的组合。10位地址通过 I2C_TENBIT 标志启用,地址字节格式为:1111 0XX + R/W,其中XX为地址高位。
2.5 完整数据传输流程
一次完整的I2C写或读操作遵循以下步骤序列。理解这个流程对于编写Linux I2C设备驱动中的 i2c_transfer 调用至关重要。
- 主设备发送起始条件(S)。
- 主设备发送7位从设备地址 + 写位(0)。 等待从设备ACK。
- 主设备发送寄存器地址/命令字节。 等待从设备ACK。
- 主设备发送数据字节。 每字节后等待从设备ACK。
- 主设备发送停止条件(P)。 释放总线。
读操作变体: 读操作通常需要先写寄存器地址,然后使用重复起始条件(Sr)切换为读方向。
- 主设备发送起始条件(S)。
- 主设备发送从设备地址 + 写位(0),等待ACK。
- 主设备发送要读取的寄存器地址,等待ACK。
- 主设备发送重复起始条件(Sr)。
- 主设备发送从设备地址 + 读位(1),等待ACK。
- 从设备发送数据字节,主设备每字节后发送ACK(继续读)或NACK(停止读)。
- 主设备发送停止条件(P)。
内核API对应: Linux I2C核心层通过
i2c_msg 结构体数组描述一次复合传输。写操作对应 msg[0].flags = 0,读操作对应 msg[1].flags = I2C_M_RD。重复起始条件由内核自动处理,驱动只需将多个 i2c_msg 放入同一个 i2c_transfer 调用即可。