I2C调试实战:从万用表到示波器,ACK与波形排查全攻略

发布时间:2026/11/6 1:05:11
I2C调试实战:从万用表到示波器,ACK与波形排查全攻略

I2C 调试中,最让人头大的场景莫过于:设备连接正常、上拉电阻也焊了、地址核对无误,可就是读不到数据。这种时候,大多数人的第一反应是拿起万用表去量,结果盯着屏幕上的电压数字发呆——因为万用表根本看不到波形。我自己也是从这个阶段摸爬滚打过来的,后来才慢慢总结出一套从万用表、示波器到 ACK 的完整排查流程。这篇文章就把我踩过的坑和验证过的方法整理出来,给还在跟 I2C 波形较劲的朋友一条可以直接照做的路径。不管是刚入门单片机的新手,还是已经做嵌入式开发几年的工程师,按照这个顺序排查,基本能覆盖大多数 I2C 通信故障。

I2C 本身只有两根线,SCL 和 SDA,加上一根地线就能工作,看起来非常简单。但恰恰是这种"简单",让很多人忽略了信号测量本身的门道。万用表适合查静态问题,示波器适合看动态时序,而 ACK 的识别则是判断通信是否真正建立的关键。下面我按实际调试中应该动手的顺序,把每个工具该用在哪一步、怎么看、怎么判断,一次说清楚。

1. 万用表能测什么:先别急着上示波器

很多人一听 I2C 调试,就条件反射地想买示波器。实际上,在示波器上场之前,万用表就能帮你排除掉一大半低级问题。这些低级问题如果没查干净,上了示波器反而更难定位,因为波形看起来"有东西在跳",但就是通信失败,容易把人绕晕。

1.1 万用表真正做得到的三件事

第一件事是测量静态电压。I2C 总线在空闲状态下,SCL 和 SDA 都应该被上拉电阻拉高,电压接近 VDD。如果测到某个引脚是 0V 或者只有零点几伏,那基本可以断定:要么上拉电阻没焊、虚焊,要么有设备把总线拉死了,要么两根线之间有短路。这一步只需要把万用表拨到直流电压档,红表笔接 SCL 或 SDA,黑表笔接地,上电后读数,三十秒就能完成。

第二件事是通断测试。当怀疑 SCL 和 SDA 之间短路、或者某一根线对地短路的时候,断电状态下用蜂鸣档直接测两两之间的阻值,是最快的方法。正常的 I2C 总线,SCL 与 SDA 之间应该是开路(因为有上拉电阻,实际会测到一个几 kΩ 的电阻,而不是零欧),对地也应该是开路。如果蜂鸣器长响,基本就是 PCB 贴片连锡、飞线破皮碰在一起这类物理问题。

第三件事是验证上拉电阻值。断电状态下,用电阻档直接量 SCL 或 SDA 对 VDD 的阻值,看是否接近你设计的上拉电阻标称值。比如你焊了 4.7kΩ,实测应该在 4.7kΩ 左右,如果测出来是几十 kΩ 甚至几百 kΩ,说明上拉电阻可能贴错、焊盘虚焊,或者走线上串了其他元件。这一步能帮你在上示波器之前,把"总线根本没有上拉"这种最基础的问题揪出来。

1.2 为什么万用表看不到 I2C 波形:采样率是硬伤

有一个问题几乎每个新手都会问:为什么我把万用表搭在 SDA 上,看到的就是一个稳定的高电平,看不到波形?答案在于采样率。

万用表的 ADC 采样速度以每秒几次到几十次计算,而 I2C 标准模式时钟是 100kHz,快速模式是 400kHz,一个高电平脉冲只有几微秒。用秒级采样率的仪器去看微秒级的信号,就好比用秒针去拍蜂鸟扇翅膀,能看到的只是模糊的平均值。所以万用表上显示"接近 VDD 的稳定电压",实际上只是高速翻转信号被积分后的结果,并不代表总线没有活动。

明白了这一点,你就知道万用表的定位是"静态检查",它回答的是"总线有没有被拉低、有没有短路、上拉有没有接"这类问题,回答不了"时序对不对、ACK 有没有返回"这类动态问题。动态问题必须交给示波器。

1.3 用万用表判断"哪一端拉低了总线"

