ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

I2C调试实战:从万用表到示波器,掌握ACK排查流程

2026/9/26 1:50:59 拓冰建站 浏览量
I2C调试实战:从万用表到示波器,掌握ACK排查流程 和 SPI 至少还有一根独立的时钟线不同I2C 只有 SDA 和 SCL 两根线而且都是开漏结构、靠上拉电阻输出高电平。测 I2C 信号用万用表只能看个大概真正的时序细节还得示波器上阵再把 ACK 一并读出来。很多朋友第一次调 I2C 都是靠“瞎猜”代码翻来覆去看逻辑分析仪也挂了波形也在跳但就是不知道从哪一步查起。这篇文章我把这两年调 I2C 时反复用到的排查流程整理出来从万用表测静态电平到示波器抓启动条件再到第九个脉冲上看 ACK一条线走完希望能帮大家少走点弯路。这套方法没有门槛不管你是刚接触嵌入式的新手还是被 I2C 从机折磨到半夜的老工程师都适用。区别只是新手需要弄清楚“协议为什么是这样”老手可以直接跳到第 4 节去对 ACK 波形。1. 先理清你要测什么I2C 的信号特征与对外特性1.1 I2C 只有两根线但这两根线的要求不低I2C 物理层是开漏加外部上拉的结构。SDA 和 SCL 内部并没有主动拉高的能力只能把总线拉低想要回到高电平完全靠外部上拉电阻把电压“抬”起来。所以总线空闲时两根线都应该是接近供电电压的高电平而在通信过程中SCL 上会出现时钟脉冲SDA 上会出现数据跳变和应答位。如果你拿万用表量到某根线电压接近 0V说明总线正被某个设备拉低这往往就是故障现场。打个比方开漏结构就像一群人围着一个点灯开关每个人只能把开关往下按但谁都没法把开关抬回去。想要灯重新亮起来只能靠一根弹簧把它弹回去。I2C 的上拉电阻就是这根弹簧只要有一个设备按住了开关总线电平就被拉低所有设备都松开电平才能恢复高。理解了这一点你就知道为什么 I2C 不适合跑特别高的速率因为弹簧弹回去需要时间也就是我们常说的上升沿时间。工作频率方面标准模式是 100kbps快速模式 400kbps快速模式 可以到 1Mbps高速模式 3.4Mbps 更多用在特殊场景。测信号之前一定要先确认自己工作在哪个速率段。同样是抓时序100k 和 400k 的示波器时间档位差好几倍如果拿 100k 的经验去调 400k 的电路很容易漏掉细节。上拉电阻的选择也很关键常见取值是 4.7k、2.2k、1k。电阻太大上升沿变慢时序就会超标电阻太小从机拉低时电流偏大可能把电平拉到 0.3V 以上影响逻辑识别。I2C 规范对上升时间有明确要求标准模式是 1us快速模式是 300ns。示波器测出来的上升沿如果明显偏慢通信不稳定就是情理之中了。1.2 电平和时序参数测量前先列一张表调 I2C 之前建议先明确几个基本参数不然抓到波形也不知道该拿什么标准去评判供电电压1.8V、2.8V、3.3V 还是 5V这直接决定逻辑电平阈值。逻辑阈值TTL 和 CMOS 不一样很多 ARM 主控的 GPIO 是 1.8V/3.3V 兼容需要查数据手册。时序参数启动保持时间、时钟低电平时间、时钟高电平时间、数据建立时间、数据保持时间、停止建立时间、ACK/NACK 位置。实际操作时不用死记每个参数但必须知道最低要求是什么。我习惯把对应数据手册里的 AC 特性表截图打印出来贴在示波器旁边。标准 I2C 总线从上电到通信会经历几个明确定义的阶段其中启动条件是最显眼的SCL 为高电平时SDA 产生一个由高到低的跳变。停止条件则是 SCL 为高电平时SDA 产生一个由低到高的跳变。在示波器上如果能清晰看到这两个拐点至少说明总线上确实有 I2C 流量而不是主控压根没发数据。2. 万用表能测电压但别指望它看时序2.1 万用表适用的三个测量场景万用表在 I2C 调试里不是没用只是用途有限。我最常用的三个场景第一测静态电压。给板上电但主控不发起通信的时候SDA 和 SCL 都应该接近 Vcc。如果某一根线电压明显偏低比如 3.3V 供电下只有 1V说明这根线被某个器件异常拉低或者上拉电阻没焊好。第二测通断。设备没上电时用蜂鸣档量主控引脚到设备引脚之间的导通性可以快速确认 PCB 走线和连接器有没有虚焊、断裂。注意要在断电状态下测不然芯片内部二极管会误导蜂鸣档。第三测对地电阻。断电后用电阻档量 SDA 对 GND、SCL 对 GND 的阻值能初步判断总线上挂了多少设备。如果板上有一颗芯片的引脚短路对地电阻会异常小。上电后我还习惯用直流电压档量一下 SDA 和 SCL 的电压这能确认上拉电源有没有正常供给。比如 5V 系统里 Vcc 没供上但主控还在跑SCL 可能就会出现奇怪的半高电平。2.2 为什么万用表在动态测量时常常“骗人”万用表采样率低测到的是信号在一段时间内的平均值。I2C 的时钟脉冲和数据跳变是高速变化的万用表的 ADC 根本跟不上所以它会显示一个介于 Vcc 和 GND 之间的读数看起来像是电平“不正常”实际只是因为表在平均波形。举个真实例子。一块板子 I2C 通信时好时坏同事拿万用表量 SCL 电压显示只有 2.0V正常 3.3V 系统里他被吓到了怀疑上拉电阻有问题。折腾半天把排阻换了好几颗问题依旧。后来我把示波器接上去一测SCL 幅值完全正常3.3V 到 0V 翻转干净利落。2.0V 这个读数纯粹是因为万用表把正在翻转的时钟波平均了。所以要记住万用表读数怪异先别慌不一定代表信号异常先想清楚你测的信号是不是一直在跳变。还有一个经常被踩的坑用万用表交直流挡测 SDA 电压来判断总线是否“忙”。如果 I2C 总线上还有其他设备在持续通信电压读数会飘忽不定这时候你无法判断总线状态。正确做法是用示波器看波形或者用逻辑分析仪看有没有帧活动。3. 示波器测量这才是正确处理 I2C 的方式3.1 开始之前通道、量程、探头设置用示波器测 I2C最标准的配置是双通道同时测量CH1 接 SCLCH2 接 SDA。通道的对应关系一定要统一建议把 SCL 固定在 CH1SDA 固定在 CH2这样后面开解码功能时不容易搞混。垂直刻度方面要让两路波形完整显示不要让信号顶到屏幕边缘。如果 3.3V 电平可以用 1V/div 或者 500mV/div如果是 1.8V 电平可以用 500mV/div。水平刻度根据速率决定100k 速率看整帧可以用 50us/div 或 20us/div400k 速率可以用 20us/div 或 10us/div如果只看某个字节的细节缩到 2us/div 甚至 1us/div。探头优先用 10x 档。很多新手为了看小信号喜欢用 1x但 1x 的带宽往往只有 6MHz 左右测 I2C 的上升沿会严重失真而且 1x 探头输入电容更大对电路的影响也更明显。探头地线夹子尽量夹在靠近测量点的 GND 上夹子线不要拉太长否则会引入噪声和振铃。如果用的是数字示波器务必检查存储深度。有些示波器默认存储深度只有几 KB在长时间捕获模式下放大波形后细节全没了。I2C 调试里我吃过这个亏抓了一整屏波形觉得数据长度够了想放大看 ACK 的第九个脉冲结果全是直线就是因为存储深度不够。3.2 示波器触发别用上升沿触发用 I2C 触发触发设置是整个测量里最关键的一步。如果只是用普通上升沿触发示波器会随机触在 SCL 的某个上升沿上波形一直在跳看着眼花缭乱也没法稳定观察。现代数字示波器几乎都带 I2C 触发功能。在触发菜单里选择 I2C把 SCL 和 SDA 通道指定好再选择触发条件为 Start启动条件。示波器会实时监测 SDA 在 SCL 高电平时的下降沿一旦出现启动条件就触发这样每次抓到的都是完整的一帧开始波形稳定地址字节也必然出现在屏幕的固定位置。部分示波器还支持按从机地址触发。比如你知道目标设备地址是 0x3C可以设置触发条件为“地址等于 0x3C”示波器只有在总线上出现这个地址时才触发。这个功能在排查多设备总线时特别有用可以只关注某颗芯片的通信过程不会被其他设备的数据刷屏。如果示波器没有 I2C 触发退而求其次可以用 SCL 的下降沿触发。把触发源设为 CH1SCL触发电平设到信号幅值的一半左右也能稳定看到时钟和数据。只是这样不一定每帧都从启动条件开始需要手动滚动波形找到帧头。3.3 需要观察和测量的关键参数打开解码功能之前先手动确认几个关键电平值。一是总线空闲电平。正常情况下SCL 和 SDA 都应该是高电平等于上拉电压。如果其中一根线在空闲时被拉低说明总线上有设备在“霸占”总线或者某个从机锁死了。二是低电平是否接近 GND。I2C 设备拉低时因为开漏管的导通电阻和上拉电阻分压低电平会略高于 0V一般在 0.1V 到 0.4V 之间。如果发现低电平超过 VIL输入低电平阈值就可能导致接收方无法正确识别。常见原因是上拉电阻太小或者 PCB 走线阻抗异常。三是时序参数。我常用的方法是用光标功能测以下几个值SCL 低电平时间tLOW、SCL 高电平时间tHIGH、数据建立时间tSU:DAT、启动条件的建立时间tSU:STA。这些数值对照规格表的 AC 特性就能判断是否超标。比如 400k 模式下tLOW 最小值是 1.3ustHIGH 最小值是 0.6us如果实测差距很大就说明时钟速率偏高或主控配置有问题。示波器解码功能打开后屏幕上会直接用文本标出地址、读写方向、数据和 ACK/NACK。但解码是数字逻辑层面的判断它不会告诉你模拟电平到底合不合格。所以我一般先看模拟波形再开解码两者结合才不会漏问题。3.4 示波器的 SCPI 指令与自动化有些朋友已经在用示波器的远程控制功能。比如力科、鼎阳、普源等品牌的示波器都支持 SCPI 指令可以通过网口或 USB 连接上位机。我曾经用 Python 写脚本控制示波器抓 I2C 波形设置触发条件、读取波形数据、自动判断 ACK再生成测试报告。对于产线测试或者长时间稳定性验证来说这个方案省了很多人工盯屏的时间。如果你手里的示波器不带 I2C 解码功能也不必急着换设备。SCPI 指令配合上位机解码同样可行用示波器采集原始波形数据导出 CSV再用 Python 对 SDA/SCL 做时序分析。很多开源库能直接解析 I2C 帧格式实测效果不输示波器自带的解码。4. ACK 响应的排查看懂第九个时钟的高低电平4.1 ACK 到底长什么样I2C 协议规定发送完每一字节8 个位后接收方必须在第 9 个时钟脉冲期间应答。主机会在第 8 个时钟下降沿之后释放 SDA 总线然后拉出第 9 个时钟如果接收方正常收到数据它会把 SDA 拉低呈现 ACK如果 SDA 在第 9 个时钟期间保持高电平就是 NACK表示从机没有应答。ACK 的本质就是“我收到了请继续”的信号。打个比方就像打电话时你说“喂是某某吗”对方回了一声“嗯”这就是 ACK如果对面没有任何反应就是 NACK。排查 I2C 通信问题时第一件事就是找这个“嗯”。很多通信失败案例的根源就是主机发了地址但总线上根本没有设备回应所有字节都卡在 NACK 上。在示波器上看 ACK 有一个很容易忽略的细节从机拉低 SDA 是在第 9 个时钟高电平之前在第 8 个时钟的下降沿之后就已经开始了。所以你要看的是第 8 个时钟到第 9 个时钟之间 SDA 的状态。如果 SDA 在第 8 个时钟高电平期间还是数据位第 9 个时钟高电平期间是低电平这就是 ACK。4.2 典型 ACK 异常的四类现象根据我的调试经验ACK 异常基本可以分成四类每类的排查思路完全不同。第一类地址字节就 NACK也就是第 9 个时钟 SDA 一直是高。这种情况先确认从机地址对不对。七位地址和八位地址非常容易搞混代码里写了 0x3C 的地址但示波器上看到的可能是 0x78因为地址左移了一位最低位是读写标志。很多新手在这上面栽过跟头包括我自己。其次确认从机有没有正常上电、复位、使能。有些传感器复位后需要延时才能响应 I2C如果主机上电后立刻发地址就会遇到 NACK。第二类有时 ACK 有时 NACK随机出现。这种多半是硬件时序不够稳健。我遇到过一块板子上拉电阻用的是 10k总线电容又很大地址字节最后一位建立时间不足相邻两次读数时好时坏。把上拉电阻改成 1k并把总线速率从 400k 降到 100k问题立刻消失。随机性故障优先查电源纹波、总线电容、外部干扰和上拉电阻。第三类第一个字节有 ACK后面字节无 ACK。这通常不是寻址问题而是从机在执行某些操作时不希望主机继续发数据。典型场景是 EEPROM 正在内部写周期期间从机不响应任何总线请求还有 I2C 从机只支持读操作不支持写操作你发寄存器地址它就 ACK但往它写数据它就 NACK。这种情况需要查阅芯片数据手册确认它允许的操作序列。第四类ACK 出现在不该出现的位置。比如主机读数据时从机发送最后一个字节后主机应该发 NACK 来告诉从机“我不要再读了”但主机却发了 ACK这会导致从机继续发数据出现多读一帧或者时序错乱。这类问题常见于底层驱动库配置错误或者手动写 I2C 中断服务程序时漏掉了应答位设置。4.3 拿到 ACK 波形后仔细确认是谁在拉低 SDA看 ACK 时有一个很容易被忽略的问题要分清当前时刻拉低 SDA 的到底是主机还是从机。主机发送地址后在第 9 个时钟释放 SDA由从机拉低应答这是从机 ACK但如果主机切换到读模式它作为接收方每读取一个字节后要在第 9 个时钟拉低 SDA 作为应答最后一个字节还要不响应NACK。两个方向下“第 9 个时钟”的含义一样但操作方完全不同。如果你抓的是主机向从机写数据的过程看到第 9 个时钟 SDA 为低说明从机应答了如果你抓的是主机从从机读数据的过程第 9 个时钟 SDA 为低说明主机在应答。搞反逻辑会得出完全相反的结论。我见过有人拿着读操作波形看到 ACK 存在但通信还是失败最后发现 ACK 是主机发的从机实际根本没准备好数据。4.4 两个真实排查案例第一个案例是 SSD1306 OLED 不亮。波形抓出来SCL 时钟帧完整SDA 在地址字节后一直是高电平第九个脉冲上完全没有低电平。查代码发现地址写的是 0x3C但 SSD1306 的实际总线上地址是 0x3C 左移一位后的 0x78写方向。代码里要么写 0x3C 由库内部左移要么写 0x78 直接给出完整地址两种写法不能混用。很多驱动库里你在初始化时传入 0x3C库会默认左移如果底层函数已经帮你处理了你再传 0x78 就会 NACK。第二个案例是 M1 主板上某颗从机不定时失联。飞线单独测试从机时完全正常但装到整机上就概率性失败。最后用示波器看总线波形发现高电平时序在地址字节末尾有明显畸变测量建立时间后发现 tSU:DAT 不够。原因是总线上挂载设备太多等效电容变大上拉电阻又偏大。降到 100k 速率后余量充足问题消失。这个案例说明I2C 不稳不一定是从机坏了很可能是整个总线电气环境不达标。5. 抓不到想要的波形先从这几个坑里排除5.1 万用表、示波器与逻辑分析仪的分工很多人在工具选择上摇摆不定其实三样工具各有明确分工。工具适合场景不适合场景万用表静态电压、通断、对地电阻动态时序、脉宽、ACK 检测示波器模拟电平质量、时序参数、ACK、毛刺长时长时间记录多个波形逻辑分析仪长时间记录、多通道协议解码模拟电平质量、过冲、上升沿细节逻辑分析仪是抓 I2C 的利器很多板级调试场景都用它。采样率几十 M 到 100M 的入门级逻辑分析仪就能满足 I2C 解码需要。它的优势是可以长时间记录比如等一个偶发故障出现还可以同时挂好几路信号把 I2C、中断、GPIO 一起抓。但它看不到模拟电压细节无法判断电平是否低于 VIL也无法看到上升沿的缓慢程度。所以我的习惯是先用逻辑分析仪判断协议层有没有问题再用示波器定位电气层细节。5.2 常见测量错误和排查现象示波器测 I2C 时最常见的错误一个是 SDA 和 SCL 通道接反另一个是触发电平设置不对。通道接反后解码出来的数据全是乱码。触发电平如果设得太高而信号幅值又不够示波器可能不触发屏幕一直处于等待状态。正确做法是把触发电平设在信号幅度中点附近比如 3.3V 信号设 1.5V 到 1.8V。存储深度这个坑我再提一次。长时间抓帧时如果存储深度只有 100k 点波形放大 100 倍后每个周期可能只有 1 个采样点细节全丢。开长捕获前先把存储深度调到最大。另一个容易出问题的是探头补偿。示波器通道的补偿电容没调好测方波时边沿会出现明显失真。I2C 信号频率不高但上升沿细节对判断故障很有价值最好在测量前用探头自带的补偿端子校准一下。5.3 我日常使用的固定排查流程这套流程是我踩了很多坑之后总结出来的。遇到 I2C 问题按顺序走一遍大部分情况都能定位。断电用万用表蜂鸣档确认 SDA、SCL 从主控到所有从机的连接没有断路。断电量 SDA 对 GND、SCL 对 GND 的电阻排除短路。上电用万用表量 SDA、SCL 空闲时的静态电压确认上拉供电正常。示波器双通道接好先看总线上有没有波形活动。如果没有波形查主控代码是否真的在发数据或者主控引脚是否被复用成其他功能。用 I2C 触发抓启动条件确认每一帧是否完整地址字节是否符合预期。检查第九个脉冲上有没有 ACK判断是哪一方没有应答。如果有偶发性失败用逻辑分析仪长时间记录统计失败频率和触发条件。回到示波器观察失败时刻前后波形细节重点看电平、上升沿和时序参数。这套流程下来I2C 的大部分问题都能被定位在协议层还是电气层。协议层问题通常是地址、寄存器、操作序列写错了电气层问题通常是上拉电阻、电容、电平转换芯片选型不对。我最怕的不是问题本身而是东看一眼西摸一下地瞎试。按流程走能少熬好几个夜。说回到开头那句话I2C 不难难得是两根线上藏着太多细节。我现在的习惯是一套固定动作先量电平再抓时钟和数据然后翻到 ACK 确认从机到底有没有应答。这套流程帮我省了不知道多少找问题的时间你下次遇到 I2C 通信异常也可以从这套动作开始。