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均为高电平,这是判断起始/停止条件的基础。
I2C 总线物理连接拓扑 主设备 (Master) 从设备1 (Slave) 从设备2 (Slave) Rp Rp SDA (串行数据线) SCL (串行时钟线)

2.2 起始条件(START Condition)与停止条件(STOP Condition)

I2C总线上数据传输的起始和结束由主设备控制,通过SDA和SCL的特定电平跳变来定义。这两个条件是所有I2C通信的帧同步标志。

  • 起始条件(S): SCL为高电平时,SDA从高电平切换到低电平。表示总线从空闲状态进入忙碌状态,开始一次新的传输。
  • 停止条件(P): SCL为高电平时,SDA从低电平切换到高电平。表示传输结束,总线释放为空闲状态。
  • 重复起始条件(Sr): 在一次传输中,不发送停止条件,直接再次发送起始条件。用于改变数据传输方向(如从写切换到读)或访问不同从设备,避免总线释放后被其他主设备抢占。
起始条件与停止条件时序 SCL SDA S P 数据字节传输(8位 + ACK) 起始 停止
驱动开发陷阱: 在实现 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)。
数据有效性:SCL高电平采样,低电平变化 D7 (MSB) D6 D5 D4 D3 D2 采样窗口 采样窗口 采样窗口

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为地址高位。
I2C 地址字节与读写位传输帧结构 S A6 A5 A4 A3 A2 A1 A0 R/W ACK 数据字节 (8位) P 地址字节 (1 Byte = 7位地址 + 1位R/W)

2.5 完整数据传输流程

一次完整的I2C写或读操作遵循以下步骤序列。理解这个流程对于编写Linux I2C设备驱动中的 i2c_transfer 调用至关重要。

  1. 主设备发送起始条件(S)。
  2. 主设备发送7位从设备地址 + 写位(0)。 等待从设备ACK。
  3. 主设备发送寄存器地址/命令字节。 等待从设备ACK。
  4. 主设备发送数据字节。 每字节后等待从设备ACK。
  5. 主设备发送停止条件(P)。 释放总线。

读操作变体: 读操作通常需要先写寄存器地址,然后使用重复起始条件(Sr)切换为读方向。

  1. 主设备发送起始条件(S)。
  2. 主设备发送从设备地址 + 写位(0),等待ACK。
  3. 主设备发送要读取的寄存器地址,等待ACK。
  4. 主设备发送重复起始条件(Sr)。
  5. 主设备发送从设备地址 + 读位(1),等待ACK。
  6. 从设备发送数据字节,主设备每字节后发送ACK(继续读)或NACK(停止读)。
  7. 主设备发送停止条件(P)。
内核API对应: Linux I2C核心层通过 i2c_msg 结构体数组描述一次复合传输。写操作对应 msg[0].flags = 0,读操作对应 msg[1].flags = I2C_M_RD。重复起始条件由内核自动处理,驱动只需将多个 i2c_msg 放入同一个 i2c_transfer 调用即可。
I2C 复合读写操作流程(写地址 + 重复起始 + 读数据) 起始 S 地址 + W (0) ACK 寄存器地址 ACK 重复起始 Sr