ARTICLE DETAIL

资讯详情

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

I2C总线调试实战:从万用表、示波器到ACK响应的排查方法论

I2C总线调试实战:从万用表、示波器到ACK响应的排查方法论 调试I2C总线有时候比调逻辑本身还让人头大。明明代码看着天衣无缝从机地址也对可就是莫名奇妙的卡死或者读到全0xFF。我这些年经手过不少传感器、EEPROM、触摸屏和电源管理芯片的调试有个很深的体会排查I2C问题顺序比技巧重要思路比工具贵。很多人一上来就抄起示波器怼波形结果抓了半天也没看出个所以然因为方向从一开始就偏了。这篇文章我想把I2C信号排查的完整路径捋一遍从最不起眼的万用表到真正能定生死的示波器再到最折磨人的ACK响应判断全部串起来。这不是教科书式的协议讲解是我在实际项目里反复用过、验证过、也踩过坑的排查方法论。如果你正被某条I2C总线折磨得睡不着这篇文章应该能帮你省下不少瞎折腾的时间。1. 排查前先把思路捋直I2C信号到底分几层很多朋友排查I2C问题习惯从波形开始抓抓到什么算什么。这种“看到了再说”的方式效率极低。我的建议是动手之前先花几分钟把故障做一次层次归因。I2C看起来是两根线的活儿实际上问题是分层的层次不同排查工具和手段完全不同。1.1 物理层、协议层、应用层排查路径的骨与肉我自己习惯把I2C问题切开成三层来看。物理层指的是SCL时钟线、SDA数据线这两根线上的电气特性。有没有短路、断路上拉电阻焊没焊上拉电阻值合不合适电平能不能被拉低到规定的阈值线太长导致波形畸变严重等等。这一层的问题用万用表就能解决大半。协议层指的是总线上的时序合不合规。起始条件、停止条件、字节传输、ACK/NACK、时钟拉伸这些时序的先后关系以及每个信号在时钟上升沿附近是否稳定。这一层的问题必须要用示波器或者逻辑分析仪看波形才能定性。应用层指的是主从设备之间交互的逻辑对不对。寄存器地址填对了没有读写方向对不对数据长度对不对从机的状态机是否正常是不是需要延时等。这一层的问题有时候波形全对但数据就是不对需要对芯片手册和代码逻辑下功夫。建议把这三个层次写在一张纸上对着故障现象一个个划掉。比如总线卡死先量静态电平排除物理层问题再用示波器看有没有持续的低电平排除从机拉死最后再看代码里是不是忘了释放总线。这样排查才能不重不漏。1.2 三层联动的典型故障模式我在实际调试里发现很多I2C故障并不是孤立的而是跨层的。举几个典型例子看起来是ACK丢失根子是物理层SCL线斜率太差上升沿太缓导致从机采样时数据已经变化从机识别不到合法的起始条件或地址自然不回复ACK。这是物理层故障引发的协议层表现。看起来是数据错乱根子是应用层读写EEPROM时忘记在页写之后加一个写周期延时结果下一次操作还在上一条指令的写周期里从机不响应。波形上可能只能看到一个NACK真正的问题在代码流程。看起来是设备没反应根子是地址冲突总线上挂了两个相同地址的传感器从机地址冲突后两个设备都拉低SDA去ACK总线出现“线与”后的古怪电平主设备读到的既不是0也不是1协议层彻底乱套。所以排查的时候别急着给问题定性。先按层次排查再考虑跨层影响。这样处理问题会比较稳妥。2. 万用表不是没用而是要先把它的活干完很多人觉得万用表测不了I2C毕竟I2C是动态信号万用表只能测静态。这个说法对但也不全对。万用表在I2C排查里的定位不是看时序而是验证物理层的健康状态。物理层如果有病示波器上看波形也是白看一堆毛刺和畸变根本没法分析。2.1 万用表能测的I2C信号有哪几样先搞清楚万用表在I2C上到底能干哪些活导通性测试判断SCL和SDA在PCB上有没有断线、虚焊、连锡。对地电阻判断总线是不是被拉低或者走线跟地之间有没有异常短路。上拉电阻阻值测量确认上拉电阻有没有焊错、焊丢、取值离谱。静态电平测量总线空闲时SDA和SCL应该都是高电平。如果测量到低电平说明有设备拉死总线或者上拉根本没接。供电电压测量确认I2C总线的逻辑高电平到底是什么电压别拿3.3V的主机和5V的从机乱拉电平不匹配会有一堆怪问题。2.2 导通、短路、上拉电阻的实测手法先说导通性测试。我习惯先把板子断电用万用表的蜂鸣挡一端点在主控的SCL引脚上另一端点在从机的SCL引脚上听蜂鸣。如果通基本可以确认这段走线没有断。SDA同样处理一遍。这个动作虽然基础但能排除掉相当一部分“飞线没焊牢”“排线接触不良”“PCB断线”之类的低级故障。然后是上拉电阻。大多数I2C系统用4.7kΩ上拉到VCC速度很高的场合用2.2kΩ或1kΩ。用万用表电阻挡测一下注意要断电测值差太远就要怀疑焊错了。我遇到过一次特别坑的情况上拉电阻标称4.7kΩ实测也是4.7kΩ但就是总线不正常。后来发现是电阻的一端虚焊万用表笔尖压上去有读数松开就断。所以测电阻的时候最好轻轻拨动一下电阻本体看看数值有没有跳变。再补充一个初始排查的“笨办法”板子不上电用万用表二极管挡分别量SCL和SDA对地的压降。正常情况走线上有上拉电阻连接到VCC二极管挡量出来的电压会在0.6V左右甚至更高因为等效于测一个二极管内部ESD保护二极管或一个电阻分压。如果测出来是0.000V妥妥短路到地了。这个办法在板子没程序、没上电的时候特别方便。2.3 静态电压法快速判断总线卡死这是万用表最被低估的一个用法。遇到I2C总线卡死不要先上示波器先拿万用表直流电压挡点一下SDA对地电压再点一下SCL对地电压。总线空闲时SDA和SCL都处于高电平读数应该接近上拉电压。比如3.3V上拉读数就在3.3V左右。如果SDA读出来是0V说明SDA被某个设备拉死了。这时候用万用表电流挡串进回路不现实更快的办法是把可能挂在总线上的从机一个一个断开每断开一个就测一次SDA电压。哪个设备断开后电压恢复3.3V哪个设备就是拉死总线的元凶。这个招数在排查“系统偶发卡死复位后恢复”的故障时特别好用。虽然卡死瞬间抓不到但只要卡死了SDA被拉低是大概率事件用万用表一扫就能锁定范围比示波器单次触发靠谱得多。因为示波器可能没开捕捉模式等你看屏幕的时候故障已经过去了。注意测静态电平的时候一定要先确认系统供电正常并且总线空闲。如果主控正在高速读写I2C万用表上会读到一个平均值看起来像1.6V这种中间值别被误导了。这种情况要么等总线空闲再测要么干脆上示波器。3. 示波器是关键把时序图变成看得见的波形万用表把物理层问题清完接下来才轮到示波器上场。示波器看的是协议层的动态时序也是I2C排查里信息量最大的环节。但示波器这东西设置不对看到的波形就是一团乱麻越看越糊涂。我这里不讲太基础的操作重点说I2C排查时示波器该怎么设置、标准波形怎么读、异常波形怎么定位。3.1 示波器参数设置时基、触发电平和存储I2C频率常见的有100kHz标准模式、400kHz快速模式、1MHz快速模式。我排查时习惯先把时基设定在一个比较宽的范围比如100µs/div这样可以先看到一整个完整的通信过程大概了解一次传输里有哪些阶段。看清整体之后再慢慢把时基缩小到20µs/div甚至5µs/div仔细看单个字节的时序细节。触发电平是个关键设不好波形根本停不下来。I2C空闲时是高的通信时被拉低所以应该用下降沿触发。触发通道选SCL或者SDA都可以但习惯上选SCL更稳因为SCL的下降沿基本等于一次传输的开始。触发电平设在总线高电平的50%左右比如3.3V系统就设1.65V。如果是1.8V系统就设0.9V规则是低于高电平、高于低电平。通道耦合别忘了设置。I2C是低频信号用直流耦合就行带宽限制20MHz打开可以滤掉不少高频噪声。探头衰减比通常1x或10x必须和示波器里的设置一致不然读到的电压值是错的这点很多人吃过亏。存储深度这个参数很多人容易忽略。在低速I2C100kHz下一次完整的寄存器读操作可能长达几毫秒。如果存储深度不够示波器只能看到部分波形或者采样率被压得极低波形失真严重。我的做法是把存储深度调到最大或者开启“滚动模式”Roll模式这样可以长时间观察总线上连续的通信而不至于丢数据。示波器的解码功能能开就开。现在主流的示波器都自带I2C解码设置好SDA/SCL通道和阈值电平屏幕上会直接解析出地址、数据、ACK等内容。即使解码不完全准也能帮助判断时序的大致正确性少很多手工数时钟周期的事。3.2 I2C标准时序波形怎么读会读标准时序波形是排查的基础功。很多人知道I2C有起始、停止、数据、ACK但看到真实波形不一定能对应上。这里把标准时序和屏幕上的波形做一次对照。起始条件STARTSCL为高时SDA产生一个高到低的跳变。在波形上就是SCL高电平区间内SDA突然掉下去。这是通信开始的标志。停止条件STOPSCL为高时SDA产生一个低到高的跳变这是通信结束的标志。数据位SDA在SCL高电平期间必须保持稳定数据有效SCL低电平期间SDA允许变化。波形上你会看到SDA的跳变总是发生在SCL低电平区间而在SCL上升沿附近SDA是稳定的。如果看到SDA在SCL高电平期间抖动那说明时序有问题很可能是从机的建立时间/保持时间不满足。ACK位主机发送完8个数据位后会释放SDA让它被上拉电阻拉高然后在第9个时钟脉冲期间SDA如果被从机拉低说明从机给出了ACK。波形上就是第9个时钟上升沿附近SDA呈现一个短暂的低电平脉冲。这个低电平有没有、幅度够不够、位置对不对就是后面要重点排查的。时钟拉伸Clock Stretching部分从机比如一些传感器和单片机模拟的I2C在需要时间准备数据时会在ACK之后把SCL拉低迫使主机等待。波形上会看到SCL的低电平时间比平时长很多。很多主机驱动不支持时钟拉伸遇到就超时这种情况在波形上简直一目了然。3.3 常见异常波形的判读与定位实际调试中异常波形比正常波形更有参考价值。我整理了几个高频出现的异常波形以及各自的常见原因。只有SCL在动SDA一直高且看不到起始条件大概率是主机没有正确启动传输。检查代码里是否忘了发起始信号或者SDA线本身已经断了但SCL完好用万用表导通挡再验一下。起始条件能看到但SDA后面没有跟随数据变化主机发了起始条件后可能因为从机地址有误或者SDA有虚焊后续数据根本没送到总线上。这种多半是物理层接触问题。SCL正常SDA在每个字节的第9个时钟都是高的且从没有低脉冲说明从机一直回的是NACK。要么地址不对、总线上没有对应设备要么从机根本没上电。SDA在第9个时钟确实有低脉冲但脉冲很窄且形状异常这往往是总线上有两个或多个设备同时应答形成了线与拉低能力不一致导致的。检查是否地址冲突或者是否有一个设备的漏电流很大。SCL低电平时SDA也异常翻转整体波形“很脏”大概率是上升沿过缓上拉电阻太大、走线过长、容性负载过重。试一下换上拉电阻为2.2kΩ或1kΩ看波形是否改善。还有一个经验之谈示波器探头接地线要短。I2C波形动不动就出现“噪声亮点”和过冲很多时候是探头地线太长形成了环路天线。把探头地线夹改成弹簧地针波形瞬间干净很多。这个细节在测MIPI这类高频信号时必须讲究在I2C上虽然影响没那么致命但波形干净一点看时序也准一点。4. ACK 响应最容易“翻车”的排查环节ACK问题说它是I2C调试里最折磨人的环节真不为过。很多项目卡壳就卡在从机不回复ACK而排查者往往无从下手。这个章节我把ACK从头到尾拆一遍讲清楚ACK的原理、地址换算规则、以及ACK丢失的高频原因。4.1 ACK 的真面目第九个时钟周期在干什么ACK的基本机制不复杂主机发送完一个字节8个SCL脉冲之后会释放SDA线然后在第9个SCL脉冲期间SDA被从机拉低表示“我收到了”。如果从机没有拉低SDA由外部上拉电阻保持高电平即是NACK。但这里面有个细节值得展开主机是怎么“释放”SDA的在驱动层主机要先把SDA方向寄存器切到输入模式或开漏输出并输出1。很多国产MCU的库函数里如果只写了输出1而没有把引脚切回输入模式那么SDA在主机侧是被强驱动的从机根本拉不动就会出现“从机明明想ACK但SDA纹丝不动”的诡异现象。这个问题在示波器上看就是第9个时钟SDA一直高疑似NACK实际上却是主机没释放SDA。另一个细节ACK时的SDA低电平要提前于SCL的上升沿建立并且保持到SCL高电平结束。如果从机ACK慢了半拍比如中断处理不及时SDA的低电平可能是迟到的等SCL上升沿采样时它还没稳定。主机能不能正确识别这个迟到的ACK取决于主机采样是在SCL上升沿还是高电平期间很多MCU的I2C外设对ACK采样点有严格的时序要求时序裕量不足就会误判成NACK。4.2 地址算错一切白搭7位地址和读写位的换算ACK丢失最最常见的原因是地址算错。I2C的7位地址和8位首字节之间的关系特别容易让新手掉坑。有些芯片手册写的是7位地址0x48有的写的是8位地址0x90含读写位。这两者其实是一回事。7位地址0x48左移一位变成0x90最低位填0表示写、填1表示读。所以写操作首字节就是0x90读操作首字节就是0x91。但如果手册只写了8位地址比如“Slave Address: 0xA0”这个0xA0通常是含了写位的。读操作你要单独发0xA1。我见过不少人在这个地方搞混手册写0xA0他脑子里觉得这就是设备地址然后发送读命令时也发0xA0结果从机当然不认回了NACK。一个轻量级的验证办法把总线挂上设备用逻辑分析仪或示波器抓一次主机的发送看看首字节的二进制值。如果和自己算出来的不一样那就说明代码里有地址移位错误。我自己的习惯是把“7位地址”“写地址”“读地址”三个值都写在代码注释里例如// 7-bit Slave Address: 0x48 // Write Address: 0x90 (0x48 1 | 0) // Read Address: 0x91 (0x48 1 | 1)每次写驱动前先对着这个注释核对一遍能少走很多弯路。4.3 ACK 丢失的5个常见原因结合过往项目里的踩坑记录ACK丢失的高频原因大致是这五类从机地址错误包括7位/8位混淆、地址看错手册、多从机地址冲突。排查手段逐个断开从机抓总线波形确认主机发出去的首字节和从机手册上要求的一致。主机没有真正释放SDA引脚方向没切输入或者开漏配置没做好。排查手段观察第9个时钟附近SDA有没有被拉低的趋势。如果瞬间有一点点下拉但又弹回去多半是主机强驱动的问题。从机没有正常上电或处于异常状态从机的电源电压不对、时钟没起振、复位引脚被拉低、内部状态机锁死都可能导致不响应ACK。排查手段用万用表量从机的电源和复位引脚确认在正常范围。上拉电阻取值不当上拉太大总线上升沿太缓从机在SCL高电平采样时SDA还没稳定上拉太小主机或从机拉不低。排查手段算一下总线上所有设备引脚的漏电流和总线电容4.7kΩ是通用起步值出现波形畸变换2.2kΩ试。总线被其他设备抢占或干扰总线上还有一个异常设备把SDA拉低或者有噪声导致主机的起始条件被干扰。排查手段示波器长时间观察总线空闲电平看是否有不明脉冲。第五类原因里有个特别隐蔽的案例有次一台从机在固件里配置了断线重连的中断中断触发时会把SDA当成普通GPIO输出一个低电平脉冲恰好这个时序落在主机读操作的第9个时钟上直接把正常的ACK变成了一个奇怪的干扰。这种问题只能靠示波器长时间捕捉再配合逐个断开从机的土办法定位。5. 实战排查流程与高频问题速查前面把工具、思路、时序都铺开了这个部分我把它们收拢成可以直接照着做的完整排查流程再加一个我自己常用的高频问题速查表。排查I2C信号我有几条多年积累的经验写在这里供你参考先易后难、先静后动、先分后合。先处理简单的物理层问题再看动态时序先看静态电平再看跳变先把从机断开单独测再合在一起看互相影响。这几条心得帮我解决了不少难缠的问题。5.1 从零开始的完整排查顺序结合前面的分析一套完整的排查流程大概是这样的这个流程我照着执行了无数次第一步物理层体检。用万用表导通挡测SCL、SDA主控端到从机端通不通断电状态下测上拉电阻阻值上电后测SCL、SDA空闲电压确认没有被拉死电平符合预期。第二步确认供电和地址。量所有I2C设备的供电电压确认在各自的工作范围查手册确认每个设备的7位地址换算成写读地址写在纸上对照代码。第三步用示波器或逻辑分析仪看整体通信。时基设宽一点触发设SCL下降沿抓一次完整的读写过程。先看有没有起始条件、停止条件、ACK/NACK的节奏再看通信频率是否符合预期100kHz/400kHz看帧格式是否正确。第四步针对异常细节缩小范围看。如果起始条件没有查主控代码如果ACK缺失按4.3节的五个原因逐个排除如果数据错乱确认寄存器地址、字节序和时序参数。第五步分设备隔离测试。把从机A断开单独测从机B把从机B断开单独测从机A。确认每个设备单独都能正常通信再一起挂上测试是否互相干扰。第六步压力测试。让主机循环连续读写从机边跑边用示波器长时间观察。很多偶发故障是在连续操作几百次甚至上千次后才出现的这种批测必须做。5.2 高频故障案例记录这里记录几个我实际遇到的高频场景方便对照。先说明一下每个人的具体环境和芯片不同案例的结论只能作为排查思路的参考不能生搬硬套。故障现象重点怀疑对象快速定位手段写操作全是NACK从机地址或写地址错误、从机状态锁死示波器看首字节数据对照手册读操作全是NACK读地址错误、从机不支持随机读、上拉电阻驱动能力不足单独发写操作验证从机是否正常总线卡死SDA持续低有设备拉死SDA、主机释放SDA失败万用表静态电压逐个断开设备通信时好时坏偶发NACK上拉电阻偏大、时钟拉伸处理不当、接触不良看波形上升沿斜率换小阻值上拉数据错乱但ACK正常寄存器地址错误、数据偏移、器件跑在错误模式回读寄存器值和手册对比多个从机总线上互相干扰地址冲突、驱动强度不匹配断开设备逐一测试看ACK脉冲是否畸形上电后第一次通信必定失败从机上电时序未就绪加延时或增加从机启动检测这里展开说一个案例。之前调一款触摸屏用的GT911控制器I2C通信总是失败表现为第一次写配置寄存器时NACK但多试几次偶尔能成功。当时排查了地址、时序、上拉都无果。最后发现触摸屏的中断引脚配置错误GT911一直处于中断未清的状态I2C控制器部分被中断服务程序占用无法正常响应通信。把中断和I2C复位时序理顺后问题彻底消失。这个案例提醒我I2C从机不响应不一定是总线的锅从机自身的状态机也可能是问题来源。5.3 好习惯能帮你省一半排查时间排查I2C有几个习惯养成后真的能省很多时间。第一个习惯是总线上每个设备预留独立的断开测试点。在PCB设计时给SDA和SCL串0Ω电阻或者留跳线帽调试时可以直接断开某一个设备快速定位谁在捣乱。量产板可以不留研发板强烈建议留。第二个习惯是代码里把所有I2C参数做成可配置项。地址、时钟频率、ACK超时时间都放到头文件的宏定义里排查时改参数要快不能每次都要翻代码找数字。特别是超时时间设为0表示无限等待的做法在调试时是灾难总线一旦卡死整个系统就挂给你看。一定要设超时哪怕只是设个几千微秒的超时跳出也比无限休眠强。第三个习惯是把每次I2C通信的完整过程打印出来。有条件的用串口把首字节、寄存器地址、数据长度、返回错误码都打出来。没条件的用逻辑分析仪抓波形存成文件留档。遇到疑难问题有记录可比对排查效率翻倍。有时候同样的波形你当周看觉得没问题两周后再回看会发现新线索。写在最后再分享一个我自己的真实体会。I2C排查这行当很多时候不是高深的技术问题而是严谨的排除法加细心观察的功夫。万用表测物理层示波器看协议层再啃清楚每一份芯片手册的时序图绝大多数I2C故障都能定位。工具贵不贵不是关键关键是你有没有把每一步排查走扎实。我调试这么多年最常犯的错误反而是“自以为看懂了”结果回头再对一遍手册才发现自己漏看了一行小字。希望大家都能耐心一点把每一根信号线、每一个ACK脉冲都盯仔细。慢慢查不着急问题总会现出原形的。
返回列表