有一个很实用的场景:SDA 测出来是低电平,但你不确定是主控拉低的、还是从机拉低的。这时候可以断电,把主控的 SDA 引脚用杜邦线或者飞线断开,然后重新上电,再量 SDA 的电平。如果还是低,说明是从机侧有问题,比如从机芯片坏、进入了错误状态、或者内部锁死;如果变成高电平了,说明是主控侧把它拉低了,比如主控初始化时序不对、GPIO 配置成了推挽输出并在发送低电平。

这个方法不花一分钱,却能直接切割故障范围。我自己在排查一块板子时,就靠这一招定位到是某颗传感器在上电瞬间把 SDA 拉死,换了供电时序后问题消失。

2. 示波器上场:触发、时基和解码的实操设置

万用表把静态问题排除干净之后,如果通信还是异常,就该轮到示波器了。示波器能看到 I2C 的每一个跳变沿、每一个电平宽度、每一帧数据的先后顺序,还能直接解码出地址和数据内容。但示波器也不是一接上就能看明白的,探头接法、触发设置、时基选择,任何一个不对,都可能让你什么都抓不到,或者抓到的波形完全不可读。

2.1 探头和接地点:很多人忽略的第一步

先说探头的物理连接。默认配置是 CH1 接 SCL、CH2 接 SDA,这样屏幕上能同时看到两根线的对应关系。地线千万不要用探头自带的那根长黑色鳄鱼夹,那个夹子连出来的地线回路很大,像一根天线,会把开关电源的噪声、周围环境的辐射全部收进来,在波形上表现为毛刺和振铃,I2C 本身又是高阻态开漏结构,特别容易受影响。正确做法是使用探头自带的短接地弹簧,直接勾在板子上的 GND 测试点,让地回路尽可能短。如果板上没有现成的测试点,可以临时焊一根短粗线到 GND。

垂直档位一般设置成 1V/div,这样 0 到 3.3V 的完整摆幅刚好占三格多,看得清楚。水平时基先不要着急,后面根据抓到的内容再调整。这里有一个经验:探头的补偿电容要先用示波器自带的 1kHz 方波校准过,否则测上升沿波形会变形,影响后面我们对时序的判断。

2.2 触发设置:抓启动条件和数据帧的两种思路

示波器触发是新手最容易卡住的地方。很多人把探头接上之后,屏幕上的波形一直在乱跑或者一片模糊,按下 Single 又什么都抓不到。问题的核心是触发条件没设对。

I2C 的通信是由主机发起 START 条件开始的,START 的定义是:SCL 为高电平期间,SDA 产生一个下降沿。所以最通用的触发方式是使用 CH1(SCL)的下降沿触发,然后把触发电平设置在 1.5V 到 1.65V 之间。这正好落在 TTL 逻辑阈值区间的中间位置,高电平是 2V 以上、低电平是 0.8V 以下,阈值放中间最稳定,能避免因为毛刺造成的误触发。

如果你的示波器支持 I2C 协议触发,那更方便。在触发菜单里选择 I2C 类型,然后可以指定触发条件为 START、STOP、ACK 或者某个具体的地址值。比如你想确认主机有没有发出正确的从机地址,就把触发条件设成"地址=0x50,读操作",然后按下 Single,示波器会在千分之一秒内抓到对应的那一帧。没有协议触发功能的老示波器也不用担心,用 SCL 下降沿触发,加上耐心,一样能抓到完整的帧。

时基怎么调?I2C 标准模式一个 bit 是 10μs,一个字节加 ACK 是 9 个时钟,也就是 90μs,一帧常见的写操作可能包含地址、寄存器地址、多个数据字节,总时长几百微秒到几毫秒。所以建议先把时基打到 50μs/div 左右,抓一帧完整的通讯过程,看清楚整体结构之后,再把时基缩小到 5μs/div 甚至 2μs/div,观察单个字节和 ACK 位的细节。

2.3 不看解码也能读时序:手动数时钟的土办法

现在的数字示波器绝大多数自带协议解码功能,在解码菜单里选 I2C,告诉示波器哪个通道是 SCL、哪个是 SDA,它就能直接在波形下面标出 START、地址、读写位、ACK、数据字节和 STOP。这确实非常省事,但我建议每个调试 I2C 的人都要掌握手动读波形的能力,因为解码器在某些情况下会骗人(后面我会专门讲这个坑)。

