4. 时钟拉伸的时序要求与参数归纳总结

时钟拉伸这件事,说穿了就是从设备把SCL拉低,让主设备等一等。我最早接触I2C时,一直以为SCL只是主设备控制的单向信号——直到有一次,一个温湿度传感器死活不响应,用示波器抓才发现,从设备把SCL拉低了整整2ms,主设备早就超时复位了。嗯,从那以后我才真正重视时钟拉伸的时序参数。

本章我们就来捋一捋:时钟拉伸到底有哪些硬性时序要求?从设备和主设备各自该满足什么参数?哪些参数最容易踩坑?

4.1 时钟拉伸的核心时序参数

先看一张总图,把各个参数的关系理清楚。我个人习惯把时钟拉伸想象成一个“握手”过程——从设备说“我慢,你等会儿”,主设备说“我等,但你别太久”。

I2C时钟拉伸时序参数关系 SCL SDA tHIGH tLOW tSTRETCH tRISE 数据保持 关键时序参数: tHIGH – SCL高电平最小时间 | tLOW – SCL低电平最小时间(不含拉伸) tSTRETCH – 时钟拉伸最大允许时间 | tRISE – SCL上升时间

从图中可以清楚看到:一个完整的SCL周期里,时钟拉伸发生在低电平阶段。从设备抓住SCL不放,主设备只能一直采样到这个低电平被释放,才能进入下一个高电平。说白了,拉伸时间 tSTRETCH 就是从设备额外占用的低电平时间。

4.2 标准规定的时序参数表

I2C规范里,不同速率模式对时钟拉伸有明确的上限。我把常用的几个模式列出来,大家调试时可以直接对照。

参数 标准模式 (100kHz) 快速模式 (400kHz) 快速+模式 (1MHz)
SCL低电平最小值 tLOW 4.7 μs 1.3 μs 0.5 μs
SCL高电平最小值 tHIGH 4.0 μs 0.6 μs 0.26 μs
时钟拉伸最大允许 tSTRETCH —(未强制) —(由主设备超时决定) —(建议不超过 25 μs)
SCL上升时间 tR ≤ 1000 ns ≤ 300 ns ≤ 120 ns
SCL下降时间 tF ≤ 300 ns ≤ 300 ns ≤ 120 ns
⚠️ 注意: 标准模式和快速模式规范中并未强制规定时钟拉伸的最大时长。但几乎所有主设备都有自己的“超时计数器”。比如常见的STM32的I2C外设,超时时间默认是25个SCL周期。如果拉伸时间超过超时阈值,主设备会直接产生超时错误(NACK或总线释放),后面数据就全乱了。我在项目中遇到过,一个老款EEPROM拉伸了足足3ms,主设备(跑400kHz)早就判了超时,导致整个系统每隔几秒就丢一包数据。

4.3 从设备视角:时钟拉伸的合理范围

从设备什么时候会拉伸时钟?无非是两种情况:

  • 需要额外处理时间(比如内部ADC采样、EEPROM页写入、传感器数据刷新)
  • 等待外部条件满足(比如温度传感器等待测量完成、按键扫描等待去抖)

从设备的设计者需要控制好拉伸时长。我个人给团队定的内部标准是:

  1. 拉伸时间不得超过主设备超时时间的 80%,留出余量。
  2. 如果拉伸时间可能超过 1 ms,建议在数据手册中明确标注“时钟拉伸最大支持 xxx ms”。
  3. 避免在同一个字节内多次拉伸——我见过一个I2C触摸芯片,每传输一位就拉伸一次,结果总线效率直接掉到不到标称的1/10。你想想看,主设备每次都要等,还不如降速来得稳定。
💡 小经验: 调试时,如果你发现SCL波形上频繁出现“锯齿状”低电平(不是规整的方波),多半是时钟拉伸。用示波器抓一下SCL的下降沿到下一次上升沿的时间,就能算出拉伸时长。我曾经只用这个办法,半小时就定位了一个奇怪的总线挂死问题——原来是一个传感器在每次应答位之后都会拉伸 1.2 ms,而主设备的超时刚好是 1 ms。

4.4 主设备视角:超时处理策略

主设备不能一味死等。你需要设置一个合理的超时值。怎么设?

  • 最保守:取标准模式一个SCL周期的10倍。比如100kHz下,周期是10 μs,超时设为100 μs。缺点:很多从设备会比这更慢。
  • 更实用:根据从设备数据手册的最大拉伸时间,再加20~50%余量。比如某温度传感器标称最大拉伸200 μs,你就设250~300 μs。
  • 如果主设备是MCU实现(比如GPIO模拟I2C),超时通常用一个硬件定时器,在主设备释放SCL低电平时启动,检测到SCL变高则清零。超过定时器阈值就产生中断,释放总线并报错。

4.5 归纳:时钟拉伸时序的几条铁律

最后,我把自己调试中总结的几条“铁律”列出来,方便大家快速参考:

  1. 从设备的 tSTRETCH 必须 ≤ 主设备超时时间 —— 否则必定超时错误。
  2. 主设备超时时间应大于所有从设备最大拉伸时间的最大值 —— 取 max(tSTRETCH,max) × 1.2 比较稳妥。
  3. 拉伸只发生在SCL低电平阶段 —— 检测SCL上升沿判断拉伸结束。
  4. 避免频繁短时拉伸(比如每个字节拉伸多次)—— 这会大幅降低总线吞吐量,而且容易触发某些主设备的“毛刺过滤”逻辑。
  5. 设计从设备时,尽量在首个数据字节之前完成所有长耗时操作 —— 比如EEPROM写入时,建议在启动条件之前就把内部写周期跑完,而不是在ACK之后才拉伸。

说白了,时钟拉伸不是什么大问题——只要双方约好“你最多等多久,我最多拉多久”,总线就能稳稳地跑。但一旦某个参数超出边界,就会出现间歇性的通信失败,定位起来特别头疼。记住这些参数和阈值,遇到总线异常时,第一个去查SCL低电平时长,很可能就找到了症结。