I2C协议深度解析:物理层、时序、起始/停止条件、数据有效性、应答机制

各位同学,咱们今天来啃I2C协议这块硬骨头。说实话,我见过不少工程师,写I2C驱动时全靠复制粘贴,一旦遇到波形异常就抓瞎了。我个人习惯是,先把协议底层的物理特性和时序搞透,再去写代码。这样出了问题,你心里有底,知道该查哪里。

2.1 物理层:两条线怎么工作的?

I2C总线就两条线:SCL(时钟线)和SDA(数据线)。但你别小看这两条线,它们的设计其实很巧妙。

开漏输出 + 上拉电阻,这是I2C物理层的核心。什么叫开漏?说白了,就是芯片的引脚只能拉低电平,不能主动拉高。高电平靠外部的上拉电阻来实现。

关键点: 总线空闲时,SCL和SDA都是高电平。谁想占用总线,谁就把SDA拉低。

我在项目中遇到过一个问题:两个从设备地址冲突了,结果总线一直被拉低,主设备根本发不了起始条件。排查了半天,才发现是地址没配对。

参数 标准模式 (100kHz) 快速模式 (400kHz) 高速模式 (3.4MHz)
上拉电阻典型值 4.7kΩ 2.2kΩ 1kΩ
总线电容上限 400pF 400pF 100pF
最大从设备数量 127(7位地址) 127 127

选电阻的小技巧: 总线长度越长、设备越多,上拉电阻就要选小一点。我一般先按4.7kΩ起步,如果波形上升沿太缓,再换2.2kΩ。千万别用10kΩ以上,否则高速模式下波形会变形。

2.2 时序:时钟是怎么控制数据的?

I2C的时序其实不复杂,但有几个关键参数你必须记住。嗯,这里我建议你用示波器实际抓一下波形,比看一百遍文档都管用。

SCL低电平时间(tLOW) 和 SCL高电平时间(tHIGH) 决定了时钟频率。标准模式下,tLOW ≥ 4.7μs,tHIGH ≥ 4.0μs。

你想想看,如果主设备时钟跑得太快,从设备跟不上,就会丢数据。我曾经调试一个温湿度传感器,读出来的数据总是跳变,最后发现是主设备时钟频率设成了400kHz,但那个传感器只支持100kHz。

避坑指南: 同一个I2C总线上挂多个从设备时,时钟频率要以最慢的那个为准。我曾经因为一个老旧的EEPROM拖慢了整条总线,排查了整整一天。

2.3 起始条件与停止条件

这两个条件是I2C通信的“开关”。没有它们,总线上的设备不知道什么时候开始、什么时候结束。

  • 起始条件(S): SCL为高电平时,SDA从高电平跳变到低电平。
  • 停止条件(P): SCL为高电平时,SDA从低电平跳变到高电平。

说白了,起始条件和停止条件都是SDA在SCL高电平期间发生的跳变。而正常数据传输时,SDA只能在SCL低电平期间变化。这个区别很重要,你写驱动时一定要保证时序正确。

注意: 起始条件之后,总线就被主设备占用了。直到停止条件发出,总线才释放。如果主设备挂了,没发停止条件,总线就死锁了。我遇到过这种情况,最后只能硬件复位。

2.4 数据有效性:什么时候读数据?

I2C协议规定:数据在SCL高电平期间必须保持稳定。也就是说,从设备只能在SCL低电平期间采样数据,在SCL高电平期间读取。

为什么会这样?因为SDA在SCL低电平时可以变化,高电平时必须稳定。这样从设备只需要在SCL的上升沿或高电平期间锁存数据就行了。

我记得刚学I2C时,总搞不清什么时候发数据、什么时候收数据。后来总结了一句话:SCL高电平读,SCL低电平写。简单吧?

2.5 应答机制:确认收到没?

I2C的应答机制是它的一大特色。每传输一个字节(8位)数据后,接收方必须发送一个应答位(ACK)或非应答位(NACK)。

  • ACK(应答): 第9个SCL时钟周期,接收方将SDA拉低,表示“收到了,继续发”。
  • NACK(非应答): 第9个SCL时钟周期,接收方释放SDA(保持高电平),表示“没收到”或“不想收了”。

实际应用: 读传感器数据时,主设备在最后一个字节发NACK,然后发停止条件。这样从设备就知道数据发完了,别再发了。我刚开始写驱动时忘了发NACK,结果从设备一直发数据,总线乱成一团。

2.6 完整通信流程

一个完整的I2C通信流程是这样的:

  1. 主设备发起始条件(S)
  2. 主设备发7位从设备地址 + 1位读写位(0=写,1=读)
  3. 从设备发ACK应答
  4. 主设备发寄存器地址(如果是写操作)
  5. 从设备发ACK应答
  6. 数据传输(每字节后跟ACK/NACK)
  7. 主设备发停止条件(P)

下面这张图把整个流程画清楚了,你对照着看:

I2C完整通信流程图 I2C 完整通信流程 起始条件 (S) 地址 + 读写位 从设备 ACK 寄存器地址 从设备 ACK 数据传输 (8位) ACK/NACK 停止条件 (P) 重复传输直到数据结束 说明: • 起始条件后,总线被主设备独占,直到停止条件发出 • 每传输一个字节(8位),接收方必须回复ACK或NACK • 读操作时,主设备在最后一个字节发NACK,然后发停止条件

2.7 常见问题与调试技巧

最后,我分享几个实际调试中遇到的问题,你以后肯定也会碰到:

问题现象 可能原因 解决方法
总线一直为低 某个设备拉死了总线 逐个断开从设备,找到问题设备
没有ACK应答 地址错误或设备未上电 用示波器抓地址波形,核对地址
数据错位 时钟频率太快 降低SCL频率,或加长上升时间
随机数据跳变 上拉电阻太大 换小阻值上拉电阻

重要提醒: 调试I2C时,示波器是你的好朋友。别光看代码,波形会告诉你真相。我曾经花了两天查一个bug,最后发现是PCB走线太长,导致信号反射。换了个短一点的飞线就好了。