尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

USB转I2C扫描工具:地址冲突排查与100KHz时序实测

发布时间:2026/9/25 1:30:49

资讯中心
01
ARTICLE

USB转I2C扫描工具:地址冲突排查与100KHz时序实测

USB转I2C扫描工具:地址冲突排查与100KHz时序实测
干我们这行的遇到“I2C总线上有设备ACK但就是读不到数据”这种问题通常比芯片烧糊了还让人上火。前阵子调一块多传感器板子I2C上同时挂了EEPROM、温度传感器和触摸控制器结果设备地址撞车排查半天才定位到问题。后来我专门做了一个“USB TO I2C Scan”的小工具通过USB转I2C适配器用上位机逐地址扫描挂载设备把结果直接落到Excel表格里顺便把100KHz总线速率档位下的时序参数也测了一遍。这篇文章就把整个过程拆开讲清楚从协议原理、扫描实现到Excel导出再到波形实测希望能帮你少踩几个坑。1. 项目由来为什么需要一张总线地址地图1.1 我遇到的实际问题那块板子上有三个I2C从机一颗AT24C02存储芯片、一颗LM75温度传感器、还有一颗GT911触摸控制器。按照手册AT24C02默认地址是0x50LM75是0x48GT911根据复位引脚电平是0x5D或0x14。听起来互不干扰但上电之后上位机怎么都读不到温度数据用示波器看波形又发现SDA上有很奇怪的应答冲突。问题就出在地址上。GT911的地址脚被拉高后走的是0x5D但它的驱动里还会临时占用一段地址去做触摸坐标轮询而AT24C02如果A2A1A0三个引脚全部悬空某些批次的默认地址也会落在0x57附近和别的器件挨得很近。这种“看起来不冲突、实际互相踩”的地址编排靠肉眼看原理图根本发现不了必须把总线上所有实际应答的地址全部扫出来做一张地址地图。1.2 能用的工具不少为什么最后选了USB转I2C排查手段其实很多逻辑分析仪、示波器、MCU里跑一段扫描代码、树莓派上i2cdetect都行。但我当时的诉求很明确第一要能在Windows主机上快速操作别每次都得插开发板第二扫描结果要方便留档最好能直接生成表格因为后面还要给产线测试做参考文档第三我想在同一套环境里验证100KHz总线速率下时序是否达标。对比了一圈逻辑分析仪适合看波形但不利于批量扫地址MCU扫描代码灵活但每次改地址范围都要重新烧录而且串口输出还得自己处理。USB转I2C适配器刚好卡在中间它把I2C时序交给固件处理上位机只管发命令和收状态天然适合做Scan和Excel导出这类二次开发。如果你手头只有USB转TTL模块也不是不行用软件模拟I2C时序也能扫但实时性和波形质量都差很多用在100KHz速率测试上很容易误判我不推荐。1.3 项目目标与最终交付物我在动手前给自己定了三个交付目标后面所有步骤都是围绕它们展开的能扫描I2C总线上所有7位地址区分“有ACK响应”和“无设备”并识别扫描过程中总线异常把扫描结果按地址、器件类型、ACK状态整理成Excel表格自动高亮发现的设备在100KHz标准速率下抓取SCL/SDA波形对照I2C规格书的时序参数做一份实测记录验证这块板子的总线余量是否足够。这三件事做完这套工具就不只是临时排查用的脚本而是一份可以复用到后续项目里的测试资产了。2. 链路与协议USB、UART、I2C是怎么串起来的2.1 USB转I2C适配器的物理链路要理解Scan的整个过程先得把“USB转I2C”这件事拆开。市面上大多数USB转I2C适配器内部其实是两层桥接USB物理层先转成UART或SPI再由一颗MCU常见的是STM32、CH341或CP2112方案把收到的数据包翻译成I2C时序。也就是说你在上位机里写的命令并不是直接变成SCL/SDA电平而是先进入适配器固件固件再按照I2C协议状态机把数据发到总线上。这带来一个实际影响上位机和桥接固件之间是异步串口通信而你真正关心的I2C时序质量完全取决于桥接固件的实现。所以选购或自制适配器时至少要确认它支持100KHz标准模式并且没有把时钟延展Clock Stretching功能阉割掉。我调试用的那台适配器走的就是USB转UART再到I2C的结构在Windows下枚举成一个虚拟串口应用层只用操作COM口不用碰USB驱动细节这一点对快速开发非常友好。USB层还有一个容易忽略的点如果上位机怎么发命令都收不到应答先别怀疑I2C总线应该用USB抓包工具或者设备管理器确认虚拟串口是否真的枚举成功。很多时候是USB线接触不良或者驱动版本问题导致应用层彻底断连白白浪费半天排查时间。2.2 I2C地址与应答机制Scan的对象I2C总线上每个从机都有一个7位地址加上读写位组成第一个字节。主机发送START信号后发出这8位数据然后释放SDA等待从机应答。从机只有在自己地址匹配时才会在第9个时钟周期拉低SDA作为ACK不匹配的设备保持高电平即NACK。Scan的原理就是基于这个应答机制对0x00到0x7F的每一个7位地址发送一个“仅地址字节”的事务然后检测是否存在ACK。只要有ACK就说明该地址上有设备在应答。因为I2C是开漏结构多个设备同时应答同一个地址会在SDA上产生冲突这种OWM多主场景下Scan结果还会出现“该地址有响应但通信不稳定”的现象这也是刚才GT911和EEPROM冲突能被打出来的原因。实际扫描时要注意地址范围并不全是可用的。0x00是General Call地址0x04到0x07、0x78到0x7B等是保留地址段普通设备不会应答这些地址。所以我的扫描范围默认是0x08到0x77但也会保留全量扫描的选项因为偶尔有非标设备会在保留地址上产生响应出现这种情况本身就是值得记录的总线异常。2.3 100KHz标准模式的时序规格I2C标准模式Standard-mode的总线速率是100KHz。很多新人不理解为什么速率测试不直接跑400KHz或者1MHz非要先测100KHz其实是因为100KHz档位是整个I2C协议兼容性的基座几乎所有桥接芯片、传感器、EEPROM都支持它而且时序余量最大。如果在100KHz下时序都不达标那跑高速档位基本不用指望。100KHz模式下的关键时序参数我做了一张简化表调试时对照这张表查波形就够了参数含义100KHz规格要求f_SCLSCL时钟频率不超过100KHzt_HIGHSCL高电平时间≥4.0ust_LOWSCL低电平时间≥4.7ust_rSCL/SDA上升时间≤1000nst_fSCL/SDA下降时间≤300nst_HD;STA起始条件保持时间≥4.0ust_SU;STA起始条件建立时间≥4.7ust_SU;DAT数据建立时间≥250nst_HD;DAT数据保持时间≥0ns且不超过3.45ust_SU;STO停止条件建立时间≥4.0ust_BUF总线释放时间≥4.7usCb每根总线最大容性负载≤400pF看这张表可以发现I2C的低速档位反而对上升时间提出了限定因为总线是开漏结构上升沿完全靠上拉电阻被动充电如果上拉电阻太大或者总线电容太大上升沿就会变得很缓导致从机误判逻辑状态。这也是为什么后面做速率测试时上拉电阻要专门拿出来算一算。2.4 为什么测试速度档选在100KHz回到项目标题里那个“100KHz总线速率测试”。除了兼容性考量还有一个现实原因Flash类存储器在写操作时内部擦写时间长达几毫秒如果总线速率太高主机侧容易在数据保持时间上卡得太紧而100KHz的低速档位能让时序余量更充足也更适合用普通逻辑分析仪抓波形。另一方面很多USB转I2C适配器的固件默认支持速率只有100KHz和400KHz两档100KHz往往是固件默认值。当你想验证一个适配器是否“诚实”时测100KHz是最公平的不会像400KHz那样暴露出桥接固件的时序抖动也不会像1MHz那样对线材和电容极其敏感。先把100KHz这一档的波形测干净再谈高速优化这是我从多次调试里总结出的顺序。3. 扫描器实现从轮询到代码3.1 扫描算法与命令帧设计在写扫描脚本之前首先得确定适配器的命令帧格式。我用的这台适配器是转发式设计上位机发一个字节命令加一个字节地址适配器固件就在总线上执行对应操作返回一个状态字节表示ACK或NACK。典型命令如下0x53S扫描命令后面跟7位地址加写位0x52R读命令用于后续探测器件类型0x57W写命令带长度和数据。扫描算法的核心就是一层循环加一次应答检测import serial ser serial.Serial(COM7, 115200, timeout1) found [] for addr in range(0x08, 0x78): addr_with_write (addr 1) 0xFE cmd bytearray([0x53, addr_with_write]) ser.write(cmd) resp ser.read(1) if resp and resp[0] 0x00: found.append(addr) print(fAddress 0x{addr:02X}: ACK) else: print(fAddress 0x{addr:02X}: NACK) print(Found devices:, , .join(f0x{addr:02X} for addr in found))这段代码的逻辑很简单但有几个细节容易踩坑。第一串口超时不能设得太短因为桥接固件在检测总线冲突或时钟延展时耗时会比正常扫描长我把timeout设成1秒扫描完整个地址范围大约十来秒保证每个地址都被充分判断。第二地址字节里的读写位必须置0否则会变成读操作某些传感器在收到读请求后会真的开始输出寄存器数据导致状态判断混乱。第三扫描时要把适配器的内部应答超时设置调小否则遇到SDA被拉死的情况整个扫描会卡在某个地址上后面全都不走了。3.2 让从机“无副作用”应答的技巧很多人第一次写扫描程序时会直接用“读一个字节”的方式去探测地址。这个做法在多数从机上没问题但对某些写敏感的芯片非常危险比如存储在收到读命令后会中断当前操作或者触屏控制器会改变中断输出状态。更稳妥的做法是只发送“起始条件 地址字节 停止条件”中间不传任何数据也就是I2C协议里的零长度事务。有些适配器固件专门为Scan优化过发出的就是这个帧。这种零长度事务在逻辑上等价于“敲门问一下”从机只要地址匹配就ACK然后主机立刻STOP不会动从机内部任何寄存器。我实际扫下来的经验是绝大多数I2C设备都能正确响应这种探测只有极少数老芯片要求必须在地址字节后带至少一个数据字节才肯应答遇到这种设备再单独加一轮带数据的写探测也不迟。3.3 上位机扫描脚本到这里就能跑了完整脚本除了扫描最好还要加两件事记录扫描耗时和保存原始日志。我在脚本里加了一个统计模块每一行输出都带时间戳方便后面和Excel导出数据对应。import time start time.time() raw_log [] for addr in range(0x08, 0x78): ... raw_log.append(f{addr:02X} {status}) print(fScan finished in {time.time() - start:.2f}s)日志格式我建议用纯文本每行“地址 状态”这样无论接入Excel还是扔进数据库都方便。实测扫描一次全地址范围大约12秒其中大部分时间花在串口交互延迟上。如果你用的适配器支持I2C连续扫描模式一次遍历所有地址再统一返回结果耗时能压到2秒以内但那种模式对从机的副作用更难控制我自己还是宁可慢一点。3.4 结果样例与实际扫描日志下面是一次真实扫描的结果片段板子上挂的是LM75、AT24C02和一颗RTC地址分别是0x48、0x50、0x68SCAN 0x48 ... ACK SCAN 0x49 ... NACK SCAN 0x4A ... NACK SCAN 0x50 ... ACK SCAN 0x51 ... NACK SCAN 0x68 ... ACK从日志看三个器件全部落在常见地址段内。这种地址地图就能直接拿去核对原理图了如果原理图上某个芯片的地址应该出现在0x51扫描结果里却是0x50那就是地址引脚焊接或者连错线的问题。我当时排查GT911时就是先扫出0x5D出现ACK然后才发现它的复位引脚被板子上另一个信号拉高了导致地址从默认的0x14变成了0x5D完全偏离了原理图标注。这种问题不看实际总线光读代码是永远发现不了的。4. 扫描结果落到Excel既是记录也是BOM4.1 为什么选Excel作为交付格式我见过不少人把扫描结果直接贴在聊天记录里或者整理成Markdown表格发到群里。这种做法应急可以但到了做产线测试文档或者项目评审时就发现格式完全不够用。Excel的好处是天生适合做“地址地图”每一行是一个地址每一列是一个属性还能用颜色把“有设备”“无设备”“异常设备”区分开别人拿到文件后一眼就能看懂。另外你测完总线时序之后也要把实测数据和规格要求放进同一张表里做对比。这些数据用Excel管理比用Markdown或代码注释方便得多还能直接做条件格式超标的参数自动变红。所以我的处理流程是先用脚本生成结构化数据再用Python脚本直接写Excel文件省去手动复制粘贴的麻烦。很多人习惯把日志先整理成Markdown表格再手工转换到Excel列宽、合并单元格、重复表头这些坑够折腾半天不如一开始就用代码生成。4.2 字段设计与器件地址推断Excel表我按这几种字段来设计字段说明示例7位地址(HEX)扫描到的设备地址0x487位地址(DEC)十进制方便排序72写地址地址左移一位写方向0x90读地址地址左移一位后置1读方向0x91ACK状态ACK / NACKACK推测器件根据地址段和经验值推断LM75 / 24C02器件类型只能写“推测”不能写“确定”。I2C的地址段可以重叠0x48可能是LM75也可能是PCF8574或者其他传感器。要确认具体型号还得再执行一次读ID寄存器或者读配置寄存器的操作我在扫描表里单独留了一列“备注”专门放二次探测的信息。这种谨慎不是多余的产线测试时如果根据推测结果错判物料返工成本就大了。4.3 openpyxl自动生成带高亮的地址表我用的导出工具是Python的openpyxl库脚本只需要几十行。关键代码是这样的from openpyxl import Workbook from openpyxl.styles import PatternFill, Font wb Workbook() ws wb.active ws.append([7位地址(HEX), 7位地址(DEC), 写地址, 读地址, ACK状态, 推测器件]) green_fill PatternFill(start_colorC6EFCE, end_colorC6EFCE, fill_typesolid) gray_fill PatternFill(start_colorD9D9D9, end_colorD9D9D9, fill_typesolid) for addr in range(0x08, 0x78): if addr in found: ws.append([f0x{addr:02X}, addr, f0x{(addr1)0xFE:02X}, f0x{(addr1)|0x01:02X}, ACK, infer_device(addr)]) ws.cell(rowws.max_row, column5).fill green_fill else: ws.append([f0x{addr:02X}, addr, f0x{(addr1)0xFE:02X}, f0x{(addr1)|0x01:02X}, NACK, ]) ws.cell(rowws.max_row, column5).fill gray_fill wb.save(i2c_scan_result.xlsx)infer_device函数只做地址段的初步判断比如0x20到0x27往PCF8574猜0x48到0x4F往温度传感器猜0x50到0x57往EEPROM猜0x68往RTC猜。这样生成的Excel不需要人工修图直接就能发出去。如果后面要加时序实测数据就再加一张Sheet放进测量的波形参数。4.4 扫描表还能怎么用这张表不只是排错用的。我在后续项目里发现它还能当“地址规划检查表”新画一块板子时把所有候选器件地址都填进去提前检查有没有冲突比等板子回来再扫要省太多时间。另外用I2C扩展器比如TCA9548A做多通道总线时每个通道都要出一张地址地图扫描表直接从Excel模板里套效率高很多。如果地址不够用扫描表也能帮你快速评估是否要加扩展器。比如一个项目里同时有8颗EEPROM、4颗传感器、2颗RTC7位地址空间看起来够用但实际器件可能分布在重叠的地址段这时扫出来的地址地图会告诉你究竟需不需要上TCA9548A这类扩展芯片用数据说话总比拍脑袋靠谱。5. 100KHz总线速率实测波形、参数与上拉电阻5.1 测量工具准备与抓取要点扫描完地址下面进入速率测试环节。这里要抓的是100KHz档位下的SCL和SDA波形。我建议至少准备一台20MHz以上带宽的示波器或者采样率不低于50MSa/s的逻辑分析仪。示波器探头要靠近总线终端接地线尽量短不然测出来的上升沿会带进大量振铃噪声误判成时序超标。抓波形时我习惯把触发条件设在SCL的下降沿上先看一整帧I2C交易的起始条件、地址字节和停止条件然后放大到单个SCL周期用示波器的光标功能分别测出t_HIGH和t_LOW。SDA上的数据建立时间最好单独抓一次因为有些适配器在从USB收到命令转成I2C时序时会在地址字节附近插入一个很窄的毛刺肉眼不放大根本看不见。5.2 实测数据对规格表我拿当前板子实测得到的数据如下和100KHz规格表对比如下参数规格要求实测结果判定f_SCL≤100KHz99.3KHzPASSt_HIGH≥4.0us4.52usPASSt_LOW≥4.7us5.06usPASSt_r≤1000ns623nsPASSt_f≤300ns182nsPASSt_SU;DAT≥250ns428nsPASSt_BUF≥4.7us6.1usPASS从数据上看余量还比较充足尤其是t_SU;DAT做到了428ns比最低要求多了将近一倍。这说明板子的布线质量还行适配器的固件时序也稳定。不过这里有个容易忽略的细节我测的是“单次读操作”的时序连续写操作或者总线冲突场景下时序会变差。所以真要做出厂验收级别的结论还得多抓几组不同类型的交易再取最小值来判断。5.3 上拉电阻怎么算、怎么选很多人测量时序不达标时第一反应是怀疑适配器固件不行其实很大概率是上拉电阻选错了。I2C总线的上升时间由上拉电阻和总线电容共同决定。选型时有两个方向下限由灌电流决定。标准模式下IO_L通常按3mA算VOL_max是0.4V所以上拉电阻最小不能低于(VDD-0.4V)/3mA。3.3V系统算出来大约是967Ω工程上一般取1KΩ以上再小就把从机的灌电流余量吃没了。上限由上升时间要求决定。100KHz模式下tr最大是1000ns估算公式是R_p_max ≈ tr_max / (0.8473 × Cb)。如果总线电容按200pF算那么上限大约是5.9KΩ所以4.7KΩ是个很稳的取值如果总线很短2.2KΩ也可以上升边会更陡。我这次板子上VDD是3.3V总线电容估在150pF左右最后选的是4.7KΩ上拉。实测上升时间623ns对比1000ns的上限还有将近40%余量。如果总线引线很长或者挂载了超过8个设备电容一上去同样的4.7KΩ就可能不够这时就得降阻值或者用强驱动的适配器。测出波形超标后先不要急着改软件优先检查上拉电阻是不是和总线长度匹配。5.4 SDA被拉死、时钟延展等现场问题处理实测过程中最头疼的不是时序参数不达标而是整个总线被某个从机卡死。最常见的现场是某个器件的复位引脚没有按手册要求拉高导致它上电后直接拉低SDA这时无论怎么发扫描命令适配器都收不到应答总线一直处于忙状态。我遇到GT911通信失败那次就是这个问题触摸控制器在异常复位时序下会把SDA钳位在低电平整个扫描过程卡在第一个地址上。处理办法分几步第一步断开出问题从机的电源确认总线是否能恢复第二步如果从机不能单独断电就给适配器发连续9个SCL时钟脉冲让挂死的从机状态机复位第三步查这个器件的复位和供电时序比如GT911要求上电后延迟至少10ms再拉高复位脚否则就会进入异常模式。这类问题本质上不是I2C协议问题而是电源管理问题但表现起来和总线故障一模一样容易被误导。另外还要关注时钟延展。SMBus上的不少芯片会在处理数据时主动拉低SCL让主机等待。如果适配器固件不支持时钟延展即使SCL频率是100KHz也可能通信失败。判断方法很简单用示波器看SCL的低电平时间正常100KHz模式下t_LOW在5us左右如果某些数据位突然出现在6us以上的时长很可能就是从机在做时钟延展。6. 排错记录USB、驱动与系统层面的坑6.1 设备管理器里“I2C HID设备代码12”调试这套USB转I2C工具时我在一台Windows机器上遇到过设备管理器报“I2C HID设备找不到足够资源可以使用代码12”。这个报错看起来很吓人但本质上不是I2C总线问题而是系统给I2C HID设备分配中断或内存资源失败。常见原因有两种一是BIOS里把I2C控制器资源占用了二是USB Hub带宽分配冲突。我的处理顺序是先换一个物理USB口排除主板USB控制器资源争用然后在设备管理器里禁用再启用该设备让系统重新分配资源最后升级芯片组驱动。如果还不行就要进BIOS检查是否有设备占用了IO资源。这个坑和总线测试本身关系不大但一旦踩上适配器的USB链路会整个失效非常耽误时间。6.2 STM32方案的USB虚拟串口无法识别很多USB转I2C适配器内部用的就是STM32如果你自己做适配器或者买到这种方案还会遇到一个问题插上电脑后虚拟串口不识别。大概率不是驱动问题而是STM32的USB枚举条件没满足。最常见的三个原因外部晶振频率不准、D上拉电阻没有正确使能、VBUS检测脚没有接好。特别是用内部RC时钟跑USB时频率偏差超过千分之一就可能枚举失败表现为Windows提示“无法识别的USB设备”。遇到这种情况先量一下D引脚上电后的电平再做USB抓包看设备描述符有没有上传。我之前在自制适配器上踩过这个坑最后发现是晶振负载电容焊错了把20pF电容当成12pF用导致USB帧头抖动严重。如果你只是想调试I2C而不是研究USB建议优先选择成熟的USB转UART桥接芯片方案省去枚举这一层麻烦。6.3 我的几个底层检查习惯折腾完这些坑我养成了几个习惯。第一每次插上新适配器先看设备管理器里枚举出的COM口号再在终端里发一个空命令确认固件有回包避免把USB链路问题误判成I2C问题。第二扫描总线之前先用万用表量一下SCL和SDA的静态电平正常空闲状态应该是高电平如果某根线是低电平那就别急着跑扫描先查上游问题。第三驱动方面使用FT231X这类USB UART芯片时尽量去官网下载对应版本驱动Windows自带的驱动虽然能用但遇到高速或者特殊命令行模式时容易抽风。这套工具做完之后我现在调试I2C设备基本就三步走先扫地址生成本底地图再抓100KHz波形验证时序最后把两张表合并进同一份Excel存档。产线上如果哪批板子出货不良拿这份基线和实际测量对比很快就能定位是器件地址异常还是时序裕量不足。后面我打算把扫描模块再扩展一下加入自动读取器件ID寄存器的功能省得每次还得手工去核对推测器件那样这张地址地图就更完整了。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。