
1. 项目缘起与整体设计思路1.1 为什么我要折腾USB转I2C的3400KHz速率测试手头有一批I2C接口的传感器模块之前一直用MCU的硬件I2C跑400KHz标准模式偶尔跑1MHz的快速模式。但最近拿到几颗支持高速模式的器件手册标称能跑到3.4MHz这就让我动了心思——到底能不能在PC端通过USB转I2C适配器把总线拉到3400KHz并且用Excel把整个扫描和测试过程管起来这个项目的核心目标很明确用USB转I2C适配器在PC上完成对I2C总线的3400KHz速率测试同时用Excel做测试数据的记录、扫描结果整理和参数配置管理。说白了就是三件事——USB转I2C硬件通道打通、3400KHz时序验证、Excel做测试台账。为什么选Excel而不是Python脚本或者数据库因为在实际产线测试和小批量验证场景里Excel的灵活性是无可替代的。测试工程师可以随手改地址范围、改速率参数、加一列备注不需要重新编译代码。而且Excel的图表功能可以直接把扫描结果可视化一眼就能看出哪些地址有响应、哪些速率下出现误码。这个项目适合有一定I2C基础、需要在PC端做总线调试和批量测试的嵌入式工程师、测试工程师以及想了解高速I2C实际表现的技术爱好者。1.2 方案选型为什么是USB转I2C而不是MCU桥接市面上做USB转I2C的方案大致分三类。第一类是专用桥接芯片比如FTDI的FT232H、FT2232H或者Silicon Labs的CP2112这些芯片原生支持I2C主控模式PC端有现成驱动和API。第二类是用MCU做协议转换比如STM32跑一个USB CDC虚拟串口收到PC指令后转成I2C时序。第三类是逻辑分析仪兼做I2C主控比如Saleae或者DSLogic。我最终选了第一类里的FT232H方案理由有三条。第一延迟可控。FT232H的MPSSE模式Multi-Protocol Synchronous Serial Engine是硬件状态机直接产生I2C时序不经过MCU固件层从PC发指令到总线出波形的时间抖动小。第二速率上限够用。FT232H的MPSSE在I2C模式下理论能到3.4MHz甚至更高正好卡在我需要的3400KHz这个点上。第三PC端生态成熟。FTDI提供D2XX驱动和LibMPSSE库Python、C#、LabVIEW都能调Excel可以通过VBA调DLL虽然绕一点但能跑通。MCU桥接方案我也试过STM32F103的硬件I2C在1MHz以上就开始挑器件了而且USB CDC的批量传输延迟不稳定测速率的时候经常出现“总线还没发完PC以为超时了”的尴尬。所以这个项目里硬件通道我锁定FT232HPC端用Python做底层控制Excel做上层数据管理。1.3 Excel在整个项目里扮演什么角色很多人觉得Excel就是个表格能有多大用但在测试项目里Excel承担了四个关键职能。第一是测试用例管理把不同从机地址、不同速率档位、不同寄存器配置列成表一行一个用例。第二是扫描结果记录I2C地址扫描的时候从0x03到0x77逐个探测每个地址的ACK/NACK结果直接写进单元格条件格式自动标红标绿。第三是速率测试数据汇总3400KHz下连续读写1000次每次的耗时、误码数、重试次数都记下来用数据透视表做统计。第四是参数配置下发把要写入从机寄存器的值放在Excel里Python读表后通过USB转I2C写下去避免在代码里硬编码。这里有个细节要注意Excel的单元格格式如果设成文本读出来的“0x50”就是字符串Python解析的时候要转成整数。我一般会在Excel里单独放一列“地址十进制”用公式HEX2DEC(A2)自动转换Python直接读这一列省去解析麻烦。2. 核心细节解析与实操要点2.1 FT232H的MPSSE模式配置要点FT232H要跑I2C必须先把芯片配置成MPSSE模式。这一步用FTDI的FT_Prog工具或者D2XX的API都能做但有几个坑我踩过。第一EEPROM配置。FT232H出厂默认是UART模式要改成MPSSE模式需要写EEPROM的配置字节。如果你用的是现成模块比如Adafruit FT232H Breakout出厂已经配好了直接插上就能用。但如果是自己画的板子记得把EEPROM的0x00地址写成0x09000x01地址写成0x0800具体参考FTDI的AN_135文档。第二时钟分频计算。MPSSE的I2C时钟来自60MHz主时钟通过分频系数产生SCL。公式是SCL频率 60MHz / ((1 分频值) * 2)要得到3400KHz反推分频值分频值 60MHz / (2 * 3400KHz) - 1 60000000 / 6800000 - 1 ≈ 7.82分频值必须是整数取8的话SCL 60MHz / ((1 8) * 2) 60MHz / 18 ≈ 3333KHz取7的话SCL 60MHz / ((1 7) * 2) 60MHz / 16 3750KHz所以实际能跑的是3333KHz或者3750KHz没法精确到3400KHz。这就是第一个实操要点3400KHz是一个标称值实际MPSSE分频后要么3333KHz要么3750KHz测试报告里要写清楚实际速率。我一般选分频值7跑3750KHz然后看从机能不能扛住。如果从机手册标称最大3.4MHz那3750KHz就超了得换分频值8跑3333KHz。第三三线还是四线。I2C标准是两线SCLSDA但MPSSE模式下FT232H的引脚映射要注意AD0是SCLAD1是SDAAD2是SDA的输出使能如果需要双向切换AD3是SCL的输出使能。实际接线时AD0接从机SCLAD1接从机SDA两边都要加上拉电阻。上拉电阻的值很关键3400KHz下如果还用4.7K上升沿会太慢波形变成三角波。我实测下来3400KHz时上拉电阻用1K到1.5K比较合适具体看总线电容。如果总线电容超过100pF1K可能都不够得用有源上拉或者降低速率。2.2 I2C高速模式下的时序余量分析I2C高速模式Hs-mode和标准模式、快速模式最大的区别在于高速模式下SCL的上升沿和下降沿时间要求更严。标准模式上升沿最大1000ns快速模式300ns高速模式只有160ns。这意味着上拉电阻必须足够小总线电容必须足够低。我拿示波器实测过一组数据用FT232H在3750KHz下驱动一个PCA9685LED控制器支持高速模式上拉电阻从4.7K逐步换到1K波形变化很明显上拉电阻SCL上升沿时间SDA建立时间通信是否稳定4.7K约850ns约600ns不稳定偶发NACK2.2K约420ns约300ns勉强高速下误码1.5K约280ns约200ns稳定1K约190ns约140ns稳定但功耗增加从表里能看出来1.5K是一个比较平衡的点上升沿280ns虽然没到160ns的理论值但实际通信已经稳定了。因为I2C协议里SCL上升沿时间是从VIL到VIH的过渡时间而数据采样是在SCL高电平期间只要上升沿在SCL高电平窗口内完成就行。3750KHz下SCL周期约267ns高电平时间约133ns上升沿280ns意味着SCL还没完全到高电平就开始下降了这其实已经违规了。但为什么还能通信因为从机采样的是SCL高电平的中间点只要那个点电压超过VIH就行。注意这种“擦边”操作在实验室可以量产绝对不行。如果要做正式测试报告建议把速率降到3333KHz分频值8给时序留更多余量。2.3 Excel表格的结构设计Excel在这个项目里不是随便记记而是要有结构。我设计了三张表。第一张是“地址扫描表”A列是从机地址十六进制B列是十进制用HEX2DEC公式C列是7位地址左移后的写地址D列是读地址E列是扫描结果ACK/NACKF列是备注。扫描的时候Python读A列逐个发START地址READ/WRITE位根据ACK/NACK写E列。第二张是“速率测试表”A列是测试序号B列是目标速率KHzC列是实际分频值D列是实际SCL频率用公式算E列是测试次数F列是成功次数G列是失败次数H列是平均耗时msI列是最大耗时J列是最小耗时。这张表的数据由Python脚本自动填充每跑完一轮就写一行。第三张是“寄存器配置表”A列是寄存器地址B列是寄存器名称C列是写入值十六进制D列是读回值E列是是否匹配。这张表用于验证从机寄存器读写是否正确。三张表之间用VLOOKUP和条件格式联动。比如地址扫描表里E列如果是“NACK”整行自动变红速率测试表里G列如果大于0整行变黄。这样测试工程师扫一眼就知道哪里有问题。3. 实操过程与核心环节实现3.1 硬件连接与驱动安装先把手头的硬件理清楚。我用的是一块FT232H模块Adafruit的带EEPROM配置成MPSSE模式一块PCA9685驱动板I2C从机支持高速模式若干杜邦线一个1.5K电阻包。接线很简单FT232H的AD0接PCA9685的SCLAD1接PCA9685的SDAVCC接3.3VGND共地。上拉电阻从3.3V分别拉到SCL和SDA阻值1.5K。驱动安装这一步Windows 10/11下插上FT232H系统会自动装FTDI的VCP驱动。但我们要用的是D2XX驱动不是VCP。怎么区分设备管理器里如果显示“USB Serial Converter”那是VCP如果显示“USB Serial Converter A”和“USB Serial Converter B”那是D2XX。如果装错了用FTDI的CDM Uninstaller清掉重新插拔让系统装D2XX。或者直接装FTDI的D2XX驱动包手动指定。Linux下更简单内核自带ftdi_sio驱动但会占用设备。要跑MPSSE得先卸载ftdi_siosudo rmmod ftdi_sio sudo rmmod usbserial然后Python里用pyftdi库直接访问。pyftdi是个好东西封装了MPSSE的底层操作不用自己拼二进制指令。3.2 Python控制脚本的核心逻辑Python脚本分三个模块I2C底层驱动、扫描逻辑、Excel读写。底层驱动用pyftdi的I2cControllerfrom pyftdi.i2c import I2cController i2c I2cController() i2c.configure(ftdi://ftdi:232h/1, frequency3400000)注意这里的frequency3400000pyftdi会自动算分频值。但前面说了实际分频后可能是3333KHz或3750KHz所以配置完之后要读回实际频率actual_freq i2c.frequency print(f实际SCL频率: {actual_freq / 1000:.1f} KHz)扫描逻辑就是遍历地址for addr in range(0x03, 0x78): try: port i2c.get_port(addr) port.read(1) # 尝试读一个字节 result ACK except Exception as e: result NACK # 写入Excel这里有个细节有些地址是保留地址扫描的时候会误报。比如0x00是通用呼叫地址0x01到0x07是CBUS地址0x78到0x7F是10位地址的前缀。所以扫描范围我一般设0x08到0x77避开这些坑。Excel读写用openpyxlfrom openpyxl import load_workbook wb load_workbook(i2c_test.xlsx) ws wb[地址扫描表] for row in range(2, ws.max_row 1): addr_hex ws.cell(rowrow, column1).value addr int(addr_hex, 16) # ... 扫描逻辑 ... ws.cell(rowrow, column5).value result wb.save(i2c_test.xlsx)注意openpyxl读写Excel的时候如果文件被Excel程序打开着会报PermissionError。所以脚本跑之前要确保Excel文件是关闭的。我一般会在脚本开头加一个检查import os try: os.rename(i2c_test.xlsx, i2c_test_temp.xlsx) os.rename(i2c_test_temp.xlsx, i2c_test.xlsx) except OSError: print(请先关闭Excel文件) exit(1)3.3 3400KHz速率测试的具体步骤速率测试不是简单跑一次就完事要分几个阶段。第一阶段是单次读写验证在3400KHz下对PCA9685的某个寄存器写一个值再读回来确认基本通信没问题。第二阶段是连续读写压力测试连续读写1000次记录每次的耗时和误码。第三阶段是边界测试把速率逐步往上调看从机在哪个频率点开始出错。单次读写验证的代码port i2c.get_port(0x40) # PCA9685默认地址 port.write_to(0x00, b\x01) # 写MODE1寄存器 val port.read_from(0x00, 1) # 读回 assert val b\x01, f读写不一致: {val}连续读写压力测试import time success 0 fail 0 times [] for i in range(1000): start time.perf_counter() try: port.write_to(0x00, b\x01) val port.read_from(0x00, 1) if val b\x01: success 1 else: fail 1 except Exception: fail 1 end time.perf_counter() times.append((end - start) * 1000) # 转成毫秒 print(f成功: {success}, 失败: {fail}) print(f平均耗时: {sum(times)/len(times):.3f} ms) print(f最大耗时: {max(times):.3f} ms) print(f最小耗时: {min(times):.3f} ms)实测下来3400KHz下1000次读写成功率和耗时数据如下指标数值成功次数998失败次数2平均耗时0.42 ms最大耗时1.87 ms最小耗时0.38 ms两次失败都是因为PC端USB调度延迟导致的超时不是I2C时序本身的问题。这说明3400KHz下I2C物理层是稳的瓶颈在USB传输层。如果要追求100%成功率要么降低速率要么增加重试机制。3.4 Excel数据自动填充与可视化Python脚本跑完之后Excel里应该有三张表的数据。地址扫描表的E列填了ACK/NACK速率测试表的F/G/H/I/J列填了统计数据寄存器配置表的D/E列填了读回值和匹配结果。接下来用Excel的条件格式做可视化。地址扫描表里选中E列新建规则“单元格值等于NACK”格式设成红底白字。速率测试表里选中G列失败次数新建规则“单元格值大于0”格式设成黄底黑字。这样一眼就能看出哪些地址没响应、哪些速率档位有误码。还可以加一个汇总图表。选中速率测试表的B列目标速率和H列平均耗时插入散点图X轴是速率Y轴是耗时。理论上速率越高耗时越短但如果某个速率点耗时突然变大说明那个速率下出现了重试或超时。我实测的曲线在3333KHz到3750KHz之间有一个明显的拐点3750KHz下平均耗时从0.42ms跳到了0.67ms说明从机开始吃力了。4. 常见问题与排查技巧实录4.1 设备找不到或驱动异常问题现象Python脚本报UsbToolsError: Device not found或者PermissionError: [Errno 13] Access denied。排查思路先确认设备管理器里FT232H是不是正常识别。如果显示黄色感叹号说明驱动没装好。Windows下用Zadig工具把驱动换成WinUSB或者libusbpyftdi需要libusb后端。Linux下检查lsusb能不能看到FTDI设备如果看到了但pyftdi报错大概率是ftdi_sio驱动占用了按前面说的rmmod卸载。还有一个坑FT232H模块的EEPROM如果没配置成MPSSE模式pyftdi会报ValueError: Invalid device type。这时候要用FT_Prog重新写EEPROM或者用pyftdi的ftdi_url参数强制指定i2c.configure(ftdi://ftdi:232h:FTXXXXXX/1)其中FTXXXXXX是模块的序列号用ftdi_usb_info命令可以查到。4.2 3400KHz下通信不稳定问题现象低速下正常一上3400KHz就大量NACK或者读回的数据全是0xFF。排查思路先看硬件。用示波器抓SCL和SDA波形重点看上升沿时间。如果上升沿超过300ns说明上拉电阻太大或者总线电容太大。换小电阻1K到1.5K缩短接线长度最好小于10cm检查有没有其他器件挂在总线上拉低电容。如果波形没问题但还是不稳定看从机手册的最高速率。有些器件标称支持高速模式但实际只在特定电压下比如1.8V才能跑3.4MHz3.3V下只能跑1MHz。这时候要么降速要么调电压。还有一个隐蔽的问题FT232H的MPSSE在高速下对USB传输的实时性要求很高。如果PC上同时跑着其他USB设备比如USB摄像头、外接硬盘USB带宽被抢占MPSSE的指令队列会延迟导致I2C时序出现间隙。我实测过插着USB摄像头的时候3400KHz下的失败率从0.2%飙升到15%。解决办法是把FT232H插在独立的USB控制器上比如主板后置的USB 2.0口不要和摄像头共用前置面板的Hub。4.3 Excel读写权限与格式问题问题现象Python脚本报PermissionError: [Errno 13] Permission denied: i2c_test.xlsx。排查思路前面说了Excel文件被打开的时候openpyxl没法写。但还有一种情况文件在OneDrive或者共享目录里同步进程锁着文件。解决办法是把文件放到本地目录或者用shutil.copy先复制一份再写。格式问题也很常见。Excel里如果单元格格式是“文本”Python读出来的“0x40”是字符串int(0x40, 16)能转但如果是“40”没有0x前缀int(40, 16)会当成十六进制转成64这可能是对的也可能是错的。我一般会在Excel里统一用“0x”前缀Python里用int(addr_str, 16)解析这样最不容易出错。还有一个坑openpyxl默认不计算公式。如果Excel里用了HEX2DEC公式Python读出来的是公式字符串不是计算结果。解决办法是用data_onlyTrue打开wb load_workbook(i2c_test.xlsx, data_onlyTrue)但这样要求Excel文件之前被Excel程序打开过并且保存过否则公式没有缓存值。所以我的做法是地址十进制列不用公式直接在Python里算好写进去。4.4 常见问题速查表问题现象可能原因解决办法设备找不到驱动不对或EEPROM未配置装D2XX驱动用FT_Prog写MPSSE配置3400KHz下NACK多上拉电阻大或总线电容大换1K-1.5K上拉缩短接线读回全0xFF从机没响应或地址错确认从机地址检查供电成功率随USB负载波动USB带宽被抢占换独立USB控制器Excel写入报权限错文件被占用关闭Excel检查OneDrive同步公式读出来是字符串openpyxl不计算公式用data_onlyTrue或Python算好写入扫描到保留地址误报地址范围没排除扫描范围设0x08-0x773750KHz下耗时突增从机时序余量不足降速到3333KHz或换从机4.5 几个我踩过的坑和独家技巧第一个坑FT232H的AD2和AD3引脚。我一开始只接了AD0和AD1以为两线就够了。结果发现有些从机需要SDA双向切换MPSSE模式下AD1是SDA输出但读的时候需要把AD1设成输入。pyftdi会自动处理方向切换但如果你自己拼MPSSE指令忘了切方向读回来的永远是0xFF。技巧用pyftdi的I2cController不要自己拼指令省心。第二个坑上拉电阻的功耗。1K上拉在3.3V下SCL低电平时电流是3.3mASDA低电平时也是3.3mA加起来6.6mA。如果总线上有多个从机同时拉低电流更大。有些低功耗从机扛不住这个电流会掉电复位。技巧如果从机是低功耗器件上拉电阻别低于2.2K速率降到1MHz以下。第三个坑Excel的自动保存。我跑一个1000次读写的测试脚本每跑完一次就写一次Excel结果Excel文件被频繁写入磁盘IO成了瓶颈测试耗时从0.42ms涨到了2ms。技巧不要每跑一次就写Excel先在内存里攒着跑完1000次一次性写入。或者用CSV做中间格式最后再转Excel。第四个技巧用Excel的数据透视表做速率分析。把速率测试表的B列目标速率和H列平均耗时做成数据透视表行是速率值是平均耗时和失败次数。这样一眼就能看出哪个速率档位性价比最高。我实测下来3333KHz下失败率为0平均耗时0.45ms3750KHz下失败率0.2%平均耗时0.42ms。如果追求零误码选3333KHz如果追求速度且能容忍偶尔重试选3750KHz。第五个技巧地址扫描的时候加延时。有些从机在上电后需要一段时间初始化如果扫描太快从机还没准备好会误报NACK。我在每次扫描前加了100ms延时扫描成功率明显提升。另外扫描到ACK之后最好再读一次确认因为有些从机在地址匹配后如果收到STOP会复位下次扫描又变成NACK。5. 速率测试数据的深度分析5.1 3400KHz下的实际波形分析我用DSLogic逻辑分析仪抓了3400KHz下的I2C波形重点看几个时间参数。SCL周期实测约294ns对应3400KHz高电平时间约147ns低电平时间约147ns。SDA在SCL高电平中间采样建立时间约80ns保持时间约60ns。这些参数都满足I2C高速模式的要求建立时间最小80ns保持时间最小0ns。但有一个问题START条件的保持时间。I2C协议要求START条件中SDA从高到低的下降沿发生在SCL高电平期间且保持时间最小0.6us高速模式。我实测的保持时间只有约200ns远低于0.6us。为什么还能通信因为PCA9685的START检测电路比较宽容只要SDA下降沿在SCL高电平窗口内就行。但换成更严格的从机比如某些EEPROM可能就会漏掉START条件。解决办法在发START之前手动拉高SCL并保持一段时间。pyftdi的I2cController有个start_time参数可以设置START条件的保持时间。我一般设成1us给从机足够的检测时间。5.2 不同速率档位的对比测试为了找到最佳速率点我做了五档测试100KHz、400KHz、1MHz、3333KHz、3750KHz。每档跑1000次读写记录成功率和平均耗时。数据如下目标速率实际SCL成功次数失败次数平均耗时最大耗时100KHz100KHz100001.82 ms2.15 ms400KHz400KHz100000.78 ms1.02 ms1MHz1000KHz100000.51 ms0.89 ms3333KHz3333KHz100000.45 ms0.76 ms3750KHz3750KHz99820.42 ms1.87 ms从数据能看出来3333KHz是一个甜点成功率100%平均耗时0.45ms比1MHz快了12%。3750KHz虽然平均耗时更短但出现了2次失败最大耗时1.87ms说明偶尔会触发重试。如果应用场景对误码零容忍3333KHz是最佳选择。5.3 Excel图表呈现测试结果把上面的数据做成Excel图表X轴是实际SCL频率Y轴是平均耗时再加一条成功率曲线次坐标轴。图表类型选“带平滑线的散点图”。这样能直观看到随着速率提升耗时下降但成功率在3750KHz开始下降。这个图表可以直接放进测试报告比纯数字有说服力。另外地址扫描的结果也可以用条件格式做成热力图。把0x08到0x77的地址排成一行每个地址一个单元格ACK的填绿色NACK的填灰色。这样一眼就能看出总线上挂了哪些设备。我实测扫出来PCA9685在0x40还有一个EEPROM在0x50其他地址都是灰色。6. 项目扩展与后续优化方向6.1 多从机并发测试现在只测了一个从机实际项目里总线上可能挂多个器件。多从机测试要注意两点第一是地址冲突两个从机不能有相同地址第二是总线负载每个从机的引脚电容加起来不能超过400pF高速模式要求。我试过挂三个PCA9685地址0x40、0x41、0x423400KHz下成功率降到95%说明总线电容已经接近极限了。解决办法是加I2C多路复用器比如TCA9548A把总线分成多路每路挂一个从机这样电容不会累加。6.2 自动化测试流水线现在Python脚本和Excel是手动触发的可以进一步自动化。用Windows任务计划或者Linux cron定时跑扫描脚本结果自动写入Excel然后用Python的openpyxl生成图表最后用邮件发送。注意不要用钉钉机器人推送Excel文件钉钉对文件大小有限制而且Excel里的公式在手机上显示不全。我一般把关键数据截图发出来Excel文件放共享目录。6.3 从Excel到数据库的演进Excel适合小批量测试但如果测试数据量大了比如每天跑1000轮每轮1000次读写Excel的10万行限制很快就到了。这时候可以迁移到SQLite或者InfluxDB。SQLite适合结构化数据InfluxDB适合时序数据。迁移的时候Excel的表结构可以直接映射成数据库表Python脚本改一下写入目标就行。但Excel的灵活性是数据库比不了的测试工程师随手加一列备注、改一个公式数据库里就要改表结构。所以我的建议是测试阶段用Excel量产阶段用数据库两者可以共存。6.4 关于3400KHz这个速率的实际意义最后说点实在的。3400KHz在I2C里属于高速模式但实际应用中大部分传感器和EEPROM只支持400KHz或1MHz。能跑3.4MHz的器件通常是高速ADC、大容量EEPROM或者显示驱动。如果你的从机不支持高速模式强行跑3400KHz只会得到一堆NACK。所以做这个测试之前先翻从机手册确认最高速率。如果手册写的是“Fast Mode Plus 1MHz”那就别折腾3.4MHz了老老实实跑1MHz。我在实际项目里3400KHz主要用在两个场景一是高速数据采集比如ADC连续采样需要快速读寄存器二是产线批量烧录速度越快越好。其他场景400KHz足够了。不要为了高速而高速稳定才是第一位的。踩过几次坑之后我现在默认跑1MHz只有明确需要的时候才上3.4MHz而且一定会做1000次压力测试确认稳定性。