第三章 I2C时序基础:起始、停止、数据有效性、时钟同步
这一章我们聊聊I2C通信的“起承转合”。说白了,就是每个传输怎么开始、怎么结束,数据到底什么时候才能读,以及多个从设备或多个主设备怎么在一条总线上和谐共处。这些细节,很多刚入行的工程师容易忽略,但恰恰是解码时最容易踩坑的地方。
我刚开始调I2C的时候,拿着逻辑分析仪抓波形,总以为只要看到SCL和SDA有变化就行。后来发现,时序边界哪怕偏差一两个微秒,数据就可能全错。嗯,咱们从最基础的开始——起始条件和停止条件。
3.1 起始条件(S)与停止条件(P)
I2C总线在空闲状态时,SCL和SDA都是高电平(上拉电阻拉高)。所有通信动作,都从SCL高电平时SDA的一个下降沿开始,这就是起始条件。反过来,SCL高电平时SDA的上升沿就是停止条件。有点绕?我记了个诀窍:
- S(Start):SDA在SCL高时从高变低——有点像“低头出发”。
- P(Stop):SDA在SCL高时从低变高——好比“抬头结束”。
避坑提醒:我曾经在一个项目中,从设备因为SCL线上的毛刺把一次停止条件误判为另一个起始条件,导致整个数据帧错位。后来我在SCL上加了一个小电容滤波,问题就解决了。逻辑分析仪抓图时,一定要看清SCL高电平区间内SDA是否有多次跳变,那往往是噪声。
起始和停止条件必须由主设备产生。从设备不能主动拉SDA来生成P,但有些从设备会通过拉低SCL来“拉伸时钟”,后面会讲到。
表3-1 总结了两种条件的时序参数(标准模式100kHz为例):
| 参数 | 符号 | 最小值 | 典型值 | 说明 |
|---|---|---|---|---|
| 起始条件保持时间 | tHD;STA | 4.0 µs | 4.7 µs | SCL下降沿之后SDA保持低电平的时间 |
| 起始条件建立时间 | tSU;STA | 4.0 µs | 4.7 µs | SDA下降沿之前SCL为高电平的时间 |
| 停止条件建立时间 | tSU;STO | 4.0 µs | 4.7 µs | SDA上升沿之前SCL为高电平的时间 |
你看,时序窗口其实挺窄的。我一般建议在MCU的I2C外设配置里,把时序参数放在中间偏上限,比如标准模式下tHD;STA设为4.7 µs而不是4.0 µs,这样留点余量。
3.2 数据有效性:SCL低电平时才能变
这个知识点很容易记混。我见过有的新手觉得“数据在SCL下降沿读取”,其实完全反了。
I2C的数据有效性规则就一条:
当SCL为高电平时,SDA上的数据必须稳定不变;只有SCL为低电平时,SDA才允许改变。
为什么这样设计?因为总线上的所有设备都在SCL的上升沿采样SDA(接收端),在下降沿之后、下一个上升沿之前改变SDA(发送端)。这样SCL高电平期间SDA处于“被读取”状态,不允许发送方改数据,否则采样就会跑偏。
我个人习惯把这条规则跟“路口的信号灯”类比:SCL高电平是红灯——车辆(数据)禁止通行;SCL低电平是绿灯——车辆可以变道。
当然,起始条件和停止条件是个例外——它们发生在SCL高电平期间SDA跳变,这不属于数据位,属于控制信号。
3.3 时钟同步机制
这个部分稍微复杂点,但理解之后你会觉得I2C的设计特别巧妙。时钟同步发生在多主设备模式,或者主设备与慢速从设备之间的协调。
核心思想:谁拉低SCL谁就控制时钟线。
具体过程是这样的:
- 主设备开始驱动SCL,产生时钟脉冲。
- 如果某个从设备(或另一个主设备)也需要“拖延”时钟,它会在SCL被主设备释放(变高)之后,主动拉低SCL并保持一段时间。
- 其他设备看到SCL被拉低,就停止计数,等待它释放。
- 当拉低的设备释放SCL后,所有设备重新检测到高电平,继续下一个时钟周期。
你想想看,这个过程就像一群人要过一扇门,一个人卡住了门不让关,其他人就在外面等着。等卡住的人过去后,门再关上。这就是时钟拉伸(Clock Stretching)的典型场景。很多从设备(如传感器、EEPROM)在收到数据后需要时间来处理,就会拉低SCL来通知主设备“慢点,我还没消化完”。
注意:不是所有从设备都支持时钟拉伸。有些从设备会使用NACK来告诉主设备“我忙”,而不是拉低SCL。我踩过的一个坑是:某款温度传感器在手册里写了支持时钟拉伸,但实际测试中它只拉低了不到1微秒,主设备I2C外设根本检测不到,导致数据混乱。后来我从软件上改成轮询等待方式才解决。
时钟同步的另一个重要应用是多主设备仲裁。当两个主设备同时发起传输时,它们会各自驱动SCL。如果其中一个主设备输出了低电平,那么SCL线就是低电平;另一个主设备虽然想输出高电平,但实际SCL还是低。然后比较SDA上的数据位——谁先输出低电平谁就赢了,赢了的主设备继续控制传输,输了的会自动变成从设备模式退出。这个机制保证了总线不会出现冲突短路。
为了让你直观理解整个时序框架,我画了一张图:
图中上方是SCL和SDA的波形,标注了起始条件(S)和停止条件(P)的位置。中间用紫色虚线框标出SCL高电平期间数据必须稳定的区域——你看,SDA在那些区域里没有跳变。下方还画了时钟拉伸的例子:从设备在某个时间点拉低SCL,主设备检测到后暂停,等从设备释放后再继续。
3.4 几个实用的排查技巧
最后分享几条实战经验,你在解码I2C波形时可以对照着用:
- 检查S和P的时序余量:用逻辑分析仪量一下SDA下降沿到SCL下降沿的时间(tHD;STA),如果接近规格下限,建议提高I2C外设的时序调整值。
- 留意SCL高电平期间的毛刺:如果看到SDA在SCL高时出现短暂的高低抖动,多半是噪声或上拉电阻太小/太大。我习惯在靠近主设备端加100pF到470pF的电容滤一下。
- 时钟拉伸超时:一些从设备可能会长时间拉低SCL(比如超过几十毫秒),这时要设置超时机制。我个人习惯在固件里加一个1ms的定时器,如果SCL一直被拉低超过1ms就放弃传输并复位总线。
核心记忆口诀:“起始下降停上升,SCL高时数据稳,谁拉低谁控时钟,拉伸释放再等等。” 反复读几遍,调试时脑子里自然就能浮现波形了。
现在你对I2C的四个基本时序要素应该有概念了。下一部分我们会把这些规则应用到具体的解码参数配置中,比如如何设置SCL频率、数据采样点偏移、以及如何处理从设备的NACK。先消化好本章的内容,后面解码时才不会手忙脚乱。