手动读 I2C 波形的思路很简单:先找到 START(SCL 高时 SDA 下降沿),然后从 START 之后 SCL 的第一个上升沿开始数时钟。前 8 个时钟是地址字节,其中前 7 个 bit 是设备地址,第 8 个 bit 是读写位(低为写,高为读)。第 9 个时钟是 ACK 位,这个位的高低就是核心关注点。SDA 在第 9 个时钟被从机拉低,就是 ACK;保持高电平,就是 NACK。

读数据字节同样简单,从第 10 个时钟开始,每 8 个时钟一组数据,每个字节之后又是第 9 个 ACK 位。你只要盯着 SDA 在每个时钟高电平期间的电平就行。这个方法看着笨,但能让你真正理解 I2C 的时序框架,排查问题时远比直接看解码结果更靠谱。

3. ACK 波形怎么看:第 9 个时钟才是关键

整个 I2C 通信过程中,ACK 是最容易被忽略、也最容易出问题的环节。很多人示波器一抓,看到波形整体在跳,觉得"好像有信号",就以为通信正常。但实际上,只要 ACK 位是 NACK,后面的数据传输就是无效的。

3.1 ACK/NACK 在波形上的样子与判定方法

ACK 的定义是:在第 9 个时钟周期,主机释放 SDA(不驱动它),从机如果接收正常,会把 SDA 拉低,表示"我收到了"。所以你在示波器上看到的正常 ACK,是第 9 个时钟对应位置的 SDA 是一个低电平脉冲。

NACK 正好相反:主机释放 SDA 后,从机没有拉低它,所以 SDA 在那个位置保持高电平。

为什么从机会不回 ACK?我总结下来大概有这么几类原因:从机地址不匹配(地址线配置错了、地址算错了)、从机没上电、从机正处于复位状态、从机内部总线锁死、从机固件异常导致无法响应、或者总线电气参数太差导致从机根本识别不了主机发出的信号。还有一种常见情况是主机在读操作结束时不发 ACK 而是主动回 NACK,这是协议规定的正常行为,不是故障,后面我会细说。

判断 ACK 最直接的示波器操作是:把时基缩放到 5μs/div 左右,找到地址字节的第 9 个时钟,看 SDA 在那个位置的电平。如果示波器有光标功能,可以分别测量第 9 个上升沿到第 10 个上升沿之间 SDA 的高低状态,定量判断。

3.2 时钟拉伸:一个容易误判为 ACK 的细节

有一种特殊情况叫时钟拉伸(Clock Stretching)。某些慢速从机在收到数据后来不及处理,会在 ACK 位之前把 SCL 拉低,强制主机暂停,等它处理完再释放 SCL。这个机制在硬件 I2C 控制器里通常是自动处理的,但如果用软件模拟 I2C,或者用万用表观察到异常,就容易让人困惑。

在波形上看,时钟拉伸表现为 SCL 的低电平时间明显变长,比正常的一个低电平时间长出几倍甚至几十倍。这个时候不能只看 ACK 位 SDA 是不是低,还要判断 SCL 是否被从机长时间占用。如果还没等主机发完 8 个时钟,SCL 就被拉低得奇形怪状,那通信流程本身就是异常的,可能是从机在等待某个条件而卡住了。

判断方法:在示波器上用两个通道同时显示 SCL 和 SDA,观察 SCL 是否始终保持着规则的脉冲间距。如果 SCL 的低电平宽度超出了正常值的 2 倍以上,基本可以断定时钟拉伸发生了。如果这是你不希望出现的现象,就去查从机的初始化配置、供电稳定性、以及它是否有独立的上电复位时序要求。

3.3 从 ACK 位置反推上拉电阻和总线电容问题

还有一个细节值得专门提:ACK 位的波形形状能反映总线电气健康度。正常的高质量 I2C 波形,SDA 在第 9 个时钟的下降沿应该干脆利落,低电平保持稳定,然后上升沿迅速回到高电平。如果你看到的是 SDA 缓慢爬升,或者低电平只勉强到 1V 就不再下降,这说明上拉电阻和总线电容的配合出了问题。

I2C 规范对上升沿时间有明确限制:标准模式要求最大上升时间 1000ns,快速模式要求 300ns,快速模式 Plus 要求 120ns。上升沿时间大约等于 0.8473 乘以(上拉电阻乘总线电容)。比如总线电容是 200pF,上拉电阻是 10kΩ,算出来的上升沿就是 0.8473 × 10000 × 200×10⁻¹² ≈ 1.7μs,远超快速模式的 300ns 限制。这种情况下,高速通信必然不稳定,轻则偶尔 NACK,重则完全不通。

所以当你在示波器上看到 ACK 波形"软绵绵"的时候,第一反应不是去怀疑从机芯片,而是去量一下上拉电阻是否过大、总线是否过长导致电容太大。把 10kΩ 换成 4.7kΩ 甚至 2.2kΩ,往往立竿见影。

4. 完整排查流程:从锁死总线的 SDA 低电平到故障定位

前面讲了工具和原理,这一节给出一条可以直接照做的排查链路。我自己调试 I2C 设备时,基本按这个顺序走,每一步都有明确的判定标准,能快速缩小故障范围。

4.1 排查步骤总览:从静态电压到协议帧

整个流程可以概括为六步:第一步量静态电压;第二步看时钟是否有输出;第三步抓 START 条件;第四步验证地址字节;第五步检查 ACK;第六步检查数据阶段和 STOP。这六步的逻辑是逐层深入:先确认物理层有没有问题,再确认主机有没有在发,再确认协议层是否对得上,最后确认数据交换是否完整。

在实际操作中,我会把前三步用万用表和示波器交替进行:先用万用表排除短路和上拉问题,然后示波器上电抓一次总线上有没有任何活动。如果连时钟都没有,问题大概率在主控侧的 I2C 外设配置、引脚复用或者时钟使能上;如果有时钟但没有完整的地址帧,问题可能在初始化流程或者 GPIO 模式配置上。

4.2 每一步的判定标准与常见失败原因

第一步,静态电压。正常状态 SCL 和 SDA 都应该高于 2V(假设 VDD 是 3.3V)。如果 SCL 低、SDA 高,说明时钟线被拉死;如果 SDA 低、SCL 高,说明数据线被拉死;两根都低,可能是共用的上拉电源没接或者两颗上拉电阻都没贴。

第二步,看时钟。用 CH1 接 SCL,下降沿触发,时基 50μs/div,按一下 Single,然后触发主机发一次通信(通常是代码里执行一次 I2C 读写)。正常会看到一串脉冲。如果完全没有脉冲,检查主控 I2C 外设的时钟使能、引脚复用配置、以及主机的初始化代码有没有真正执行到读写函数。

第三步,抓 START。很多情况下时钟有、SDA 也有活动,但 START 条件不对。START 要求 SCL 高电平期间 SDA 产生下降沿。如果你看到 SDA 的下降沿发生在 SCL 低电平期间,那这个"START"是无效的,可能是主机 GPIO 驱动时序写错了,也可能是软件模拟 I2C 的延时不对。

第四步,验证地址字节。从 START 后数出前 8 个 bit,换算成十六进制,确认是不是目标从机的地址。注意 7 位地址在总线上是左移一位的,比如传感器地址是 0x50,总线上的地址字节是 0xA0(写)或者 0xA1(读)。软件里 I2C 驱动通常要求你传入的是 7 位地址,它内部自己移位。如果换算出来对不上,去核对数据手册和驱动代码。

第五步,检查 ACK。方法已经在前面详细说过了。这里要特别提醒:如果是读操作,主机读完最后一个字节后主动发一个 NACK 再发 STOP 是标准行为,别把它当成故障。

第六步,看数据阶段和 STOP。对写操作来说,地址后面跟的寄存器地址和数据字节要完整;对读操作来说,经常出现的问题是主机发了读地址之后,从机返回的数据乱掉,这通常是因为读写切换少了 Repeated Start,也就是没有在写完寄存器地址后重新发一次 START 再转成读模式。

4.3 读取寄存器地址时的常见死法:少了一次 Repeated Start

