1. I2C 总线概述
I2C(Inter-Integrated Circuit)总线由 Philips 半导体(现 NXP)于 1982 年发明,最初用于电视接收机内部芯片间的低速通信。经过 40 余年发展,它已成为嵌入式系统中最广泛使用的板级总线之一,被几乎所有微控制器、传感器、存储器、I/O 扩展芯片所支持。
1.1 总线历史与演进
| 版本 | 发布年份 | 最大速率 | 关键特性 |
|---|---|---|---|
| Standard-mode (Sm) | 1982 | 100 kbit/s | 原始规范,7 位地址 |
| Fast-mode (Fm) | 1992 | 400 kbit/s | 兼容 Sm,增加快速模式器件 |
| Fast-mode Plus (Fm+) | 2007 | 1 Mbit/s | 更强驱动能力,更大总线电容 |
| High-speed mode (Hs) | 2000 | 3.4 Mbit/s | 主从间电流提升传输,需额外 I/O 电平 |
| Ultra Fast-mode (UFm) | 2012 | 5 Mbit/s | 单向推挽,不再需要上拉电阻 |
驱动开发提示: 当前嵌入式 Linux 内核 I2C 子系统原生支持 Sm、Fm、Fm+ 模式。Hs 模式需要芯片级特殊时序支持,UFm 因单向特性较少用于传统 I2C 从设备。
1.2 物理层特性:SDA 与 SCL
I2C 总线仅使用两条双向信号线:
- SDA(Serial Data Line) — 串行数据线,用于传输地址和数据。
- SCL(Serial Clock Line) — 串行时钟线,由主设备产生,同步所有数据传输。
两条线均为开漏(open-drain)结构,即器件只能将线拉低,不能主动拉高。高电平由外部上拉电阻提供。这种结构允许多个主设备共存,且支持线与(wired-AND)仲裁。
关键概念: 开漏输出 + 上拉电阻 = 多主设备仲裁的基础。任何设备都可以拉低总线,但只有所有设备都释放时总线才会回到高电平。
1.3 电气特性与上拉电阻选择
上拉电阻的取值直接影响总线功耗、信号上升时间和最大通信速率。选择不当会导致信号畸变、通信失败甚至器件损坏。
上拉电阻计算公式
最小上拉电阻受 VOL 和 IOL 限制:
Rp(min) = (VDD - VOL(max)) / IOL(max)
其中:
VDD = 总线供电电压(典型 3.3V 或 5V)
VOL(max)= 输出低电平最大电压(标准 0.4V @ 3mA)
IOL(max)= 器件最大灌电流(标准 3mA)
最大上拉电阻由总线电容 Cb 和所需上升时间 tr 决定:
Rp(max) = tr / (0.8473 × Cb)
其中:
tr = 最大允许上升时间(Sm: 1000ns, Fm: 300ns, Fm+: 120ns)
Cb = 总线总电容(PCB 走线 + 器件引脚电容,典型 10~400pF)
常见场景推荐值
| 总线模式 | VDD | 总线电容 | 推荐 Rp | 说明 |
|---|---|---|---|---|
| Standard (100kHz) | 3.3V | ~100pF | 4.7kΩ | 最通用,兼容性好 |
| Fast (400kHz) | 3.3V | ~100pF | 2.2kΩ | 平衡功耗与速度 |
| Fast+ (1MHz) | 3.3V | ~50pF | 1.0kΩ | 需强驱动,注意功耗 |
| 多从设备 (≥8个) | 5.0V | ~400pF | 1.8kΩ | 补偿大电容,保证上升沿 |
驱动开发避坑: 在 Linux 内核中,I2C 适配器驱动通常不直接控制上拉电阻。但设备树中
clock-frequency 属性会影响总线时序参数。如果硬件上拉电阻选择过小(如 330Ω),会导致 SCL/SDA 低电平无法被拉低到 VOL 以下,造成总线锁死。务必在原理图阶段确认 Rp 取值。
1.4 总线空闲状态与起始/停止条件
理解总线空闲状态是编写 I2C 驱动的基础:
- 空闲状态: SDA 和 SCL 均为高电平(由上拉电阻维持)。
- 起始条件(START): SCL 为高电平时,SDA 从高电平切换到低电平。
- 停止条件(STOP): SCL 为高电平时,SDA 从低电平切换到高电平。
1.5 地址与数据传输基本单元
每个 I2C 从设备拥有唯一的 7 位或 10 位地址。主设备通过起始条件后发送的地址字节选择从设备:
- 7 位地址模式: 地址字节高 7 位为地址,最低位为 R/W(0=写,1=读)。
- 10 位地址模式: 第一个字节高 5 位为 11110,后跟地址高 2 位 + R/W;第二个字节为地址低 8 位。
每个字节传输后,接收方必须发送一个应答位(ACK):
- ACK(0): 接收方在第 9 个 SCL 周期将 SDA 拉低,表示成功接收。
- NACK(1): 接收方在第 9 个 SCL 周期保持 SDA 高电平,表示未就绪或传输结束。
驱动开发要点: Linux I2C 核心层自动处理地址发送和 ACK/NACK 检测。驱动开发者只需通过
struct i2c_msg 填充地址、数据缓冲和读写标志,由 i2c_transfer() 完成总线事务。NACK 会以错误码 -EIO 或 -EREMOTEIO 返回。