ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

I2C总线调试实战:用示波器与万用表定位ACK故障的排查流程

I2C总线调试实战:用示波器与万用表定位ACK故障的排查流程 做硬件调试这几年I2C 是我遇到问题最多的总线没有之一。它只有 SDA、SCL 两根线靠电平高低传数据看起来比 UART 简单可一旦设备扫不到、读出来全是 0xFF、或者偶尔好偶尔坏很多朋友就不知道该拿万用表还是示波器下手。这篇文章不是复述 I2C 协议文档而是按我自己的工程习惯从万用表该测什么、示波器怎么设触发、怎么用第 9 个时钟上的 ACK 位做分段定位整理成一条能照着做的排查流程。适合刚接触 I2C 的嵌入式新手也适合那些示波器买了很久、但遇到问题只会乱戳按钮的同学。1. 动手之前先弄懂 I2C 的电气和时序底子很多排查卡壳不是示波器不会用而是脑子里没有一套“正常的波形应该长什么样”的参照。I2C 的工作方式和推挽输出的 SPI 不一样先把这个底子补齐后面所有测量才有意义。1.1 两根线上到底发生了什么开漏结构和上拉电阻I2C 的 SDA 和 SCL 都是开漏/开集电极结构芯片内部的 MOS 管只能把线拉低不能主动拉高。线上想要出现高电平必须靠外部上拉电阻接到电源 VDD。所以空闲状态下总线电压应该被上拉电阻拉到接近 VDD某个设备要发送逻辑 0就打开内部 MOS 管把线拉到地要发送逻辑 1就释放这条线让它被上拉电阻拉回高电平。这种设计的优点是多设备可以直接“线与”谁输出低谁就赢不需要额外的片选信号。代价是对上拉电阻很敏感。电阻选太大总线上升沿太慢超过 I2C 规范容许的上升时间从机就可能采不到正确电平电阻选太小待机电流变大而且部分从机灌电流能力有限低电平可能抬不到规范允许的 VIL 以下。上拉电阻的估算其实不难。总线上的分布电容、芯片引脚寄生电容加在一起典型值在 50pF 到 200pF 左右。上升时间大概遵循 RC 特性工程上按 30% 到 70% 电平常用的估算公式是tr ≈ 0.8473 × R × C。举个例子3.3V 供电、总线电容 100pF、上拉 4.7kΩ算出来上升时间接近 397ns跑 100kHz 模式可以接受但跑 400kHz 就会紧张因为后者最大上升时间只有 300ns。更稳妥的做法是直接按从机手册推荐值布板调试阶段先挂 2.2kΩ 或 1kΩ 实验再用示波器实测确认。我调试时习惯先查一个关键状态总线空闲时 SDA 和 SCL 应该都是高电平。如果某根线在空闲时就被拉低说明有设备在“霸占”总线或者上拉电阻没焊好。这两根线不是“有波形就行”它们的静态电平、上升沿斜率、高电平幅度每个参数都可能成为故障源。1.2 时序和 ACK第 9 个时钟是排查的分水岭I2C 的时序没有并行总线的时钟约束那么复杂但有几个关键点必须背下来。第一数据在 SCL 高电平期间必须保持稳定只能在 SCL 为低电平时变化。第二START 条件是 SCL 为高时 SDA 产生一个下降沿STOP 条件是 SCL 为高时 SDA 产生一个上升沿。平时我们看波形先要能找到这个“高电平期间跳变”的起点因为它代表一帧传输的开头。每个字节发完 8 位数据后在第 9 个 SCL 时钟周期会有一个 ACK 位。发送方在第 8 个时钟结束后主动释放 SDA接收方如果正常接收会把 SDA 拉低让主机在第 9 个时钟的高电平期间采样到低电平这就是 ACK。如果接收方不响应或者没收到SDA 线会被上拉电阻拉高主机采样到高电平就是 NACK。这里有个细节经常被新手忽略写操作时主机发完数据后释放 SDA由从机回应 ACK读操作时主机作为接收方在前面的字节也要主动拉低 SDA 回 ACK只有读到最后一个字节前主机要故意不拉低发一个 NACK 告诉从机“不要再发了”。如果你在示波器上看到读取操作时主机地址之后从机已经 ACK但数据段全是高电平很可能不是从机没响应而是主机代码在接收最后一个字节前没有正确处理 NACK 时序。ACK 位是整个排查流程的“分水岭”。只要抓住“第 9 个时钟上 SDA 是低还是高”这个观测点就能把问题切成三段地址发出去有没有 ACK、寄存器地址有没有 ACK、数据字节有没有 ACK。绝大多数 I2C 故障都能在这三个位置上找到对应症状。2. 万用表在 I2C 排查中的真实定位在拿出示波器之前先用万用表把电气基础检查一遍能节省大量盲目抓波的时间。万用表虽然看不到时序但在“总线死锁”“上拉缺失”“短路”这三类问题上效率比示波器高得多。2.1 万用表能测什么不能测什么万用表能测的是静态或者慢变化量空闲电平、电源是否到位、上下拉电阻是否虚焊、线路是否短路和断路。给系统上电后可以把表笔分别点在 SDA 和 SCL 上对 GND 测电压。正常情况下两块表笔测到的都接近 VDD。如果测到 0V首先怀疑没上拉或者设备没上电测到 VDD 的一半左右多半是总线被某个设备拉低和上拉电阻在“较劲”的动态平均值说明有芯片输出级损坏或者死锁别继续猜马上查是哪个设备把线咬死了。万用表的输入阻抗通常是 10MΩ对 I2C 总线并行接入的负载效应不算大测空闲电平没问题。但它的采样率太低抓不到毫秒级的 ACK 脉冲也看不出上升沿是否太慢。更实际的作用是“断电后的电阻测量”把系统断电用蜂鸣档检查 SDA、SCL 有没有对地短路测一下上拉电阻是否等于标称值顺带检查排针、连接器各脚和杜邦线是不是接触良好。调试板上杜邦线松动导致 I2C 偶发不通的情况我碰到过不止一次这种问题直接用示波器去抓反而很难定位。有一点要提醒不要在总线正在通信时用万用表去碰高速变化的数据线试图靠数字跳变判断“有没有信号”。万用表显示的是积分后的均值跳来跳去反而误事。它擅长回答“有没有电”“是不是断”“是不是短”不擅长回答“时序对不对”。只要涉及波形和 ACK就轮到示波器上场了。2.2 用万用表定位“总线忙”和上拉缺失具体排查顺序我习惯从最简单的“空闲电平”开始。第一步系统上电但不发任何通信指令用万用表直流电压档红表笔点 SDA黑表笔接 GND记录电压再点 SCL记录电压。如果两个都是接近 VDD 的高电平说明上拉和供电正常可以放心打开逻辑分析仪或者示波器。如果任何一个电压明显偏低就沿着这根线把所有并联设备逐个断开每断开一个重新量一次看哪一步后电平恢复正常。第二步处理总线死锁。我见过最多的情况是从机内部的 I2C 状态机卡在了中间状态一直拉着 SDA 不放。此时常见做法是给主控发一个软件复位或者对 SCL 连续产生 9 个额外时钟脉冲帮助从机把状态机走完、释放总线。但如果你不知道哪个从机在捣乱最稳的办法仍然是断电、断开疑似设备、重新上电再用万用表确认总线已经回到高电平。先把“静态电平不对”这个问题消除再谈后面的时序排查。第三步检查上下拉。I2C 系统里常见的错误是上拉电阻缺失或者用了 100kΩ 这种明显过大的值。把板上电后掉电测量上拉电阻到 VDD 的通路以及电阻两端阻值。如果使用的是 MCU 内部上拉调试阶段先别太依赖它内部上拉一般只有几十 kΩ很多人误以为“单片机有内部上拉所以不用外接”结果在 400kHz 模式下波形完全走样。如果你不确定可以选择开漏模式加外部 4.7kΩ/2.2kΩ这是最不容易出问题的组合。万用表这部分能做的事情就这么多结论很直接电平基础没问题继续电平基础有问题先解决再往下走。不要觉得这个步骤绕路它往往能让你避开一次两小时的示波器空抓。3. 示波器从基础设置到协议解码示波器是 I2C 排查的主力。先花十分钟把探头、触发、时基设置对比事后对着一屏乱糟糟的波形猜答案要划算得多。这里说的都是很具体的操作不是为了讲参数而讲参数。3.1 探头、通道、触发的标准设置通道分配方面我习惯把 CH1 接 SDACH2 接 SCL这样看协议解码时不容易搞混。探头打到 ×10 档先做探头补偿。多数数字示波器前面板有 1kHz 方波校准输出把探头接上去调到屏幕上看到平顶的方波为止。很多稀奇古怪的波形畸变根源就是探头没补偿好尤其是二手示波器或者探头换来换去的情况。如果你手上是正点原子 DM40 这类入门示波器同样先确认探头补偿波形再做信号测量。触发设置是抓 I2C 的关键。直接用 SCL 的边沿触发抓到的一般只是反复变化的时钟看不出帧的开头。更好的方法是利用“START 条件”触发在 SCL 为高电平时SDA 突然拉低的这一跳变作为触发点。中端以上的示波器比如鼎阳、普源、力科的常见型号都有“斜率触发”“脉冲触发”或者专门的串行触发菜单能指定“SDA 下降沿且 SCL 为高”这样的条件如果菜单里没有退而求其次用 SDA 通道的下降沿触发再配合一段时基后移也能把完整帧拉进屏幕。触发方式选对以后示波器才会稳定地显示一帧从 START 到 STOP 的完整波形而不是满屏乱跳。时基设置取决于通信速率。100kHz 模式下一帧假设是“地址寄存器一个数据字节”总时长大约 270µs用 100µs/div 到 200µs/div 可以看到整帧400kHz 模式下时间缩短到 70µs 左右用 20µs/div。先保证采样率够用再把水平刻度拉开观察整帧和 ACK。部分示波器有自动测量功能可以把 SCL 频率直接读出来这比肉眼数格快很多。顺带一提力科和鼎阳等品牌的示波器支持 SCPI 指令或者网页远程控制调试过程中需要连续监控总线状态时可以把触发后的波形导出到电脑上分析比一张张按截图按钮省事。3.2 看懂一帧从 START 到 ACK 的标志位检查把示波器调好以后先建立一个“标尺”到底什么样的波形算正常。正常情况下空闲时两根线都是高位START 出现后SCL 开始产生方波SDA 在每个字节的第 9 个时钟之前会被拉低一下。这个低脉冲就是 ACK。我用光标分别卡在第 8 个时钟下降沿和第 9 个时钟高电平中间直接观察 SDA 的电平状态。SDA 为低这次响应成功SDA 为高对端没有 ACK。别看其他东西先看这一位。示波器屏幕上的 I2C 波形SDA 是一串不规则的高低变化而 SCL 是规则方波。不要试图去“读”每一位二进制那是逻辑分析仪和协议解码器的工作。手动排查时我只需要确认三件事START 位置是否明确、SCL 频率是否和目标一致、第 9 个时钟时 SDA 是否是低。只要这三个都正常就可以判断通信流程走到了哪一步。如果示波器有 I2C 协议解码功能那会快很多。一般菜单里有“串行解码”或者“总线解码”选择 I2C指定 SDA 和 SCL 对应的通道再设置逻辑阈值。常用阈值选 VDD/2比如 3.3V 供电就设 1.65V 左右。设好之后屏幕上会直接标出地址字节、数据字节和 ACK/NACK 标记。很多入门示波器比如 DM40 也有这一功能别只当它是一个普通双通道示波器用。需要提醒的是解码器标记的 NACK 是“它认为 SDA 在第 9 个时钟上没有变低”这和张照片手动观察的结论等价但偶尔会因为阈值设置错误而误判。最好还是切到原始波形用光标亲自验一次。4. 核心排查流程从“无波形”到“NACK”到“偶发掉线”前面把工具准备好了这部分是真正动手的排查路线。我习惯把所有异常归成三类完全没有波形、地址 ACK 失败、数据阶段错乱每一类有不同的原因和对应操作。这里重点讲怎么用 ACK 做分段定位。4.1 按字节分段排查地址 ACK、寄存器 ACK、数据 ACK一次典型的 I2C 写操作是“START 从机地址 写位 ACK 寄存器地址 ACK 数据 ACK STOP”。三次从响应方回 ACK 的机会每次都对应不同的故障点。第一段是地址 ACK。主机发完地址字节后第 9 个时钟上 SDA 有没有变低如果这里就是高电平说明从机根本没有响应。别急着怀疑时序先检查从机供电、地址引脚状态再看代码里的地址是不是写错了。I2C 地址最大的坑是 7 位地址和 8 位读写地址的换算。手册上写设备地址是 0x50这通常是 7 位地址代码里写 I2C 发送时要左移一位变成 0xA0写或者 0xA1读。如果驱动封装已经帮你做了移位你却重复移位一次地址就完全不对NACK 也是必然的。拿示波器看波形直接在屏幕上读出地址字节的二进制值比对是不是期望值很容易排除这种问题。第二段是寄存器地址 ACK。如果地址字节后已经看到 ACK但随后寄存器地址字节后是 NACK说明从机收到了地址但认为后续的寄存器地址不合法。可能是寄存器的分页问题、该地址只支持写不支持读也可能从机正忙比如 Flash 写入期间不接受新的寄存器操作。这时候去看数据手册的寄存器表并检查代码里是不是把“存储器写”和“寄存器写”的操作码搞混了。第三段是数据字节阶段的 ACK 问题。写操作时从机对每个数据字节都要回 ACK如果某段数据后出现 NACK常见原因是从机写保护开启、写入长度超过页大小、或者连续写太多字节把从机内部的缓冲区撑爆。读操作时情况反过来主机在收到最后一个字节前必须发 NACK 来示意停止。如果你在读取时发现从机已经正常 ACK但读到的全是 0xFF先别猜器件坏看看是不是主机在最后字节上没有正确释放 SDA导致后续数据线一直被主机拉低。典型的“总线上永远低电平”现象往往就是主机状态机里漏了读操作最后一个字节的“释放总线”动作。4.2 三个高频问题定位方法第一个高频问题是总线死锁症状是 SDA 或 SCL 一直低电平万用表测出来就是 0V。造成死锁最常见的原因是从机状态机在传输中途被中断比如发送方已经开始一个字节主机却因为某次异常响应主动结束传输从机还停在“等待第 9 个时钟”的状态里死死拉着 SDA。此时给它正常的 SCL 时钟但 SDA 一直被拉低之后所有传输都会失败。解决办法是在 SCL 线上手动产生 9 个额外脉冲把从机“踢”出等待状态如果还不行就断开从机电源确认总线恢复高电平后再从主机开始排查。这里面有一个容易忽略的点产生复位脉冲时要保证 SCL 始终正常翻转同时 SDA 保持高很多人在软件里模拟 9 个脉冲时把两根线顺序搞反越复位越乱。第二个高频问题是地址 NACK但硬件看起来没什么问题。这种时候优先怀疑地址配置引脚。不少 I2C 从机有 A0/A1/A2 硬件地址引脚焊接时直接接地或者悬空地址就会不同。板子上看到的丝印和芯片手册的推荐接法不一定一致最好用示波器读一下实际线上跑的地址再对照手册确认。另外还有一些 I2C 从机不支持“重复起始条件”主机却在同一事务里先写地址后发读命令设备就会在读到第二个地址时 NACK。遇到这种问题不要只调示波器还要看主机驱动里是否使用了 repeat-start必要时改成“先 STOP 再 START”的分离式读写。第三个高频问题是偶发 NACK 或偶发数据错误。波形大多数正常但每隔一段时间就出现一个 NACK或者读回来的字节某一位被毛刺污染。这时候重点测上升时间打开示波器的自动测量看看 SCL 和 SDA 的上升沿是不是已经超出所在速率模式的规定。100kHz 模式最大上升时间 1000ns400kHz 模式是 300ns1MHz 模式是 120ns。如果上升沿超了优先减小上拉电阻。如果总线连线超过 10cm、用了排线或者长杜邦线串一个 22Ω 到 100Ω 的电阻靠近主控端能抑制振铃。电源纹波也要顺带看一眼I2C 是电平判决总线VDD 上叠加高频噪声很容易让从机把低电平误判成高电平。4.3 逻辑分析仪与自动化脚本的配合示波器不是唯一工具。几十块钱的 USB 逻辑分析仪在 I2C 排查里也很有用尤其是多字节长事务。它直接采样引脚高低电平软件自动解码成地址、数据和 ACK 标记还能连续抓很长时间适合复现偶发故障。逻辑分析仪的问题在于看不到电压幅度和上升沿属于“只懂逻辑、不懂电气”。所以我通常的逻辑是先用示波器确认电气参数和触发位置再用逻辑分析仪做长时间统计监控两边互补。如果你手上的示波器支持把波形导出成 CSV也可以用 Python 写一个简单脚本把捕获到的 SDA/SCL 电平变化按边沿恢复成数据位自动打印十六进制帧。这个小工具不复杂但对于长期调试某些只在特定操作序列下出错的问题很有效。拿 VISA 库调力科、鼎阳等示波器的 SCPI 接口也能实现类似功能不过这是进阶玩法刚开始不必折腾。5. 常见故障速查表和实操中的几个习惯最后把经验压缩成一张表方便大家在实际布线、焊接、调驱动时对照。这张表是我多次故障笔记的浓缩比单纯背协议管用。5.1 常见现象、原因、解决方向对应表现象可能原因排查方向空闲时 SDA 或 SCL 为 0V上拉缺失、设备未上电、从机死锁万用表逐段测电平断开设备重新上电SCL 有方波SDA 一直高地址错误、从机未激活、重复启动时序冲突示波器读地址二进制值对照手册和驱动地址后有 ACK寄存器地址后 NACK寄存器地址非法、寄存器分页、设备忙查手册寄存器表确认操作码写操作数据后 NACK写保护、页长度超限、缓冲满检查写保护引脚拆分写包读操作返回 0xFF主机读操作释放 SDA 时序错误、从机空闲观察读操作数据段第 9 个时钟的 SDA 电平通信偶发 NACK/数据错上升沿太慢、长线振铃、电源噪声测上升时间减小上拉加串阻看电源纹波低速正常高速模式出错上拉电阻太大、从机不支持高速按速率规范重新计算上拉降低时钟测试5.2 实操中我习惯坚持的注意事项第一每次换通道、换探头、换示波器都要先做一次探头补偿。很多看起来像是 I2C 时序畸变的波形其实是探头电容没匹配好。不要高估记忆也别嫌这一步麻烦用示波器之前的探头校准输出30 秒钟的事情。第二在信号不稳定时把示波器的触发时基调到几十毫秒然后从 STOP 条件往回找异常点。I2C 的偶发故障不会每次都压在同一个字节上直接盯第一帧反而容易漏。使用长时间采集让示波器等待“再一次触发”并保持波形冻结再去判断是哪个字节出了问题。很多入门示波器在“扫描”或者“滚动”模式下也能实现类似效果别只盯着普通触发界面。第三调试之前先断开所有无关的 I2C 从设备只保留一个最小系统。如果单从机能通、多设备时不通问题往往出在地址冲突、总线电容过大、或者某个从机上拉又被额外挂了一路上拉。这类“上拉叠加”的问题在示波器上看起来只是高电平偏高实际却可能导致低电平电压超标让从机无法识别逻辑 0。第四保存一份“健康波形”作为参照。我一般在拿到一块新板子、第一次跑通 I2C 后会把示波器离线的地址字节、ACK 位置、上升沿时间截图存档。后面一旦出现定位困难的偶发故障把这些历史截图调出来对比哪个字节变样一目了然比重新分析协议高效得多。最后再分享一个个人习惯排查 I2C 时我会同时打开示波器的协议解码和原始波形解码器负责提示“哪里是 NACK”原始波形负责确认“为什么 NACK”。只看解码器容易忽略电气层面的诱因只盯波形又太慢。两边配合绝大多数 I2C 问题都能在半小时内收敛到某个具体原因上。下次再拿到一台“怎么都连不上”的传感器模块不妨按这个顺序走一遍你会有种“原来是这样”的踏实感。
返回列表