4. I2C总线协议基础:物理层、时序、地址、读写操作与多主机仲裁
I2C(Inter-Integrated Circuit)总线是嵌入式系统中使用最广泛的板级通信协议之一,尤其适合传感器、EEPROM、ADC/DAC等低速外设的连接。在Zephyr OS的传感器驱动框架中,I2C是默认的首选物理层接口。理解I2C的物理层特性、时序约束、地址机制、读写流程以及多主机仲裁规则,是编写稳定、可移植的传感器驱动的必要前提。
4.1 I2C物理层特性
I2C总线仅需两根双向信号线即可完成多设备通信:
- SDA(Serial Data Line):串行数据线,用于传输地址、控制字和数据。
- SCL(Serial Clock Line):串行时钟线,由主机(Master)驱动,决定通信速率。
所有设备通过开漏(Open-Drain)输出连接到SDA和SCL,外部必须接上拉电阻(典型值4.7kΩ @100kHz,2.2kΩ @400kHz)。这种结构允许任何设备拉低总线(实现时钟拉伸和仲裁),且不会造成短路。
关键参数速查:
- 标准模式(Sm):100 kbit/s
- 快速模式(Fm):400 kbit/s
- 快速模式+(Fm+):1 Mbit/s
- 高速模式(Hs):3.4 Mbit/s
- 最大总线电容:400 pF
- 上拉电阻范围:1kΩ ~ 10kΩ(取决于速率和电容)
Zephyr实践提示: 在设备树中通过
clock-frequency = <100000> 属性配置I2C速率。传感器驱动通常使用标准模式或快速模式,高速模式需要额外的I/O口驱动能力。
4.2 I2C时序与信号约定
I2C通信的每个bit由SCL时钟同步,数据在SCL低电平时变化,高电平时采样。以下是核心时序定义:
| 时序参数 | 符号 | 标准模式 (100kHz) | 快速模式 (400kHz) | 说明 |
|---|---|---|---|---|
| SCL时钟频率 | fSCL | 100 kHz | 400 kHz | 由主机决定 |
| 保持时间(起始条件) | tHD;STA | 4.0 μs | 0.6 μs | SCL低后SDA变化 |
| 建立时间(起始条件) | tSU;STA | 4.7 μs | 0.6 μs | SDA变化到SCL高 |
| 数据保持时间 | tHD;DAT | 0 μs | 0 μs | SCL低后数据有效 |
| 数据建立时间 | tSU;DAT | 250 ns | 100 ns | SCL高前数据稳定 |
| SCL低电平时间 | tLOW | 4.7 μs | 1.3 μs | 时钟低脉冲宽度 |
| SCL高电平时间 | tHIGH | 4.0 μs | 0.6 μs | 时钟高脉冲宽度 |
| 上升/下降时间 | tr / tf | 1000 ns | 300 ns | 受总线电容影响 |
常见陷阱: 传感器驱动中如果I2C时钟拉伸(Clock Stretching)处理不当,会导致通信超时。Zephyr的I2C API默认支持时钟拉伸,但需要确保从机在SCL低电平的保持时间不超过主机设置的超时阈值(通常10ms)。
4.3 设备地址与7位/10位寻址
I2C总线上的每个从机设备都有一个唯一的地址。标准I2C使用7位地址,理论上支持112个设备(保留16个地址)。10位地址扩展了设备容量,但在传感器领域极少使用。
4.3.1 7位地址结构
地址字节在起始条件后发送,格式如下:
| Bit 7 (MSB) | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 (LSB) |
|---|---|---|---|---|---|---|---|
| A6 | A5 | A4 | A3 | A2 | A1 | A0 | R/W# |
- A6~A0:7位设备地址,由芯片厂商分配或通过引脚配置。
- R/W#:读写标志位,0表示主机写(发送),1表示主机读(接收)。
常见传感器地址示例:
- BME280:0x76(ADDR=GND)或 0x77(ADDR=VDDIO)
- MPU6050:0x68(AD0=GND)或 0x69(AD0=VDD)
- LSM6DSO:0x6A(SA0=GND)或 0x6B(SA0=VDD)
reg = <0x76>; 指定地址。
4.3.2 保留地址
以下地址被I2C规范保留,不能分配给从机设备:
| 地址(7位) | 用途 |
|---|---|
| 0x00 | 通用呼叫地址(General Call) |
| 0x01 | 起始字节(Start Byte) |
| 0x02 | CBUS地址 |
| 0x03 | 保留 |
| 0x04 ~ 0x07 | 高速模式主机码 |
| 0x78 ~ 0x7F | 10位地址扩展标识 |
4.4 I2C读写操作流程
I2C通信的基本单元是帧(Frame),每个帧包含一个起始条件、地址字节、若干数据字节、应答位和停止条件。传感器驱动中最常用的两种操作模式是写寄存器和读寄存器。
4.4.1 写操作(主机→从机)
- 主机发送起始条件(S)。
- 主机发送7位地址 + W(0),等待从机ACK。
- 主机发送寄存器地址(1字节或2字节),等待从机ACK。
- 主机发送数据字节,每字节后等待从机ACK。
- 主机发送停止条件(P)。
4.4.2 读操作(主机←从机)
- 主机发送起始条件(S)。
- 主机发送7位地址 + W(0),等待从机ACK。
- 主机发送寄存器地址,等待从机ACK。
- 主机发送重复起始条件(Sr)。
- 主机发送7位地址 + R(1),等待从机ACK。
- 主机读取数据字节,每字节后主机发送ACK(继续读)或NACK(最后一字节)。
- 主机发送停止条件(P)。
Zephyr API对应:
// 写操作:i2c_burst_write + i2c_reg_write_byte
int ret = i2c_reg_write_byte(dev, 0x76, 0x1D, 0x01); // 向地址0x76的寄存器0x1D写入0x01
// 读操作:i2c_burst_read + i2c_reg_read_byte
uint8_t val;
ret = i2c_reg_read_byte(dev, 0x76, 0x1D, &val); // 从地址0x76的寄存器0x1D读取1字节
4.5 多主机仲裁与时钟同步
I2C支持多个主机同时连接到总线,通过仲裁(Arbitration)机制决定哪个主机获得总线控制权。仲裁发生在SDA线上,基于“线与”特性:
- 仲裁原则:多个主机同时发送数据时,谁先拉低SDA谁就赢得仲裁。发送高电平但检测到SDA为低电平的主机失去仲裁,立即释放总线。
- 时钟同步:多个主机可能产生不同频率的SCL,通过“线与”机制,SCL的低电平时间由最慢的主机决定,高电平时间由最快的主机决定,最终同步出一个共同的时钟。
| 场景 | 仲裁结果 | 处理方式 |
|---|---|---|
| 地址匹配 + 数据相同 | 所有主机继续通信 | 无冲突,正常传输 |
| 地址匹配 + 数据不同 | 发送0的主机获胜 | 失败主机释放总线,可重试 |
| 地址不同 | 地址值小的主机获胜 | 失败主机检测到地址不匹配,释放总线 |