1. I2C总线基础:概述、物理层特性与协议架构
各位同学好,我是老周。做嵌入式十几年,调试过的总线协议少说也有七八种。但要说哪个协议让我又爱又恨,那非 I2C 莫属。爱它接线简单,恨它时序坑多。今天咱们就从最基础的地方开始,把 I2C 的底裤扒干净。
1.1 什么是 I2C 总线?
I2C,全称 Inter-Integrated Circuit,是飞利浦公司在 80 年代搞出来的。说白了,就是给芯片之间聊天用的。你想想看,一个板子上可能有传感器、ADC、显示屏、EEPROM,每个器件都要跟主控通信。要是每个都单独拉几根线,板子早就成蜘蛛网了。
I2C 只用两根线——SDA(数据线)和 SCL(时钟线),就能挂载几十个设备。每个设备有个唯一的地址,主设备点名,从设备应答。就这么简单。
- 两根线:SDA(数据)、SCL(时钟)
- 多主多从,支持总线仲裁
- 标准模式 100 kbps,快速模式 400 kbps,高速模式 3.4 Mbps
- 7 位或 10 位地址,最多挂 127 或 1024 个设备
- 同步、半双工通信
1.2 物理层特性——开漏输出与上拉电阻
这里我要重点讲一下。很多新手搞不懂为什么 I2C 要开漏输出,直接推挽不行吗?
答案是:不行。 因为 I2C 支持多主多从,多个设备可能同时往总线上发数据。如果是推挽输出,一个设备拉高、另一个拉低,那就短路了,芯片直接冒烟。我早年刚入行时就干过这事,把一片 STM32 的 I/O 口烧了,从此记住了这个教训。
开漏输出的原理是这样的:
- 输出低电平时,MOS管导通,把总线拉到 GND
- 输出高电平时,MOS管截止,总线由上拉电阻拉到 VCC
- 多个设备可以同时拉低总线,谁先拉低谁赢——这就是仲裁的基础
上拉电阻的取值很关键。我一般这样选:
| 总线模式 | 推荐上拉电阻 | 总线电容 | 注意事项 |
|---|---|---|---|
| 标准模式 (100 kHz) | 4.7 kΩ ~ 10 kΩ | < 200 pF | 阻值大,功耗低,但上升沿慢 |
| 快速模式 (400 kHz) | 1.5 kΩ ~ 4.7 kΩ | < 100 pF | 阻值小,上升沿快,但功耗大 |
| 高速模式 (3.4 MHz) | 0.5 kΩ ~ 1.5 kΩ | < 50 pF | 需要严格控制走线长度 |
1.3 I2C 总线协议架构
搞清楚了物理层,咱们来看看协议层。I2C 的协议其实不复杂,但细节很多。我把它拆成几个关键动作:
1.3.1 起始条件与停止条件
这是 I2C 通信的开关。SCL 高电平时:
- 起始条件: SDA 从高变低
- 停止条件: SDA 从低变高
为什么要这么设计?因为 SCL 高电平时,SDA 本应保持稳定。利用这个"不该变化"的时刻来发起始/停止信号,从设备就能识别出来。嗯,这里要注意,起始和停止条件只能由主设备发出。
1.3.2 数据传输与应答机制
数据传输以字节为单位,每字节 8 位,后面跟一个应答位(ACK/NACK)。
- 发送方在 SCL 低电平时改变 SDA 数据
- 接收方在 SCL 高电平时采样 SDA
- 第 9 个时钟周期:发送方释放 SDA,接收方拉低表示 ACK,保持高表示 NACK
1.3.3 完整通信流程
一次典型的 I2C 写操作是这样的:
- 主设备发送起始条件
- 主设备发送 7 位从设备地址 + 写位 (0)
- 从设备应答 ACK
- 主设备发送寄存器地址(1 字节或 2 字节)
- 从设备应答 ACK
- 主设备发送数据字节
- 从设备应答 ACK
- 重复步骤 6-7 直到数据发完
- 主设备发送停止条件
读操作稍微复杂一点,需要先写寄存器地址,再重新发送起始条件(这叫重复起始条件),然后发读命令:
- 起始条件 → 从设备地址 + 写位 → ACK
- 发送寄存器地址 → ACK
- 重复起始条件 → 从设备地址 + 读位 (1) → ACK
- 读取数据字节 → 主设备发 ACK(继续读)或 NACK(最后字节)
- 停止条件
1.4 I2C 协议架构总览
下面这张图把 I2C 的协议层次和关键要素串起来了,方便大家建立整体认知: