3 · 应答机制详解:ACK/NACK定义、时钟拉伸、从机应答条件

开始聊应答之前,我先问个问题:你真的看懂I2C总线上的那个第九个时钟了吗?

很多工程师学I2C,知道有ACK、有NACK,但一到逻辑分析仪上抓波形就懵了——哪个电平是应答?谁该拉低?主机没收到应答怎么办?

这节我们就把应答这件事彻底讲透。我做了十几年嵌入式,调试过的I2C设备少说也有上百种,应答失败踩过的坑至少占了一半。

I2C 应答机制知识结构 应答机制 ACK(应答) NACK(非应答) 时钟拉伸 SDA 第9钟低电平 从机拉低SDA SDA 第9钟高电平 从机释放SDA 主机产生停止条件 SCL被从机拉低 主机等待SCL变高 核心原则:只有接收方才能发出ACK/NACK;时钟拉伸只由从机发起 逻辑分析仪抓波形时,重点观察第9个时钟沿的SDA电平与SCL低电平时间

3.1 ACK与NACK:第九个时钟的真相

先复习一下基础。I2C每传输一个字节(8位数据),后面紧跟着一个应答位,也就是第9个SCL时钟。

ACK(应答):在第9个时钟的高电平期间,接收方把SDA拉低。说白了就是告诉对方“我收到了,再来一个”。

NACK(非应答):在第9个时钟的高电平期间,接收方让SDA保持高电平。意思就是“我不要了”或者“我收不了”。

记住这个口诀:

  • 第9钟,谁接收谁应答
  • SDA低 = ACK(收到)
  • SDA高 = NACK(拒绝)

但这里有一个常见的混淆点——谁该在什么时候发出ACK?

我列出三种情况,你想想看是不是都清楚:

传输方向谁发字节谁应答应答方行为
主机 → 从机(地址+方向位)主机从机从机拉低SDA表示存在
主机 → 从机(写数据)主机从机从机每收到一个字节就ACK
从机 → 主机(读数据)从机主机主机在最后一个字节发NACK

最后一条很多人会搞反。主机在读操作时,前面的字节必须ACK,只有最后一个字节要发NACK,通知从机停止发送。我在项目中遇到过,一个同事连续写了八个ACK,结果从机就一直发数据,总线彻底卡死。

3.2 时钟拉伸:被忽视的“慢速从机”机制

时钟拉伸是I2C协议里一个非常实用的特性,但也是逻辑分析仪抓波形经常被误解的地方。

什么是时钟拉伸?

从机在某个时刻拉低SCL,强制主机等待。直到从机准备好,才释放SCL,让时钟继续跑。说白了,从机用这个办法告诉主机:“你太快了,等我一下。”

避坑提醒: 我曾有一次调试一个温湿度传感器,主机始终收不到ACK,逻辑分析仪上看SCL波形特别奇怪——每个字节的第9个时钟都很宽。后来才发现从机把时钟拉伸用在了应答位上。主机不支持时钟拉伸,直接超时报错。

时钟拉伸可以发生在任何SCL低电平期间,最常见的位置是:

  • 地址字节的应答位之前
  • 数据字节的应答位之前
  • 从机内部正在处理数据时(比如写入EEPROM)

在逻辑分析仪上怎么识别?看SCL的低电平时间是否明显比主机产生的时钟低电平长。正常的SCL高低电平比例由主机决定,但如果某个低电平拖长了,大概率是从机在拉伸时钟。

小经验: 多数MCU的硬件I2C外设会自动处理时钟拉伸,但如果你用GPIO模拟I2C,一定要在检测到SCL被拉低时,把SCL也设为输入并等待它变高。强行输出高电平会跟从机打架,严重时可能烧管脚。

3.3 从机应答条件:什么情况下从机会发NACK?

从机不是每次都乖乖应答的。以下是我总结的从机发NACK的常见原因:

  1. 地址不匹配——总线上没有设备响应这个地址。我建议在写驱动前先用逻辑分析仪确认从机地址的7位或10位编码无误。
  2. 从机忙——典型如EEPROM正在页写入,你发任何命令都会被NACK。连续重试几次就好了。
  3. 不支持的操作——有些设备只支持写,你尝试读就会收到NACK。别笑,我见过有人把温度传感器的只写寄存器当读写用。
  4. 非法数据——比如写入的值超出了从机的允许范围,从机直接NACK拒绝。
  5. 硬件故障——SDA/SCL上拉电阻断开、从机没上电、地址线拉错电平……这些都会导致持续NACK。
场景波形特征常见排查方向
地址NACK地址字节第9钟SDA高检查地址、上拉、从机电源
数据NACK数据字节第9钟SDA高检查寄存器地址、数据范围
时钟拉伸误判为NACKSCL低电平特别长,主机提前认为NACK确认MCU是否支持时钟拉伸

还记得我一开始说的吗?逻辑分析仪是I2C调试最好的伙伴。 遇到NACK,别急着改代码,先抓一段波形看看第9个时钟上到底发生了什么。

重要: 不要在NACK发生后还继续发后续数据。正确的流程是主机检测到NACK后立即产生停止条件,然后总线恢复到空闲状态。强行发数据只会让总线死锁更严重。

3.4 结合逻辑分析仪观察应答位

讲个实际案例。有一次我调试一个视频解码模块,I2C通信时好时坏。我习惯性地在软件里加了重试,没解决问题。

后来用逻辑分析仪抓了一整段波形,放大看第9个时钟,发现:

  • 地址字节的应答位正常(SDA被从机拉低)
  • 前两个数据字节也正常ACK
  • 第三个数据字节第9钟SDA变高了(NACK)

解码模块的datasheet说得很清楚——每包最多只能写两个字节。我第三个字节越界了,所以从机直接拒绝。把单次写入改成两个字节一组,问题彻底解决。

你看,如果没有逻辑分析仪的波形,我可能会在软件重试上浪费更多时间。

查看应答位的波形技巧:

  • 把采样率设为至少4倍于SCL频率(比如SCL=100kHz,采样≥400kHz)
  • 触发条件设在SCL下降沿,确保抓住第9个时钟
  • 添加I2C协议解码器,直接看解码出的ACK/NACK标记
  • 如果解码器显示“NACK”,立即用游标测量SDA高电平持续时间

3.5 应答失败后的总线恢复

应答失败后,总线可能处于不确定状态——从机仍在占用SDA,或者SCL被拉低。这时候强行发下一个启动条件只会让情况更糟。

我常用的恢复步骤:

  1. 主机主动产生最多9个SCL时钟(模拟),观察SDA是否在第9个时钟变高
  2. 如果SDA一直低,说明从机在死锁,需要硬件复位或掉电重启
  3. 如果SDA在第9个时钟变高(NACK),主机立即发送停止条件
  4. 等待至少4.7μs(标准模式下总线空闲时间)再尝试通信

我个人的习惯: 在驱动层实现一个“三次重试+总线恢复”的机制。第一次NACK直接重发,第二次NACK先恢复总线再重发,第三次NACK直接报错并输出最后一次的波形快照。这样调试效率会高很多。