1. I2C总线概述:历史、物理层与多主机仲裁

I2C(Inter-Integrated Circuit)总线由飞利浦半导体(现恩智浦)于1982年推出,最初用于电视接收机内部各IC之间的低速通信。其设计目标是以最少的引脚(仅需两根线)实现多主多从、同步、半双工的数据传输。经过40余年发展,I2C已成为嵌入式系统中最广泛使用的板级总线之一,被纳入几乎所有MCU和SoC的外设标准。

核心设计哲学: 用两根线(SDA + SCL)解决板级多设备互联,支持多主机仲裁,无需片选信号,地址由软件决定。

1.1 物理层特性:SDA与SCL

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

  • SDA(Serial Data Line):串行数据线,用于传输地址、数据和控制位。
  • SCL(Serial Clock Line):串行时钟线,由主机产生,同步所有数据传输。

两条线均为开漏(Open-Drain)输出结构,通过外部上拉电阻接至VDD(通常为3.3V或5V)。当总线空闲时,SDA和SCL均保持高电平。

为什么用开漏? 开漏结构允许多个设备同时驱动同一根线而不发生短路——任何设备拉低总线时,总线即被拉低;只有所有设备都释放(高阻),总线才被上拉至高电平。这是多主机仲裁和时钟拉伸的物理基础。

电气连接标准

参数 标准模式 (100 kHz) 快速模式 (400 kHz) 高速模式 (3.4 MHz)
最大SCL频率 100 kHz 400 kHz 3.4 MHz
总线最大电容 400 pF 400 pF 100 pF
上拉电阻典型值 4.7 kΩ 2.2 kΩ 1.0 kΩ
最大从设备数量 127(7位地址) 127 127
VIL 最大 0.3 × VDD 0.3 × VDD 0.3 × VDD
VIH 最小 0.7 × VDD 0.7 × VDD 0.7 × VDD
上拉电阻选择关键: 电阻过小会导致功耗增大、上升沿变缓;电阻过大则无法在SCL周期内将总线拉至高电平。实际设计中需根据总线电容和SCL频率计算,常用公式:Rp(min) = (VDD - VOL(max)) / IOL,Rp(max) = tr / (0.8473 × Cb)。

1.2 电气连接拓扑

I2C总线采用总线型拓扑,所有设备的SDA引脚并联至SDA线,SCL引脚并联至SCL线。每个设备都有一个唯一的7位或10位地址。主机通过地址选择从设备,无需片选信号。

I2C总线电气连接拓扑 Rp Rp VDD SDA SCL 主机 (Master) MCU / SoC 从设备 1 地址 0x50 从设备 2 地址 0x68 从设备 3 传感器 说明: 所有设备的SDA和SCL引脚分别并联至两根总线。上拉电阻Rp将总线默认拉高。 任何设备拉低总线时,总线即被拉低;所有设备释放时,总线恢复高电平。

1.3 多主机仲裁机制

I2C总线支持多主机(Multi-Master)工作模式,即多个主机可以同时尝试控制总线。仲裁机制确保最终只有一个主机赢得总线控制权,且不会丢失数据。

仲裁过程

  1. 总线空闲检测: 每个主机在启动传输前检测SDA和SCL是否均为高电平(空闲状态)。
  2. 同时启动: 多个主机可能在检测到空闲后几乎同时产生START条件(SCL高电平时SDA下降沿)。
  3. 逐位仲裁: 在SCL的每个时钟周期,多个主机同时向SDA发送数据位。如果某个主机试图拉低SDA(输出0),而另一个主机试图释放SDA(输出1),则SDA实际被拉低(0)。
  4. 失败主机退出: 发送1但检测到SDA为0的主机知道自己失去了仲裁,立即停止驱动SDA和SCL,转为从模式监听总线。
  5. 获胜主机继续: 赢得仲裁的主机继续完成剩余的数据传输,整个过程对从设备透明。
关键特性: I2C仲裁不会丢失数据——失败主机在仲裁过程中发送的地址或数据位与获胜主机一致,因此从设备不会收到错误信息。仲裁既可在地址阶段发生,也可在数据阶段发生。

仲裁与时钟同步

多主机仲裁与时钟同步(Clock Synchronization)紧密配合。多个主机可能产生不同频率的SCL时钟,通过以下方式同步:

  • 所有主机在SCL高电平期间检测总线状态。
  • 如果某个主机拉低SCL(开始低电平期),其他主机检测到SCL被拉低后,也立即进入低电平等待状态。
  • 所有主机释放SCL后,SCL才被上拉至高电平,开始下一个时钟周期。
  • 最终,SCL的低电平时间由最慢的主机决定,高电平时间由最快的主机决定。
I2C多主机仲裁流程 主机 A 主机 B SDA 总线 检测空闲 SDA=1, SCL=1 检测空闲 SDA=1, SCL=1 发送START SDA↓ while SCL=1 发送START SDA↓ while SCL=1 发送地址位 A: 0, B: 1 发送地址位 A: 0, B: 1 赢得仲裁 继续传输 失去仲裁 转为从模式 仲裁结果: 主机A发送0(拉低SDA),主机B发送1(释放SDA)。SDA被拉低为0,主机B检测到SDA≠其发送值, 立即退出仲裁。主机A继续传输,整个过程无数据丢失。

1.4 总线状态与信号时序

I2C总线定义了几种关键信号状态,所有设备必须严格遵守:

信号 SDA SCL 说明
空闲(Idle) 高电平 高电平 总线空闲,可启动传输
START条件 下降沿 高电平 主机启动传输,SDA在SCL高电平时由高变低
STOP条件 上升沿 高电平 主机结束传输,SDA在SCL高电平时由低变高
数据有效 稳定数据 高电平 SCL高电平时SDA数据被采样,必须保持稳定
数据变化 允许变化 低电平 SCL低电平时SDA可以变化,准备下一位数据
应答(ACK) 低电平 高电平 接收方在第9个SCL周期拉低SDA表示应答
非应答(NACK) 高电平 高电平 接收方不拉低SDA,表示未应答或结束
时钟拉伸(Clock Stretching): 从设备可以在SCL低电平期间拉低SCL,迫使主机等待,直到从设备准备好继续传输。这是I2C实现流控的唯一机制,在低速传感器和EEPROM中常见。Linux I2C子系统通过 i2c_adapter 的 quirks 字段支持时钟拉伸检测。

1.5 地址与传输格式

I2C使用7位或10位地址。7位地址最多支持127个从设备(地址0x00为广播地址,0x01~0x7F可用)。每个传输帧包含:

  • START条件
  • 7位从设备地址 + 读/写位(R/W):R/W=0表示主机写,R/W=1表示主机读
  • 应答位(ACK/NACK):从设备应答
  • 数据字节(1~N字节):每个字节后跟随应答位
  • STOP条件
// I2C传输帧结构(主机写示例)
START | 7位地址 | R/W=0 | ACK | 数据字节1 | ACK | 数据字节2 | ACK | ... | STOP

// I2C传输帧结构(主机读示例)
START | 7位地址 | R/W=1 | ACK | 数据字节1 | ACK | 数据字节2 | ACK | ... | NACK | STOP
Linux内核中的I2C消息结构: 在Linux I2C子系统中,一次传输由多个 struct i2c_msg 组成,支持组合传输(Combined Transfer),即先写寄存器地址再读数据,无需STOP/START间隔。

1.6 本章小结