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 需要严格控制走线长度
我的经验: 如果拿不准,先焊 4.7 kΩ。示波器看上升沿,如果太缓就换小一点的。我曾经在一个项目里用了 10 kΩ,结果 400 kHz 下波形都快成三角波了,通信时好时坏。换了 2.2 kΩ 后一切正常。

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
常见坑: 很多新手写 I2C 驱动时,忘了在发送完 8 位数据后释放 SDA 线。结果从设备想拉低应答,却发现主设备还在死抓着 SDA 不放。我曾经调试一个 EEPROM 驱动,折腾了两天才发现是 GPIO 方向没切换。

1.3.3 完整通信流程

一次典型的 I2C 写操作是这样的:

  1. 主设备发送起始条件
  2. 主设备发送 7 位从设备地址 + 写位 (0)
  3. 从设备应答 ACK
  4. 主设备发送寄存器地址(1 字节或 2 字节)
  5. 从设备应答 ACK
  6. 主设备发送数据字节
  7. 从设备应答 ACK
  8. 重复步骤 6-7 直到数据发完
  9. 主设备发送停止条件

读操作稍微复杂一点,需要先写寄存器地址,再重新发送起始条件(这叫重复起始条件),然后发读命令:

  1. 起始条件 → 从设备地址 + 写位 → ACK
  2. 发送寄存器地址 → ACK
  3. 重复起始条件 → 从设备地址 + 读位 (1) → ACK
  4. 读取数据字节 → 主设备发 ACK(继续读)或 NACK(最后字节)
  5. 停止条件

1.4 I2C 协议架构总览

下面这张图把 I2C 的协议层次和关键要素串起来了,方便大家建立整体认知:

I2C 总线协议架构总览 物理层 • 两根线:SDA(数据线) + SCL(时钟线) • 开漏输出 + 上拉电阻(4.7kΩ 典型值) • 总线电容限制:标准模式 < 200pF,快速模式 < 100pF 链路层 • 起始条件:SCL高时 SDA 下降沿 • 停止条件:SCL高时 SDA 上升沿 • 数据采样:SCL高电平时采样 SDA,SCL低电平时改变 SDA 传输层 • 7位/10位从设备地址 + 读写位(R/W) • 应答机制:每字节后跟 ACK(低)或 NACK(高) • 重复起始条件:不停止总线直接切换读写方向 应用层 • 寄存器读写协议、SMBus、PMBus 等上层协议

1.5 常见问题与避坑指南

总线锁死: 这是 I2C 最经典的故障。从设备拉低 SCL 或 SDA 后不释放,主设备干等。我曾经在一个温控项目中遇到从设备死机,总线一直低。解决办法:在驱动里加超时机制,超时后发 9 个时钟脉冲让从设备复位,或者直接硬件复位从设备。
上拉电阻选型口诀: 低速用大阻值省电,高速用小阻值保波形。总线设备多、走线长,阻值往小了选。我一般 100 kHz 用 4.7kΩ,400 kHz 用 2.2kΩ,心里踏实。