干嵌入式这行最烦的就是桌面被各种调试器、下载线、转接小板堆满。尤其是出门去趟现场或者临时调一块板子ST-Link、USB转串口、SPI编程器恨不得全塞包里。我自己被折腾烦了之后开始用一颗芯片同时顶两摊活儿CH347F。这颗芯片给STM32做SWD调试时OpenOCD直接认它把它切到SPI模式连一颗SPI NOR Flashflashrom也原生支持。一颗芯片两组主流开源工具链覆盖我从日常debug到固件抢救的所有高频场景。这篇文章就是围绕这个“一芯多用”的实战过程写的包括硬件接线、模式切换、OpenOCD和flashrom的配置命令以及我在实际使用中踩过的一堆坑。1. 为什么是CH347F一颗USB转接芯片把两套工具链串起来了1.1 一颗芯片覆盖两类高频场景CH347F是WCH沁恒出的一颗USB 2.0高速转接芯片支持UART、SPI、I2C、JTAG等协议。乍一听好像就是个“万能转接”但真正让它和CH340、CH341这些老前辈拉开差距的是两件事一是USB接口是高速480Mbps不是CH340那种Full Speed二是它明确支持JTAG/SWD模式SPI口的速率也高得多。这就带来一个很实在的好处在嵌入式开发里最常用的两个场景它都能接管。第一个场景是STM32的SWD调试。STM32的调试接口默认就是SWD只要引出SWCLK和SWDIO两根线就能烧录、调试。CH347F的JTAG模式里四个引脚TCK/TMS/TDI/TDO其中TCK和TMS在SWD协议下正好对应SWCLK和SWDIO所以它能干ST-Link的活儿。OpenOCD从0.12.0之后的版本开始加入了对CH347的原生支持libusb方式访问WCH官方也维护了对应的OpenOCD分支所以在OpenOCD里把它当普通调试器用非常顺。第二个场景是SPI Flash的备份和烧录。玩路由器、工控板、各种带SPI NOR Flash的板子经常需要把固件从Flash里读出来备份或者把改好的固件写回去。CH347F的SPI模式可以直连Flash芯片flashrom这个开源烧录工具直接就支持ch347_spi这个programmer一条命令就能识别、读取、擦除、写入。换句话说以前我要带ST-Link加一个CH341A编程器现在只带一个CH347F转接板就够了。1.2 和传统方案比差异在生态支持先放一张对比表方便大家直观理解方案USB速率SWD调试SPI Flash编程开源工具支持ST-Link/V2Full Speed原生支持不支持OpenOCD支持但HLA模式功能有限J-Link EDUFull Speed原生支持可配合J-Flash但受授权限制OpenOCD支持DAPLinkFull Speed原生支持需要固件支持CMSIS-DAP的SPIOpenOCD支持SPI能力看固件CH341AFull Speed不支持支持但速度慢稳定性一般flashrom支持但无SWDCH347FHigh Speed通过JTAG接口支持SWD支持速率高OpenOCD flashrom均原生支持注意最后一行CH347F在开源工具链里的待遇是它最大的护城河。OpenOCD和flashrom这两个工具几乎成了嵌入式Linux和固件工程事实上的标准它们原生支持一个硬件意味着你不用装一堆厂商私有软件也不用担心厂商软件在某个操作系统版本上突然抽风。我自己用下来CH347F的SPI时钟速度在实际操作中比CH341A稳定太多。CH341A不是不能用是当你面对一颗16MB的W25Q128用低速SPI读完整片那等待时间真的让人抓狂。CH347F的SPI标称最高速率在几十MHz量级实际我一般跑到10MHz以内读16MB也就几秒钟的事情。1.3 三种工作模式SPI和JTAG不在同一个模式下这里必须提前说清楚一个关键限制也是新手最容易懵的地方CH347F的三种工作模式是互斥的SPI和JTAG不能同时开启。模式02路UART适合纯串口需求。模式11路UART SPI主机 I2C主机。模式21路UART JTAG支持SWD。所以“一芯多用”并不是说同一时刻SPI和JTAG同时工作而是同一颗芯片通过切换模式分别覆盖两种场景。切换模式需要Windows下用WCH的配置工具CH347SW改配置改完重新插拔USB设备生效。这个限制在实际使用中影响不大因为很少有人会同时要求SWD调试和SPI Flash烧录并行。真正需要注意的是每次切换模式后OpenOCD或flashrom里对应的设备访问方式是不同的驱动层面也要确保设备没有被系统串口驱动占用。2. 模式切换和硬件接线上手CH347F之前必须清楚的细节2.1 拿到手先配模式市面上的CH347F转接板通常会引出两组排针一组标着TXD/RXD另一组标着SCK/SDI/SDO/CS0或者TCK/TMS/TDI/TDO。具体丝印取决于板子设计但核心逻辑是这些引脚在不同模式下复用了。我拿到一块新板子第一步永远是确认它当前处于什么模式。在Windows下插上设备打开设备管理器看识别出的端口和设备如果只有一个串口设备和几个未知设备大概率是模式1或模式2。用CH347SW工具可以直观看到当前模式并切换。这里有一个细节SPI Flash烧录需要模式1SWD调试需要模式2。切换模式不只是改配置还会影响USB设备描述符所以切换后需要重新插拔并且如果之前用Zadig换过驱动可能还要再确认一次设备驱动是否匹配。2.2 SWD调试接线表模式2JTAG下把CH347F的TCK和TMS接到STM32的SWCLK和SWDIO即可。我一般在STM32最小系统板上留一个4Pin排针3V3、SWDIO、SWCLK、GND方便直接怼。CH347F引脚STM32目标引脚说明TCKSWCLKSWD时钟线TMSSWDIOSWD数据线GNDGND必须共地TDI/TDO可不接纯SWD调试时不参与NRST可选接复位引脚防止芯片进入低功耗导致连接失败这里有个我踩过的坑一开始只接了SWCLK、SWDIO和GND连接ST-Link没问题但换CH347F后偶尔连接失败。后来查了OpenOCD的文档和社区讨论发现加上NRST线配置reset_config srst_only在芯片处于低功耗状态或者调试口被软件关闭时能提高连接成功率。从那以后我统一留4PinNRST虽然大部分时候NRST用不上但关键时刻能救命。2.3 SPI Flash直连接线表切到模式1UARTSPII2C后CH347F的SPI主机接口就可以直连SPI NOR Flash了。常见Flash是SOP8封装引脚包括CS#、CLK、DIMOSI、DOMISO、VCC、GND、WP#、HOLD#。CH347F引脚Flash芯片引脚说明CS0CS#片选低电平有效SCKCLKSPI时钟SDIDI / MOSI主机输出到FlashSDODO / MISOFlash输出到主机3V3VCC供电脚必须共用3.3VGNDGND共地-WP#必须拉高到3.3V-HOLD#必须拉高到3.3V最后两行是很多人忽略的重点。WP#写保护如果拉低Flash的写操作会被禁止HOLD#保持如果悬空或电平异常Flash会暂停SPI通信。我第一次接线时偷懒没管这两个脚结果flashrom能识别芯片型号但一写入就报错。把WP#和HOLD#都接到3.3V之后问题立刻消失。2.4 电压匹配与供电注意事项CH347F的IO电平和供电都是3.3V目标Flash或MCU也必须是3.3V逻辑电平否则要加电平转换。很多STM32核心板的调试排针上标了3V3但那个3V3是板子自己产生的不能直接当成CH347F的供电来源。CH347F转接板必须通过USB供电目标板单独供电两边只要共地就行。我见过有人为了省事用目标板的3V3去给CH347F转接板供电结果CH347F工作不稳定SWD连接时好时坏。正确做法是各供各的只连GND。另外SPI Flash直连时Flash的3.3V电源最好能提供稳定供电。用烧录夹子夹住板载Flash时目标板如果还在上电状态建议统一用目标板的电源如果目标板没上电可以让CH347F转接板的3.3V给Flash供一点电压但要注意电流不大只适合一颗Flash空载的情况。如果板上有其他负载老老实实给目标板上电。3. OpenOCD CH347FSTM32的SWD调试完整配置与排错3.1 安装与驱动准备OpenOCD对CH347的支持依赖libusb所以第一件事是确保操作系统能通过libusb访问CH347设备。Linux下比较简单安装OpenOCD后写一条udev规则让普通用户也能访问设备即可。我习惯在/etc/udev/rules.d/下新建一个50-ch347.rules内容类似SUBSYSTEMusb, ATTR{idVendor}1a86, ATTR{idProduct}55db, MODE0664, GROUPplugdevWCH的VID是1a86PID视设备和模式而定。如果你不确定PID可以在插上设备后用lsusb查看。写入udev规则后执行sudo udevadm control --reload-rules并重新插拔设备。Windows下稍微麻烦一点。默认CH347的驱动是WCH官方驱动OpenOCD的libusb版本不一定能直接访问。解决办法是用Zadig把设备的驱动替换为WinUSB。注意替换驱动后串口功能可能受影响所以如果你还需要用CH347F当串口用建议先确认自己是否接受这个取舍。我个人的做法是准备两套驱动方案日常当串口用时用官方驱动需要OpenOCD/flashrom时先用Zadig切到WinUSB备用。3.2 openocd.cfg配置与启动OpenOCD的CH347支持在配置里体现为# ch347-swd.cfg adapter driver ch347 adapter speed 1000 transport select swd如果你下载的是OpenOCD较老版本可能没有ch347这个driver建议直接用WCH官方维护的OpenOCD分支或者升级到主线较新版本。配置文件的路径和语法各版本略有差异以实际报错为准。启动命令我习惯写成openocd -f interface/ch347-swd.cfg -f target/stm32f4x.cfg对于不同型号的STM32替换target目录下对应的cfg文件就行比如F1系列用stm32f1x.cfgF4系列用stm32f4x.cfgH7系列用stm32h7x.cfg。启动后OpenOCD会监听4444端口用telnet连上去就能操作telnet localhost 44443.3 烧录、调试、校验一条龙我最常用的OpenOCD命令是直接一条龙烧录并复位运行openocd -f interface/ch347-swd.cfg -f target/stm32f4x.cfg -c program build/firmware.elf verify reset exit这条命令会打开OpenOCD连接目标芯片把ELF文件烧进Flash校验然后复位运行最后退出。适合日常开发完快速下载固件。如果是交互式调试常见操作包括reset halt flash write_image erase build/firmware.elf verify_image build/firmware.elf reset run当OpenOCD正确连接上芯片后会看到类似Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints的输出看到这个就稳了。这里值得一提的是OpenOCD的verify不是可选的。烧录完不校验你永远不知道是不是因为Flash写入异常导致程序行为诡异。我在量产小批量的时候程序烧录完成后一定会跑一遍verify确保芯片里的内容和bin文件完全一致。3.4 SWD连接失败的排查链路SWD连接失败是最常见的问题尤其第一次用CH347F时。我整理了一个自己的排查顺序先确认OpenOCD报错信息里有没有提到设备。如果找不到设备八成是驱动问题lsusb或Windows设备管理器确认再检查udev规则。确认接线。SWCLK接TCKSWDIO接TMSGND必须共地。这个顺序我反复强调是因为真有人把SWDIO接到了TDO上导致无法识别。降速。把adapter speed从1000降到100很多接触不良、杜邦线过长、干扰严重的问题降速后就能连上。SWD不是越快越好稳定才是第一位。检查复位引脚。如果目标芯片已经处于低功耗模式或者SWD引脚被软件复用连接不上时尝试接NRST并配置reset_config srst_only或者用OpenOCD的-c reset_config srst_only启动。终极方案断开目标板电源按住复位键不放再启动OpenOCD等到连接信息出现时松开复位键。这个“连接瞬间复位”的技巧能绕开很多初始化逻辑。还有个情况是STM32开了读保护RDP level 1OpenOCD会报target not halted之类的错误。这时候只能先擦除选项字节来解除保护代价是芯片Flash内容全部清空。如果不想丢数据就得走SPI Flash通道去备份这正是文章第五部分要讲的内容。4. flashrom CH347FSPI Flash的备份、擦除与烧录实战4.1 flashrom怎么识别CH347flashrom在1.3版本之后加入了对CH347 SPI的原生支持programmer名字叫ch347_spi。在Linux下直接执行flashrom -p ch347_spi:busspi这条命令会尝试枚举连接在CH347F上的SPI Flash并识别型号。如果设备访问有问题flashrom会提示打不开设备这时同样需要检查驱动和权限。在Windows下如果之前用Zadig把驱动换成了WinUSBflashrom的Windows版本也能直接访问。识别成功后flashrom会打印出类似以下的内容Found Winbond flash chip W25Q128 (16384 kB, SPI) on ch347_spi.如果这里什么都没识别到先别急着换芯片检查硬件接线和供电90%的情况是接线问题而不是芯片坏了。4.2 备份固件流程拿到一块新板子第一件事永远是备份不是烧写。谁知道板上固件是什么版本、有没有加密、分区表长什么样只有先把原始固件完整抓下来后面怎么折腾心里都有底。备份命令flashrom -p ch347_spi:busspi -c W25Q128 -r backup.bin如果flashrom没有自动识别型号就手动用-c指定芯片型号。芯片型号一般在芯片表面能看到丝印比如W25Q128、MX25L25645G、GD25Q64之类。不确定的话可以把检测失败的信息发到搜索引擎大多数都能找到对应的颗料型号。关于备份速度CH347F跑10MHz SPI时钟时读16MB的Flash大约30秒以内就能完成相比CH341A动辄几分钟的体验谁用谁知道。4.3 擦除、写入与校验SPI NOR Flash的特性是只能把1写成0所以写入前必须先擦除把整片变成0xFF。flashrom的写入命令会自动执行擦除步骤也可以手动先擦除再写入。完整流程# 擦除整片 flashrom -p ch347_spi:busspi -c W25Q128 -E # 写入新固件 flashrom -p ch347_spi:busspi -c W25Q128 -w new_firmware.bin # 校验 flashrom -p ch347_spi:busspi -c W25Q128 -v new_firmware.bin写入时flashrom会自动做整片擦除再编程所以大部分人习惯直接-w一把梭。但如果你只想更新部分内容而不是整片flashrom也支持--ifd这类按区域操作的参数不过日常用整片-w最省心。写入完成后一定要做校验。flashrom的-v会把Flash里的内容和bin文件逐字节对比。我习惯在写完之后再多跑一次-v确认虽然flashrom写完后默认也会校验但这个额外的确认能挡住一部分因为供电不稳导致写入偶发失败的情况。4.4 夹子直连与烧录座的选择SPI Flash烧录有两种主流接线方式用SOP8烧录夹直接夹住板载芯片或者把芯片拆下来放到烧录座里。夹子直连的好处是不用动焊适合快速备份、修改后烧回。缺点是夹子接触电阻大、容易受干扰尤其是目标板上的其它电路会拉低信号质量。用夹子时SPI频率建议从默认值往低调我一般设置成不超过3MHz。另外夹子直接夹在目标板上时一定要确保目标板没有上电或者整个系统的3.3V电平一致否则可能同时点亮目标板上其它器件导致异常电流。拆下来放烧录座是最稳的方式适合批量烧录和芯片级别的维修。SOP8烧录座十几块钱一个买CH347F转接板时老板通常也卖配套的烧录座。对于板载Flash是SOP8封装的场景强烈推荐多备几个烧录座省下的调试时间绝对值得。4.5 常见识别失败原因遇到flashrom识别不了芯片我按以下顺序排查供电不足Flash没上电或供电不稳用万用表量一下VCC引脚对地电压是不是3.3V。WP#/HOLD#悬空这两个引脚必须接高电平我前面提过这里再强调一次。CS片选选错CH347F可能不止一个片选引脚确认你接的是CS0还是接到了其他GPIO/CS脚上。接线太长SPI频率高的时候杜邦线太长会出毛刺尝试缩短线缆或降低频率。芯片本身防护熔断部分Flash支持软件保护或硬件保护需要用flashrom先解除保护比如-p ch347_spi:busspi --wp-disable。5. 组合拳SWD连不上时用SPI Flash通道救砖5.1 当SWD连接彻底失败STM32开发中有一个让人头疼的场景程序里把SWD调试引脚复用成了GPIO或者开了读保护导致OpenOCD怎么也连不上。这时候如果板子上有一颗SPI NOR Flash存放着固件CH347F的第二项能力就能派上大用场。我以前遇到一个情况客户板子是STM32F103 W25Q64外置Flash的组合固件的多级引导逻辑全在外置Flash里。我在调试时不小心把内部Flash里的bootloader刷坏了芯片复位后直接跑飞SWD口也连不上。如果用ST-Link的“Connect under reset”还能抢救一下但当时更省事的做法是把CH347F切到模式1直接通过SPI通道把外置Flash整个备份下来然后重新写入恢复固件。这个场景的价值在于SWD是芯片内部的调试口它失效的原因很多SPI Flash则是一个普通的外设芯片只要物理线路还在总能用编程器接管。5.2 从Flash镜像里找原因通过flashrom把Flash完整备份出来后下一步是分析这个镜像。有几种常见情况固件加密芯片固件整个是密文没有密钥就看不出内容只能整片原样写回。分区表可读可以用binwalk或hexdump先看镜像里有没有明文信息比如文件系统、字符串、UBoot版本号。校验区和配置区异常有的板子会在Flash特定位置存校验和或配置字修改任意字节都会导致启动失败。我自己的习惯是备份完先做三件事md5sum backup.bin binwalk backup.bin hexdump -C backup.bin | head -100把原始镜像的MD5保存好留作后续恢复时的对照基准。binwalk看一眼有没有明显的大区块结构比如文件系统、压缩包能快速判断这个Flash里存的是什么类型的数据。5.3 恢复操作与注意事项确认了Flash里的内容就是完整固件后恢复操作就变成了最简单的写入flashrom -p ch347_spi:busspi -c W25Q64 -w backup.bin写入后重新插到目标板上电。如果板子还是起不来就要往回推固件确实在Flash里、芯片供电正常、启动引脚配置正确、Flash和MCU之间的片选和中断引脚没被改动过。这里最容易忽略的是启动引脚。STM32的BOOT0和BOOT1引脚决定芯片从哪里启动如果从系统存储器或SRAM启动代码不会执行Flash里的固件你会误以为Flash内容有问题。所以救砖恢复后先确认BOOT0是不是拉低状态从主Flash启动。另一个注意事项是写回前把Flash的写保护关掉。部分Flash出厂时带保护直接写入会报错。flashrom识别后如果显示有保护先执行--wp-disable关掉保护再写。6. 踩坑记录与常备工作流6.1 Windows下驱动冲突CH347F在Windows下算是体验最好也是最难受的体验好是因为有官方图形化配置工具切换模式方便难受是因为官方驱动会独占设备访问权OpenOCD和flashrom的libusb后端访问不了默认驱动下的设备。我踩过一次很无语的坑在Windows下用Zadig把CH347F换成WinUSB驱动flashrom识别正常了结果CH347SW配置工具再打开时提示找不到设备。后来才反应过来官方配置工具也需要官方驱动才能访问设备。所以我现在的做法是需要切模式时先通过设备管理器把驱动换回官方驱动。模式切好之后再用Zadig切到WinUSB然后开flashrom或OpenOCD。这个来回切换确实麻烦但一旦习惯这个流程Windows下也能顺畅使用。Linux下就没有这些破事装好udev规则直接开干。6.2 杜邦线和高速时钟的博弈CH347F的SPI和SWD速率上限都不低但实际能用多高取决于你的连接质量。杜邦线面包板这种玩法跑个1MHz、2MHz轻轻松松但往上到10MHz以上就开始看人品了。我有一次用20cm杜邦线去夹一个路由器板子的SPI Flashflashrom识别芯片时好时坏后来把杜邦线换成10cm以内的短粗线问题立刻没了。后来我养成一个习惯所有接CH347F的线全部控制在15cm以内并且SWCLK/SWDIO或者SCK/SDI/SDO尽量用双绞线或就近并行减少信号环路面积。真正的工程板上建议直接用排线或者飞线焊接别用长杜邦线。6.3 我现在的固定工具清单经过这半年多的实际使用我形成了固定的工具链组合CH347F转接板一块常驻工作台。三种连接线材SWD调试用4Pin3V3/SWDIO/SWCLK/GND杜邦线组SPI Flash用SOP8测试夹短杜邦线另有SOP8烧录座用于拆芯片烧录。软件栈OpenOCDWCH分支或主线新版本负责SWDflashrom负责SPI Flash串口调试用minicom或PuTTYCH347F模式0或模式1里的UART顺手也能顶上。这套组合的好处是不管是给STM32下载固件、现场调试一个跑飞了的板卡还是从工控设备里备份固件、给路由器换Flash我都不用翻箱倒柜找工具。一根USB线插上CH347F剩下的就是敲命令的事。最后分享一个小技巧所有重要芯片的原始固件我都会在拿到板子的第一时间用CH347F备份出来文件名加上日期和板卡编号。这些备份平时看着占地方真到救砖的时候它们比任何调试器都值钱。
