☰
CH347/339W开源调试器实测:一个USB口搞定SPI/I2C/JTAG/SWD/UART
2026/10/21 8:59:59 网站建设 项目流程

做嵌入式这行,抽屉里没几个调试器是不可能的。今天翻出一块吃灰的开发板,明天又要给传感器模块测时序,后天可能还得帮朋友看看ARM核能不能连上调试口——于是USB转TTL、逻辑分析仪、原厂调试器、杜邦线越堆越多,桌面上像盘丝洞。直到我在某开发者社区看到这个CH347/339W开源项目,思路才彻底变了:一个USB口,把SPI、I2C、JTAG、SWD、UART、GPIO全包圆。这篇文章是系列第二篇,重点聊聊我把这块板卡从"能点亮"到"能干活"的完整过程,包括硬件设计取舍、驱动适配、各协议实测数据,以及一堆文档里不会写的坑。

1. 桌面调试器泛滥成灾:这个项目解决的真实痛点

1.1 三类用户的共同困境

先说说为什么这种"多协议合一"的工具会有人做,而且做了还不止一个版本。我接触过的使用者大概分三类,需求完全不同,但最后都指向同一个痛点。

第一类是刚入坑的学生和爱好者。手头预算有限,不可能把每个协议对应的调试器都买一遍。买个USB转UART发现还要读I2C传感器,买个I2C工具发现又要写SPI Flash,最后一算账能买一块不错的开发板了。第二类是像我这样的嵌入式工程师,工作对象五花八门:今天调一个Wi-Fi模组要串口日志,明天写一块NOR Flash要用SPI,后天芯片起不来要拿JTAG看一眼PC指针。如果每个协议都换一套工具,光接线和装驱动就耗掉半天。第三类是产测和测试工程师,他们最烦的是"同一台PC上同时挂几套不同厂商的工具链"——脚本里调用这个的DLL、那个的命令行,依赖一多就翻车。他们需要的是用同一根USB线、同一套API,把各种测试动作串成自动化流程。

CH347/339W项目正好把这三种需求压在一颗芯片上。硬件上它是围绕CH347这颗USB2.0高速转接芯片做的核心板,软件上既有原厂驱动和动态库,又有社区适配的开源路径,USB物理层统一了,上层的协议通道全部暴露在同一个设备节点下,这对工程化的意义非常大。

1.2 一张表看清CH347的能力边界

很多人拿到板卡第一反应是"什么都能干,是不是什么都干不好"。这个担心有道理,所以我先用一张表把CH347在339W板卡上的典型能力边界列清楚,后文再逐个协议展开实测。

功能典型用途硬件本质实测注意点
UART串口日志、AT指令、ISP下载USB枚举为标准串口用DTR/RTS做自动复位时要额外搭电路
SPI读写Flash、传感器、显示屏硬件SPI主机CPOL/CPHA必须按从机手册选对
I2C传感器、EEPROM、PMIC配置硬件I2C主机7位/8位地址换算和上拉电阻最坑
JTAGMCU调试、FPGA配置访问硬件JTAG状态机电平不匹配容易烧IO
SWDARM内核调试用GPIO软件模拟速率不高,胜在通用
GPIO复位控制、电平采样、继电器触发普通IO口输出驱动能力有限,不能直驱大负载

从表里能看到一个关键信息:除了SWD是软件模拟,其他协议大部分都有硬件逻辑支撑。这意味着SPI/I2C/JTAG的时序是芯片内部自动产生的,CPU只管发命令,不用自己数时钟。

1.3 为什么不用现成的多合一产品

市面上确实有商业多合一调试器,有的还带隔离、带逻辑分析仪,功能很完整。但开源项目的价值不在"省那几百块钱",而在于透明和可扩展。