这个坑我踩过不止一次,值得单独拿出来讲。很多第一次接触 I2C 传感器的人,会把读操作写成:发 START → 发送写地址 → 发送寄存器地址 → 直接发读地址 → 读数据。但 I2C 协议规定,在改方向之前必须再发一次 START(Repeated Start)。正确的读流程是:发 START → 发送写地址 → 发送寄存器地址 → 再发 START → 发送读地址 → 从机返回数据 → 主机回 NACK → 发 STOP。

如果少了那一次 Repeated Start,从机根本不会切换为输出模式,示波器上看到的表现为地址和寄存器地址都正常发送、ACK 也正常,但到了读数据阶段 SDA 完全没有数据输出,总线保持高电平。这种故障用示波器看特别明显:前面的波形都是完整的,后面突然一片空白。解决办法就是在驱动层面检查是否在读操作前重新调用了发送 START 的函数。

5. 我踩过的几个坑与对应的处理办法

这些坑不是从书上看来的,是我自己在实际项目中一个个踩出来的,每个都花过不少时间。写出来给大家当参考,能避一个是一个。

5.1 上拉电阻选型:电阻值不能拍脑袋

有一块板子,I2C 通信时好时坏,室温下能跑,温度一降就概率性失败。用示波器量发现 SDA 的上升沿明显变缓,差不多到了 800ns。算了一下,总线上挂了两颗从机,飞线长度加在一起有 20 多厘米,总线电容大概有 300pF,上拉电阻用了 10kΩ,理论上升沿就是 0.8473 × 10k × 300p ≈ 2.5μs。这明显超出了快速模式的要求。我把上拉换成 2.2kΩ,再把飞线剪短到 10 厘米以内,问题彻底消失。

经验是:上拉电阻的选择要根据总线电容和通信速率来定。标准模式用 4.7kΩ 起,快速模式建议 2.2kΩ,如果总线上挂了 4 个以上的从机,或者布线距离超过 20 厘米,直接按 1.5kΩ 到 2.2kΩ 设计,宁可稍微降低一点低电平功耗,也别让上升沿拖后腿。

5.2 电平不匹配与总线锁死:两个高频事故

第二个常遇到的是电平不匹配。主控是 3.3V 的 MCU,从机是 5V 的传感器,如果从机板子自带上拉电阻到 5V,那么 SDA 和 SCL 的高电平就是 5V,直接灌进 3.3V 主控的引脚,长时间工作会损坏主控。正确的做法是使用电平转换芯片,比如 PCA9306,或者确保总线上的所有上拉电阻都接到 3.3V,从机的开漏输出本身可以承受 3.3V 的高电平,因为开漏器件只是把总线拉低,并不会主动输出 5V 高电平。

总线锁死也是个高频事故,特征是一上电 SDA 就是低电平,SCL 看起来有脉冲或者也没有脉冲。这种情况通常是从机在上电瞬间或者异常状态下把 SDA 拉死了。我的处理办法是:先用万用表确认确实是锁死状态,然后把 SDA 用一根杜邦线临时接到 VDD,强制拉高一两秒,让从机的状态机复位,再撤掉线。大多数情况下从机会恢复正常。如果反复锁死,就得去查从机的供电时序和复位引脚。

5.3 示波器解码也会骗人:永远以原始波形为准

最后分享一个容易被忽视的坑:示波器的协议解码功能不是万能的。我碰到过一次情况,解码器显示地址是 0x50、ACK 正常、数据也对,但设备就是不工作。我手动去看原始波形,才发现第 9 个时钟位置的 SDA 其实只是一瞬间低了一下,马上就被外部干扰弹回了高电平,实际情况是 NACK。解码器按自己的算法把那个毛刺判成了有效 ACK,而设备没有真正收到正确数据。

从那以后,我养成了一个习惯:解码器显示结果只作为参考,关键时刻一定放大原始波形,用光标测量 ACK 位的实际电平持续时间。特别是排查间歇性故障时,这一点几乎能决定你是在瞎猜还是真的定位到了问题。顺便说一句,如果条件允许,用逻辑分析仪长时间抓取总线,配合解码头,能更高效地捕捉偶发故障,这是示波器单次触发很难做到的一个补充手段。

我现在的调试习惯是:万用表查静态、示波器看时序、逻辑分析仪抓偶发,三者配合使用。每次遇到 I2C 问题,先把这套流程走一遍,基本都能在半小时内把问题范围缩得很小。希望这篇整理对你也有同样的帮助。