1. I2C 总线协议基础

I2C(Inter-Integrated Circuit)总线由 Philips 公司(现 NXP)于 1982 年发明,是一种同步、多主从、串行通信总线。它仅需两根信号线即可完成板级芯片间的数据交换,是嵌入式系统中使用最广泛的片间总线之一。本章从物理层特性、信号时序、帧格式三个维度建立 I2C 协议的知识地基。

1.1 I2C 总线物理层特性

I2C 总线使用两根双向开漏(Open-Drain)信号线:

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

两根线均通过上拉电阻(通常 4.7 kΩ 或 10 kΩ)连接到 VDD(典型 3.3 V 或 5 V)。总线空闲时,SCL 和 SDA 均为高电平。开漏结构允许多个设备共享总线,通过线与(Wired-AND)机制实现仲裁。

参数 标准模式(100 kHz) 快速模式(400 kHz) 高速模式(3.4 MHz)
最大时钟频率 100 kHz 400 kHz 3.4 MHz
最大总线电容 400 pF 400 pF 100 pF
最大从设备数量 127(7 位地址) 127 127
上拉电阻典型值 4.7 kΩ 2.2 kΩ 1.0 kΩ
工程提示: 总线电容受 PCB 走线长度和从设备数量影响。当总线电容超过规格时,SCL/SDA 上升沿变缓,可能导致通信失败。驱动开发中需在设备树中正确配置时钟频率,避免高速模式下的信号完整性问题。

1.2 SCL / SDA 信号与电气特性

I2C 信号的关键电气规则:

  • 开漏输出:设备只能将信号线拉低(输出 0),不能主动拉高。高电平由上拉电阻实现。
  • 线与逻辑:多个设备同时拉低时,总线保持低电平;只有所有设备释放(高阻),总线才恢复高电平。
  • 噪声容限:输入低电平阈值 VIL = 0.3 × VDD,输入高电平阈值 VIH = 0.7 × VDD。
  • 上升时间:标准模式最大 1000 ns,快速模式最大 300 ns。
驱动开发要点: 在 Linux 内核中,I2C 适配器驱动通过 i2c_adapter 结构体管理时序参数。设备树中 clock-frequency 属性直接影响 SCL 周期,需与硬件上拉电阻匹配。
I2C 总线物理连接拓扑 RP RP VDD VDD SCL SDA 主设备 (Master) 从设备 1 (Slave 0x50) 从设备 2 (Slave 0x68) 从设备 3 (Slave 0x76) I2C 总线物理拓扑 所有设备通过开漏方式挂接在 SCL/SDA 上,上拉电阻 RP 保证空闲高电平

1.3 起始条件与停止条件

I2C 通信的每次传输以起始条件(START)开始,以停止条件(STOP)结束。这两个条件是总线上的特殊信号序列,由主设备产生。

条件 SDA 状态 SCL 状态 时序描述
起始条件(S) 高 → 低 高 SCL 为高时,SDA 从高电平切换到低电平
停止条件(P) 低 → 高 高 SCL 为高时,SDA 从低电平切换到高电平
重复起始条件(Sr) 高 → 低 高 在未发停止条件前,再次产生起始条件
常见错误: 起始条件和停止条件必须在 SCL 高电平时采样 SDA 的跳变。如果 SDA 在 SCL 低电平时跳变,会被视为普通数据位变化,而非起始/停止条件。驱动开发中,I2C 控制器硬件自动处理这些时序,但软件需确保不违反总线状态机。
I2C 起始条件与停止条件时序 SCL SDA 起始条件 SDA↓ 且 SCL=H 停止条件 SDA↑ 且 SCL=H 数据传输区域(SCL 低电平时 SDA 变化,高电平时采样)

1.4 数据帧格式

I2C 数据帧以字节(8 位)为单位传输,每个字节后跟一个应答位(ACK/NACK)。完整的传输帧结构如下:

  1. 起始条件(S):主设备产生。
  2. 从设备地址(7 位或 10 位):高位在前,低位在后。
  3. 读/写位(R/W):0 表示写,1 表示读。
  4. 应答位(ACK):从设备在第 9 个 SCL 周期拉低 SDA 表示应答。
  5. 数据字节(8 位 × N):每个字节后跟一个应答位。
  6. 停止条件(P):主设备产生,释放总线。
帧格式示例: 主设备向地址 0x50 的从设备写入 1 字节数据 0xA5。
S + 0x50(写) + ACK + 0xA5 + ACK + P
帧段 位数 方向 说明
起始条件(S) 1 主 → 总线 SCL 高时 SDA 下降沿
从设备地址 7 主 → 从 高位在前,如 0x50 = 1010000
读/写位 1 主 → 从 0=写,1=读
应答位(ACK) 1 从 → 主 从设备拉低 SDA 表示应答
数据字节 8 × N 主 ↔ 从 MSB 优先,每字节后跟 ACK
停止条件(P) 1 主 → 总线 SCL 高时 SDA 上升沿
I2C 完整数据帧格式(7 位地址 + 写操作) S 从设备地址 7 位 R/W 1 位 ACK 数据字节 8 位 ACK 更多字节 ... P I2C 写操作帧格式(7 位地址) 主设备发送起始条件 → 从设备地址 + 写位 → 等待 ACK → 发送数据字节 → 等待 ACK → 停止条件 1 7 1 1 8 1 1

1.5 应答机制与仲裁

应答(ACK/NACK):每个字节传输完成后,接收方必须在第 9 个 SCL 周期输出应答信号。主设备发送地址后,从设备应答表示存在;主设备发送数据后,从设备应答表示接收成功。读操作时,主设备在最后一个字节发送 NACK 表示传输结束。

总线仲裁:当多个主设备同时尝试控制总线时,通过 SDA 线的线与逻辑进行仲裁。在仲裁过程中,输出高电平的主设备若检测到 SDA 被拉低,则自动退出竞争。仲裁不会丢失数据。

驱动开发关联: Linux 内核的 i2c_msg 结构体通过 flags 字段控制读写方向和应答行为。设备树中 reg 属性定义从设备地址,驱动通过 i2c_transfer() 发送完整帧序列。
// Linux 内核 I2C 消息结构体示例
struct i2c_msg {
    __u16 addr;    // 从设备地址(7 位或 10 位)
    __u16 flags;   // 标志位:I2C_M_RD 表示读,0 表示写
    __u16 len;     // 数据长度(字节数)
    __u8 *buf;     // 数据缓冲区指针
};

1.6 本章小结

I2C 总线协议的核心要点:

  • 两根开漏信号线(SCL + SDA)配合上拉电阻实现多设备共享。
  • 起始条件(SDA↓ while SCL=H)和停止条件(SDA↑ while SCL=H)界定传输边界。
  • 数据帧由地址 + 读写位 + 应答 + 数据字节 + 应答组成,MSB 优先。
  • 应答机制保证传输可靠性,总线仲裁支持多主设备共存。
  • Linux 驱动通过 i2c_msg 和 i2c_transfer() 实现协议层交互。