2. I2C协议详解:起始条件、停止条件、数据有效性、应答机制、7位与10位地址模式

I2C(Inter-Integrated Circuit)总线是一种由Philips公司(现NXP)开发的两线式串行通信协议,广泛应用于车载嵌入式系统中连接传感器、执行器、编解码器、触摸控制器等低速外设。本章深入剖析I2C协议的五个核心要素:起始条件、停止条件、数据有效性、应答机制以及地址模式,为后续驱动开发和设备挂载调试奠定坚实的协议基础。

2.1 总线基本结构

I2C总线仅需两根线:串行数据线(SDA)和串行时钟线(SCL)。所有设备通过开漏输出方式连接到总线,并通过上拉电阻接至电源电压。总线空闲时,SDA和SCL均为高电平。

关键特性: 主设备(Master)负责产生时钟信号并发起通信;从设备(Slave)响应主设备的寻址和命令。车载系统中,主设备通常是SoC或MCU,从设备包括温度传感器、光照传感器、音频Codec、触摸屏控制器等。
I2C总线物理拓扑与信号线 I2C总线物理拓扑与信号线 R R VDD SCL SDA 主设备 (SoC) 从设备1 (温度) 从设备2 (Codec) 说明: • SDA和SCL均为开漏输出,通过上拉电阻R接至VDD。 • 总线空闲时,SDA和SCL均为高电平。任何设备均可通过拉低SDA发起通信。

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

I2C总线上数据传输的起始和结束由SDA和SCL的特定电平变化定义,这两种条件具有唯一性,确保总线状态的明确切换。

条件类型 信号变化顺序 说明
起始条件(S) SCL保持高电平 → SDA从高电平跳变为低电平 表示一次新的传输开始;主设备在发送起始条件后获得总线控制权
重复起始条件(Sr) 与起始条件相同,但在一次传输中间产生 用于改变数据传输方向(读/写切换)或重新寻址,而不释放总线
停止条件(P) SCL保持高电平 → SDA从低电平跳变为高电平 表示一次传输结束;主设备释放总线,总线恢复空闲状态
实战要点: 在车载Linux驱动中,起始条件和停止条件由I2C控制器硬件自动产生。驱动开发者只需通过I2C核心API(如 i2c_master_send()、i2c_master_recv())发起传输,无需手动操作SDA/SCL电平。但在调试阶段,使用逻辑分析仪捕获起始/停止条件是判断通信是否正常启动和结束的关键手段。

2.3 数据有效性(Data Validity)

I2C协议规定,数据在SCL时钟的配合下进行传输,每个数据位对应一个时钟脉冲。数据有效性遵循以下规则:

  • 数据稳定期: SCL为高电平时,SDA上的数据必须保持稳定,此时接收方采样SDA电平。
  • 数据变化期: SCL为低电平时,SDA允许发生变化(即发送方更新下一个数据位)。
  • 字节格式: 每次传输以字节(8位)为单位,高位(MSB)在前,低位(LSB)在后。
  • 传输速率: 标准模式(100 kbit/s)、快速模式(400 kbit/s)、高速模式(3.4 Mbit/s)。车载系统常用标准模式和快速模式。
常见错误: 如果SCL高电平时SDA发生跳变,会被从设备误判为起始条件或停止条件,导致通信混乱。驱动调试时若出现总线挂死或ACK异常,应首先检查SDA/SCL时序是否满足数据有效性要求。

2.4 应答机制(ACK/NACK)

I2C协议采用应答机制确保数据传输的可靠性。每发送一个字节(8位数据)后,接收方必须在第9个时钟周期内给出应答信号。

应答类型 SDA电平(第9个SCL周期) 含义
ACK(应答) 低电平 接收方已成功接收上一个字节,可以继续发送下一个字节
NACK(非应答) 高电平 接收方未成功接收或不再接收数据;主设备应发送停止条件终止传输

应答机制在车载驱动中的典型场景:

  • 地址寻址应答: 主设备发送从设备地址后,若总线上存在该地址的从设备,则从设备回复ACK;否则回复NACK,驱动应返回-ENXIO错误。
  • 数据写入应答: 从设备每接收一个字节后回复ACK;若从设备忙或写保护,可能回复NACK。
  • 数据读取应答: 主设备每接收一个字节后,由主设备回复ACK表示继续读取,回复NACK表示读取结束。
