1. I2C 总线协议基础
1.1 I2C 总线历史与演进
I2C(Inter-Integrated Circuit)总线由 Philips 半导体(现 NXP)于 1982 年推出,最初用于电视接收机中微控制器与外设之间的低速通信。其设计目标是以最少的引脚数(仅需两根线)实现板级多设备互联。
| 版本 | 推出年份 | 最大速率 | 关键特性 |
|---|---|---|---|
| Standard-mode | 1982 | 100 kbit/s | 7 位地址,基本时序 |
| Fast-mode | 1992 | 400 kbit/s | 兼容标准模式,速率提升 |
| Fast-mode Plus | 2007 | 1 Mbit/s | 更强驱动能力,更多从设备 |
| High-speed mode | 1998 | 3.4 Mbit/s | 主设备间高速传输,需特殊 I/O 缓冲 |
| Ultra Fast-mode | 2012 | 5 Mbit/s | 单向推挽,移除上拉电阻 |
嵌入式调优提示: 在 Linux I2C 驱动中,
struct i2c_adapter 的 class 字段和 quirks 结构体用于声明适配器支持的模式与速率上限。调优 DMA 时需确认硬件是否支持 Fast-mode Plus 或 High-speed mode,否则 DMA 传输可能因时序不匹配而失败。
1.2 物理层特性
I2C 总线仅使用两条双向信号线:SDA(串行数据线)和 SCL(串行时钟线)。两条线通过上拉电阻连接到电源电压(通常为 1.8V、3.3V 或 5V)。
- 开漏输出: 所有设备通过开漏(open-drain)或开集电极(open-collector)驱动 SDA 和 SCL。总线空闲时,两条线均为高电平。
- 线与逻辑: 多个设备可同时驱动总线低电平,实现仲裁(arbitration)和时钟同步(clock synchronization)。
- 上拉电阻: 阻值取决于总线电容和速率,典型值为 1 kΩ ~ 10 kΩ。高速模式需更小阻值以缩短上升时间。
- 最大总线电容: 标准模式 400 pF,Fast-mode Plus 可达 550 pF,超过此值需使用缓冲器或中继器。
1.3 SDA/SCL 信号时序
I2C 通信基于 SCL 时钟同步 SDA 数据。每个时钟脉冲传输 1 位数据。关键时序事件如下:
- 起始条件 (START): SCL 为高电平时,SDA 从高电平切换到低电平。由主设备产生,表示总线从空闲变为忙碌。
- 停止条件 (STOP): SCL 为高电平时,SDA 从低电平切换到高电平。由主设备产生,表示总线释放。
- 数据有效性: SCL 高电平期间,SDA 必须保持稳定;SDA 仅在 SCL 低电平期间允许变化。
- 应答位 (ACK/NACK): 每传输 8 位数据后,接收方在第 9 个 SCL 脉冲拉低 SDA(ACK)或保持高电平(NACK)。
驱动开发关键点: Linux 内核中
i2c_msg 结构体的 flags 字段控制起始/停止条件的生成。DMA 调优时需注意:I2C_M_NOSTART 和 I2C_M_STOP 标志位会影响 DMA 链表的构建方式,错误设置可能导致总线协议违规。
1.4 7 位与 10 位地址模式
I2C 总线使用地址来区分不同的从设备。地址在起始条件后立即发送,占用第一个字节的高 7 位或高 10 位。
7 位地址模式
- 地址范围:0x00 ~ 0x7F(128 个地址),其中 0x00 为通用呼叫地址,0x01 ~ 0x07 保留,实际可用约 112 个。
- 传输格式:起始条件后发送 1 字节,高 7 位为地址,最低位为 R/W 方向位(0=写,1=读)。
- 示例:地址 0x50(AT24C02 EEPROM)写操作时发送 0xA0(0x50 << 1 | 0)。
10 位地址模式
- 地址范围:0x00 ~ 0x3FF(1024 个地址),与 7 位地址兼容。
- 传输格式:起始条件后先发送 5'b11110 + 地址高 2 位 + R/W,再发送剩余 8 位地址。
- 第一个字节的高 5 位固定为 11110,用于与 7 位地址区分。
| 特性 | 7 位地址 | 10 位地址 |
|---|---|---|
| 最大从设备数 | 112(实际可用) | 1024 |
| 地址字节数 | 1 字节(含 R/W) | 2 字节 |
| 兼容性 | 所有设备支持 | 需从设备硬件支持 |
| Linux 内核表示 | addr & 0x7F |
addr | I2C_TENBIT |
| 典型应用 | 传感器、EEPROM、RTC | 多路复用器、复杂 SoC 外设 |
常见陷阱: 在 Linux I2C 驱动中,
i2c_client 的 addr 字段存储的是 7 位地址(左对齐)。如果从设备使用 10 位地址,必须在 i2c_board_info 中设置 I2C_CLIENT_TEN 标志。DMA 调优时,10 位地址的传输需要额外的字节,可能影响 DMA 描述符的预分配大小。
1.5 总线仲裁与时钟同步
I2C 总线支持多主设备环境,通过以下机制避免冲突:
- 仲裁 (Arbitration): 多个主设备同时驱动 SDA 时,输出低电平的设备获胜。仲裁在 SDA 上进行,不损失数据。
- 时钟同步: 多个主设备将 SCL 拉低,最长的低电平周期决定 SCL 低电平时间;最短的高电平周期决定 SCL 高电平时间。所有主设备同步到统一的时钟。
- 丢失仲裁: 检测到 SDA 电平与自身输出不一致的主设备立即释放 SDA 和 SCL,转为从设备模式。
驱动实现要点: 在
i2c_algorithm 的 master_xfer 回调中,必须处理 -EAGAIN 返回值以支持重试。DMA 调优时,仲裁失败可能导致 DMA 传输中途终止,需在中断处理中正确重置 DMA 通道状态。