3 · 应答机制详解:ACK/NACK定义、时钟拉伸、从机应答条件
开始聊应答之前,我先问个问题:你真的看懂I2C总线上的那个第九个时钟了吗?
很多工程师学I2C,知道有ACK、有NACK,但一到逻辑分析仪上抓波形就懵了——哪个电平是应答?谁该拉低?主机没收到应答怎么办?
这节我们就把应答这件事彻底讲透。我做了十几年嵌入式,调试过的I2C设备少说也有上百种,应答失败踩过的坑至少占了一半。
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的常见原因:
- 地址不匹配——总线上没有设备响应这个地址。我建议在写驱动前先用逻辑分析仪确认从机地址的7位或10位编码无误。
- 从机忙——典型如EEPROM正在页写入,你发任何命令都会被NACK。连续重试几次就好了。
- 不支持的操作——有些设备只支持写,你尝试读就会收到NACK。别笑,我见过有人把温度传感器的只写寄存器当读写用。
- 非法数据——比如写入的值超出了从机的允许范围,从机直接NACK拒绝。
- 硬件故障——SDA/SCL上拉电阻断开、从机没上电、地址线拉错电平……这些都会导致持续NACK。
| 场景 | 波形特征 | 常见排查方向 |
|---|---|---|
| 地址NACK | 地址字节第9钟SDA高 | 检查地址、上拉、从机电源 |
| 数据NACK | 数据字节第9钟SDA高 | 检查寄存器地址、数据范围 |
| 时钟拉伸误判为NACK | SCL低电平特别长,主机提前认为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被拉低。这时候强行发下一个启动条件只会让情况更糟。
我常用的恢复步骤:
- 主机主动产生最多9个SCL时钟(模拟),观察SDA是否在第9个时钟变高
- 如果SDA一直低,说明从机在死锁,需要硬件复位或掉电重启
- 如果SDA在第9个时钟变高(NACK),主机立即发送停止条件
- 等待至少4.7μs(标准模式下总线空闲时间)再尝试通信
我个人的习惯: 在驱动层实现一个“三次重试+总线恢复”的机制。第一次NACK直接重发,第二次NACK先恢复总线再重发,第三次NACK直接报错并输出最后一次的波形快照。这样调试效率会高很多。