3、时钟拉伸的产生原因:从设备处理数据需要额外时间时的行为

时钟拉伸(Clock Stretching)是 I²C 协议里一个很容易被忽略,但关键时刻能让你抓狂的特性。说白了,就是从设备告诉主设备:“你再等等,我还没准备好。” 然后从设备把 SCL 线拉低,强行让主设备停住时钟。

我在做传感器项目时第一次遇到它。当时读温度值,主器一直读到 0xFF,折腾了两天才发现是时钟拉伸没处理好。嗯,这节课我们就把它彻底讲清楚。

3.1 什么场景会触发时钟拉伸?

时钟拉伸不是随时都会发生,它只在从设备需要「额外处理时间」的时候才出现。重点看三种典型情况:

  • 读取数据时(从设备准备数据):主设备发送了寄存器地址,然后产生重复起始条件准备读。从设备需要时间从内部存储中取出数据,来不及在下一个时钟边沿把数据放到 SDA 上。它就会拉低 SCL,等数据准备好了再释放。
  • 写入数据时(从设备忙):主设备发完一个字节后,从设备需要把收到的字节写入内部寄存器或 EEPROM。写入需要几个微秒甚至几毫秒。这段时间它会把 SCL 拉低,主设备必须等待。
  • 内部处理中(比如 AD 转换未完成):一些带模数转换的从设备,比如温度传感器、压力传感器,收到读取指令后才启动转换。转换完成前,从设备会一直拉低 SCL。

记住这个判断标准:只要从设备在 ACK 位之后的任意时刻发现自己无法在下一个时钟周期完成操作,它就可以拉低 SCL。

3.2 从设备如何“拉低”SCL?

物理上很简单。I²C 总线的 SCL 和 SDA 都是开漏输出,由主设备通过上拉电阻拉到高电平。从设备内部有一个 N-MOS 管,漏极接到 SCL 引脚。当从设备想拉伸时钟时,它让这个 MOS 管导通,把 SCL 拉到 GND。

你可能会问:主设备不也在驱动 SCL 吗?对了,关键就在这里。I²C 协议规定,主设备输出时钟时也用的是开漏结构。所以只要从设备拉低 SCL,无论主设备想输出高电平还是低电平,总线电平都会被拉低。这就是“线与”逻辑。

个人经验:我习惯在示波器上同时抓 SCL 和 SDA。当时钟拉伸发生时,SCL 的低电平时间会明显变长,超过正常的 50% 占空比。这就是肉眼判断拉伸最简单的办法。

3.3 时钟拉伸的持续时间有多长?

没有标准上限,完全看从设备的设计。我自己碰到过的范围:

设备类型 典型拉伸时间 说明
简单 GPIO 扩展芯片 0 µs(不拉伸) 无需内部处理,即时响应
EEPROM 写入 1 ms ~ 5 ms 页写入时需要擦除时间
温度传感器转换 10 ms ~ 100 ms 取决于分辨率,越高越久
气体传感器 可达 1 秒以上 内部化学反应时间长

主设备的 I²C 控制器通常有一个超时计数器,比如 1 ms、10 ms 或 100 ms。如果时钟拉伸超过这个超时时间,控制器会认为总线卡死,主动复位或产生错误标志。

3.4 我踩过的坑:不处理时钟拉伸的后果

有一次调一个 PM2.5 传感器模块。从设备官网上说支持标准 I²C,我就没用示波器。代码写好后,通信成功率只有 30%,动不动就卡死。最后抓波形,发现每次读数据前,传感器会拉低 SCL 约 15 ms。我的主控用的是硬件 I²C,超时设成了 5 ms。每次拉伸到第 3 ms,主控就直接报超时复位了。

解决方案有两个:

  • 改用软件 I²C 模拟,在 while 循环里等待 SCL 变高,没有超时限制。
  • 如果必须用硬件 I²C,把超时时间改成 50 ms 以上。

警告:不是所有硬件 I²C 外设都支持时钟拉伸!有些便宜的 MCU 硬件 I²C 模块在从设备拉伸 SCL 时,自己的时钟输出会“卡住”,但状态机可能错乱,导致后续通信失败。我建议项目前期先验证硬件 I²C 的时钟拉伸兼容性。

还有一个容易忽略的细节:多个从设备挂在同一总线上。如果一个设备正在拉伸 SCL,另一个设备也要通信怎么办?答案很简单——按协议,SCL 被拉低时总线被锁住,所有设备都只能等待。所以千万别让某个从设备无限期拉伸,否则整条总线就瘫痪了。

时钟拉伸产生原因与流程 场景一:读取数据 场景二:写入数据 场景三:内部处理 从设备需要额外时间 (准备数据 / 写入 EEPROM / 转换中) 从设备拉低 SCL (N-MOS 导通,强制 SCL=0) 主设备等待 SCL 释放 (时钟停止,保持状态) 总线被锁住 (其他设备无法通信) 从设备释放 SCL → 通信继续 关键判断: SCL 低电平时间 超过正常半个周期

你看这个流程,核心就一句话:只要从设备没准备好,它就拉低 SCL。主设备不能反抗,只能等。如果主设备设了超时,不等了,那通信就崩了。

总结时钟拉伸的三种根因:

  1. 从设备需要时间准备要发送的数据。
  2. 从设备需要时间处理刚收到的数据(写入、存储)。
  3. 从设备正在进行内部的测量或转换。

无论哪种情况,从设备都会通过拉低 SCL 告知主设备“请等待”。这就是产生时钟拉伸的根本行为。