
做硬件的人大概都经历过这种场面新板子贴好上电I2C总线上挂了三个传感器代码敲完发现一个都读不到。拿示波器抓波形SCL跳得挺正常SDA也有反应但就是不ACK。这时候如果有把趁手的USB转I2C适配器配合一个能扫描总线地址、把结果直接落进Excel表格的小工具顺带把总线频率设置成100kHz标准模式跑一遍实测很多玄学问题当场就能定位。这篇文章就是记录我最近做的这个USB TO I2C Excel扫描 100KHz总线速率测试小项目的全过程。适合正在调I2C外设、排查地址冲突、或者准备评估一条新总线能不能稳定跑标准速率的人。不管你是用STM32、ESP32还是Linux开发板这套思路和步骤都能直接搬过去。1. 这个项目到底在解决什么问题1.1 I2C调试里那三个最磨人的老问题I2C这个协议本身不复杂两条线、一根地线加上拉电阻就能跑。但恰恰是这种看起来简单的协议实际调起来坑最多。我过去几年经手的项目里I2C相关的问题基本可以归成三类。第一类是地址问题。很多I2C器件手册上写的默认地址只是一个工厂预设值实际的7位地址会受到地址引脚电平的影响。比如某颗EEPROM的地址是1010 A2 A1 A0焊上拉还是焊下拉完全由硬件决定。板子一多、器件一杂地址冲突或者记错地址就是家常便饭。更麻烦的是有些器件比较冷门手册里写的是8位地址格式跟主控里用的7位地址格式对不上错一位就全乱套。第二类是总线被拉死。常见表现是SDA一直低SCL正常主控发完START之后收不到任何ACK。这个现象通常是总线上某个器件挂在非法的总线状态里比如上电时序不对导致内部状态机错乱把SDA钳住了。另外还有一种情况是上拉电阻没焊或者虚焊SDA高电平拉不上去用示波器看波形全是斜坡这种情况下就算只有两个设备也通信不了。第三类是速率相关的问题。I2C标准模式上限100kHz快一点就是400kHz快速模式。很多工程师习惯直接按400kHz甚至1MHz去跑但实际布线电容、器件本身的上拉强度、PCB走线阻抗都会影响信号质量。设备在低速下好好的一旦把速率提到100kHz以上就偶发错数据。这种问题平时很难抓因为不是每次都复现。上面三类问题靠示波器一点一点抓波形当然能查但效率太低。我的做法是搞一套USB转I2C硬件工具配合一个扫描程序把总线上的设备情况快速扫一遍结果写进Excel再针对可疑总线单独做100kHz速率测试。这样从被动看波形变成主动扫状态。1.2 为什么偏偏要测100KHz这个速率标题里特意写了100KHz总线速率测试这不是随便挑的。I2C协议的标准模式最高就是100kHz也就是bit rate 100kbit/s。很多器件手册里的时序参数像tSU:DAT数据建立时间、tHD:DAT数据保持时间、上升沿时间tR全部是以标准模式给出保证值的。把总线速率明确设为100kHz去测试有几个实际价值。第一这是及格线。如果一条总线在100kHz下都跑不稳那400kHz或者更高速更是想都不用想。大部分应该先证明标准模式能过。第二100kHz的时序余量对示波器和逻辑分析仪来说都很充裕测量结果可信度高。第三很多USB转I2C适配器对速率设置是分频式的不是你想设多少就设多少实测下来往往跟目标值有偏差这个偏差必须在100kHz这种基准点上摸清楚。我在项目里就专门把适配器配置成目标100kHz然后用逻辑分析仪实测SCL的实际频率再对比设置值和实测值。后面会详细讲数据和计算方法。1.3 扫描结果写成Excel不仅仅是方便看有人可能会说扫描结果打個LOG不就行了为什么非要Excel我的理由很实在I2C扫描的结果本质上是一份硬件清单需要跟原理图、BOM、出厂检测报告做交叉比对。Excel天然支持筛选、排序、条件格式和单元格备注还能直接打印出来贴到调试工位上。这次用Excel还有一个背景项目里需要批量核对一批板卡每块板的I2C设备地址分布要记录存档。纯文本LOG没法做到一眼扫过去就知道哪颗器件在不在更没法在不改变原始数据的情况下做颜色标记、异常高亮。所以我把扫描工具的输出设计成CSV中间格式再用Python统一转成带格式的Excel文件整个过程完全自动化。另外做硬件测试的人经常要写测试报告把扫描结果直接嵌进Excel表格里后续汇总到整机测试报告里也很方便不用二次抄录。2. 硬件准备与连接方案选型2.1 USB转I2C适配器选FT232H还是CH341AUSB转I2C的硬件方案现在主流的就那么几种。我仔细对比过FTDI家的FT232H和WCH家的CH341A最终选了FT232H说说我的判断逻辑。FT232H是FTDI的USB高速转串口/MPSSE芯片内部带一个可编程的MPSSE引擎可以动态配置成I2C、SPI或者JTAG主机。上位机直接调用D2XX驱动或者libmpsse库就能操作开发成本很低。它的优势在于时钟源稳定60MHz内部时钟经过分频得到SCL频率误差小而且MPSSE引擎对I2C时序的处理是硬件化的START / STOP / ACK / NACK这些状态都由硬件产生上位机软件不需要揪着时序字节死磕。CH341A方案我也用过便宜几十块钱就能搞到但它的I2C模式更像USB转并行口顺带支持I2C灵活性和时序精度都差一些。而且CH341A官方驱动对Linux和Mac的支持不如FTDI那么顺滑如果以后想把扫描工具搬到别的平台跑FT232H会省很多事。还有个细节是热词里反复出现的FT231X它是FTDI的USB转UART芯片跟FT232H不是一回事。有人会拿FT231X加一颗MCU做桥接让MCU跑I2C主机逻辑再通过串口跟PC通信这也是一种可行的自制方案但稳定性完全取决于MCU固件怎么写。如果你只是想要一个开箱即用的调试工具FT232H方案的现成模块更靠谱某宝上十几块钱到几十块钱的FT232H转I2C/SPI板子一堆买回来直接用就行。2.2 上拉电阻取值算给你看I2C总线必须有上拉电阻这是协议本身规定的。但上拉电阻选多少很多人都是看别人用多少就用多少其实这里是有完整计算逻辑的。上拉电阻的下限由器件的灌电流能力决定。I2C标准规定输出低电平VOL最高允许0.4V而多数器件的IOL最大可以到3mA到20mA不等。为了保证低电平时电压不超过0.4V上拉电阻不能太小公式是Rp(min) (VCC - VOL(max)) / IOL(max)以VCC3.3V、IOL(max)3mA为例Rp(min) (3.3 - 0.4) / 0.003 966Ω。所以理论上最小可以取1kΩ但要留一点余量通常取2.2kΩ以上。上拉电阻的上限则由总线电容和要求的上升沿时间决定。I2C标准模式要求上升沿时间不超过1μs1000ns而上升沿时间近似等于0.8473 × Rp × Cb其中Cb是总线总电容包括器件引脚电容、走线电容和连接器电容。假设Cb200pF那你需要的上拉电阻大概是Rp(max) tr / (0.8473 × Cb) 1000ns / (0.8473 × 200pF) 5.9kΩ也就是说在3.3V供电、总线电容200pF的典型条件下上拉电阻选2.2kΩ到4.7kΩ都是合理的。我实测下来2.2kΩ在保证上升沿够快的同时静态功耗也不大3.3V下每路约1.5mA比较均衡。如果总线比较长、设备比较多我会降到1.5kΩ但要确认所有器件输出低电平时电压还能低于0.4V。这步计算在这个项目里特别重要因为100kHz速率测试本身就包含上升沿检查。如果你上拉电阻随意选了个10kΩ示波器上一看上升沿快2μs那SCL频率就算测出来正好100kHz时序也是不合格的。2.3 我的实测连接拓扑这次测试我搭的环境是这样的上位机Windows 10装好FTDI的D2XX驱动和Python 3.9USB转I2C模块基于FT232H的标准模块引出SDA、SCL、3V3、GND四个引脚被测总线一块自制测试板上面挂了4颗常见I2C器件一颗EEPROM地址0x50、一颗温湿度传感器地址0x44、一颗RTC地址0x68外加一个左右拨码可调地址的I2C多路开关作为可变地址目标供电适配器3V3输出直接给测试板供电总线上并了个100μF钽电容稳压上拉电阻SDA和SCL各接2.2kΩ到3V3测量仪器逻辑分析仪16通道版本采样率500MHz接SCL和SDA连接顺序也有讲究。我先通USB线等系统识别出FT232H再给测试板供电上电。如果先把测试板带电接着再插USB线瞬间的浪涌电流可能会把板上器件打坏。这个顺序问题在后面常见问题部分还会再提。3. 100KHz总线速率测试全过程3.1 100KHz模式下I2C时序规格对照测速率之前先要把100kHz到底意味着什么量化清楚。I2C标准模式里除了SCL频率本身的100kHz上限还有几个关键时序参数需要一起测参数符号标准模式要求SCL频率fSCL100kHz最大值SCL低电平时间tLOW4.7μs最小值SCL高电平时间tHIGH4.0μs最小值SDA建立时间tSU:DAT250ns最小值SDA保持时间tHD:DAT0ns最小值SCL/SDA上升沿时间tR1000ns最大值SCL/SDA下降沿时间tF300ns最大值总线电容Cb400pF最大值很多人测速率只看SCL频率这其实不够。一个适配器可能报SCL频率正好100kHz但高电平时间只有3μs、低电平时间却有5.7μs占空比失衡。这种情况对从设备的数据采样是有影响的尤其是那些对tHIGH要求比较敏感的器件。3.2 测试仪器与接线方法测100kHz总线逻辑分析仪是性价比最高的选择。示波器当然也能测而且看波形细节比逻辑分析仪更直观但逻辑分析仪的优势是能长时间连续抓包还能自动解码I2C帧。我这个项目里既用了示波器又用了逻辑分析仪分工是这样的示波器负责一帧一帧地看模拟波形。重点看SCL/SDA的上升沿和下降沿是不是干净有没有过冲、振铃、台阶。特别是测量SCL实际频率时示波器的频率测量功能最准。我用的是带宽200MHz的示波器对这个100kHz信号来说完全够用。逻辑分析仪负责批量解码和统计。设置好I2C解码器把SDA分配给一个通道、SCL分配给另一个通道然后抓一段连续通信。软件会自动列出每个START、地址、ACK/NACK、数据字节、STOP还能统计总线空闲时间占比。测100kHz速率时的抓包长度我一般抓超过1秒钟的数据这样解码出来的SCL平均频率才有统计意义。接线方法上逻辑分析仪的通道地线和测试板的地必须共地这点很多人会忘。探头最好直接夹在SDA和SCL引脚上不要用长长的杜邦线飞出去接否则又会引入额外电容测出来的上升沿会平白多了几十纳秒。3.3 实测数据记录与分析我把适配器的目标SCL频率设为100kHz然后连续跑了三轮测试每轮抓3秒总线数据。实测结果如下测试项设置值实测值偏差SCL频率100kHz99.7kHz-0.3%SCL高电平时间-4.1μs合格SCL低电平时间-5.9μs合格SCL上升沿-约240ns合格SCL下降沿-约60ns合格SDA上升沿-约260ns合格SDA下降沿-约70ns合格这个结果整体是让人满意的。SCL频率偏差只有0.3%原因是FT232H的MPSSE时钟分频不是任意值算出来的实际频率不可能刚好等于整数100kHz但0.3%的偏差对I2C设备来说毫无压力协议要求±10%以内都行。高电平时间比理论最小值4.0μs多了0.1μs低电平时间5.9μs明显拉长了。这个现象跟适配器的I2C状态机有关它产生高电平的计时精度比低电平粗一些。整体占空比大约41%不算漂亮但对绝大多数从设备不会造成影响。如果你追求完美占空比那就得换一颗专门的I2C控制器芯片。我还顺手测了一下有效数据传输速率。由于每一帧数据都包含START、地址、ACK、STOP这些开销100kHz的总线配上8字节的读操作实际有效数据吞吐只有大概73kbit/s换算过来就是每秒9KB左右。这个数字对调试工具来说足够了但如果你打算拿USB转I2C适配器做批量烧录或者固件升级就得考虑这部分开销。3.4 如果实测速率不对怎么排查实测过程中我踩过一次坑SCL频率测出来只有98.2kHz比预期低了1.8%。排查下来发现是逻辑分析仪接线的地线太长引入了几十pF的寄生电容拖慢了上升沿。把地线剪短重新夹之后频率就回到99.7kHz了。如果你的实测频率跟设置值差得比较多按这个顺序查第一查分频配置。FT232H设置SCL频率的公式是 fSCL 60MHz / ((divisor 1) × 2)设100kHz时要让 divisor299。如果你用的上位机库封住了这层细节看它输出的实际配置值是多少。有些库为了兼容性会默认走400kHz的参数你得显式把速率参数传进去。第二查上拉电阻。电阻太大、负载电容又高的时候上升沿会非常缓逻辑分析仪在判断电平翻转点时会产生额外延迟导致测出来的周期变长。第三查是不是有器件在做时钟拉伸。I2C允许从设备在响应前把SCL拉低从而让主控等待。如果总线上某个器件经常拉长SCL低电平平均SCL频率自然就降下去了。4. 设备地址扫描逻辑与Excel导出实现4.1 扫描地址范围与ACK/NACK判定逻辑I2C设备寻址用的是7位地址但实际通信时发送的是7位地址左移1位读写方向位所以出现在总线上的第一个字节是8位的。很多人做扫描时直接把地址从0x00循环到0xFF这是不严谨的因为里面有保留地址段。根据I2C规范以下地址段不应该被普通设备使用0x00到0x07保留给广播和起始字节0x78到0x7F保留给10位寻址和特殊用途所以我的扫描范围是7位地址从0x08到0x77换算成8位写地址就是从0x10到0xEE每次步进2。总共112个地址逐个发START写地址字节然后等待ACK。如果有ACK说明这个地址上有设备在监听如果返回NACK说明该地址无设备。这个操作每地址只要几微秒到几十微秒全部扫完也就几秒钟。还有一个细节有些器件对写方向不响应但读方向会响应比如某些只读传感器。为了保险我的扫描程序对每个地址先发写方向探测收到NACK后再补发一次读方向探测。如果读方向有ACK同样记录为设备存在只读。这个双方向探测逻辑帮我抓到过两颗只在读模式下响应的传感器。4.2 扫描结果表结构设计Excel表格的结构在设计阶段就要想清楚否则后续转格式和处理会很痛苦。我最终确定的列结构如下列名含义示例序号行号17位地址(HEX)I2C七位地址0x50写地址(HEX)左移一位后的写方向字节0xA0读地址(HEX)左移一位后的读方向字节0xA1写响应写方向探测结果ACK / NACK读响应读方向探测结果ACK / NACK推断器件根据地址和已知BOM推测器件EEPROM扫描时间探测该地址的时刻12:03:22.145这里推断器件一列很实用。我把整个测试板的BOM导入了一份映射表扫描完成后程序自动把地址跟BOM里的地址做匹配匹配上就直接填器件型号没匹配上的留空方便人工再查。条件格式方面只要有任一方向返回ACK整行就标浅绿色两个方向都是NACK则标灰色。这样打开Excel一眼就能看出总线上哪些地址有货。4.3 用Python把CSV转成Excel并加条件格式扫描程序先把结果写成CSV因为CSV是最通用的中间格式任何语言都能处理。然后再用Python的openpyxl库把CSV读进来生成最终的Excel文件。这里贴一段核心代码import csv from openpyxl import Workbook from openpyxl.styles import PatternFill, Font from openpyxl.utils import get_column_letter wb Workbook() ws wb.active ws.title I2C Scan Result with open(scan_result.csv, r, encodingutf-8) as f: reader csv.reader(f) for row in reader: ws.append(row) # 根据ACK状态加颜色 green_fill PatternFill(start_colorC6EFCE, end_colorC6EFCE, fill_typesolid) gray_fill PatternFill(start_colorD9D9D9, end_colorD9D9D9, fill_typesolid) for row in ws.iter_rows(min_row2, max_rowws.max_row): write_resp row[4].value read_resp row[5].value if write_resp ACK or read_resp ACK: for cell in row: cell.fill green_fill else: for cell in row: cell.fill gray_fill # 列宽自动调整 for col in range(1, ws.max_column 1): ws.column_dimensions[get_column_letter(col)].width 16 wb.save(I2C_Scan_Result.xlsx)这段代码的运行效果是扫描一次刷新一次Excel整个流程就能完全无人值守。我还加了一个定时任务每两分钟自动扫描一遍总线如果发现新设备上线或者原有设备掉线就在Excel里追加一行记录并标黄相当于给总线做了个简易监控看板。这个功能在做长期老化测试的时候特别有用你可以清楚地看到哪颗器件在哪个时间点掉了。4.4 给Excel扫描工具加一个总线健康快照除了扫描地址我还往Excel里塞了一页总线健康快照每次扫描完成自动更新。内容包括目标SCL频率、实测SCL频率、上拉电阻配置、VCC电压、上升沿时间、下降沿时间、总线上设备总数、扫描耗时。这页放在第二个Sheet里。这样做的好处是回头不管是做测试报告还是排查问题都有据可查。比如两周后有人反馈某个产线批量出现I2C通信偶发失败你把当时的健康快照调出来发现那一批板子的上升沿时间偏大、VCC只有3.0V原因立刻就能锁定。没有快照记录就只能重新搭环境复现浪费时间。5. 常见问题与排查技巧实录5.1 扫描全部失败先别怀疑适配器我第一次搭好环境跑扫描结果112个地址全部NACK。一开始以为是适配器坏了后来冷静下来按顺序排查发现是测试板的I2C器件供电没打开。测试板上有电源开关适配器的3V3只给了上拉电阻供电器件本身的VCC是断开的。器件没通电自然不会响应地址。这个坑提醒我扫描工具只能探测总线上有没有设备在应答但它不能帮你确认设备是否真的上电了。所以扫描器全灭时先检查目标设备的供电再检查上拉电阻然后才考虑适配器问题。我还遇到过适配器没问题但SCL和SDA接反了的低级错误信号全乱扫描结果自然会乱成一团。5.2 扫描结果有幽灵设备是怎么回事有次扫描结果里多了一个0x77的设备但拆开BOM核对后没找到任何器件用这个地址。反复查证后发现这个幽灵其实是我手上拿着一块带I2C接口的评估板它本身有默认地址0x77而且它的SDA/SCL引脚跟测试板的总线通过面包板不小心连到了一起相当于两条总线被意外桥接。把评估板挪走之后扫描就干净了。排查这类幽灵设备的方法很简单把总线上所有器件逐一断开每断开一个就扫描一次看哪个地址消失那个地址就属于被断开的器件。如果断开所有外部器件后还有幽灵地址那就很可能是SDA/SCL短路或者上拉电阻没装导致总线上出现虚假的波形尖峰。5.3 总线死锁后的恢复顺序I2C总线有个经典死锁场景某个从设备在传输中途收到了非法信号内部状态机卡住把SDA钳在低电平。此时主控怎么发START都发不出去因为SDA拉不上去整个总线被这个设备锁死。恢复死锁有个通用手法模拟9个SCL时钟脉冲让卡住的设备状态机走完剩余的数据位从而释放SDA。我的扫描工具里专门加了一个总线恢复按钮点一下就执行9个SCL脉冲STOP的序列。实测对大部分智能传感器都有效但对一些比较顽固的器件需要先把VCC断电几毫秒再重新上电。死锁恢复之后建议立刻重新扫描一遍确认总线正常。如果还能稳定复现死锁就要考虑是不是某次通信的地址或数据发错了从软件层面也要检查有没有可能在总线上发半个字节就中断了。5.4 USB供电与电平兼容的坑USB转I2C模块如果直接从USB口取电最大输出电流通常是500mAUSB 2.0标准实际还要看具体模块设计。如果总线上挂了多个传感器个别传感器工作电流又大USB口的电压会被拉下来。我测过一些模块满载时3V3输出会跌到2.8V左右这会让部分器件进入欠压保护直接不响应。电平兼容是另一个容易踩的坑。测试板是3.3V器件没问题但如果被测板是5V的I2C器件那FT232H的I2C引脚能不能兼容5V要看具体模块有没有做电平转换。我建议是不要直接接5V系统容易把FT232H的引脚烧掉正确做法是中间串一颗电平转换芯片或者用支持5V输入的隔离模块。最后再分享一个环境上的小经验如果你习惯把Windows下的测试软件用虚拟化环境跑记得把USB设备从虚拟机里直通给客户机不然适配器永远枚举不到。我最早就是忘了这一步在虚拟机里折腾了半天驱动结果发现在宿主机里直接识别一下就解决了。这类的坑在硬件调试里太常见多留个心眼能省不少时间。