首先是引脚定义透明。商业工具的引脚定义和内部电路你得看手册猜,开源项目直接给你原理图和PCB文件,哪个引脚连到芯片的哪个功能、中间有没有电平转换、有没有ESD保护,一清二楚。其次是可改造。我的板卡上就自己飞线引出了两路额外GPIO,用来控制目标板的电源通断,这个需求商业工具很难满足。最后是驱动路径清晰,出了问题你能一直追到寄存器级别,而不是对着一个黑盒子干瞪眼。

2. 339W板卡的硬件骨架:供电、电平与引脚复用的取舍

2.1 电源树设计:从5V到3.3V的第一次考验

拿到339W板卡后,我第一件事不是插USB,而是先看板上的电源树。绝大多数这类核心板的设计逻辑是一样的:USB的VBUS进来,经过一颗低 dropout 稳压器变成3.3V,给CH347芯片供电,同时把3.3V作为所有IO引脚的默认参考电平。板卡上一般还会放几个电容做退耦,靠近芯片电源引脚的地方放0.1uF,USB口附近放10uF左右的储能电容。

这里有一个实测经验:如果你的目标板是5V逻辑,千万不要把5V直接灌到CH347的IO上。这颗芯片的内部IO驱动器和保护结构是按低压逻辑设计的,直连5V轻则功能异常,重则损坏引脚。我见过有人拿5V的Arduino和这块板卡对接,SPI读回来的数据全是乱的,查了半天才发现是电平问题。

正确做法是外置电平转换。比如SPI接口用TXB0108这类双向电平转换芯片,I2C用带电平转换的模块,或者干脆找一块支持5V容忍的转接板。总之记住一条:先确认两边逻辑电平一致,再谈时序和协议。

2.2 电平转换与ESD:调试器最先坏的永远是IO

调试器这种设备,使用场景决定了它天生要和各种"状态不明"的目标板打交道。目标板可能有电,可能没电,可能电源有毛刺,可能地线没有真正共地。所以板卡上IO保护做得怎么样,直接决定它的寿命。

我拆解过的339W类开源板卡里,做得好的设计一般有两层保护。第一层是ESD防护,在排针或者对外接口处放ESD二极管阵列,比如常见的USBLC6-2,它能吸收静电放电的尖峰,避免芯片IO直接承受静电打击。第二层是串联电阻,有些设计会在关键信号线上串22Ω到47Ω的小电阻,既能抑制振铃,又能在意外短路时起到限流作用。

这层设计平时看不见,但非常重要。我做过一个小实验:把板卡上的SPI时钟线故意对地短接,没有保护的设计很快发热,有串阻的设计则只是信号异常,拔掉后恢复。所以如果你要自己复刻这个项目,别省那两个电阻。

2.3 引脚复用冲突:所有协议同时要跑?先算算账

这颗芯片最容易被忽略的问题是引脚复用。物理引脚就那么多,SPI要占CS、CLK、MOSI、MISO,I2C要占SCL、SDA,JTAG要占TMS、TCK、TDI、TDO,这些功能在部分引脚上是重叠的。也就是说,"集多种协议于一体"不等于"所有协议可以同时满速运行"。

实际选型时要先列一个使用场景清单:我同一时间需要哪几个协议同时工作?比如最常见的组合是"UART看日志 + I2C读传感器 + SPI写Flash",这三者如果硬件上分配到了互不冲突的引脚,就能同时跑。而"JTAG调试 + SPI写Flash"同时进行就可能产生冲突,因为两组接口争抢同一批引脚。

我在这个项目里学到最重要的一点是:别追求大而全,先想清楚你最常用的组合是什么,再去看板卡的引脚分配表。这也是很多人在布线阶段反复改板的原因——功能配置对了,引脚打架,一切白搭。

3. 驱动和上位机:让芯片从"能识别"变成"能干活"

3.1 驱动安装:最容易劝退新手的环节

硬件接线接好,插上USB,这时候才开始真正的考验。CH347在Windows下枚举出来的东西不是一个设备,而是多个接口的组合:UART通道会变成一个标准COM口,其他通道在设备管理器里会显示为一个厂商设备。很多新手看到"未知设备"就慌了,其实最多的情况是驱动没装全,或者芯片当前的工作模式和你预期的不一样。

这里第一个坑是:CH347的工作模式不是出厂固定死的,需要通过官方配置工具或者特定指令去切换。如果你想要的组合是"UART + SPI + I2C + JTAG",而芯片当前被配置成了纯UART模式,后面几个通道根本不会枚举出来。我一开始就是因为没注意这点,折腾了半天SPI工具,结果发现设备压根没开这个功能。

装驱动时还有一个经验:尽量把其他USB设备拔掉再装。这类芯片的驱动安装程序有时会枚举所有同VID设备做匹配,如果手头同时插了多颗同类芯片,可能会出现驱动对不上号的情况。装完后看一眼设备管理器,确认COM口和厂商设备都出现,再继续下一步。

3.2 两条软件路线:官方库与libusb

芯片识别了,接下来的问题是"怎么操作"。

在Windows下,最省心的路径是用原厂提供的动态库。官方会提供完整的头文件和示例程序,C/C++项目里直接调用对应接口就能做SPI读写、I2C读写、JTAG操作。它的优势是稳定、功能全、资料多,缺点是绑定了Windows平台,而且依赖运行库。

另一条路是libusb。这个方案跨平台,Windows、Linux、macOS都能跑,只要系统认到USB设备,你就能通过控制传输和设备节点直接和芯片对话。很多人喜欢用libusb是因为它能配合Python,写产测脚本非常方便。代价是你得多读一些USB协议层的资料,比如端点怎么分配、设备描述符怎么解析,刚开始会有一点陡峭。

我的建议是:如果你只是调个传感器、读个Flash,直接用官方库最省事;如果你要在产线上做自动化,或者目标平台是Linux,那libusb方案值得投入时间。两条路不冲突,我实际项目中是两者混用:Windows下用官方库快速验证功能,Linux下用libusb做产测脚本。

3.3 最小示例:用Python发起一次协议访问

以libusb为例,一个最小可用的Python流程大致是这样的:先找到设备,打开设备,然后通过控制传输或者批量端点发起协议命令。注意具体端点地址和命令格式要以官方头文件为准,不同固件版本可能有差异,但骨架是通用的。

import usb.core import usb.util # 设备VID通常以某厂商ID开头,具体PID需根据枚举结果查询 dev = usb.core.find(idVendor=0x1A86) if dev is None: raise ValueError("没有找到设备,检查VID/PID和驱动状态") # 打印设备基本信息,确认通道枚举正确 print(dev) # 发起一次协议访问的通用套路: # 1. 构造请求数据 # 2. 通过control_transfer发送命令 # 3. 通过read/write端点读取返回数据 ret = dev.ctrl_transfer( bmRequestType=0x40, # 主机到设备的方向 bRequest=0x00, # 具体请求码需查官方定义 wValue=0x0000, wIndex=0x0000, data_or_wLength=64, timeout=1000 ) usb.util.dispose_resources(dev)

这段代码跑通以后,就是你自己工具链的地基了。后面要做SPI读ID、I2C读温度、GPIO拉高拉低,全都是在这个结构上扩展。我强烈建议把这个最小框架保存在一个公共文件里,后续所有脚本都基于它,避免每个脚本重复写设备枚举的代码。

4. SPI/I2C/JTAG实测:时序、接线与波形验证

4.1 SPI:先把时钟极性和相位搞清楚

SPI看起来简单,四根线一接就能收发,但实际项目中至少一半问题出在CPOL和CPHA上。这两参数决定时钟空闲电平是高还是低、数据在上升沿还是下降沿采样。读目标器件的datasheet,上面通常会画出时序图,你要做的是把代码里的模式配置成和它一致。