驱动调试技巧: 在Linux内核中,I2C传输的应答状态由I2C控制器硬件自动处理。驱动可通过检查传输返回值判断是否收到NACK。例如,i2c_transfer()返回负值表示NACK或总线错误。在调试阶段,使用i2cget、i2cset等用户空间工具可以快速验证从设备是否正常应答。

2.5 7位地址模式与10位地址模式

I2C协议支持两种从设备地址格式:7位地址模式和10位地址模式。车载系统中,绝大多数从设备使用7位地址模式,10位地址模式用于需要更多设备挂载的场景。

7位地址与10位地址帧结构对比 7位地址与10位地址帧结构对比 7位地址模式帧结构: S 7位地址 (A6-A0) R/W ACK 数据字节 ACK P 10位地址模式帧结构: S 11110 A9 A8 R/W ACK A7-A0 (低8位) ACK 数据字节 ACK P 地址模式对比说明: • 7位地址:地址范围 0x00 ~ 0x7F(128个地址),其中保留地址(如 0x00~0x07、0x78~0x7F)不可用。 • 10位地址:地址范围 0x000 ~ 0x3FF(1024个地址),通过保留的11110前缀标识。 • 10位地址传输分两个阶段:先发送11110 + 高2位地址 + R/W,再发送低8位地址。 • 车载系统常用7位地址;10位地址用于需要挂载大量从设备的复杂系统(如多屏显示、多传感器阵列)。

2.6 地址模式在Linux内核中的处理

Linux I2C子系统通过struct i2c_client中的addr字段表示从设备地址,地址类型由flags字段中的I2C_CLIENT_TEN标志区分。

// 7位地址设备声明示例
static struct i2c_board_info my_sensor_info = {
    I2C_BOARD_INFO("tmp117", 0x48),  // 7位地址 0x48
};

// 10位地址设备声明示例
static struct i2c_board_info my_10bit_device = {
    I2C_BOARD_INFO("my_codec", 0x2A0),  // 10位地址 0x2A0
    .flags = I2C_CLIENT_TEN,             // 标记为10位地址
};
驱动开发提示: 在设备树(Device Tree)中,7位地址直接使用 reg = <0x48>; 表示。10位地址需要在 reg 中填入完整10位值,并在驱动中通过 I2C_CLIENT_TEN 标志识别。绝大多数车载I2C从设备使用7位地址,驱动开发时优先查阅数据手册确认地址格式。

2.7 完整I2C数据传输流程

结合上述所有要素,一次完整的I2C写操作流程如下:

  1. 主设备发送起始条件(S):SCL高电平时,SDA由高变低。
  2. 主设备发送从设备地址 + 写标志(R/W = 0):共8位,高位在前。
  3. 从设备回复ACK:拉低SDA表示确认。
  4. 主设备发送寄存器地址/命令字节:8位数据,高位在前。
  5. 从设备回复ACK。
  6. 主设备发送数据字节:可连续发送多个字节。
  7. 从设备每接收一个字节回复ACK。
  8. 主设备发送停止条件(P):SCL高电平时,SDA由低变高。

读操作流程类似,但在发送从设备地址后,R/W位设置为1(读),且主设备在接收数据后由主设备回复ACK(继续读)或NACK(结束读)。

车载实战案例: 在调试车载触摸屏控制器时,若出现触摸无响应,使用逻辑分析仪捕获I2C总线波形,检查起始条件是否正常、从设备地址是否匹配、ACK是否出现。常见问题包括:地址错误(如7位地址左移1位未处理)、从设备未上电导致NACK、SCL时钟频率不匹配导致数据有效性违规。

2.8 本章小结

I2C协议的起始条件、停止条件、数据有效性、应答机制和地址模式构成了总线通信的基石。在车载Linux驱动开发中,理解这些底层细节有助于:

  • 正确配置I2C控制器时序参数(时钟频率、上升/下降时间)。
  • 准确解析从设备数据手册中的通信时序图。
  • 快速定位总线调试中的常见问题(无ACK、总线挂死、数据错位)。
  • 合理选择7位或10位地址模式,优化系统设备挂载方案。