news 2026/6/24 12:50:15

通俗解释I2C总线应答与非应答信号

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
通俗解释I2C总线应答与非应答信号

I2C通信中,为什么最后一个字节要发NACK?一文讲透应答机制的底层逻辑

你有没有遇到过这样的情况:STM32读取传感器数据时,程序卡在I²C接收循环里出不来?或者用逻辑分析仪抓包发现,明明只想要两个字节,但从设备还在不停地发第三个、第四个?

如果你排查到最后发现是“忘了发NACK”,那这篇文章就是为你写的。

我们今天不堆术语、不抄手册,就从一个最朴素的问题出发:I²C通信中,每传完一个字节后那个“应答位”到底是谁控制的?什么时候该拉低(ACK),什么时候必须放手(NACK)?

搞懂这个问题,不仅能让你写出更稳健的驱动代码,还能在调试通信故障时一眼看出波形里的“致命破绽”。


一根数据线,如何确认对方“听到了”?

想象你在嘈杂的工厂车间里,对着对讲机喊:“老张,把阀门关一下!”
如果老张没回应,你是继续喊第二遍,还是以为他已经去执行了?

在电子世界里,这种“确认收到”的机制叫应答(ACK)。而I²C总线的设计者很聪明——他们不用额外信号线,就在每个字节后面加了一个“确认周期”,让接收方亲自表态。

这个周期只有1个时钟(SCL)宽度,在这期间:
- 如果接收方把SDA拉低 → 表示“我收到了,继续”
- 如果接收方不碰SDA(保持高电平)→ 表示“停!别发了”或“我没准备好”

这就是所谓的第9位——不是数据,也不是命令,而是纯粹的握手信号。

⚠️ 注意:SDA是开漏输出,靠上拉电阻维持高电平。所以“不动作”就等于“发高电平”,也就是NACK。


谁来决定ACK还是NACK?规则其实很简单

很多人被I²C状态机图搞得头晕,其实核心规则就一条:

谁是接收方,谁负责生成ACK/NACK。

来看几个典型场景:

场景1:主设备写数据给从设备

[主] 发地址 → [从] 接收 → 拉低SDA(ACK) [主] 发寄存器地址 → [从] 接收 → 拉低SDA(ACK) [主] 发数据 → [从] 接收 → 拉低SDA(ACK)

全程都是从设备在接收,所以每次都要由它来拉低SDA表示确认。

场景2:主设备读数据(重点来了!)

[主] 发起Start + 地址+读标志 [从] 开始发送第一个字节 [主] 接收 → 此时主是接收方!→ 必须由主拉低SDA(ACK)告诉从:“我还想再听一个” [从] 发送第二个字节 [主] 接收 → 这次是最后一个 → 主不再拉低SDA(即NACK)→ 通知从:“到此为止” [主] 立刻发Stop

看到关键了吗?读操作中,主设备虽然是“发起者”,但在接收数据时,角色变成了接收方,必须主动控制ACK/NACK!

这也是为什么很多初学者会犯错:以为“主机永远主导一切”,结果在最后一步忘记关闭应答,导致从设备以为还要继续发数据,死死拽着SDA不放,总线就被锁死了。


NACK不只是“错误”,更是“控制指令”

很多人一看到NACK就以为是通信失败,其实大错特错。

NACK有三种含义,取决于上下文:

情况含义是错误吗?
写地址后立即NACK从设备不存在 / 地址错了 / 没上电✅ 是问题
写数据过程中NACK从设备忙(如EEPROM正在写入)⚠️ 正常现象,需重试
读最后一个字节后NACK主设备明确说“够了”❌ 不是错误,是正确行为

最后一个尤其重要。比如你要从温度传感器读2个字节,流程应该是:

  1. 发Start + 设备地址(写)
  2. 写寄存器地址
  3. 重复Start + 设备地址(读)
  4. 收第1字节 → 主发ACK → “继续”
  5. 收第2字节 → 主发NACK → “到此为止”
  6. 发Stop

✅ 第5步必须NACK!否则从设备会继续发第3个字节,可能造成缓冲区溢出或总线僵持。


实战演示:HAL库怎么自动处理NACK?

以STM32 HAL库为例,当你调用:

uint8_t data[2]; HAL_I2C_Mem_Read(&hi2c1, DEV_ADDR, REG_TEMP, I2C_MEMADD_SIZE_8BIT, data, 2, 100);

你传了2作为长度,HAL库内部就会这样安排:

  • 前1个字节:开启应答(ACK)
  • 最后1个字节:关闭应答(NACK),然后发Stop

它背后做了什么?其实就是这几句关键操作:

// 接收前N-1个字节:保持ACK使能 I2C_AcknowledgeConfig(I2Cx, ENABLE); // 接收最后一个字节前:提前关闭ACK I2C_AcknowledgeConfig(I2Cx, DISABLE); // 手动产生Stop条件 I2C_GenerateSTOP(I2Cx, ENABLE);

如果你写裸机驱动,这些细节就必须自己把控。一旦漏掉DISABLE Ack,通信就会卡住。


常见坑点与调试秘籍

❌ 坑1:读操作末尾没发NACK,总线被从设备“霸占”

现象:主设备已经发了Stop,但从设备还在输出数据,SDA一直被拉低。
原因:主设备没有通过NACK明确终止通信,从设备误以为主机还想收更多。
解决:确保最后一次接收前关闭应答功能。

❌ 坑2:地址扫描时误判“设备不存在”

有些开发者写了个循环,挨个试0x08~0x77的地址,看哪个能返回ACK。
但如果某个地址返回NACK,就断定“设备不在”——这也不一定准确!

