ARTICLE DETAIL

资讯详情

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

嵌入式I2C调试实战:从万用表体检到示波器波形与ACK信号分析

嵌入式I2C调试实战:从万用表体检到示波器波形与ACK信号分析 做嵌入式这些年半夜还在跟 I2C 搏斗的次数实在太多了。两根线、一个地看着比 SPI 还简单可真到设备不响应、寄存器读回全是 0xFF 的时候没有一套测量思路就只能靠猜和试。这篇文章不绕弯子把我平时测 I2C 信号的路数完整捋一遍万用表先做静态体检示波器抓动态时序最后盯住 ACK 这个最容易被忽略、又最能说明问题的信号位一步步把故障范围缩到最小。主要适合搞单片机、嵌入式 Linux 驱动和硬件调试的朋友也适合刚入门想真正看懂 I2C 时序图的新人。1. 测量之前先搞清楚 SDA/SCL 在线上到底长什么样很多人上来就把示波器探头往 SDA 上一搭看到一堆跳动的波形就懵了。其实 I2C 这种协议你在物理线上能看到的电压形态非常固定搞清楚底层机制比记一堆时序图更管用。1.1 开漏结构决定了电压只有两种“常态”I2C 线上的每一个设备输出级基本都是开漏open-drain或者开集电极结构。什么意思就是每个芯片的输出端只是一个“对地的开关”它只能把线拉低不能主动把线推高。线要变高靠的是电路板上的上拉电阻把 SDA 和 SCL 接到 VDD。打个比方一条绳子两端绑着弹簧把绳子绷直绷高但线上挂着好几只手任何一只手都能把绳子按到地上松开之后绳子又被弹簧拉回高处。这只手就是某个芯片的开漏输出弹簧就是上拉电阻。所以你在测量时会看到两种现象总线空闲时SDA 和 SCL 都应该是高电平接近 VDD总线忙碌时谁发送数据谁就把线往下拉其余时间线保持高。明白这个之后排查很多问题就顺了。比如你用万用表量到 SDA 是 0V那说明要么没有上拉电阻要么有设备一直按着线不松要么线对地短路了。这三种情况的处理方向完全不一样。1.2 起始、停止和数据位SDA 什么时候能翻I2C 协议最核心的一条规则是SDA 只能在 SCL 为低的时候改变SCL 为高的时候 SDA 必须保持稳定。这句话看着简单但它直接决定了你在示波器上看到的波形。起始条件START是 SCL 为高时 SDA 从高变低停止条件STOP是 SCL 为高时 SDA 从低变高。因为正常的数据位不允许在 SCL 高时改变 SDA所以这两个特殊的跳变就成了解码的“锚点”。读波形时我习惯先找 START再顺着时钟数位数。每一字节是 8 个数据位后面必然还有第 9 个时钟脉冲用来给 ACK/NACK所以一帧里实际上每隔 9 个时钟脉冲就是一次字节传输的边界。用示波器看时如果看到 SDA 在 SCL 为高的时候跳来跳去那不是正常的 I2C 数据多半是你的触发条件没找对或者是信号干扰导致协议错乱。1.3 速率、上拉电阻和总线电容边沿快慢有讲究I2C 常见速率有标准模式 100 kbps、快速模式 400 kbps以及快速模式 1 Mbps。很多人忽视了上拉电阻和总线电容对波形的影响其实这两个参数决定了信号上升沿有多“陡”。上升时间大致可以用一个 RC 公式估算t_r ≈ 0.8473 × R_p × C_b。其中 R_p 是上拉电阻C_b 是总线上所有引脚电容加上走线分布电容的合计。举个例子100 pF 总线电容配 4.7 kΩ 上拉上升时间约 398 ns跑 100 kbps 没问题但跑 400 kbps 时规范要求上升沿不超 300 ns这个组合就有点悬。所以我实际选型会参考这张表速率推荐上拉范围备注100 kbps4.7 kΩ ~ 10 kΩ短总线、距离近阻值可以大400 kbps2.2 kΩ ~ 3.3 kΩ常用 2.2 kΩ负载稍重也稳1 Mbps 左右1 kΩ ~ 2.2 kΩ走线要短设备数量别太多上拉阻值太大边沿变缓可能导致设备采样不到正确的电平上拉阻值太小设备拉低时的电流变大有些弱驱动的传感器会把低电平拉得不够低。后面排查时你会发现很多“偶发读错数据”的问题根源就在这里。2. 万用表不是废柴电压、通断和上拉电阻的快速体检很多人一说到测 I2C 就直接上示波器其实万用表能帮你快速排除一大半电气基础问题。我调板子的习惯是先把万用表能查的查完再决定要不要上示波器效率反而高。2.1 电压档看空闲电平三秒钟判断总线有没有“塌掉”把万用表打到直流电压档黑表笔接 GND红表笔分别点 SDA 和 SCL。前提是不要让系统正在通信最好把主机的 I2C 外设停掉或者让代码停在初始化之前。测出来的空闲电平应该是接近 VDD 的。举个例子VDD 是 3.3 V那 SDA 和 SCL 都应该是 3.3 V 左右。如果量到接近 0 V总线被拉死了。可能原因是没有上拉电阻、某设备开漏输出一直导通、SDA 或 SCL 对地短路。只有 1.6 V 到 2.5 V 之间的“中间电平”这种情况最诡异。可能是总线在不停地通信你看到了平均值也可能是某个器件内部有弱下拉或者上拉电阻没接好电压被分压了。第一个动作我会先确认上拉电阻的上端接的是哪一路电源。以前遇到过板子上 SDA 的上拉接到了 5 V而主控 IO 是 3.3 V 的导致主控引脚经常被灌电压读回来的数据随机出错。所以测空闲电平时顺便把电阻两端都量一下确认上端确实接在正确的电源轨上。2.2 通断档和电阻档查短路、虚焊和上拉阻值断电之后用万用表的通断档查三组关系SDA 对 GND、SCL 对 GND、SDA 对 SCL。正常情况下三组都应该不通。如果哪组蜂鸣响了基本可以断定有短路接下来就是分段割线找问题源。再用电阻档量上拉电阻。比如板子上画的是 4.7 kΩ你量出来只有 2.3 kΩ那很可能是板子上还有一组并联的上拉电阻或者主控芯片内部的上拉也在生效。并联之后阻值变小通常问题不大但你心里要有个数因为它会影响后面的边沿和驱动能力。如果量出来是几百欧甚至更小那就得警惕了说明上拉选得有问题或者有别的通路把线拽住了。这里有个新手特别容易踩的坑用二极管档量 SDA 对 GND读数显示 0.4 V 左右就以为短路了。其实那可能是芯片引脚内部的保护二极管在导通属于正常现象。判断短路尽量用电阻档不要单靠二极管档。2.3 万用表的边界它看不到 ACK只能做“静态体检”必须认清一个事实万用表没有时间分辨率它看不到任何协议层面的动态行为。你在通信过程中量 SDA只会得到一个随数据 1/0 比例变化的平均值比如大部分时间是高电平的波形量出来可能是 3.1 V 左右这个数字什么都说明不了。所以我的分工是万用表负责回答“线上电平和通断对不对”示波器负责回答“时序和协议对不对”。万用表量出来的结果如果正常那只是排除了电气层的基础问题不代表总线能跑通反过来如果万用表已经量出明显故障就别急着上示波器了先把基础问题修好。3. 示波器抓波形探头位置、触发电平和时间基准的搭配示波器是测 I2C 的主力工具。但我见过太多人不是不会用示波器而是触发和时间基准没设置好抓回来的波形要么缺头缺尾要么密密麻麻根本没法看。3.1 探头选择与接地测量误差往往来自测量本身测 I2C 我建议用 10× 无源探头不要用 1×。10× 探头的输入电容一般在 10~15 pF对总线的影响小1× 探头电容常常超过 100 pF接上去之后相当于给总线并联了一个不小的电容边沿会变慢甚至让原本勉强工作的总线彻底挂掉。这属于“测量行为改变了被测对象”的典型情况。另外上机之前先做探头补偿校准用示波器自带的 1 kHz 方波信号把探头微调电容拧到波形上下沿平直为止。很多人忽略这一步看到圆角上升沿就以为总线有问题其实只是探头没校准。接地线也别偷懒。长鳄鱼夹地线会引入电感在 400 kbps 或更高速率的边沿处激起振铃波形上全是毛刺。我习惯用示波器配的短接地弹簧直接套在探头尖端旁边把地和被测点尽量靠近量出来的波形干净很多。3.2 触发设置想抓“完整一帧”先找起始条件新手最容易犯的错是拿 SCL 的上升沿触发结果屏幕上全是乱跳的波形因为你触发在了一个周期性的普通时钟上无法区分每一帧从哪开始。正确的思路是触发在 SDA 的下降沿并且这个下降沿必须发生在 SCL 为高的时候那才是 START。现在不少示波器有 I2C 触发菜单直接选 START 触发最省事。没有的话可以用 SDA 下降沿触发再把 Trigger Holdoff 调到 1 ms 到 10 ms。Holdoff 的意思是触发之后一段时间内不再响应新的触发条件这样一帧只会触发一次波形就能稳定停在完整的帧头。时间基准的搭配也有规律。100 kbps 时一字节加 ACK 是 9 个时钟耗时约 90 μs。如果是一次写操作通常包含地址、寄存器、数据一帧可能 300 μs 到 1 ms。我习惯先设 100 μs/div 看整帧结构抓到之后再放大到 20 μs/div 看每一位。400 kbps 时把这些值按比例缩小 4 倍整帧用 50 μs/div看细节用 5 μs/div。3.3 波形上要看的四个东西电平、边沿、时序和毛刺抓到波形之后我不会急着看数据内容而是先检查四个指标。第一是高低电平。高电平应该接近 VDD低电平应该接近 0。3.3 V 系统里VIL 一般是 0.3 × VDD ≈ 0.99 VVIH 是 0.7 × VDD ≈ 2.31 V。如果高电平只有 1.8 V信号余量就不够了抗干扰能力很差。第二是边沿。上升沿应该是平滑且快速的过程如果看到明显倾斜的斜坡就是上拉电阻偏大或总线电容偏大。下降沿一般很陡因为它是有源器件主动拉低的。第三是时序关系。看 SDA 的变化是不是都发生在 SCL 为低期间如果在 SCL 为高时 SDA 也有跳变那个位置如果不在帧头和帧尾就说明协议出问题了要么是干扰要么是主机软件没按协议来。第四是毛刺和振铃。SCL 为高时SDA 上不应该有任何抖动。如果你看到 SDA 在高电平期间有超过 0.5 V 的尖刺设备采样时就可能读到错误的位。这种问题往往要查走线、地回路或者考虑在线上串一个 22 Ω 到 100 Ω 的电阻抑制反射。3.4 手动解码没有协议分析功能时怎么读出一帧不是每台示波器都带 I2C 解码但你可以手动读。方法不复杂找 STARTSDA 在 SCL 为高时下降。从 START 后第一个 SCL 上升沿开始在每一个上升沿读取 SDA 电平连续读 8 位这是地址字节。第 9 个上升沿再读一次 SDA。为低就是 ACK为高就是 NACK。后面每个字节重复同样的动作直到出现 STOPSCL 为高时 SDA 上升。举个例子假设起始后你顺着时钟读到 SDA 依次是 1、0、1、0、0、0、0、0这串二进制就是 0xA0。它的低 1 位是 0表示写方向高 7 位 1010000 就是地址 0x50。第 9 拍如果看到 SDA 被拉低说明地址为 0x50 的设备在线并给了 ACK。这个手动读法虽然慢但能帮你建立对时序的直觉比直接依赖解码功能更不容易出错。4. ACK 是 I2C 的灵魂第九个时钟周期怎么判读如果让我只选一个信号位来判断 I2C 调试的方向我选 ACK。它几乎能告诉你“设备在不在、有没有准备好、你发的地址对不对”。4.1 ACK 的物理机制第九拍 SDA 被从机拉低每一字节传输完主机都要额外产生第 9 个时钟脉冲专门留给应答位。发送完 8 位数据后主机把 SDA 释放掉让上拉电阻把线拉高如果接收方存在并且准备好接收它会在第 9 个时钟期间主动把 SDA 拉低这就是 ACK。如果接收方不拉低SDA 在第 9 拍保持高电平那就是 NACK。在示波器上看 ACK 的关键是把目光集中到第 9 个 SCL 上升沿在那附近读 SDA 电平。ACK 时你会看到 SDA 在第 9 拍期间被压到低电平NACK 时 SDA 一直挂在高的位置。这个反差非常明显看几次就记住了。有个细节必须提醒读操作时主机是接收方第 9 拍的 ACK 是由主机自己发出的从机只负责前 8 拍发送数据。所以读数据过程中如果最后一字节是主机主动不回 ACK而是给一个 NACK然后发 STOP那是完全正常的顺序很多协议分析软件也会把这种 NACK 标成“最后由主机终止读操作”。新手看到这里 NACK 就以为设备坏了其实这是标准做法。4.2 不同位置的 ACK/NACK 各代表什么同样的 ACK 信号出现在不同的字节位置含义完全不同。我整理过一张对照表排查时直接对照位置现象含义与排查方向地址字节第 9 拍ACK地址正确设备在线且能响应地址字节第 9 拍NACK设备不在该地址、没上电、处于复位或者 SDA/SCL 接反寄存器地址第 9 拍ACK该寄存器/命令有效寄存器地址第 9 拍NACK寄存器不存在、设备忙、写保护生效或者地址超出范围写数据字节第 9 拍ACK设备正常接收数据写数据字节第 9 拍NACK设备忙、写保护、不支持该操作读数据过程中的第 9 拍ACK主机要求继续读读数据最后第 9 拍NACK主机主动结束读属正常还有一个容易被当成 ACK 问题的情况时钟延展Clock Stretching。有些从机在没准备好时会主动把 SCL 拉低主机必须等待 SCL 重新变高才能继续。在示波器上你会看到 SCL 的低电平时间明显变长。这不是 NACK也不是死机而是从机的正常动作。如果你的主机是纯软件模拟的 I2C而且不支持检测时钟延展总线就会在某个字节之后错位看起来就像协议卡死。4.3 从波形反推“卡在哪一步”实际排查时我经常遇到一帧波形里只有一个 ACK 的情况。比如START → 0xA0 → ACK → 0x00 → NACK → STOP这个波形说明地址 0x50 的设备是存在的但对寄存器地址 0x00 不应答。结合设备手册可能的解释是这个寄存器不存在、设备正处于内部写周期比如 EEPROM 正在写这时候它会暂时对后续命令 NACK、或者你操作的不是可写的寄存器。整体排查方向就很明确了不需要再去量电平、查接线。反过来如果第一个字节就 NACK比如START → 0xA0 → NACK那问题基本不在寄存器层而是设备层或物理层。优先查设备有没有上电、地址到底是多少、SDA 和 SCL 是不是接反了、总线空闲电平是否正常。这块我后面还会展开讲。4.4 逻辑分析仪是示波器的好搭档如果你的示波器没有 I2C 解码功能而你需要连续分析几十帧数据建议直接上一个便宜的 USB 逻辑分析仪。采样率要在 SCL 频率的 4 倍以上400 kbps 时建议至少 8 MHz 到 16 MHz。通道 0 接 SDA通道 1 接 SCL软件会自动解析出地址、数据、ACK/NACK 标志。逻辑分析仪的好处是解析速度快、信息直观但它只能看到逻辑电平看不到模拟电压。如果怀疑是电平幅度或边沿问题还是得回到示波器。我习惯的方案是先用逻辑分析仪确认协议逻辑是否正确再用示波器确认模拟波形质量是否可靠两个工具配合绝大多数 I2C 问题都能定位。5. 完整排查链路从设备上电到定位故障的实操路线有前面的工具思路做底子下面给一条可以直接照做的排查流程。我不喜欢一上来就瞎换器件而是按层逐步缩小范围。5.1 先过一遍“症状到动作”对照表我整理了排障时最常用的几条路径每条都从症状出发指向具体的测量动作症状先做测量可能原因下一步动作设备完全无响应万用表量 SDA/SCL 空闲电平没上拉、线上拉死、没供电查电源和上拉断电量对地短路SCL 不翻转示波器探头点 SCL主机根本没在发时钟查主控 GPIO 配置、时钟使能、代码逻辑有时间波但整帧乱示波器触发在 START看数据位触发不对、地址方向搞错、干扰先正确触发再手动解码第一字节第一字节就 NACK观察地址字节后第 9 拍地址错、设备不在、SDA/SCL 接反用 i2cdetect 或自己写扫描确认设备地址地址 ACK 但寄存器 NACK看寄存器字节后第 9 拍寄存器不存在、写保护、设备忙查手册延长延时或解锁写保护偶发性读错数据示波器看上升沿和毛刺上拉过大、总线电容大、干扰调整上拉加串阻降低速率这张表不是万能药但它能帮你把“我该测什么”这个问题落到具体动作上。5.2 分段定位电气层、协议层、设备响应层我把 I2C 问题切成三层每层的检查手段完全不一样。电气层看空闲电平、上拉阻值、边沿时间、振铃。这层主要靠万用表和示波器的模拟测量。协议层看起始/停止条件是否清晰、字节顺序是否正确、ACK 位置是否对、有没有被时钟延展卡住。这层靠示波器手动解码或逻辑分析仪。设备响应层看设备对地址和寄存器的具体反应查手册确认寄存器定义、写周期时间、写保护和初始化时序。这层需要协议分析工具加上软件开发配合。为什么这么做因为很多时候一个现象可能来自三层中的任何一层。比如“设备在扫描时出现”但“就是读不到数据”电气层可能没问题协议层也可能没问题问题藏在设备响应层——寄存器地址写错了或者设备需要先配置某个使能位才能输出数据。如果你不分层就只能在三个方向上瞎试。5.3 五个典型故障样本波形特征一看便知下面这几种故障我都在项目里遇到过它们的波形特征差异很大值得单独记一下。总线拉死。SDA 和 SCL 都是平的 0 V一点波形都没有。断电后量 SDA 对 GND可能只有几欧。这时候逐个断开从机每断一个量一次找到拖死总线的那一个。常见原因是从机芯片损坏或者某引脚被外部电压顶住导致内部保护二极管导通。上拉缺失。空闲时 SDA/SCL 电压飘忽示波器上能看到波形低得很干净但上升沿特别缓慢甚至高电平只能爬到 1 V 左右。这是因为线上没有东西把它拉高只能靠探头内部寄生路径慢慢充。解决方法是补上合适的上拉电阻。地址 NACK。波形正常时钟完整但地址字节后第 9 拍 SDA 一直高。这时用扫描程序确认设备实际地址。特别留意 7 位地址和 8 位地址的差别比如很多数据手册写“器件地址 0xA0”但扫描工具填的是 7 位 0x50这两个是同一个地址别再换算上栽跟头。时钟延展。SCL 低电平时间明显比正常值长。检查主机代码是否在等待 SCL 释放。用硬件 I2C 外设一般会自动处理用软件模拟 I2C 时发送每一位前都要判断“SCL 现在是不是真的被拉高了”否则就会错位。边沿太缓导致采样错误。波形整体幅度正常但上升沿呈“圆弧”形在 400 kbps 下高电平爬不够时间。把上拉电阻从 10 kΩ 换到 2.2 kΩ或者把速率降到 100 kbps问题往往会消失。5.4 软件配合两段最小验证代替“盲改”排查到协议层之后我强烈建议不要只在示波器上猜写两段最小验证代码效率和准确性都更高。第一段是地址扫描。在 Linux 下可以直接用 i2cdetecti2cdetect -y 1它会遍历所有 7 位地址向每个地址发送一个读请求能拿到 ACK 的地址就会列出来。如果你的设备在扫描里出现了说明电气层和地址基本没问题。没出现就回头查硬件。第二段是单寄存器写读回。选一个确定可读可写的寄存器先写入一个值再读回来比对是否一致。如果一致总线通信链路是通的问题大概率在配置初始化或业务逻辑里。如果不一致再用逻辑分析仪抓一次完整传输往往能立刻看出是写的时候丢了 ACK还是读的时候地址方向错了。这两段验证代码加起来不到五十行但比频繁换传感器、换电阻效率高得多。6. 最后说几个我在项目里踩过的真实坑工具和方法都讲完了再分享几个我自己吃亏的经验这些细节经常不会写在数据手册里但能帮你省下好几天的排查时间。6.1 二极管档差点让我拆了一块好板子有块板子 I2C 读不到数据我用万用表二极管档量 SDA 到 GND显示 0.4 V 导通压降第一反应是短路。后来用电阻档一量阻值正常再对照 SCL 也差不多才知道那是芯片内部保护二极管。差点误伤一块正常板子。以后凡是量“疑似短路”我都会同时用通断档和电阻档交叉验证并且拿正常通道做对照。6.2 探头接地线太长波形全成毛刺有段时间某传感器偶发读取错误示波器上看到 SDA 高电平期间全是尖刺。我怀疑是硬件干扰查了半天。后来换了个短接地弹簧波形立刻干净了。原来就是那个 15 cm 的长接地线在 400 kbps 边沿上引出的振铃。信号本身没问题问题出在我的测量方法上。所以量 I2C 之前先把地线换短这件事当成固定动作。6.3 把时钟延展当成了设备死机一款温湿度传感器在读取转换结果时会把 SCL 拉低等待约 200 μs我用软件模拟 I2C 驱动它时总线显示“卡死”。示波器一看SCL 低电平时间特别长我一开始以为设备坏了后来查手册才知道是正常的时钟延展。最终改成在发送每一位前检查 SCL 是否确实为高问题立刻解决。像这类带有时钟延展功能的传感器主控代码必须显式支持否则不要轻易换硬件。6.4 上拉电阻被并联到“太强”从机拉不低一块主板上已经有 4.7 kΩ 上拉我又在传感器模块上保留了 4.7 kΩ 上拉。并联之后等效 2.35 kΩ本来没什么问题。但后来为了长线通信把主板上拉也换成了 2.2 kΩ并联等效变成约 1.1 kΩ。结果某些弱驱动能力的从机在拉低 SDA 时低电平只能到 0.6 V 左右超过 VIL 上限主机就误判成了高电平。排查到最后把模块上的上拉电阻去掉只保留主板一组 2.2 kΩ一切恢复正常。这个案例提醒我多设备共用总线时上拉电阻要按“整条总线上并联后的等效值”来算不能只看某一处的标称值。最后分享一个我现在的固定习惯手头常备一个带 I2C 解码的示波器和一个 USB 逻辑分析仪排查时先用万用表确认空闲电平和通断再上示波器抓一帧完整的 START 到 STOP盯着第 9 个时钟脉冲看 ACK。只要 ACK 的位置和电平对了协议链路基本就是通的哪里不对就回到对应的层去查。这套流程虽然朴素但绝大多数 I2C 故障基本都能在十几分钟之内定位。
返回列表