第三章 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谁就控制时钟线。

具体过程是这样的:

  1. 主设备开始驱动SCL,产生时钟脉冲。
  2. 如果某个从设备(或另一个主设备)也需要“拖延”时钟,它会在SCL被主设备释放(变高)之后,主动拉低SCL并保持一段时间。
  3. 其他设备看到SCL被拉低,就停止计数,等待它释放。
  4. 当拉低的设备释放SCL后,所有设备重新检测到高电平,继续下一个时钟周期。

你想想看,这个过程就像一群人要过一扇门,一个人卡住了门不让关,其他人就在外面等着。等卡住的人过去后,门再关上。这就是时钟拉伸(Clock Stretching)的典型场景。很多从设备(如传感器、EEPROM)在收到数据后需要时间来处理,就会拉低SCL来通知主设备“慢点,我还没消化完”。

注意:不是所有从设备都支持时钟拉伸。有些从设备会使用NACK来告诉主设备“我忙”,而不是拉低SCL。我踩过的一个坑是:某款温度传感器在手册里写了支持时钟拉伸,但实际测试中它只拉低了不到1微秒,主设备I2C外设根本检测不到,导致数据混乱。后来我从软件上改成轮询等待方式才解决。

时钟同步的另一个重要应用是多主设备仲裁。当两个主设备同时发起传输时,它们会各自驱动SCL。如果其中一个主设备输出了低电平,那么SCL线就是低电平;另一个主设备虽然想输出高电平,但实际SCL还是低。然后比较SDA上的数据位——谁先输出低电平谁就赢了,赢了的主设备继续控制传输,输了的会自动变成从设备模式退出。这个机制保证了总线不会出现冲突短路。

为了让你直观理解整个时序框架,我画了一张图:

I2C时序核心要素示意图 SCL SDA S P SCL高→数据稳定 SCL高→数据稳定 时钟拉伸 从设备拉低SCL(等待) 从设备处理数据,释放SCL后继续 时序要点总结: • 起始条件(S):SCL高电平时SDA下降沿 • 停止条件(P):SCL高电平时SDA上升沿 • 数据有效性:SCL高电平期间SDA必须稳定,SCL低电平期间可变化 • 时钟同步/拉伸:设备通过拉低SCL请求等待,释放后继续

图中上方是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。先消化好本章的内容,后面解码时才不会手忙脚乱。