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

I2C(Inter-Integrated Circuit)总线是一种由Philips公司(现NXP)开发的两线式串行总线,广泛应用于嵌入式系统中连接微控制器与外围设备。本章深入剖析I2C协议的核心机制,为后续Linux内核驱动开发奠定坚实的硬件基础。

2.1 总线物理层与信号定义

I2C总线仅使用两条双向信号线:

  • SCL(Serial Clock):串行时钟线,由主设备驱动。
  • SDA(Serial Data):串行数据线,用于传输地址和数据。

两条线均通过上拉电阻连接到VCC,空闲时为高电平。设备以开漏输出方式驱动总线,实现“线与”逻辑。

关键概念:开漏结构允许多个设备共享总线,任何设备都可以将总线拉低,但只有主设备控制时钟。这是I2C总线仲裁和时钟拉伸的基础。

2.2 起始条件与停止条件

I2C总线的数据传输以起始条件(START)开始,以停止条件(STOP)结束。这两个条件是总线上的特殊信号,用于标识传输的边界。

条件 信号时序 说明
起始条件(S) SCL为高电平时,SDA从高电平切换到低电平 表示总线从空闲状态进入活跃状态,主设备开始传输
停止条件(P) SCL为高电平时,SDA从低电平切换到高电平 表示传输结束,总线返回空闲状态
重复起始条件(Sr) 在传输过程中,不发送停止条件,直接再次发送起始条件 用于改变数据传输方向或切换从设备,避免总线释放
实战要点:在Linux内核I2C驱动中,起始条件和停止条件由硬件控制器自动生成。驱动开发者只需通过struct i2c_msg结构体描述传输方向和数据,控制器硬件负责时序生成。重复起始条件常用于组合传输(combined transfer),例如先写寄存器地址再读数据。
I2C起始条件与停止条件时序图 SCL SDA 起始条件 SDA↓ while SCL=H 停止条件 SDA↑ while SCL=H 数据传输区域(SCL低电平时改变SDA,高电平时采样) 空闲 空闲

2.3 数据有效性

I2C协议对数据线上的信号有严格的时序要求,确保数据在正确的时刻被采样:

  • 数据稳定期:在SCL时钟的高电平期间,SDA上的数据必须保持稳定。
  • 数据变化期:在SCL时钟的低电平期间,SDA上的数据允许发生变化。
  • 每个时钟脉冲传输1位数据:从最高位(MSB)开始,逐位传输。
常见错误:初学者容易混淆起始条件与数据变化。起始条件是在SCL高电平时SDA发生下降沿,而正常数据位变化必须在SCL低电平时进行。如果在SCL高电平时SDA发生非预期的变化,从设备会将其误判为起始或停止条件,导致通信紊乱。

2.4 应答机制(ACK/NACK)

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

应答类型 SDA电平(第9个SCL周期) 含义
ACK(应答) 低电平 接收方已成功接收字节,可以继续发送下一字节
NACK(非应答) 高电平 接收方未成功接收或不想继续接收,主设备应终止传输
驱动开发视角:在Linux内核中,I2C核心层自动处理应答信号的检测。当从设备发送NACK时,i2c_transfer()函数会返回负错误码(通常是-EIO或-ENXIO)。驱动开发者应检查返回值,实现错误恢复逻辑,例如重试或复位总线。
I2C字节传输与应答机制 SCL SDA MSB 6 5 4 3 2 1 LSB ACK SDA=L 8位数据字节(从MSB到LSB依次传输) 第9个SCL周期 主设备驱动SCL 从设备驱动SDA

2.5 7位地址模式

7位地址模式是I2C最基本的寻址方式,允许总线上挂载最多112个从设备(地址0x00为广播地址,0x01~0x07保留)。

地址帧结构:

  • 起始条件:总线开始传输。
  • 7位从设备地址:高位在前(MSB first)。
  • 读/写位(R/W):0表示主设备写(发送),1表示主设备读(接收)。
  • 应答位:从设备确认地址匹配。
内核驱动映射:在Linux中,7位地址通过struct i2c_client的addr字段表示,范围0x03~0x77。驱动开发者在设备树中通过reg属性指定地址,例如:
/* 设备树示例:7位地址0x48 */
temp_sensor: temperature@48 {
    compatible = "ti,tmp102";
    reg = <0x48>;
};

2.6 10位地址模式

10位地址模式扩展了I2C的寻址能力,允许总线上挂载更多设备。它与7位地址模式向后兼容。

10位地址传输过程:

  1. 第一个字节:11110XX + R/W,其中XX是10位地址的高2位。
  2. 从设备应答:匹配高2位的从设备发送ACK。
  3. 第二个字节:10位地址的低8位。
  4. 从设备再次应答:完整地址匹配后发送ACK。
特性 7位地址模式 10位地址模式
地址范围 0x00 ~ 0x7F(部分保留) 0x000 ~ 0x3FF
最大从设备数 112(实际可用) 1024
地址传输字节数 1字节(含R/W位) 2字节
兼容性 所有I2C设备支持 需要从设备明确支持
Linux内核支持 默认模式,广泛使用 通过I2C_TENBIT标志启用
兼容性注意:10位地址模式在Linux内核中需要特殊处理。驱动开发者在创建i2c_client时,必须设置I2C_CLIENT_TEN标志,否则内核会按7位地址解析。大多数消费级传感器和外围设备使用7位地址,10位地址主要用于需要大量从设备的工业场景。
7位地址与10位地址帧结构对比 7位地址帧: S 7位从设备地址 R/W ACK 数据字节(8位) P 10位地址帧: S 11110 + 地址高2位 R/W ACK 地址低8位 ACK 数据 地址模式关键区别 • 7位地址:1字节地址帧,支持最多112个从设备,适用于大多数传感器和外围设备 • 10位地址:2字节地址帧,支持最多1024个从设备,需要从设备硬件支持 • Linux内核通过I2C_TENBIT标志区分两种模式,驱动需在client创建时正确设置

2.7 总线仲裁与时钟拉伸

I2C总线支持多主设备环境,通过以下机制实现冲突管理:

  • 总线仲裁:多个主设备同时驱动SDA时,输出低电平的设备赢得仲裁。仲裁过程在SCL高电平时进行,不会丢失数据。
  • 时钟拉伸:从设备可以拉低SCL线,强制主设备等待,直到从设备准备好继续传输。这是从设备进行流控制的唯一手段。
驱动开发提示:时钟拉伸在Linux内核中默认由I2C控制器硬件处理。但某些低速或老旧从设备可能拉伸时钟时间过长,导致控制器超时。驱动开发者可以通过调整struct i2c_adapter的超时参数(timeout)来适配,通常设置为100ms~1s。

2.8 本章小结