从万用表到示波器:I2C信号排查与ACK异常定位实战
先说个现实情况I2C 协议本身简单得让人掉以轻心两根线、一个时钟、一个数据看起来比 UART 和 SPI 都好伺候。但真正到了板子不工作、设备无响应的时候你会发现手里那把万用表和示波器根本不知道往哪儿戳。数据手册上那句“必须由主机发送起始条件并等待 ACK”读起来轻飘飘可当示波器上 SDA 一直保持高电平第九个时钟过去了也没有被拉低那一刻你才明白什么叫“看懂了协议没看懂信号”。这篇文章写给所有被 I2C 折腾过的人不管你是刚接触嵌入式的小白还是已经在用逻辑分析仪调驱动的老手。我会从最简单的万用表测电平讲起一路说到示波器抓时序、定位 ACK 异常把排查 I2C 信号问题的完整思路和实操细节讲透。后面所有内容都是我自己踩过坑之后总结出来的按这个顺序走一遍大多数“设备没应答”的问题都能在十分钟内找到方向。1. 测量前的准备先搞清 I2C 信号的脾气1.1 两根线上跑的是什么I2C 总线只有两条线SCL时钟和 SDA数据。工作的时候主机负责产生时钟数据线上按 bit 传输地址、寄存器地址和读写数据。这里有个前提很多人忽略——I2C 是开漏结构也就是说芯片内部不会主动把线拉高只会把线拉低拉高靠外部上拉电阻。那“开漏”到底什么意思你可以想象一根绳子绳子上绑了很多人每个人手里只有一个向下拽的钩子谁想说话谁就往下拽而让绳子回到高处的是顶部的弹簧。弹簧就是上拉电阻拽绳子的钩子就是芯片内部的开漏 MOS 管。正因为大家只能往下拽所以多个设备才能共用两根线也正因为只能往下拽总线一旦被某个芯片拽住不放整个总线就全都死了。这个电气特性决定了测量策略测 I2C 信号本质上是在测“谁在拽、谁在放、弹簧够不够力”。电压、边沿、ACK 全是这几个因素的直接反映。1.2 为什么 I2C 不能像 UART 那样随便测之前有人跟我抱怨说用万用表测 I2C 的 SDA 线电压只有 1.2V断定是芯片坏了结果换了个新芯片问题照旧。其实 I2C 的高电平不是稳定的直流它在空闲时被上拉到 VCC一旦有数据传输就会不停地在高低电平之间跳变。万用表测出来的电压是平均值100kbps 速率下波形有接近一半时间在低电平平均值自然低于 VCC。还有一个比平均值更隐蔽的坑I2C 报文的起点永远是一个“SCL 高电平期间 SDA 从高跳低”的下降沿。你拿示波器去抓波形如果触发条件设的是普通的上升沿那大概率抓到的是一个数据位的跳变而不是起始条件。UART 有线空闲电平可以随便触发I2C 必须顺着协议结构去触发否则你看到的波形是断断续续的、无法理解和翻译的。所以正确测量 I2C 的第一步不是调示波器而是先搞清楚两种测法各自的定位万用表只能回答“线大概通不通、有没有电平异常”示波器才能回答“协议对不对、ACK 有没有”。后文所有排查步骤都围绕这个分工展开。1.3 需要准备的工具与信号特征速查桌面级排查我常用的工具组合是一只三位半或四位半的万用表、一台带宽 100MHz 以上的数字示波器、一对示波器探头最好 10x 档有条件的话备一个逻辑分析仪。品牌上普源、鼎阳这类国产机器性价比很高力科、泰克的高端机器性能当然更好但排查 I2C 这种低速信号100MHz 带宽已经完全够用不需要为了追 I2C 去买贵仪器。开始测之前先建立一个基准标准 100kbps 模式Standard Mode下每个 bit 占 10 微秒400kbps 快速模式Fast Mode下每个 bit 占 2.5 微秒1Mbps 快速模式 Plus 下每个 bit 是 1 微秒。高电平一般等于芯片 I/O 电压3.3V 或 5V低电平应该低于 0.3 倍 VCC 左右。空闲状态下SCL 和 SDA 都应该是稳定的高电平。如果测到其中一根线空闲时是低电平这本身就是最典型的故障征兆。2. 万用表粗测从“能不能通”到“被谁拉死”2.1 电压档快速判断总线状态万用表在 I2C 排查里不是测波形的主力但它能在一分钟内告诉你总线“活没活着”。具体做法先断掉设备电源用电压档测 SCL 和 SDA 对地的电阻记录数值再上电测两根线对地电压。正常工作状态下两根线的静态电压应该接近上拉电压通常是 VCC。如果上电后 SDA 只有 0.3V 左右且不随读写操作变化那几乎可以断定有设备把 SDA 拉死了。常见元凶是两个某个从机芯片损坏内部 MOS 管击穿短路或者某个 GPIO 被错误配置成了输出低电平比如 Linux 驱动初始化失败后引脚状态保持异常。还有一种情况需要特别注意把万用表拨到直流电压档去测正在通信的总线读数是“晃动的中间值”。比如 SDA 在 3.3V 和 0V 之间来回跳万用表积分后可能显示 1.6V新手会觉得“这个电平不对”。我的习惯是直流档读数低于 VCC 时不轻易下结论先把示波器接上看看波形说不定只是正常的通信过程。2.2 用通断档和电阻档排查上拉、短路上拉电阻的阻值可以用电阻档直接测但必须断电测。断电后万用表红黑表笔在板上测到的阻值往往是上拉电阻和芯片内部电路的并联结果读数可能偏小这是正常的。如果你看到 SCL 和 SDA 之间直接导通、阻值几乎为零那很有可能 PCB 上两根线之间存在锡桥短路或者芯片封装内部连锡这种问题在手工焊板时代特别常见。通断档在做线束排查时效率很高。拿万用表单根表笔从主控引脚滑到排线另一头的从机引脚一路听蜂鸣如果中间某段不响就能快速定位断线或者虚焊位置。这个操作虽然基础但在“设备完全无响应供电也正常”的情况下往往比上示波器更快因为逻辑没问题物理没导通怎么测波形都是白搭。2.3 万用表测 I2C 的边界和误判陷阱明确一点万用表永远测不出 ACK。ACK 是第九个时钟之后的 1 bit 电平持续只有几微秒到几十微秒万用表根本反应不过来。所以如果你问“用万用表能不能判断 I2C 有没有应答”答案是能间接猜但不能直接测。怎么间接猜把 SDA 电压读数和通信行为结合起来。如果万用表显示 SDA 在“高、低、高、低”缓慢变化说明总线有活动主机的读写在持续发生。如果 SDA 保持高电平一动不动那大概率每次通信都停在了 NACK 位置——主机发出了请求但没有任何从机响应。这种情况不用看波形都能猜个八九不离十但真正确认还得靠示波器。我这几年用空余时间也折腾过一些老式万用表的拨盘铜片、电路图之类的东西比如经典的 MF50 型万用表它的拨盘档位切换靠铜片接触位置决定用久了铜片氧化档位就会乱跳。这类仪表维护起来很有意思但拿来测 I2C 就力不从心了所以工具定位要想清楚万用表是“哨兵”示波器才是“主力”。3. 示波器测量把每一 bit 都“看”出来3.1 示波器触发的三个关键设置示波器抓 I2C90% 的问题都出在不会设触发。我用的是一台国产 100MHz 四通道示波器配合 10x 探头设置方法具备普遍性鼎阳、普源、力科的机器菜单虽有差异但核心逻辑一样。第一个设置是垂直档位。把探头接到 SCL 和 SDA注意共地垂直档位调到 1V/div 左右如果是 3.3V 系统波形大约占三格多。耦合方式必须是 DC用 AC 耦合会把直流分量滤掉你看到的电平会严重失真。第二个设置是时基。100kbps 速率时每个 bit 占 10 微秒一帧常见的 I2C 报文起始、地址、数据、停止大约 200 微秒把时基设在 50 微秒/div 到 100 微秒/div 之间比较合适一眼能看到整帧。400kbps 时把时基缩到 10 微秒/div 到 20 微秒/div。第三个设置是触发。这是最关键的一步触发源选择 SDA 通道触发方式选下降沿。为什么因为 I2C 的起始条件就是“SCL 高电平期间 SDA 下降沿”只要在这个下降沿触发示波器就能稳定地把一帧报文从起点展现在你面前。如果你想等一张完整的时序图再把触发模式设为单次Single然后去操作主机发起通信。3.2 一帧 I2C 报文在屏幕上长什么样设置好之后触发一次读操作你会看到屏幕上出现一条很典型的波形先是 SDA 下降沿紧接着 SCL 开始以固定频率拉高拉低SDA 在 SCL 高电平期间保持稳定在 SCL 低电平期间完成切换到了第九个时钟SDA 会有一个明显的较短低脉冲这就是应答位最后 SCL 高电平期间 SDA 上升沿结束整帧。这里有个新手特别容易看错的点地址字节后的第九个时钟方向完全相反。主机发送地址时SDA 由主机驱动但从机应答时SDA 的驱动权交到了从机手里。从波形上看第九个时钟的高电平期间SDA 如果被拉低就是 ACK如果保持高就是 NACK。NACK 恰恰也是最常见的故障现象你会看到一帧报文规规矩矩地走完但最后一个低脉冲缺席了。还有一个值得多说一句的现象叫时钟拉伸Clock Stretching从机在收完地址后如果内部还没准备好数据会把 SCL 拉低不放主机就只能等着。屏幕上你会看到 SCL 的高电平被无限延长。很多示波器如果不设置“超时”或者不看协议解码单看波形会把时钟拉伸误判为总线挂死。这个细节后面排查时会重点讲。3.3 定位 ACK/NACK 的实操步骤为了定位 ACK我通常会做三个动作。第一步打开示波器光标功能把 x 光标放在地址字节后第九个上升沿第二步用 y 光标测量此时 SDA 的电压如果电压接近 0V低于 0.3VCC说明从机应答了如果是接近 VCC 的高电平说明没有应答。第三步再看一眼第九个脉冲之后 SDA 有没有变化——如果 NACK 后主机立刻发了停止条件那基本可以断定从机对当前地址无响应。实际操作时有一个小技巧保持 SCL 通道常开SDA 用脉宽触发或斜率触发这样即使起始条件抓不到也能抓住数据位。之前用过的力科示波器支持 SCPI 指令可以写个小脚本自动读取光标测量值但那是做自动化测试的时候才需要的玩法手工排查没必要上这么重的手段。如果你手里暂时没有示波器也可以用逻辑分析器配合协议解码插件。逻辑分析仪边沿触发能力比一般示波器强采样率只要 4M 以上就足够解码 100kbps I2C解码结果会直接标出每个字节的内容和 ACK/NACK非常直观。Proteus 这类仿真软件里带虚拟示波器Tina 也有搞纯软件仿真时能用来验证时序逻辑但实物排查还是以真仪器为准。4. ACK 异常排查从波形到根因的完整路径4.1 ACK 到底是谁拉低的搞清楚 ACK 是谁拉低的是排查问题的基础。I2C 标准里数据方向由 R/W 位决定主机发送地址时R/W 为 0数据方向是主机到从机也应答也由从机来拉主机读数据时地址字节里的 R/W 为 1从机在地址 ACK 之后开始把数据放到 SDA 上主机在每收到一个字节后负责拉低 SDA 表示 ACK。也就是说ACK 不是主机单方面的行为而是“谁接收数据谁应答”。排查时必须先看当前帧的方向再判断 SDA 被拉低是否正确。举个例子主机向一个只读器件发写命令从机完全可以返回 NACK因为器件不支持写反过来说主机读的时候如果主机侧代码没有被正确配置为发送 ACK从机会觉得主机没有继续读的意愿通信也会中断。4.2 NACK 的常见含义一个电平六种可能NACK 在示波器上只是一个“第九个时钟后 SDA 没有变低”的现象但背后的原因五花八门。我总结过遇到 NACK 至少要考虑六种可能从机地址不匹配。这是最常见的情况尤其是 7 位地址和 8 位地址的换算。总线上挂着地址 0x50 的 EEPROM你代码里写的却是 0xA0看起来好像对实际上这两个值相差一个方向位示波器上从机确实不会应答因为你访问的地址根本不是它。这个坑我在教新手的时候几乎每次都会碰到。从机根本没上电。电源设计里有时候会通过 GPIO 控制外设供电主机先访问再开电时序上就会一直 NACK。拿万用表量一下从机供电脚立刻见分晓。从机复位脚被拉死。很多传感器有 RESET 引脚如果被错误地拉低芯片一直处于复位状态自然不响应总线。这种问题万用表不好查你得顺着复位脚看控制信号。总线上电容太大。信号边沿变缓后时序参数不满足从机压根不认识你发来的地址。把示波器时基缩到 1 微秒/div 去看 SDA 上升沿如果上升沿走了几百纳秒还没到高电平就要考虑上拉电阻太小或总线过长。从机正忙。比如 EEPROM 在写内部 flash 的时候会返回 NACK 表示“忙”。这种情况排查时要看是不是写操作之后紧跟着读操作中间有没有留写周期时间。总线多设备地址冲突。两个从机都响应同一个地址主机发出的 ACK 反而会被其中一个设备拉低但数据总是怪怪的。这种问题用示波器抓单帧看不出来得配合总线扫描来定位。4.3 两个真实案例GT911 触摸屏与 I2C HID 设备我自己调试之前有块板子用 GT911 电容触摸屏I2C 通信失败现象是触摸屏完全无反应。用示波器抓总线发现地址字节发送后一直是 NACK。刚开始怀疑是触摸屏坏了但是查了数据手册之后发现GT911 的地址可以通过引脚配置为 0x5D 或者 0x14而且它上电后需要一定时间初始化。问题就出在主机启动太快触摸屏还没准备好就开始访问在初始化完成前它不响应任何地址。后来在驱动里加上 100ms 延时问题立刻解决。这个案例说明NACK 不一定代表硬件坏了很多时候是时序竞争。另一个值得说的是 Windows 设备管理器里经常出现的“I2C HID 设备找不到足够资源可以使用代码 12”。这类问题一般在 x86 主板上装触控板驱动时出现本质上是 I2C 控制器资源被占用或者 BIOS 配置里 I2C 通道没有正确启用。虽然这是系统层面的问题但如果你用示波器去量触控板 I2C 总线会发现总线上根本没有通信波形——因为在系统资源被占用的情况下驱动根本没法和器件握手。遇到这种问题先查 BIOS 里 I2C 是否开启、中断号有没有冲突比盲目换驱动更有效。5. 总线级排查与经验速查5.1 挂死总线、地址冲突和多路复用排查 I2C 问题很多时候问题不在某一帧的协议而在总线的整体状态。最经典的就是总线挂死某个设备在半途异常断电SDA 恰好在低电平期间掉电于是 SDA 被它内部残存的 MOS 管死死拉住。此时总线上所有通信都不进行因为主机看到 SDA 不空闲根本不会发起起始条件。示波器上你能看到 SDA 稳稳地趴在低电平上SCL 却安静如鸡。解决挂死的方法并不优雅把总线上的设备逐个排除哪颗芯片拆下来总线恢复正常就是哪颗的问题。如果是量产板也可以软件上做总线恢复主机连续翻转 SCL 九次然后发送停止条件让挂死的从机释放 SDA。这个方法我实际用过成功概率不低。当总线上的设备多了还要考虑多路复用问题。很多开发板集成了 I2C 多路开关像 PCA9546 这类一个主控可以切换访问几路不同的总线。如果你发现某路设备完全测不到信号先别急着量末端看看多路开关有没有切到正确的通道。这类芯片的“通道选择寄存器”如果没配置好总线末端就是悬空的当然测不出任何东西。5.2 上拉电阻的计算与边沿时间验证很多“偶尔通信成功、偶尔失败”的问题根源都在上拉电阻和总线电容的配合上。I2C 标准规定100kbps 模式下上升沿最长不能超过 1 微秒400kbps 模式下这个时间要缩短到 300 纳秒。上升沿时间和上拉电阻、总线寄生电容之间的关系可以用一个简化公式估算tR 0.8473 × R_pullup × C_bus其中 tR 是上升沿时间R_pullup 是上拉电阻值C_bus 是总线总电容。举个例子总线上有 4 个设备每个引脚寄生电容约 10pFPCB 走线再加 20pF总线电容大约 60pF。想满足 100kbps 的 1 微秒上升沿要求上拉电阻要小于 1μs / (0.8473 × 60pF) ≈ 19.7kΩ。市面上常见的 4.7kΩ 和 10kΩ 在这个场景下都够用。但如果总线长了、电容大了10kΩ 就会显得“拉不动”上升沿变慢导致误码。也不能用太小的上拉电阻。电阻太小芯片开漏拉低时流过的电流太大可能超过数据手册规定的最大灌电流。一般计算下极限是 R_min (VCC - Vol_max) / Iol_max比如 3.3V 系统Vol_max 取 0.4VIol_max 取 3mA最小电阻就是 (3.3 - 0.4) / 0.003 ≈ 967Ω。所以大部分板卡选 2.2kΩ 到 4.7kΩ 是比较稳妥的区间。如果你用示波器看到 SDA 上有很多毛刺或者边沿明显过冲那大概率是上拉太强可以换个更大的阻值试试。5.3 问题速查表与我的实操套路我把这几年遇到过的 I2C 问题整理了一个速查表排查时按图索骥很快现象可能原因优先排查动作两根线都高但没有通信波形软件没发起通信或 GPIO 配置错误检查驱动日志用万用表确认主控引脚有翻转SDA 长期低电平从机挂死或 GPIO 输出低逐设备排除软件九脉冲恢复地址后无 ACK从机未上电、地址错误、器件忙、复位拉死量供电、核对 7/8 位地址、示波器配合查时序有 ACK 但读回全 FF从机正忙、寄存器地址未设置、总线电容过大检查写周期重新核对寄存器操作顺序偶尔通信失败上拉电阻不合理、时钟拉伸不被主机支持计算上升沿查看从机是否有拉伸时钟多设备数据错乱地址冲突、复用开关通道错误扫描总线地址核对开关寄存器在实际排查中我的顺序永远是先量静态电平再抓起始条件再数第九个时钟最后看 ACK。这套流程看似笨拙但每次都能把问题收敛到“电气”“时序”“逻辑”三个层面中的某一个。有一点我要特别强调排查 I2C 问题最忌讳一上来就改代码。很多驱动工程师看到设备没响应本能反应是加大延时、改速度、换通信库折腾半天没有效果。实际上绝大多数问题在波形上一眼就能看出来与其反复尝试不如先把示波器接好让数据说话。这个习惯我踩了很多次坑才养成。最后再说一个小技巧调试时尽量把 I2C 速率先降到 100kbps。很多高速模式下失败的模式降低速率之后就能正常通信这能帮你快速区分“协议逻辑问题”和“电气时序问题”。如果降速之后问题依旧那大概率软件层面有问题如果降速就好了回过来查上拉电阻和总线电容方向会明确很多。我个人的体会是I2C 排查从来不是靠智商而是靠流程。把“测量电平—抓起始—查 ACK”这套动作练成肌肉记忆再配合对总线电气特性的理解绝大多数字设备上的疑难杂症都能在半小时内定性。希望这篇整理对你有用下次再遇到 ACK 不回来的时候别急着怀疑芯片先看看示波器上的第九个脉冲答案往往就藏在那一个低电平里。