1. I2C总线协议基础

I2C(Inter-Integrated Circuit)总线是由 Philips 公司(现 NXP)在 1980 年代推出的一种同步、多主从、串行通信总线。它仅需两根信号线即可完成板级芯片间的数据交换,是嵌入式系统中使用最广泛的低速外设总线之一。理解 I2C 的物理层特性、信号时序、拓扑结构和地址机制,是编写正确、健壮的 I2C 设备驱动的第一步。

1.1 I2C 总线物理层特性

I2C 总线在物理层上采用开漏(Open-Drain)结合上拉电阻的架构。所有设备的 SCL(时钟线)和 SDA(数据线)引脚均为开漏输出,通过外部上拉电阻连接到电源电压 VDD。

参数 标准模式 (100 kHz) 快速模式 (400 kHz) 高速模式 (3.4 MHz)
最大时钟频率 100 kHz 400 kHz 3.4 MHz
总线最大电容 400 pF 400 pF 100 pF
上拉电阻典型值 4.7 kΩ 2.2 kΩ 1.0 kΩ
逻辑低电平 (VIL) ≤ 0.3 × VDD ≤ 0.3 × VDD ≤ 0.3 × VDD
逻辑高电平 (VIH) ≥ 0.7 × VDD ≥ 0.7 × VDD ≥ 0.7 × VDD
驱动开发要点: 在 Linux 内核中,I2C 适配器驱动负责控制 SCL 频率和时序。设备驱动通常不需要关心物理层上拉电阻值,但必须确保从设备支持主控器所配置的总线速率。不匹配的速率可能导致通信失败或数据损坏。

1.2 SCL 与 SDA 信号

SCL(Serial Clock)和 SDA(Serial Data)是 I2C 总线上仅有的两根信号线。所有数据传输都遵循严格的时序规则:

  • 空闲状态: SCL 和 SDA 均为高电平(由上拉电阻拉高)。
  • 起始条件(START): SCL 为高电平时,SDA 由高电平切换到低电平。表示一次传输开始。
  • 停止条件(STOP): SCL 为高电平时,SDA 由低电平切换到高电平。表示一次传输结束。
  • 数据采样: 数据在 SCL 的上升沿被采样,在 SCL 的低电平期间允许 SDA 变化。
  • 应答位(ACK/NACK): 每传输 8 位数据后,接收方在第 9 个 SCL 周期拉低 SDA(ACK)或保持高电平(NACK)。
关键信号时序图: 起始条件、数据位传输和停止条件的波形关系如下所示。
I2C 总线信号时序图 I2C 总线信号时序图(起始、数据、停止) SCL SDA 起始 数据位 (MSB) 停止

1.3 I2C 总线拓扑结构

I2C 总线采用多主从总线拓扑,所有设备通过 SCL 和 SDA 两条线并联连接。每个设备都有一个唯一的地址,主控器通过地址选择通信对象。

  • 主控器(Master): 发起总线传输、产生时钟信号、发送起始/停止条件的设备。一个 I2C 总线上可以存在多个主控器,通过仲裁机制解决冲突。
  • 从设备(Slave): 被主控器寻址并响应传输的设备。从设备不产生时钟,只被动接收或发送数据。
  • 总线仲裁: 当多个主控器同时尝试控制总线时,通过 SDA 线的“线与”特性进行仲裁。输出低电平的主控器赢得仲裁。
I2C 总线拓扑结构图 I2C 总线多主从拓扑结构 RP RP VDD SCL SDA 主控器 1 主控器 2 从设备 1 从设备 2 从设备 3 所有设备共享 SCL 和 SDA 总线,通过唯一地址区分。主控器可发起通信,从设备只能响应。

1.4 I2C 设备地址

每个 I2C 从设备都有一个7 位或 10 位的芯片地址。在 Linux 内核中,绝大多数设备使用 7 位地址。地址由设备制造商固定或通过引脚电平配置。

  • 7 位地址: 范围 0x00 ~ 0x7F。其中 0x00 为通用呼叫地址,0x01 ~ 0x03 保留,0x04 ~ 0x77 可用,0x78 ~ 0x7F 为 10 位地址预留。
  • 10 位地址: 范围 0x00 ~ 0x3FF。通过第一个字节的前 5 位(11110xx)标识。
  • 读写位: 地址字节的最低位为 R/W 位。0 表示主控器写,1 表示主控器读。
常见驱动错误: 设备数据手册中给出的地址通常是 7 位地址(如 0x48),但有些手册会给出 8 位地址(左移 1 位后的值)。在 Linux 内核中,i2c_client 的 addr 字段必须使用 7 位地址。务必确认手册地址格式,否则驱动将无法正确寻址设备。
I2C 7 位地址帧格式 I2C 7 位地址帧格式 S A6 A5 A4 A3 A2 A1 A0 R/W ACK

1.5 数据传输流程

一次完整的 I2C 数据传输遵循以下步骤:

  1. 主控器发送起始条件(START)。
  2. 主控器发送 7 位从设备地址 + R/W 位。
  3. 从设备应答(ACK): 地址匹配的从设备拉低 SDA 表示应答。
  4. 数据传输: 主控器(写模式)或从设备(读模式)逐字节发送数据,每字节后跟一个应答位。
  5. 主控器发送停止条件(STOP)或重复起始条件(Repeated START)。
内核驱动视角: Linux 内核的 I2C 核心层封装了上述流程。驱动开发者只需填充 i2c_msg 结构体数组,指定地址、方向、缓冲区和长度,然后调用 i2c_transfer() 即可。无需手动操作 SCL/SDA 时序。
// Linux 内核 I2C 消息结构体定义
struct i2c_msg {
    __u16 addr;    // 从设备 7 位地址
    __u16 flags;   // 传输标志:I2C_M_RD 表示读,0 表示写
    __u16 len;     // 消息数据长度(字节数)
    __u8 *buf;     // 数据缓冲区指针
};

通过本章的学习,你已经掌握了 I2C 总线的物理层特性、信号时序、拓扑结构和地址机制。这些基础知识是后续编写 I2C 设备驱动、配置设备树和调试通信问题的前提。