2. I2C 总线协议详解:起始条件、停止条件、数据有效性、应答机制、完整数据传输时序
I2C(Inter-Integrated Circuit)总线是一种由 Philips(现 NXP)公司开发的两线式串行通信协议,广泛应用于嵌入式系统中连接微控制器与各种外设(如传感器、EEPROM、ADC/DAC、RTC 等)。本章将深入剖析 I2C 总线协议的核心要素,包括信号定义、时序要求、数据帧格式以及完整的传输流程,为后续编写 Linux 内核 I2C 设备驱动奠定坚实的硬件协议基础。
2.1 I2C 总线物理层与电气特性
I2C 总线仅使用两条双向信号线:
- SCL(Serial Clock Line):串行时钟线,由主设备(Master)驱动,用于同步数据传输。
- SDA(Serial Data Line):串行数据线,用于在主设备和从设备(Slave)之间双向传输数据。
两条线均通过上拉电阻连接到电源电压(VDD)。当总线空闲时,SCL 和 SDA 均为高电平。设备输出级为开漏(Open-Drain)结构,允许多个设备共享总线,通过“线与”逻辑实现仲裁和时钟拉伸。
关键参数:标准模式(Standard-mode)最高 100 kbit/s,快速模式(Fast-mode)最高 400 kbit/s,高速模式(High-speed mode)最高 3.4 Mbit/s。驱动开发中需根据从设备数据手册配置 I2C 控制器时钟频率。
2.2 起始条件(START Condition)与停止条件(STOP Condition)
I2C 总线上每一次通信都以起始条件开始,以停止条件结束。这两个条件是总线状态变化的特殊序列,由主设备产生。
| 条件 | 信号序列 | 说明 |
|---|---|---|
| 起始条件(S) | SCL 为高电平时,SDA 从高电平切换到低电平 | 通知所有从设备:总线被占用,准备开始传输 |
| 停止条件(P) | SCL 为高电平时,SDA 从低电平切换到高电平 | 通知所有从设备:传输结束,总线释放为空闲状态 |
| 重复起始条件(Sr) | 在未产生停止条件前,再次产生起始条件 | 用于改变数据传输方向或切换从设备,无需释放总线 |
驱动开发要点:在 Linux 内核 I2C 核心层中,
i2c_msg 结构体的 flags 字段通过 I2C_M_STOP 和 I2C_M_NOSTART 控制停止条件和重复起始条件的生成。理解硬件时序是正确配置这些标志的前提。
2.3 数据有效性(Data Validity)
I2C 总线上数据的有效性由 SCL 时钟信号严格定义:
- 数据变化:SDA 线上的数据必须在 SCL 为低电平时发生变化。
- 数据采样:SDA 线上的数据必须在 SCL 为高电平时被接收方采样读取。
每个时钟脉冲传输 1 位数据。在 SCL 高电平期间,SDA 必须保持稳定,否则会被误判为起始条件或停止条件。
常见错误:在 SCL 高电平期间改变 SDA 状态是协议违规,会导致通信混乱。驱动中的 GPIO 模拟 I2C 实现必须严格遵循此时序,使用
udelay 或 ndelay 确保时序窗口。
2.4 应答机制(ACK/NACK)
I2C 总线采用强制性的应答机制来确保数据传输的可靠性。每传输完 1 个字节(8 位数据),接收方必须在第 9 个时钟周期内产生应答信号。
| 应答类型 | 第 9 位 SDA 状态 | 含义 | 典型场景 |
|---|---|---|---|
| ACK(应答) | 低电平 | 接收方已成功接收字节,准备接收下一字节 | 正常数据传输、从设备地址匹配 |
| NACK(非应答) | 高电平 | 接收方无法接收或处理数据 | 从设备忙、地址未匹配、读操作结束 |
驱动中的应答处理:在 Linux I2C 驱动中,主设备发送地址后检测 ACK/NACK 决定是否继续;读操作时,主设备在最后一个字节发送 NACK 并产生停止条件,通知从设备停止发送。内核
i2c_transfer 返回值可反映应答状态。
2.5 完整数据传输时序
一次完整的 I2C 数据传输由以下步骤组成:
- 起始条件(S):主设备产生起始条件,占用总线。
- 发送从设备地址(7 位或 10 位):主设备发送从设备地址,高位在前。
- 发送读/写位(R/W):第 8 位为 0 表示写操作,为 1 表示读操作。
- 从设备应答(ACK):地址匹配的从设备在第 9 个时钟周期拉低 SDA 作为应答。
- 数据传输:根据 R/W 位,主设备发送数据(写)或接收数据(读),每字节后跟随 ACK/NACK。
- 停止条件(P):主设备产生停止条件,释放总线。
以下是一个典型的写操作帧格式:
| S | 从设备地址 (7位) | R/W=0 | ACK | 数据字节 1 | ACK | 数据字节 2 | ACK | ... | 数据字节 N | ACK | P |
读操作帧格式:
| S | 从设备地址 (7位) | R/W=1 | ACK | 数据字节 1 | ACK | 数据字节 2 | ACK | ... | 数据字节 N | NACK | P |
重复起始条件(Sr)应用:在复合操作(如先写寄存器地址再读数据)中,主设备在写操作后不产生停止条件,而是直接产生重复起始条件,然后发送读操作地址。这种“组合事务”在 Linux 内核中通过
i2c_msg 数组和 I2C_M_NOSTART 标志实现。
2.6 协议关键点总结
| 协议要素 | 关键规则 | 驱动开发影响 |
|---|---|---|
| 起始/停止条件 | SCL 高电平时 SDA 的下降沿/上升沿 | GPIO 模拟时需精确控制时序;硬件控制器自动处理 |
| 数据有效性 | SCL 低电平变数据,高电平采样 | 确保 SCL 高电平期间 SDA 稳定,避免误触发 |
| 应答机制 | 每字节后第 9 个 SCL 时钟,接收方控制 SDA | 检测 ACK/NACK 决定重试或报错;读操作最后发 NACK |
| 地址与方向 | 7 位地址 + R/W 位组成第一个字节 | 内核中通过 addr 和 flags 设置 |
| 重复起始条件 | 不产生停止条件直接再次起始 | 用于组合事务,避免总线被其他主设备抢占 |
调试提示:使用逻辑分析仪抓取 SCL 和 SDA 波形是调试 I2C 驱动最有效的手段。重点关注起始条件、地址字节后的 ACK 位、以及停止条件是否完整。NACK 通常指示从设备不存在、地址错误或从设备忙。