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 周期,需与硬件上拉电阻匹配。
1.3 起始条件与停止条件
I2C 通信的每次传输以起始条件(START)开始,以停止条件(STOP)结束。这两个条件是总线上的特殊信号序列,由主设备产生。
| 条件 | SDA 状态 | SCL 状态 | 时序描述 |
|---|---|---|---|
| 起始条件(S) | 高 → 低 | 高 | SCL 为高时,SDA 从高电平切换到低电平 |
| 停止条件(P) | 低 → 高 | 高 | SCL 为高时,SDA 从低电平切换到高电平 |
| 重复起始条件(Sr) | 高 → 低 | 高 | 在未发停止条件前,再次产生起始条件 |
常见错误: 起始条件和停止条件必须在 SCL 高电平时采样 SDA 的跳变。如果 SDA 在 SCL 低电平时跳变,会被视为普通数据位变化,而非起始/停止条件。驱动开发中,I2C 控制器硬件自动处理这些时序,但软件需确保不违反总线状态机。
1.4 数据帧格式
I2C 数据帧以字节(8 位)为单位传输,每个字节后跟一个应答位(ACK/NACK)。完整的传输帧结构如下:
- 起始条件(S):主设备产生。
- 从设备地址(7 位或 10 位):高位在前,低位在后。
- 读/写位(R/W):0 表示写,1 表示读。
- 应答位(ACK):从设备在第 9 个 SCL 周期拉低 SDA 表示应答。
- 数据字节(8 位 × N):每个字节后跟一个应答位。
- 停止条件(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 上升沿 |
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()实现协议层交互。