我实测过一款SPI Flash,JEDEC ID读取指令是9F,返回三个字节。把CPOL配错以后,读回来的数据不是全FF就是全00,一开始我还以为是接线问题,换了好几根杜邦线,最后用逻辑分析仪抓波形才发现时钟极性和从机期望的完全相反。从那以后我的习惯是:任何新的SPI从机,先用30MHz以下的低速把ID读成功,再谈提高速率。

关于速率,CH347这类的硬件SPI主机能跑到的极限不低,但实际传输速率会被杜邦线、面包板的寄生电容严重拖累。我做过一个对比:短粗线(10cm以内)情况下,20MHz时钟波形还很干净;换成30cm的杜邦线,到10MHz以上就能看到明显的过冲和振铃。所以如果你的项目对速率敏感,布线要短、要粗,最好用排线而不是飞线。

接线方面的三个硬性要求:共地、CS拉高、MISO/MOSI不乱接。共地是所有通信的前提,正因为如此,有些目标板必须用它自己的电源而不能用USB直接供电,否则地电平不一致会导致持续通信错误。CS拉高是SPI从机进入待机状态的默认条件,初学者经常忘记接上拉导致从机一直选中。MISO和MOSI接反的后果是数据全部写飞,这种错误只能靠仔细核对原理图来避免。

4.2 I2C:地址换算与上拉电阻

I2C的坑和SPI不一样。SPI是点对点的思维,I2C是总线思维:一根SCL、一根SDA,上面挂多个设备,每个设备有地址。

第一个坑是地址换算。器件手册里通常给的是8位写地址,比如0xA0,但代码配置时用的是7位地址0x50。很多人直接把0xA0填进代码,结果设备不应答。换算规则很简单:8位地址右移一位就是7位地址。但就是这么个简单操作,我见过不止一个同事栽在上面。

第二个坑是上拉电阻。I2C总线是开漏结构,必须有上拉电阻才能拉高电平。很多传感器模块板上已经放了上拉,你不用管;但如果你自己搭板子供电,或者模块没有上拉,就必须在SCL和SDA上各接一个4.7kΩ到10kΩ的电阻到3.3V。电阻太小,灌电流大;电阻太大,上升沿变缓,高速模式稳不住。

实测中我常用的套路是:先用默认100kHz标准模式把设备地址扫描一遍。写一个简单脚本,往0x03到0x77的每个7位地址发一个空读命令,哪些地址有ACK就被记录下来。这个扫描动作能快速确认设备挂没挂上、地址到底是多少,比翻手册猜地址快得多。读温度传感器时,通常先写寄存器地址,再读两个字节,把字节拼成16位整数,再除以缩放系数就是实际温度值。只要地址对、上拉有,这部分一次就能通。

4.3 JTAG:TAP状态机不是玄学

JTAG之所以让很多人觉得难,是因为它不像SPI那样有个明确的"开始和结束",而是一台状态机。TMS引脚每次采样时的电平决定状态机下一步走到哪里,TCK提供时钟,TDI在移位状态送入数据,TDO在移位状态送出数据。

CH347的JTAG是硬件实现的,芯片内部自动处理TAP状态机的推进,这比软件模拟省心得多。你要做的只是告诉它:进入Test-Logic-Reset、然后Shift-DR、送指令、Shift-IR、送数据。听上去复杂,但实际调用库函数时就是一个高层的JTAG操作接口。

我用JTAG做过两件事:一是给一块跑飞了的MCU恢复调试口,通过JTAG发一条指令把芯片复位到初始状态;二是访问一块FPGA的配置寄存器,检查加载状态。这两件事的逻辑都是类似的:先确认TCK/TMS/TDI/TDO四根线都接到了目标芯片的正确引脚,然后从最基础的"读取IDCODE"开始验证链路。

JTAG最容易出问题的地方是电平匹配和目标芯片的JTAG使能。有些MCU出厂时JTAG功能不是默认打开的,或者被复用成了GPIO,这时候无论你时序多标准都连不上。别一上来就怀疑板卡,先用万用表确认目标芯片的JTAG引脚确实连接到了排针上。

