I²C收到NACK,为何不能直接重发?
NACK只是从机没有在第九时钟拉低SDA;事务是否结束,还取决于主机有没有发出合法STOP并释放总线
I²C读写遇到NACK,程序进入错误分支,延时几毫秒后直接重发地址。第一次偶发失败变成了后续全部失败,外设BUSY也一直不清。很多重试代码只处理了“没应答”,却没有把当前事务收尾。
I²C是有状态的总线。START让总线进入占用,地址、数据和第九时钟按顺序推进,只有STOP产生后才明确回到空闲。
工程判断是:收到NACK不等于总线已经结束。重试前必须知道失败发生在哪一拍,并恢复到SDA、SCL都被释放的空闲状态。
先看开漏总线能不能真的回高
SDA和SCL通常由开漏输出驱动,任何器件都只能主动拉低,高电平依赖上拉电阻。主机软件写1,实际上只是释放引脚。
图1 I²C总线由上拉电阻恢复高电平,器件只主动拉低
若某个从机仍在拉低SDA,或主机GPIO方向没有切回释放状态,重发START不会形成合法的高到低跳变。此时软件命令执行了,线上协议却没有开始。
检查空闲不能只读外设BUSY位,还要直接读取SDA、SCL电平。两线都高,才具备重新产生START的物理条件。
NACK发生在第九个时钟每传完8位数据,第九个时钟用于应答。接收方拉低SDA表示ACK,保持释放则是NACK。NACK可能出现在地址阶段、写数据阶段,也可能由主机在读最后一个字节时主动发出。
图2 START与STOP都在SCL保持高电平时由SDA边沿定义
地址NACK通常表示没有从机响应、地址不对或器件尚未就绪;数据NACK可能表示从机不接受当前数据;读末字节的NACK则是正常的结束信号。三种情况不能共用一个错误解释。
逻辑分析仪应标出失败属于哪一阶段。只在日志里打印“HAL_ERROR”,会丢掉最重要的协议位置。
错误分支要先完成STOP若主机仍控制总线,应在允许的时机释放SDA,让SCL回高,再产生SDA低到高的STOP。STOP成功后,总线才明确回到空闲。
图3 8位数据之后的第九时钟决定ACK或NACK
硬件I²C外设还可能保留NACK、仲裁丢失、总线错误或STOP标志。清除顺序因MCU系列不同,应按参考手册处理,不能只把软件状态变量置零。
如果SCL被从机拉低进行时钟延展,主机要先等待或超时处理;在SCL低时直接翻转SDA,既不是STOP,也可能制造新的非法边沿。
重试前做三层恢复第一层是协议收尾:按阶段发NACK或STOP;第二层是外设状态:清标志、复位I²C外设或重新初始化;第三层是物理总线:确认SDA/SCL释放。
若SDA持续被从机拉低,可按目标从机和I²C规范允许的恢复方法输出额外SCL脉冲,再尝试STOP。这个动作要限定次数,并在失败后上报,而不是无限循环。
图4 完整读事务包含重复起始、最后一个NACK和STOP
重试也要有边界。记录失败阶段、恢复结果和重试次数,避免设备在总线异常时长时间阻塞主循环,掩盖真正的电源、时序或拉升时间问题。
上拉电阻过大时,上升沿太慢,逻辑分析仪可能把释放后的高电平识别得很晚;过小又增加低电平灌电流。选值要按总线电容和目标速率计算。
多主机场景还要区分NACK与仲裁丢失。仲裁失败的主机必须停止驱动并等待总线空闲,不能按普通从机NACK流程立即重发。
某些从机写内部Flash或完成测量期间会暂时NACK地址,这属于轮询就绪机制。重试可以存在,但每次仍要合法STOP并设置总超时。
读取最后一个字节时,主机主动发送NACK是为了告诉从机停止继续输出,随后再发STOP。若误发ACK,从机可能继续准备下一字节,收尾状态就会不同。
若错误只在电源插拔后出现,还应检查从机复位时序。主机已经启动而从机电源未稳定,地址NACK是结果,恢复根因可能在电源和复位,而不是协议重试次数。
使用DMA或中断模式时,错误回调可能与完成回调并发触发。恢复代码要防止重复发STOP、重复释放信号量或在旧事务尚未结束时启动新事务。
逻辑分析仪的协议解码可能把毛刺过滤后仍显示一个完整字节。遇到偶发NACK时,应放大原始SDA/SCL边沿,确认建立保持时间和高电平门限。
总线较长或设备较多时,电容增大使上升时间恶化。地址阶段最先NACK并不一定是地址错,也可能是第一个高电平没有及时达到门限。
恢复完成后可先发送一个无数据的地址探测,确认从机响应,再恢复业务事务。若探测仍失败,应退出重试并上报电源、复位或硬件连接故障。
结论I²C收到NACK后直接重发,最危险的假设是把“没有应答”当成“总线已经空闲”。协议状态和物理电平必须一起恢复。
下一次遇到BUSY不清,先截取第九时钟、STOP和两根线的空闲电平。知道事务停在哪一步,恢复代码才不会越修越乱。
声明:
本文由凡亿教育整理,转载请注明来源!
投稿/招聘/广告/课程合作/资源置换 请加微信:13237418207

扫码关注





