真实案例:某项目中BMP280始终检测不到,打印显示所有地址都NACK。
结果用逻辑分析仪一看,地址帧其实是ACK的!但因为后续没发寄存器地址,直接进读操作,协议断了,所以整体失败。

✅ 正确做法:先写地址+寄存器 → 再读数据,才算完整事务。

🛠 调试技巧:用逻辑分析仪看第九位

这是最直观的方法。抓一段I²C波形,重点关注每个字节后的第9个SCL周期:

  • 如果是低电平 → ACK
  • 如果是高电平 → NACK

比如你期望读两个字节,波形应该是:
[Byte1][ACK][Byte2][NACK][Stop]
如果看到[Byte2][ACK],那就说明你的代码还在等下一个字节,很可能陷入超时等待。


设计建议:让I²C通信更可靠

1. 上拉电阻别省

标准值4.7kΩ,负载重可降到2.2kΩ。太大会导致上升沿缓慢,影响高速模式;太小则功耗高。

2. 多设备共存?注意地址冲突

常见传感器如AT24C02(EEPROM)、SHT30(温湿度)、OLED屏都可能使用0x78/0x7A这类地址。焊接前务必查清设备实际地址。

3. 别迷信“设备就绪”函数

HAL_I2C_IsDeviceReady()本质就是不断发地址+等待ACK。对于某些响应慢的设备(如刚上电的传感器),可能需要重试多次才能成功。

可以加个延时再探测:

HAL_Delay(10); // 上电稳定 while (HAL_I2C_IsDeviceReady(&hi2c1, addr, 2, 100) != HAL_OK) { HAL_Delay(5); }

4. 总线锁死怎么办?

如果SDA一直为低,可能是某个从设备崩溃了,死死拉着总线。

急救方法:模拟9个SCL脉冲,强迫从设备释放SDA:

// 手动翻转SCL引脚9次(GPIO模式) for (int i = 0; i < 9; i++) { HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET); delay_us(5); } // 然后发一个Stop条件恢复

结语:掌握ACK/NACK,才算真正入门I²C

你可以不会配置DMA,可以不懂时钟延展,但如果你不清楚每一个字节之后的那个“确认位”是怎么来的、谁该负责、何时该放手,那你离写出稳定可靠的I²C代码还差最后一公里。

记住一句话:

在I²C的世界里,礼貌很重要——发完数据要等对方点头(ACK),想结束对话要说“再见”(NACK + Stop)。

下次当你面对一堆跳动的波形时,不妨问自己一句:
“现在轮到谁来拉低SDA了?”

答案清楚了,问题也就解开了。

如果你在项目中遇到过因NACK引发的奇葩bug,欢迎在评论区分享经历,我们一起排雷拆弹。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 5:14:06

L298N驱动直流电机入门教程:从接线到运行

从零开始玩转L298N&#xff1a;驱动直流电机的完整实战指南你有没有试过用Arduino直接控制一个轮子飞转的小车&#xff0c;结果刚通电&#xff0c;单片机就“罢工”了&#xff1f;问题很可能出在这里&#xff1a;MCU的IO口带不动电机。别急&#xff0c;这不是代码写错了&#x…

作者头像 李华
网站建设 2026/6/18 20:21:50

Proteus 8.0汉化后功能异常修复:系统学习应对策略

Proteus 8.0 汉化后功能异常&#xff1f;别急&#xff0c;一文讲透根源与实战修复方案 在电子设计的世界里&#xff0c;Proteus 是许多工程师和学生心中的“老伙计”。它不仅能画原理图、布PCB&#xff0c;还能直接仿真单片机程序&#xff0c;真正实现了软硬件协同验证。但对于…

作者头像 李华
网站建设 2026/6/10 12:43:15

新浪科技转发:Fun-ASR登上GitHub趋势榜Top10

Fun-ASR为何能登顶GitHub趋势榜&#xff1f; 在远程办公、智能会议和语音笔记日益普及的今天&#xff0c;语音识别技术早已不再是实验室里的高冷概念&#xff0c;而是实实在在影响着每个人的生产力工具。然而&#xff0c;一个现实问题始终存在&#xff1a;市面上的语音转文字方…

作者头像 李华
网站建设 2026/6/11 21:38:46

arm64与x64交叉编译中ABI差异通俗解释

arm64 与 x64 交叉编译中的 ABI 差异&#xff1a;从崩溃到稳定的实战解析你有没有遇到过这样的场景&#xff1f;一段在你的开发机上跑得好好的 C 程序&#xff0c;一交叉编译部署到 ARM 开发板上就直接段错误&#xff1b;或者函数传参莫名其妙“错位”&#xff0c;返回值像被随…

作者头像 李华
网站建设 2026/6/14 22:30:52

origin数据分析前处理:语音实验记录转结构化文本

语音实验数据自动化处理&#xff1a;从录音到结构化文本的无缝衔接 在心理学、语言学等实证研究中&#xff0c;语音实验是获取被试口语反应的重要手段。然而&#xff0c;当几十甚至上百段音频堆积如山时&#xff0c;研究人员面临的首要难题不再是数据分析&#xff0c;而是如何高…

作者头像 李华
网站建设 2026/6/11 21:39:16

开发者必看:Fun-ASR API接口调用示例与集成方案

开发者必看&#xff1a;Fun-ASR API接口调用示例与集成方案 在智能办公、远程协作和自动化服务日益普及的今天&#xff0c;语音识别技术正从“可用”迈向“好用”。无论是会议纪要自动生成、客服录音分析&#xff0c;还是教学内容转写&#xff0c;企业对高精度、低延迟、可私有…

作者头像 李华