ARTICLE DETAIL

资讯详情

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

示波器调试I2C完整指南:从波形抓取到故障定位

示波器调试I2C完整指南:从波形抓取到故障定位 很多嵌入式工程师第一次用示波器抓I2C都会经历一个挺尴尬的阶段探头明明挂上了SCL和SDA触发也设了屏幕上波形密密麻麻一片但根本分不清哪里是起始条件、哪里是应答位。更头疼的是当你怀疑I2C通信有问题时示波器一抓屏幕上要么什么都没有要么就是一根平平的线加几条毛刺完全不知道从哪儿下手。这篇文章想把这整套方法彻底理清楚。从示波器怎么接线、触发怎么设到波形怎么读、问题怎么定位我会按照自己实际调试时走的完整流程来讲。内容既适合刚接触总线的单片机新手也适合那些被硬件问题折腾了两三天、想找点排查思路的工程师。全文不搞虚的全部围绕一条主线把示波器变成你调试I2C的眼睛让每个bit都在屏幕上现出原形。1. 用示波器抓I2C前的准备先搞定接线、触发和时基1.1 通道与探头怎么接最省事示波器抓I2C本质上就是同时观察两根线上的电平变化。I2C总共就两条信号线SCL时钟线和SDA数据线所以示波器至少要有两个模拟通道这是硬性门槛。我一般习惯把CH1接SCLCH2接SDA颜色上对应好看波形时大脑里会自动形成“上边时钟、下边数据”的映像。如果你用的探头是刚拿出来的先别急着夹先做两个基础动作。第一个动作确认探头补偿。把探头接到示波器自带的1kHz方波校准输出上如果显示的方波边角有明显的圆角或过冲用探头上的微调电容旋钮调到方波顶部平整、边沿干脆为止。这一步不做好后面看I2C边沿时你会被假象骗了。第二个动作接地尽量短。I2C信号本身幅度不大地线夹子如果甩出一长条就像一个环形天线把电源噪声、开关噪声全都耦合进测量回路里波形上全是毛刺。我见过有人把探头地线夹直接夹在开发板的USB座金属外壳上结果抓出来的SDA波形跟心电图一样排查了半天才发现是地线接触不良。通道耦合方式用直流耦合。I2C是低频信号交流耦合会把电平的真实直流分量滤掉你根本没法判断SDA是不是被拉死了所以直接用DC。带宽限制如果有可以开个20MHz或50MHz的低通能把高频噪声压掉不少对于标准100kHz、快速400kHz的I2C来说绰绰有余。1.2 触发和时基设置的三个关键参数很多人抓不到I2C波形问题就出在触发上。I2C总线的空闲状态是高电平通信从SDA拉低开始。所以最简单有效的触发方式是把触发源选在SDA通道上触发方式设为下降沿触发。一旦总线上有起始条件产生SDA从高跳低示波器立刻触发你就能看到完整的一帧传输。如果触发源选错了比如选在SCL上SCL是周期性方波每次下降沿都触发波形要么乱跳要么抓不到起始的那个瞬间看起来完全没有头绪。第二个关键参数是时基。时基没设对波形会缩成一团或者拉得特别开。I2C标准模式下一位2.5us一个字节的9个时钟周期大概是22.5us加上地址帧停止条件一次完整的事务通常在100us量级。如果你在400k快速模式下调试一帧大概20us到50us就结束。所以时基设在20us/div到50us/div比较合适屏幕上一帧数据刚好能完整铺开又不会因为太密集看不清细节。上面这些数字不用死记我习惯直接看一眼当前速率心里算一下一位的时间倒数是波特率再乘上9个时钟就是一字节的时间然后据此调时基。第三个参数是采样率和存储深度。示波器屏幕上显示的是一条由采样点组成的波形采样率太低边沿会失真毛刺可能被完全漏掉。现在的数字示波器一般默认自动采样率但你要检查一点当水平时基调大之后采样率会不会被自动降低。有一种典型情况你把时基拉到50ms/div想观察总线上偶发的干扰脉冲结果采样率被降到几十kS/s一个5us的毛刺根本采样不到。所以调完时基后看一眼屏幕顶部的采样率标识至少要让采样率在信号频率的20倍以上抓400k I2C采样率最好不低于10MS/s。如果你用的是带SINGLE单次触发功能的示波器强烈建议在抓偶发性I2C异常时用这个模式。按下SINGLE键示波器会等待一次触发之后冻结波形把触发前后的大段数据存进深存储里然后你再慢慢放大分析这样能捕捉到很多一闪而过的异常。我调试从机偶发死锁问题时就是靠SINGLE模式抓到了那只有几十微秒的异常窗口。2. I2C协议底层细节不懂这些看不懂波形2.1 开漏输出加上拉电阻为什么这么设计先说一个很多人学I2C时都会问的问题为什么I2C要设计成开漏输出加外部上拉电阻而不是直接用推挽输出原因有两个核心在于多设备共享和电平兼容。推挽输出意味着高电平由芯片内部主动拉高低电平也由芯片内部主动拉低。如果两个设备一个输出高、一个输出低两个芯片的输出级就会直接对打轻则通信错误重则烧毁引脚。而开漏输出的设备只能主动把引脚拉低高电平完全靠外部上拉电阻提供。任何设备想发出低电平就把对应的管脚内部MOS管导通把线路拉低想让她恢复高电平就断开内部MOS管让上拉电阻慢慢把电平拉起来。这样两个设备即使同时对总线操作也只会出现“线与”的效果不会出现电流倒灌短路。I2C的多主机仲裁本质上就是利用这个特性在总线上做逻辑与运算的。第二个好处是电平转换方便。只要上拉电阻接在合适的电源上总线高电平就是那个电源的电压。3.3V的单片机跟5V的传感器通信只要两边器件都支持开漏模式上拉电阻接5V总线高电平就是5V两边器件都能识别完全不用额外的电平转换芯片。但开漏设计带来一个绕不开的问题上升沿完全受RC时间常数控制也就是上拉电阻和总线等效电容的乘积。总线电容来自设备引脚电容、PCB走线寄生电容、探头电容等挂的设备越多、走线越长电容越大上升沿越慢。上升沿太慢超过协议规定的最大上升时间数据在SCL高电平采样时SDA还没到达稳定的高电平通信就会出错。这就是为什么上拉电阻不是随便焊一个就行的阻值选大了信号上不去选小了灌电流超标影响驱动能力和信号完整性。2.2 一帧数据到底长什么样从起始条件到停止要从波形里读出I2C通信内容必须先在心里刻一幅“协议地图”。I2C的关键事件并不多但每个事件在波形上都有非常明确的特征。起始条件在SCL保持高电平期间SDA从高电平跳变到低电平。这是唯一一个SCL高电平时期SDA允许发生变化的场景。波形上你会看到SDA先降下去然后SCL才开始输出时钟脉冲。这个动作告诉了所有挂在总线上的设备主机要开始通信了大家都准备好。停止条件正好相反在SCL保持高电平期间SDA从低电平跳变到高电平。波形上SCL先回到高电平然后SDA再升上去一次传输结束总线释放为空闲状态。从起始条件到停止条件之间SCL会持续输出时钟脉冲。每个时钟周期传输一个bit数据在SCL高电平期间必须保持稳定在SCL低电平期间允许变化。所以看波形读数据的时候永远盯住SCL高电平那一刻SDA是什么状态。每个字节传输8个bit数据紧跟着第9个时钟是应答位ACK。正常情况下接收方会在第9个时钟把SDA拉低表示“收到请继续”。如果第9个时钟SDA保持高电平这就叫NACK表示接收方不响应了后面通常就是停止条件。如果你用示波器抓一次完整的EEPROM读写看到的波形就是起始条件然后是地址字节7位从机地址加1位读写标志然后是EEPROM的ACK拉低接着是寄存器地址字节再次ACK然后才是具体数据。一次写操作通常是指起始条件、地址字节、寄存器地址、数据字节、停止条件。2.3 时序参数速查表协议对各个事件之间的时间间隔有明确要求超出范围就会出现通信不稳定。下面这张表我平时调试时打印出来贴在工位上的。参数含义标准模式100kHz快速模式400kHztSU;STA起始条件的建立时间最小4.7us最小0.6ustHD;STA起始条件的保持时间最小4.0us最小0.6ustSU;DAT数据建立时间最小250ns最小100nstHD;DAT数据保持时间最小0ns最小0nstSU;STO停止条件的建立时间最小4.0us最小0.6ustBUF停止与下一次起始之间的总线空闲时间最小4.7us最小1.3ustR上升沿时间最大1000ns最大300ns这张表最常见的用途是排查两类问题一类是主机用的是软件模拟I2CGPIO翻转而不是硬件I2C外设代码里翻转IO口的速度太慢导致建立时间或保持时间不满足最小值从机完全不理你另一类是上拉电阻太大导致上升沿时间超了表里的tR最大值从机在SCL高电平窗口内采样时SDA电平还没稳定。这两种问题在波形上都非常有辨识度后面会详细讲怎么观察。3. 从波形还原整段通信手把手读帧3.1 先找起始和停止给波形画格子拿到一段I2C波形别急着去读里面是什么数据。我自己的习惯是第一步先做“结构识别”就像阅读一段文章前先看标点符号一样。先把波形中所有符合起始条件特征的点找出来也就是SCL高电平期间SDA的下降沿再把所有符合停止条件特征的点找出来也就是SCL高电平期间SDA的上升沿。这两个边界一确定一帧数据的范围就框出来了。接下来的关键是“给波形画格子”。因为I2C的每一位都在SCL高电平时有效所以我喜欢在屏幕上数SCL的脉冲数来划分位区间。调整水平时基让一个时钟周期大概在屏幕上占据1到1.5个格子的宽度这样每个时钟的高低电平都能看清楚。然后从起始条件后的第一个SCL上升沿开始数每9个SCL脉冲是一组8个数据位加1个应答位。在纸上按顺序把每一组的位序标出来数据读起来就非常直观。如果你用的示波器带有光标测量功能可以用X光标测量单个SCL周期的宽度然后用1除以周期算出实际通信速率跟预期速率对比。这个方法非常有用我经常发现代码里配置的是400k实际上因为系统时钟分频配置错误跑出来只有120k。波形看起来一切正常通信也稳定但性能远低于预期。这时候读实际速率就能快速发现问题。3.2 逐位读出地址和数据现在到了真正“显示技术”的时刻。读位的方法很简单把目光锁定在每个SCL的上升沿之后、高电平平台的中间位置看这个时刻SDA是高还是低。高电平记为1低电平记为0。这个过程不能着急一位一位往下读。假设你看到的第一个字节是1 0 1 0 0 0 0 0第9个SCL上SDA被拉低有ACK。那么这8个bit组合起来就是0xA0这是7位地址0x50向左移一位最低位0表示写操作。I2C的7位从机地址在波形上是这样体现的地址的7个bit放在一个字节的高7位最低位是读写标志位。很多新手读到0xA0就直接去代码里找从机地址0xA0找了半天发现代码里写的是0x50这就是没搞明白7位地址和8位地址字节的区别。实际上EEPROM芯片手册里通常会给出8位形式比如AT24C02的写地址是0xA0由芯片地址引脚A2/A1/A0状态决定但你要心里清楚这是0x50的7位地址加上一个写标志组成的。数据位也是一样一位一位读。读出来之后对照从机手册里的寄存器地址表就知道当前在操作哪个寄存器、写入或读出的是什么值。举个实际例子有一次我调试一个温湿度传感器读出来的温度寄存器数据波形上是一位一位读出来的十六进制0x1A2B我就知道温度等于0x1A2B按手册公式换算后的值。而不是只看到一堆乱码就抓瞎。3.3 用真实波形案例读一次EEPROM写入我找一个最常见也最直观的案例来讲向AT24C02的地址0x00写入一个字节0x55。这在示波器上会出现以下序列。先出现的是起始条件波形上是SDA先从高降到低紧接着SCL开始输出脉冲。第一个字节是地址字节0xA08个数据位结束后的第9个SCL位置SDA波形上会出现一个很明显的下拉低电平这是从机的ACK。接下来第二个字节是寄存器地址0x00后面同样跟着从机ACK。第三个字节是数据0x55二进制是01010101你在第9个时钟位置会看到SDA依然是低电平表示这次也收到了。最后SCL停在低电平一段时间后回到高电平然后SDA从低跳到高形成停止条件。如果你读到的波形在这个标准序列之外比如在地址字节发出的第9个SCL位置SDA始终为高那说明总线上没有设备回应这个地址。要么地址写错要么从机没上电或没连接好要么从机地址引脚配置不对。这就是NACK后面主机通常会直接发出停止条件结束传输。这种波形一眼就能看出来问题定位起来也很快。4. 常见I2C故障波形与定位思路4.1 总线卡死SDA常低这是我在实际开发中遇到频率最高的I2C故障现象非常经典你用示波器一抓SCL还在正常输出方波但SDA一直是一条低电平的直线无论怎么触发都看不到它跳高。通信完全卡死主机的I2C状态机可能一直停在等待ACK的状态里出不来。SDA常低的本质是某个设备把SDA线拉住了却不松手。常见原因有两个。第一个原因是从机内部逻辑跑飞了处于异常状态比如从机供电电压不稳、复位信号异常、或者之前一次通信在中途被打断从机状态机停在某个半途状态SDA输出级一直导通。第二个原因是主机自己的软件模拟I2C代码出问题比如GPIO方向配置错误把本来应该输出高电平的引脚配成了开漏输出但外部又没有上拉电阻这个引脚永远就是低电平或者SDA被误配置成了推挽输出且直接输出低电平。排查这种问题的思路是先确认到底是谁拉低了SDA。把示波器探头拿开用万用表测SDA对地阻值如果非常低说明总线上有器件内部短路或击穿。如果阻值正常再把板上I2C外设逐个断开先断开所有从机单独给主机上电量SDA是否恢复高电平。如果恢复高说明是某个从机把总线拉住了一个一个接回去就能找到元凶。如果断开所有从机SDA依然是低那问题就在主机引脚或者上拉电路上。这里有个非常有用的软件排查手段在初始化I2C模块时做一次总线恢复。因为有时候总线卡死并不是硬件损坏而是上一次通信异常后有从机接收了不完整的数据状态机卡死。通过连续翻转SCL输出9个时钟脉冲然后在SCL高电平期间给SDA一个上升沿模拟一次停止条件可以让大部分卡死的从机状态机复位回到空闲状态。代码里加这个逻辑能解决很多“上电后第一次通信就失败”的诡异问题。4.2 上升沿太慢与上拉电阻计算另一种常见故障是波形看起来有正常的时钟和数据但从机就是偶尔应答、偶尔不答或者读回来的数据是错乱的。这时候你要重点观察波形的上升沿斜率。如果SCL或SDA的上升沿是一条很缓的斜线而不是比较陡峭的跳变说明RC时间常数太大了上拉电阻和总线电容的配合出了问题。上升沿时间的具体计算可以用公式tr 0.8473 × R × C其中C是总线等效电容包括所有设备引脚电容、PCB走线寄生电容和探头电容。假设你总线上挂了两个设备每个引脚电容约10pFPCB走线大约100pF加上示波器探头电容约15pF总电容按150pF估算。如果你用的上拉电阻是10k算一下tr 0.8473 × 10000 × 150pF大约1.27us。对照协议标准400k快速模式要求上升沿最大300ns100k标准模式要求最大1000ns算完你就会发现10k上拉在100k标准模式下已经超标了通信时好时坏是必然的。正确的上拉电阻范围上限由允许的最大上升沿决定下限由灌电流能力决定。按100k标准模式最大1000ns上升沿来算R_max 1000ns / (0.8473 × 150pF)约7.87k。所以10k的上拉在100k下都勉强400k更是完全不行。而不要选太小的原因是拉低时电流会过大比如上拉电阻1k接5V电源一个设备拉低时灌电流达到5mA虽然大多数器件能承受但总线电平可能因为压降产生偏差。工程上针对3.3V系统常用2.2k到4.7k这个范围兼顾上升沿和功耗。如果总线上挂载数量特别多走线特别长可以考虑适当降低I2C速率到100k以下或者换上拉电阻更小的值。具体的计算可以用“查表实测”两步走先按估算值选一个电阻换上去再用示波器实测上升沿时间直到波形边沿陡峭、不再有圆角为止。另外有一个很多开发板新手容易忽略的坑部分单片机的GPIO内部有上拉电阻但I2C总线的上拉不能完全依赖内部上拉。因为内部上拉的阻值通常在30k到50k范围对I2C来说太大了上升沿会非常慢所以外部必须加上拉电阻。内部上拉只能作为辅助不能替代外部上拉。4.3 ACK缺失与地址冲突第9个时钟上SDA不拉低也就是NACK这种故障特别好认。波形上地址字节之后紧接着的ACK位置SDA是高电平随后主机发出一段时间的延迟后产生停止条件。看到这个波形我一般按下面顺序排查。先检查从机地址对不对。特别是多位从机地址可能由硬件引脚配置比如AT24C02的A2、A1、A0引脚如果悬空或接法不对实际地址跟你代码里写的就对不上。用示波器读一下波形上的地址字节再对照手册确认从机实际地址这个方法最快。再检查从机是否真的上电工作。有些传感器模块的供电引脚比较隐蔽板子上电了但传感器本身没电I2C引脚自然不会有响应。用示波器量一下从机电源引脚和对地电压排除供电问题再继续。如果从机能应答某个地址但不能应答另一个地址那就要怀疑从机地址冲突。总线上挂了两个相同地址的设备会出现一种现象一个设备应答了另一个设备也可能跟着应答或者两个设备同时驱动SDA导致波形上的ACK位置出现极窄的低脉冲或竞争毛刺。用示波器把ACK位置展开来看如果波形上出现不稳定的半高电平、毛刺这就是多个设备同时拉低总线的迹象。解决方法是给设备改地址或者从硬件上把一个设备拆下来再测试。4.4 毛刺与时序违例的应对最后一种故障表现比较隐蔽从机偶尔通信失败波形看起来大体正常但仔细放大看SCL高电平期间SDA上跳了一个毛刺。前面协议部分强调过SCL高电平期间SDA必须保持稳定哪怕是一个几百纳秒的毛刺从机采样时也可能误判数据位。毛刺来源通常有三个。第一个是电源噪声耦合传感器或EEPROM突然拉低总线瞬间会产生电流突变在PCB地线上产生地弹地弹又耦合到信号线上形成毛刺。第二个是走线过长导致信号反射特别是在飞线连接传感器的情况下长导线会在边沿处产生振铃。第三个是主机GPIO配置为上拉输入模式总线上同时有外部上拉和内部上拉阻值差异导致边沿出现台阶。排查毛刺时先把示波器时基调大用SINGLE单次触发抓异常波形抓到了再放大毛刺位置看它出现在什么上下文里。如果是跟某个数据位翻转同步出现的毛刺大概率是地弹或串扰如果是SCL时钟边沿之后出现的振铃大概率是反射。针对性地在从机电源引脚加一个100nF去耦电容把飞线改成双绞线或屏蔽线或者在SDA和SCL上串联33到100欧姆的电阻抑制振铃都能显著改善。另外一个让I2C通信更稳的技巧是从时序上入手。如果主机用的是软件模拟I2C可以通过修改代码增加数据建立时间。比如在GPIO设置SDA数据电平之后SCL拉高之前插入一个非常短的延时。这样即使硬件上存在一些边沿不干净的问题从机也能在稳定的窗口内采样到正确的电平。很多时候软件上多做几个细微的时序调整比换一块PCB还管用。5. 调试完成后我还想分享几条实在经验调试I2C这几年我最大的体会是示波器最大的价值不是告诉你答案而是帮你把问题的边界画清楚。很多人一上来就盯着数据手册和代码反复看看了一个小时也看不出问题但用示波器抓一下信号哪个设备没上电、哪个信号边沿不对、从机有没有应答几十秒就一目了然了。学会用示波器去问“总线到底发生了什么”比单纯靠逻辑推理高效太多。最后再分享一个我自己习惯用的工作流套用到I2C调试上很顺手。第一步先量静态电平确认SCL和SDA空闲时都是高电平且高电平电压符合预期。第二步用SINGLE单次触发抓一段通信波形确认起始条件和停止条件存在。第三步逐位读地址字节确认从机地址和通信方向。第四步读数据字节确认寄存器地址和数据内容。第四步如果卡住了就回到第三步去排查设备是否应答。这套流程走下来绝大多数I2C问题都能定位到具体环节。示波器不是用来“秀”的它是你手上最趁手的硬件放大镜多抓几次波形经验自然就有了。
返回列表