3. I2C 通信时序:起始条件、停止条件、数据采样、ACK/NACK 归纳总结

说实话,搞 I2C 时序看起来眼花缭乱,但核心就四个动作:起始、停止、数据采样、应答(ACK/NACK)。我早年第一次调 I2C 时,就被示波器上的波形绕晕过——明明 SCL 和 SDA 都在跳,为什么读回来的数据全错?后来才发现是起始条件和停止条件没卡对位置。这种事,经历一次就记住了。

3.1 起始条件(Start Condition)与停止条件(Stop Condition)

这两个条件是 I2C 通信的“起跑线”和“终点线”。总线空闲时,SCL 和 SDA 都是高电平。主机要开始传输,必须产生一个起始条件;传输完毕,必须产生一个停止条件。

关键规则:
  • 起始条件:SCL 保持高电平时,SDA 从高电平切换到低电平。
  • 停止条件:SCL 保持高电平时,SDA 从低电平切换到高电平。

我记得在某个项目里,从机芯片对时序要求特别苛刻——停止条件之后必须等至少 4.7µs 才能发下一个起始条件。我当时没仔细看 datasheet,连续发了两个起始条件,结果从机直接不理人了。嗯,从那以后我养成了习惯:每次写 I2C 驱动前,先翻到电气特性表,把时序参数全抄下来贴在显示器上。

条件SCL 电平SDA 跳变方向典型保持时间
起始条件高高→低tHD;STA ≥ 4µs (标准模式)
停止条件高低→高tSU;STO ≥ 4µs (标准模式)
小细节:很多人以为起始条件和停止条件只出现在一帧的开头与结尾。其实,在“重复起始”场景里,你可以在没有停止条件的情况下直接再发一个起始条件。举个例子,当你读一个从机寄存器时,先写地址+寄存器号,然后发重复起始,再读数据——省去一次停止再重开的总线仲裁时间。

3.2 数据采样:时钟的上升沿和下降沿

一个 I2C 数据位的传输,靠的是 SCL 时钟。每发一个时钟脉冲,就传输一个 bit。说白了,主机掌控时钟,从机跟随时钟。

采样时刻发生在 SCL 的上升沿(或者说高电平期间)。为什么?因为 I2C 规范要求:SDA 上的数据必须在 SCL 低电平时改变,在高电平时保持稳定。这样接收方就能在 SCL 高电平中间稳稳地读 SDA。

曾经踩过的坑:我之前用软件模拟 I2C(即 GPIO 翻转 SCL/SDA),延时没算准,导致 SDA 在 SCL 高电平期间发生了跳变。示波器上看,数据完全错了。后来我加了一个“数据建立时间”的检查——每次写 SDA 后必须等一小段时间再拉高 SCL。建议你如果也做软件模拟,一定要在 SCL 高电平前至少 100ns(快速模式)把 SDA 稳住。

为什么要强调“高电平稳定”?你想想看,如果数据在 SCL 高电平时变化,接收方可能采样到错误的电平,甚至把数据变化误判为起始或停止条件。时序的严谨性就在这里。

3.3 ACK(应答)与 NACK(非应答)

每发送完 8 个数据位,第 9 个时钟周期就是应答位。主机释放 SDA(变成高阻/输入),从机拉低 SDA 表示 ACK,保持高表示 NACK。这个机制是整个 I2C 通信的握手基础。

应答类型SDA 在第 9 个 SCL 高电平时含义
ACK低电平(从机拉低)从机已接收字节,可以继续
NACK高电平(从机不拉低)从机忙/不支持该操作/最后字节

我个人习惯把 ACK/NACK 当成 I2C 的“回执”。主机发完地址后,如果没收到 ACK,说明这个从机不存在或地址错了。我记得有一次调试一个温湿度传感器,怎么都读不到数据,万用表量 SDA 一直是高电平。我换了两个器件才发现,原来我把地址左移了一位没处理(I2C 地址是 7 位,需要进行左移+读写位),从机根本不认。NACK 就是它给我的态度。

注意:在接收模式中,主机可以通过发送 NACK 来告知从机“不要再发了”。比如读最后一个字节前,主机先发 NACK,然后发停止条件,从机就会释放总线。这是一个标准的“读结束”流程。

3.4 时序组合:一帧完整的传输

把上面三个元素串起来,一次典型的 I2C 写操作长这样:

  1. 主机产生 起始条件(SDA 下降沿,SCL 高)。
  2. 主机发送 7 位从机地址 + 1 位写标志(0),共 8 bit。
  3. 主机释放 SDA,从机拉低 SDA 作为 ACK。
  4. 主机发送 8 bit 数据(比如寄存器地址)。
  5. 从机回复 ACK。
  6. 主机发送下一个 8 bit 数据(要写入的值)。
  7. 从机回复 ACK。
  8. 主机产生 停止条件(SDA 上升沿,SCL 高)。

你可能会问:如果中间某一步从机回复了 NACK 怎么办?我一般做法是:立即停止本次传输,发停止条件,然后重试或报错。别傻等着,因为从机 NACK 之后通常不会再回应任何数据。

I2C 一帧时序流程(写操作示例) I2C 时序核心逻辑:起始→数据→应答→停止 起始条件 SDA↓, SCL高 7位地址 + W 8 bit ACK 从机拉低SDA 数据字节 8 bit ACK 从机确认 停止条件 SDA↑, SCL高 上行为第1~4字节,下行为第5~6字节 注:实际传输中可重复 数据字节+ACK多次

3.5 时序参数速查(标准模式)

在实际项目中,你不可能每次都去翻 datasheet。我整理了一张常用参数表,建议你收藏——反正我自己是打印出来贴墙上的。

参数符号最小值最大值说明
SCL 时钟频率fSCL0100 kHz标准模式
起始条件保持时间tHD;STA4.0 µs—SCL 高后 SDA 低要保持
数据建立时间tSU;DAT250 ns—SDA 变化到 SCL 上升沿
数据保持时间tHD;DAT03.45 µsSCL 下降后 SDA 要稳住
停止条件建立时间tSU;STO4.0 µs—SCL 高后 SDA 上升
总线空闲时间tBUF4.7 µs—停止到下次起始的最小间隔
我曾经翻过一次车:在一个需要低功耗的项目里,我贪快把 SCL 速率提到了 150 kHz(超过标准模式上限),结果从机偶尔丢数据。后来查手册才发现那个从机只支持到 100 kHz 标准模式。超频看似爽,但 I2C 从机可能跟不上。记住:标准模式 ≤ 100 kHz,快速模式 ≤ 400 kHz,别混用。

3.6 小结:时序就是通信的语言

起始、停止、数据采样、应答——这四件事构成了 I2C 的全部通信语法。你掌握的越扎实,写驱动、调 bug 就越快。说白了,你只要在示波器上看到 SCL 高电平时 SDA 发生了不该有的跳变,十有八九是时序错了。

我个人习惯每次写完 I2C 代码,先拿逻辑分析仪抓一帧波形,对照本文的流程检查一遍。不要嫌烦,这能省下后面好几个小时的排查时间。