5. SWD和GPIO组合拳:没有原生SWD也能调试ARM

5.1 SWD为什么不能"原生"支持

很多人拿到板卡后会问一个问题:既然有JTAG,为什么还要单独拿一个GPIO去模拟SWD?

答案其实很简单:SWD是ARM生态推出的调试接口,只用了SWDIO和SWCLK两根线,更适合引脚紧张的MCU。但CH347这类转接芯片不像专门的调试器那样内置SWD协议引擎,它提供的是通用IO。所以开源项目最常见的做法是:通过GPIO引脚,用软件按照SWD协议时序去产生复位序列、切换序列,然后发DP/AP命令。

这也就决定了它的速率不会太高,我做过的软件模拟SWD,稳定工作的时钟频率通常在1MHz上下。这个速率调试个简单MCU完全够用,但如果你要做Trace、要做高速Flash下载,那还是得用专门的调试器。它的定位不是替代原厂调试器,而是让你在临时调板子时"能用"。

5.2 用GPIO模拟SWD的工程细节

SWD的软件模拟核心是两步:先把目标芯片从复位状态拉起来,并让SWDIO线保持一个确定的初始电平;然后发送至少50个时钟周期的SWCLK,让线进入SWD协议状态。接着发送一条JTAG-to-SWD的切换序列,目标芯片收到后会把调试口切换到SWD模式。

我做过的实测里最常用的操作是读IDCODE。IDCODE能告诉你芯片内核是Cortex-M0还是M4、厂商ID是多少,确认连接无误后,再尝试读写Core Debug寄存器。比如往DHCSR寄存器写一个值来halt内核,读PC指针,这些操作在调试裸机程序时非常救命——上次跑飞的原因到底是中断向量表错位还是栈溢出,看一眼PC就能判断。

GPIO模拟还有一个独特优势:因为GPIO是可以灵活控制的,你不仅能模拟SWD,还能顺便做复位引脚控制、Boot0引脚拉高拉低。有些开源板卡会专门把空闲GPIO引出来,配合模拟SWD做成一键进入Bootloader的辅助功能。这在实际项目中很实用,我调一块MCU时就是靠一路GPIO控制复位、一路GPIO拉高Boot0,再加SWD读写,三路配合就完成了原本需要专门烧录器的操作。

6. UART与产测场景:从单次读写到批量自动化

6.1 UART:最省心的一个通道

六个功能里最省心的就是UART。因为CH347的UART在Windows下直接枚举成标准COM口,任何串口工具都能用。在Linux下它变成ttyUSB节点,minicom、picocom随便选。这带来的便利是:无论你用哪个平台,日志输出、AT指令交互都像操作普通串口一样。

但省心不等于没有坑。最典型的坑是"自动下载电路"。玩过ESP类模组的人都知道,很多模组进入下载模式需要同时控制EN和IO0引脚,而PC端串口工具通过DTR和RTS信号去控制。CH347的UART虽然也有DTR/RTS输出,但要真正实现"一点下载",往往需要在板卡上额外设计晶体管或三极管电路,把这两个串口控制信号转换成目标芯片复位和Boot引脚的电平。

如果没有这个电路,你就得手动操作:先拉低Boot引脚、再按一下复位、再松开,听起来也没多麻烦,但产线上连续烧一百块板子时,这个动作的体验就很糟糕了。所以如果你要把这块板卡用在批量下载场景,建议在硬件上把自动下载电路补全,或者通过GPIO软件控制复位和Boot引脚。

另一个UART相关的坑是波特率精度。虽然CH347这类芯片的波特率发生器已经比较准了,但如果你用非标准波特率,某些串口工具会计算出偏差很大的分频系数。我一般只使用标准的9600、115200、460800、921600这几个档位,实测都很稳。

6.2 产测脚本:把调试习惯变成自动化用例

这个项目给我的最大惊喜,不是某个单独的协议好用,而是把所有协议统一到一个USB设备后,产测脚本变得非常简单。

举个例子,我之前负责过一个小批量硬件的产测,测试项包括:串口是否能收到模组上电日志、I2C温度传感器是否能读到合理值、板卡上一颗SPI Flash能否正常写入和读出、一路GPIO控制的LED能否点亮。传统做法是分别用串口助手、I2C工具、SPI编程器、万用表去测,每项都要手点一下,还要记录结果。

用这个方案以后,我把上述所有动作写进一个Python脚本:先枚举设备,然后依次读串口日志、读I2C传感器、写入SPI Flash再读回比对、拉高GPIO检查电流。整条流程在三秒内跑完,结果自动打PASS/FAIL,日志落盘。那一次的测试效率提升了至少五倍。这就是"多功能合一"在产测场景下的真正意义——不是为了炫技,而是为了让测试动作在同一个脚本框架里无缝串联。

我总结的产测脚本编写要点有三条:第一,每个测试项独立成函数,异常要单独捕获,不能因为I2C失败就跳过SPI测试;第二,每步操作之间留足够延迟,特别是传感器上电后需要稳定时间;第三,所有结果要带时间戳和原始数值落盘,出了问题能回溯。

7. 常见问题排查清单与后续扩展

7.1 高频症状与根因排查表

把这段时间踩过的坑整理成一张排查表,方便你遇到问题时对照:

症状可能原因排查顺序
设备管理器显示未知设备驱动没装全/工作模式不对先重装驱动,再用配置工具检查模式
某个协议通道始终无响应对应功能未启用或引脚被复用检查模式配置,核对引脚分配
SPI读回全FF时钟极性相位错/MISO接反/从机没上电先低速读ID,再查接线
I2C总线卡死SDA一直低从机拉死总线/地址错误复位从机,扫描地址,检查上拉
SWD连不上目标芯片SWD被禁用/速率过高降到最低速率,确认复位时序
UART乱码波特率不对/地线没共地换标准波特率,确认共地
GPIO输出无法驱动负载IO驱动能力不足加外部驱动电路或改用继电器模块

这里想特别强调一下"SPI读回全FF"和"I2C总线卡死"这两个症状。全FF代表的往往是"线上根本没有数据返回",优先怀疑接线和从机供电,而不是时序配置。I2C卡死在你拔掉设备后依然存在,说明SDA被某个设备拉低了,最常见的原因是从机地址错误导致它持续尝试应答,复位从机或重新上电一般能恢复。

7.2 这个项目还能往哪走

最后聊聊扩展方向。我见过几个人的玩法很有意思,也可以给你参考。

第一个方向是叠加逻辑分析仪功能。GPIO本身能采样,板卡速度也够,你可以写个固件或上位机脚本把多路GPIO变成简易逻辑分析仪,专门分析目标板按键时序或者传感器中断波形。

第二个方向是网络化。把板卡插在一台小主机或者嵌入式Linux设备上,通过网络远程访问USB设备,这样你就不用在工位上也能对着远程板卡调试。调试服务器这种玩法在团队协作时特别有用。

第三个方向是做压测治具。利用GPIO控制继电器或POGO探针,再用SPI/I2C做协议检测,把板卡变成一个半自动的离线测试治具,适合小批量生产。

这些都是这板卡的想象空间。核心还是那句话:它不是一个只能完成单一任务的工具,而是一个把多种协议能力交到你手里的开放平台。

我在实际使用中最大的体会是,这种多合一工具最怕的就是"样样通样样松",但CH347/339W这个方案在常用协议上并没有明显的短板,尤其是把稳定性放在第一位之后,很多问题都是可以绕过的。如果你也准备上手,我建议别贪快,第一次板子通电先跑最简单的I2C扫描,把整个链路从USB到协议的每个环节都验证扎实,再去折腾SWD和JTAG。工具链一旦跑通,你会发现原来桌面上那堆调试器,其实大部分都可以收进抽屉了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询