0x00 序言:各种 IDE 和调试器
如标题所言,本文的主要任务是自行制作一个 DAPLink(事实上是「采用 CMSIS-DAP 协议的调试器」,但简中互联网已经将所有 CMSIS-DAP 兼容的调试器都称为 DAPLink 了,本文亦从之),用于调试 Cortex-M 单片机。笔者主要使用 RP2040 和 STM32 这两种 MCU,这两个月来体验了各种开发环境,因此在谈论硬件之前,先简单评论几句这些 IDE。
首先说说 PlatformIO。这是笔者当初使用 arduino 框架时,使用的 IDE。主要优点在于开箱即用,在 VS Code 中即可完成编码、下载、调试和串口监控。但问题也比较明显:
- 黑盒构建系统。它使用了私有的 build 方式(尽管底层是用 CMake 实现的),用户需要将库的
.h
和.c/.cpp
文件放在 PlatformIO 指定的目录。当然,这是它的特性,使得 arduino 用户可以无缝迁移到 PlatformIO;但对于笔者这种主业偏软件的开发者来说,一个黑盒构建系统显然是难以接受的。 - GUI 鲁棒性堪忧,宛如本科生的大作业。叠加上其黑盒特性,有时难以定位问题。
- PlatformIO 会与其他插件互相干扰,造成一些麻烦。例如,PlatformIO 不兼容 clangd,在使用 PlatformIO 时需要将 clangd 换成默认的 C/C++ LSP server。这些都是琐碎的小问题,虽然能逐一解决,但虚耗精力。
笔者过去两个月主要使用 CLion 编写 RP2040 程序。它也是开箱即用的,直接支持 openocd 和 pyocd 调试,不过串口监控则需另找软件实现(如 MobaXterm)。特点:
- 基于 CMake 的构建系统。这是非常现代化的设计,使得软件工程师可以轻易参与到硬件开发中。笔者绝不愿意把时间浪费在学习 Keil 的私有构建系统上,但愿意学几天 CMake,因为它是开放且通用的。CLion 可以完美适配 pico sdk,且能将 STM32CubeMX 生成的项目转化为 CMake 项目,因此也算是完美支持 STM32 HAL。
- 基于 clangd 的语言引擎,见 Jetbrains 文档。clangd 是目前最优秀的 C/C++ LSP server。
另外,笔者使用 SEGGER Embedded Studio 开发 STM32。特点:
- 黑盒构建系统。能很好地支持 STM32CubeMX 项目(见官方 wiki),但对 RP2040 不甚友好(见树莓派论坛)。
- 开箱即用,而且有大量优化,例如将 printf 默认实现为 semihosting,一方面不再需要连接串口,另一方面可以节省 MCU 的 ROM 空间。
- 与 J-Link 密切配合,原生支持 RTT 技术。
另外,这些 IDE 针对各种调试器的支持矩阵如下。其中「gdb server」表示用 openocd 或 pyocd 运行 gdb server(监听 TCP 端口),然后 IDE 作为 gdb client 连入。「原生支持」表示 IDE 厂商为调试器提供了专门适配。
PlatformIO | CLion | SEGGER | STM32CubeIDE | |
---|---|---|---|---|
DAPLink | gdb server | gdb server | 原生支持 | gdb server |
STLINK | gdb server | gdb server | 原生支持 | 原生支持 |
J-Link | gdb server | gdb server | 原生支持 | 原生支持 |
SEGGER Embedded Studio 是原生支持 CMSIS-DAP 的,但需要将 Target Connection 设为 J-Link。这十分反直觉,笔者是从一篇论坛讨论中得知的。
下面,我们粗略评测各种调试器在各种烧录软件下的表现。笔者手头有 STLINK-V3SET、J-Link edu mini、树莓派 debugprobe(支持 DAP 协议,复用了 CMSIS-DAP 项目代码,见本站的源码阅读文章),以及各种国产 DAPLink。合影:
我们先来测试一下它们的下载速度。测试方法是用 SWD 协议下载 1MB 固件到 STM32H743VIT6,记录下载耗时,同时使用逻辑分析仪观察波形,记录 SWCLK 频率。
一般情况下,SEGGER IDE 和 pyocd 不是全量烧录,而是只烧录 rom 中被更改的部分。因此,我们每次下载前都要完全擦除固件,以免影响测试结果。相关指令如下:
# 擦除固件
openocd -f interface/jlink.cfg -c "transport select swd" -f target/stm32h7x.cfg -c "init" -c "halt" -c "wait_halt" -c "stm32h7x mass_erase 0"
# openocd 下载 (5MHz)
openocd -f interface/jlink.cfg -c "transport select swd" -f target/stm32h7x.cfg -c "adapter speed 5000" -c "init" -c "halt" -c "flash write_image erase h7-download-test.elf 0x00000000" -c "reset" -c "shutdown"
# pyocd 下载 (5MHz)
pyocd flash -t stm32h743vitx -f 5mhz .\h7-download-test.elf
测试结果如下(擦除和下载过程都计入耗时):
调试器 | 软件 | 擦除+下载速度 | 设定频率 | SWCLK 实际最高频率 | 日志 |
---|---|---|---|---|---|
J-Link edu mini | SEGGER | 82.76 KB/s | 4 MHz | 3.01 MHz | Total: 13.042s (Prepare: 0.139s, Compare: 0.414s, Erase: 6.594s, Program: 5.432s, Verify: 0.362s, Restore: 0.099s) Program speed: 188 KB/s |
J-Link edu mini | openocd | 61.033 KB/s | 5 MHz | 3.01 MHz | wrote 1019264 bytes from file h7-download-test.elf in 16.308746s (61.033 KiB/s) |
J-Link edu mini | pyocd | 10.89 KB/s | 5 MHz | 3.01 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 10.89 kB/s |
STLINK-V3SET | SEGGER | 61.44 KB/s | auto | 34.48 MHz | erase 149.1 KB/s, download 103.1 KB/s |
STLINK-V3SET | SEGGER | 61.06 KB/s | 100 MHz | 34.48 MHz | real freq applied is 24000 KHz, erase 147.3 KB/s, download 103.1 KB/s |
STLINK-V3SET | openocd | 66.486 KB/s | 30 MHz | 34.48 MHz | wrote 1019264 bytes from file h7-download-test.elf in 14.971294s (66.486 KiB/s) |
STLINK-V3SET | pyocd | 32.05 KB/s | 30 MHz | 34.48 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 32.05 kB/s |
树莓派 debugprobe | SEGGER | 79.55 KB/s | 20 MHz | 15.87 MHz | Total: 13.479s (Prepare: 0.142s, Compare: 0.371s, Erase: 6.605s, Program: 5.907s, Verify: 0.345s, Restore: 0.107s) Program speed: 173 KB/s |
树莓派 debugprobe | SEGGER | 71.08 KB/s | 10 MHz | 9.01 MHz | Total: 15.074s (Prepare: 0.174s, Compare: 0.421s, Erase: 6.618s, Program: 7.385s, Verify: 0.347s, Restore: 0.127s) Program speed: 138 KB/s |
树莓派 debugprobe | SEGGER | 45.34 KB/s | auto(2MHz) | 1.96 MHz | Total: 23.402s (Prepare: 0.363s, Compare: 0.447s, Erase: 6.625s, Program: 15.327s, Verify: 0.353s, Restore: 0.285s) Program speed: 66 KB/s |
树莓派 debugprobe | openocd | 66.144 KB/s | 20 MHz | 15.87 MHz | wrote 1019264 bytes from file h7-download-test.elf in 15.048535s (66.144 KiB/s) |
树莓派 debugprobe | pyocd | 35.62 KB/s | 20 MHz | 15.87 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 35.62 kB/s |
创芯工坊 PWLINK2 Lite | SEGGER | 91.98 KB/s | 20 MHz | 10.99 MHz | Total: 11.697s (Prepare: 0.092s, Compare: 0.366s, Erase: 6.618s, Program: 4.203s, Verify: 0.344s, Restore: 0.072s) Program speed: 243 KB/s |
创芯工坊 PWLINK2 Lite | openocd | 66.163 KB/s | 20 MHz | 10.87 MHz | wrote 1019264 bytes from file h7-download-test.elf in 15.044182s (66.163 KiB/s) |
创芯工坊 PWLINK2 Lite | pyocd | 37.09 KB/s | 20 MHz | 10.87 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 37.09 kB/s |
国产 DAPLink (C6T6A) | SEGGER | 17.93 KB/s | 20 MHz | 1.47 MHz | Total: 58.742s (Prepare: 1.442s, Compare: 0.468s, Erase: 6.716s, Program: 48.785s, Verify: 0.426s, Restore: 0.903s) Program speed: 21 KB/s |
国产 DAPLink (C6T6A) | openocd | 34.459 KB/s | 20 MHz | 1.09 MHz | wrote 1019264 bytes from file h7-download-test.elf in 28.886044s (34.459 KiB/s) |
国产 DAPLink (C6T6A) | pyocd | 17.41 KB/s | 20 MHz | 1.09 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 17.41 kB/s |
国产 DAPLink (C8T6) | SEGGER | 17.93 KB/s | 20 MHz | 12.05 MHz | Total: 58.381s (Prepare: 1.088s, Compare: 0.468s, Erase: 6.727s, Program: 48.770s, Verify: 0.427s, Restore: 0.898s) |
国产 DAPLink (C8T6) | openocd | 34.480 KB/s | 15 MHz | 5.59 MHz | wrote 1019264 bytes from file h7-download-test.elf in 28.868071s (34.480 KiB/s) |
国产 DAPLink (C8T6) | pyocd | 18.01 KB/s | 15 MHz | 5.62 MHz | Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 18.01 kB/s |
可以看出,openocd 烧写算法的速率上限就是 66 KB/s 左右,我们的 STLINK-V3SET、树莓派 debugprobe 和创芯工坊 PWLINK2 Lite 都达到了这个上限。其他的国产 DAPLink 速率较慢,它们的 MCU 是 STM32F103C8 甚至 C6,应该是 CMSIS-DAP 方案。
STLINK-V3SET 内置的 MCU 是 STM32F723IEK6,主频 216 MHz,最后跑出的结果跟 RP2040 差不多,显得有点浪费性能(笑)。不过 V3SET 多出了 jtag、swim、can bus、spi 等接口。
另外,我们也可以看到,整体的下载速度与最高 SWCLK 频率几乎无关(事实上,在下载过程中,SWCLK 频率也很不稳定,例如 STLINK-V3SET 在一些时候能达到 34.48 MHz,在更多时候仅为 ~10 MHz )。因此,下载速率的瓶颈显然不在 swd 频率上,而是在其他地方。
有一篇帖子值得一读:让 CMSIS-DAP DAPLink 功能和性能上升到新高度。此文总结了限制下载速率的因素。
0x01 选型
我们已经考察了各种调试器的性能,现在该为自己的项目选型了。在 J-Link、STLINK、CMSIS-DAP 三种协议中,只有 CMSIS-DAP 是开放的,生态良好,且被各种 IDE 支持,所以我们选择自制一个 CMSIS-DAP 协议的调试器。
协议确定之后,该考虑用何种硬件实现。目前值得考虑的实现方案如下:
- STM32F103xB。该 MCU 获 DAPLink 项目原生支持。
- RP2040。使用树莓派 debugprobe 项目,连固件都无需自行编译。此前已经在学习笔记中实践过这一方案。
- CH552。这是增强型 8051 单片机,优势在于免晶振,且价格低廉。项目见 posystorage/CH55x_HS_DAP-Link-v2。
- CH32V203/CH32V305。它们是 RISC-V 内核的 MCU,前者免晶振,后者支持 usb hs(但其他方案的速度瓶颈应该不在 usb fs 上)。项目见 XIVN1987/DAPLink。
从稳定性的角度考虑,DAPLink 和 debugprobe 项目最合适,它们接受了社区开发者的广泛验证。debugprobe 对笔者而言是最简单的方案,且在上文的测试中表现优秀。另一方面,沁恒方案也很有吸引力:让我们考虑 J-Link OB 或者 STM32 Nucleo 系列开发板的板载 STLINK,假如只需要一片 CH552 和一些阻容就能实现它们的功能,让开发板自带 SWD 和串口,那何乐而不为呢?功耗、成本、占用面积都未增大多少,而我们只需要一根 usb 线连接电脑,无需掏出调试器和杜邦线了。
因此,笔者打算实践上述所有方案,把坑都踩一遍。现在来设计一块 pcb,由 type c 线连接到电脑,用 CH334P 提供 usb hub 功能,连接到四块待测试的 MCU。这些 MCU 需要提供 swd 功能和串口功能,通过一个 8 pin 接口和一个 stdc14 接口引出。
原理图:
T_VCP_RX
,即 target MCU 的 RX、调试器的 TX。上面的原理图上搞反了 13、14 号引脚。接下来利用 KiCad 的层次原理图,设计四种调试器的电路。RP2040 方案:
DAPLink STM32F103CBT6 方案:
CH552 方案:
其实可以考虑省略硬件 RST 信号,因为我们一般利用 swd 来复位目标 MCU。
CH32V203 方案:
0x02 实验:CH552 方案
焊好 pcb,我们开始测试这些方案。首先是 CH552,新芯片上电之后自动进入 usb 下载模式(尽管文档中称下载时必须提供 5V 供电,但笔者实践上发现初次下载时 3V3 供电亦可工作)。用沁恒的烧录工具刷入 CH55x_DAPLink_3V3_IO_16M.hex 固件之后,可以识别到 CMSIS-DAP 调试器:
连到目标芯片,擦除 flash。过程中出了一点问题:
再重复运行擦除指令,这次成功执行。用 SEGGER 能连接上 CMSIS-DAP 设备:
Connecting 'J-Link' using 'USB'
Loaded C:/Program Files/SEGGER/SEGGER Embedded Studio 8.14a/bin/JLink_x64.dll
Firmware Version: DAPLink CMSIS-DAP
DLL Version: 7.98a
Hardware Version: V2.00
Target Voltage: 3.300
Device "STM32H743VI" selected.
ConfigTargetSettings() start
ConfigTargetSettings() end - Took 10us
InitTarget() start
SWD selected. Executing JTAG -> SWD switching sequence.
DAP initialized successfully.
InitTarget() end - Took 10.8ms
Found SW-DP with ID 0x6BA02477
DPv0 detected
CoreSight SoC-400 or earlier
Scanning AP map to find all available APs
AP[3]: Stopped AP scan as end of AP map has been reached
AP[0]: AHB-AP (IDR: 0x84770001)
AP[1]: AHB-AP (IDR: 0x84770001)
AP[2]: APB-AP (IDR: 0x54770002)
Iterating through AP map to find AHB-AP to use
AP[0]: Core found
AP[0]: AHB-AP ROM base: 0xE00FE000
CPUID register: 0x411FC271. Implementer code: 0x41 (ARM)
Cache: L1 I/D-cache present
Found Cortex-M7 r1p1, Little endian.
FPUnit: 8 code (BP) slots and 0 literal slots
CoreSight components:
ROMTbl[0] @ E00FE000
[0][0]: E00FF000 CID B105100D PID 000BB4C7 ROM Table
ROMTbl[1] @ E00FF000
[1][0]: E000E000 CID B105E00D PID 000BB00C SCS-M7
[1][1]: E0001000 CID B105E00D PID 000BB002 DWT
[1][2]: E0002000 CID B105E00D PID 000BB00E FPB-M7
[1][3]: E0000000 CID B105E00D PID 000BB001 ITM
[0][1]: E0041000 CID B105900D PID 001BB975 ETM-M7
[0][2]: E0043000 CID B105900D PID 004BB906 CTI
I-Cache L1: 16 KB, 256 Sets, 32 Bytes/Line, 2-Way
D-Cache L1: 16 KB, 128 Sets, 32 Bytes/Line, 4-Way
Current Speed: 2000 kHz
ConfigTargetSettings() start
ConfigTargetSettings() end - Took 10us
InitTarget() start
但是 SEGGER 下载失败:
Preparing target for download
Executing Reset script TargetInterface.resetAndStop()
Reset: Halt core after reset via DEMCR.VC_CORERESET.
Reset: Reset device via AIRCR.SYSRESETREQ.
Setting Startup Completion Breakpoint
Startup completion breakpoint set at __startup_complete
Downloading 'h7-download-test.elf' to STM32H743VI
Programming 995.3 KB of addresses 08000000 - 080f8d6d
Failed to preserve target RAM @ 0x24000000-0x2407FFFF.
Failed to prepare for programming.
Communication timed out: Requested 4 bytes, received 0 bytes !
Download failed
openocd 在下载中途也提示失败:
openocd -f interface/cmsis-dap.cfg -c "transport select swd" -f target/stm32h7x.cfg -c "adapter speed 5000" -c "init" -c "halt" -c "flash write_image erase h7-download-test.elf 0x00000000" -c "reset" -c "shutdown"
逻辑分析仪观察到 SWCLK 在大约 7s 后便不翻转:
考虑是否 CH552 无法提供 5 MHz 的 swd 频率。降频到 500 kHz,成功烧录:
openocd -f interface/cmsis-dap.cfg -c "transport select swd" -f target/stm32h7x.cfg -c "adapter speed 500" -c "init" -c "halt" -c "flash write_image erase h7-download-test.elf 0x00000000" -c "reset" -c "shutdown"
[stm32h7x.cpu0] halted due to debug-request, current mode: Handler HardFault
xPSR: 0x01000003 pc: 0x080001ca msp: 0x2407ffe0
Info : Device: STM32H74x/75x
Info : flash size probed value 2048k
Info : STM32H7 flash has dual banks
Info : Bank (0) size is 1024 kb, base address is 0x08000000
Info : Padding image section 0 at 0x080f8d6e with 18 bytes (bank write end alignment)
Warn : Adding extra erase range, 0x080f8d80 .. 0x080fffff
auto erase enabled
wrote 1019264 bytes from file h7-download-test.elf in 28.301851s (35.170 KiB/s)
逻辑分析仪观察到 SWCLK 的实际频率是 1.33 MHz 左右,我们在 openocd 中设置的 adapter speed 参数似乎并不影响 SWCLK 频率。
再次尝试使用 SEGGER 烧录,频率设为 100 kHz,仍然失败。pyocd 倒是可以正常下载:
pyocd flash -t stm32h743vitx -f 5mhz .\h7-download-test.elf
0001143 W Board ID 3V3- is not recognized [mbed_board]
0002084 I Loading C:\Users\blue\Desktop\work\0730\h7-download-test\h7-download-test\Debug Internal\h7-download-test.elf [load_cmd]
[==================================================] 100%
0043176 I Erased 1048576 bytes (8 sectors), programmed 1019904 bytes (996 pages), skipped 0 bytes (0 pages) at 24.24 kB/s [loader]
CH552 方案总结:
调试器 | 软件 | 擦除+下载速度 | 设定频率 | SWCLK 实际最高频率 | 备注 |
---|---|---|---|---|---|
CH552 | SEGGER | - | - | - | 无法使用 SEGGER 下载 |
CH552 | openocd | 35.170 KB/s | 500 kHz | 1.79 MHz | 无视用户指定的 swd 频率,但太高会导致失败 |
CH552 | pyocd | 24.24 KB/s | 5 MHz | 1.79 MHz | 无视用户指定的 swd 频率 |
实验发现,CH552 方案鲁棒性很差,出错之后,经常需要手动复位 CH552 或目标 MCU,才能继续工作。另外,在烧录 flash 之后,openocd 擦除指令第一次执行时必定失败,第二次执行则可成功。
结论:posystorage/CH55x_HS_DAP-Link-v2 方案不实用。另外,粗看了一眼代码,复用了 CMSIS-DAP 项目的 DAP.c
,使用沁恒提供的 usb 协议栈。代码质量非常之差,似乎作者没有受过软件工程训练。
0x03 实验:CH32V203 方案
CH32V203C8T6 是沁恒推出的 32 位 RISC-V MCU,拥有 64 kB flash 和 20 kB RAM,最高主频 144 MHz,远高于 MCS-51 内核的 CH552,且价格也相当低廉(市价 2.3 CNY;与之对比,8051 内核的 CH552G 为 1.59 CNY)。烧录 CH32V203 需要使用 WCH-Link。
我们使用 XIVN1987/DAPLink 项目,21ic 论坛有作者的帖子。这个项目需要用沁恒的 MounRiver IDE 编译,观察代码:
此项目复用了 CMSIS-DAP 的全部代码,使用沁恒的 usb hid 协议栈。代码质量高于前文提到的 CH552 项目。
烧录:
电脑识别到 CMSIS-DAP 和串口:
现在开始测试。SEGGER 下载正常:
Preparing target for download
Executing Reset script TargetInterface.resetAndStop()
Reset: Halt core after reset via DEMCR.VC_CORERESET.
Reset: Reset device via AIRCR.SYSRESETREQ.
Setting Startup Completion Breakpoint
Startup completion breakpoint set at __startup_complete
Downloading 'h7-download-test.elf' to STM32H743VI
Programming 995.3 KB of addresses 08000000 - 080f8d6d
J-Link: Flash download: Bank 0 @ 0x08000000: 1 range affected (1048576 bytes)
J-Link: Flash download: Total: 58.480s (Prepare: 1.080s, Compare: 0.472s, Erase: 6.721s, Program: 48.878s, Verify: 0.428s, Restore: 0.898s)
J-Link: Flash download: Program speed: 21 KB/s
Download successful
Memory map 'after startup completion point' is active
openocd 和 pyocd 也正常工作。测试结果:
调试器 | 软件 | 擦除+下载速度 | 设定频率 | SWCLK 实际最高频率 | 备注 |
---|---|---|---|---|---|
CH32V203 | SEGGER | 17.90 KB/s | 20 MHz | 7.14 MHz | Total: 58.480s (Prepare: 1.080s, Compare: 0.472s, Erase: 6.721s, Program: 48.878s, Verify: 0.428s, Restore: 0.898s) |
CH32V203 | SEGGER | 17.88 KB/s | 10 MHz | 5.88 MHz | Total: 58.607s (Prepare: 1.141s, Compare: 0.472s, Erase: 6.714s, Program: 48.947s, Verify: 0.432s, Restore: 0.898s) |
CH32V203 | SEGGER | 17.88 KB/s | 1 MHz | 0.90 MHz | Total: 58.613s (Prepare: 1.138s, Compare: 0.483s, Erase: 6.747s, Program: 48.910s, Verify: 0.429s, Restore: 0.903s) |
CH32V203 | openocd | 34.451 KB/s | 100 MHz | 16.67 MHz | |
CH32V203 | pyocd | 17.83 KB/s | 100 MHz | 16.67 MHz |
XIVN1987/DAPLink CH32V203 方案的测试结果与国产 DAPLink(STM32F103 方案)非常相似,体现为 SEGGER 下载速度 ~17.9 kB/s、openocd 下载速度 ~34.5 kB/s。可以反推国产 STM32 DAPLink 的代码来源极有可能与 XIVN1987 一致,为 CMSIS-DAP firmware 的完全移植,且应该均使用了 SW_DP.c
来控制 SWCLK 和 SWDIO。
结论:XIVN1987/DAPLink CH32V203 方案可用。代码质量没有问题,但下载速度略慢。它的 usb 通讯采用了 HID 而不是 winusb,因此 34.5 kB/s 可能已经达到了理论速度上限(usb 2.0 HID 最快每 1ms 传输 64 字节,所以理论上信道的通讯速率上限就是 64 kB/s)。编译产物,flash 占用 25068 B,RAM 占用 9064 B,可以在 CH32V203F6/CH32V203G6 上运行。
0x04 实验:RP2040 方案
现在来测试 RP2040 的 debugprobe 项目。笔者已经非常熟悉这份代码。烧录 v2.0.1 固件:
pyocd flash -t rp2040 -f 10mhz debugprobe_on_pico.elf
识别到 winusb 设备和串口设备:
由于产品版与 pico 版固件的差异仅在 LED 和 GPIO 分配上,所以预期我们的 RP2040 版 DAPLink 也能跑出与 debugprobe 产品一致的性能。测试结果如下:
调试器 | 软件 | 擦除+下载速度 | 设定频率 | SWCLK 实际最高频率 | 备注 |
---|---|---|---|---|---|
RP2040 | SEGGER | 80.19 KB/s | 20 MHz | 16.67 MHz | Total: 13.360s (Prepare: 0.121s, Compare: 0.377s, Erase: 6.602s, Program: 5.809s, Verify: 0.346s, Restore: 0.102s) |
RP2040 | openocd | 66.191 KB/s | 20 MHz | 16.67 MHz | |
RP2040 | pyocd | 36.38 KB/s | 20 MHz | 16.67 MHz |
性能完全符合预期。
结论:RP2040 方案可行,速度很快,且由于直接使用树莓派官方的 debugprobe 代码,稳定性有保证。缺点在于,若制作板载调试器,则比 CH32V203 方案多出 flash 和晶振这两个组件,额外占用 pcb 面积,且价格比 CH32V203 稍高(RP2040 市价 3.8 CNY,晶振 0.2 CNY,flash 1 CNY,加起来是 5 CNY)。我们 DIY 玩家虽然不太考虑价格,但如果大规模做产品,这个成本差距是有点大的。
0x05 实验:STM32F103CBT6 方案
按照 DAPLink 项目的指引,先烧入 bootloader:
pyocd flash -t stm32f103cb -f 10mhz stm32f103xb_bl_crc.hex
0001768 I Loading C:\Users\blue\Downloads\bootloaders-0257-c782a5ba\armcc\stm32f103xb_bl_crc.hex [load_cmd]
[==================================================] 100%
0004294 I Erased 34816 bytes (34 sectors), programmed 34816 bytes (34 pages), skipped 13312 bytes (13 pages) at 18.84 kB/s [loader]
然后电脑不识别 u 盘。怀疑 pcb 设计有问题(例如也许需要外置 1.5k D+ 上拉电阻;也许走线有问题)。折腾了几个小时也没解决,考虑到 CH32V 方案已经可用,决定暂且放弃 STM32F103 方案。
有兴趣的读者可以参考:https://oshwhub.com/yacter/stm32-daplink
0x06 实验总结
我们已经跑通了 CH552、CH32V203 和 RP2040 这三种方案。其中,CH552 方案存在重大缺陷,不能采用。详细对比 CH32V203 和 RP2040:
RP2040(debugprobe) | CH32V203(XIVN1987/DAPLink) | |
---|---|---|
usb 通讯方式 | winusb | HID |
SEGGER 烧录速率 | 80.19 KB/s | 17.90 KB/s |
openocd 烧录速率 | 66.19 KB/s | 34.45 KB/s |
pyocd 烧录速率 | 36.38 KB/s | 17.83 KB/s |
最小电路所需元件(电容除外) | RP2040 + W25Q16JVUXIQ + 晶振 | CH32V203G6U6 |
pcb 占用面积(单位为 $\text{mm}^2$) | 68.2(QFN56) + 10.7(USON8) + 13.6(SMD3225) = 92.5 | 27.0(QFN28) |
引出接口 | swd、串口 | swd、串口、jtag、reset |
首先讨论速率问题。虽然纸面上 17.83 KB/s 的速率非常慢,下载 128 KB 的固件需要 7.2 秒,但这是在最极端的情况下测出的——我们在测试前把 flash 清空了。事实上,如果不清空 flash,直接重复下载相同的固件,则 CH32V203 方案的 pyocd 下载速度从原先的 17.83 KB/s 上升到 44.48 KB/s,RP2040 方案从 36.38 KB/s 上升到 181.58 KB/s。考虑到真实开发过程中,一般都是在旧固件的基础上烧录新固件,故 CH32V203 方案的速度似乎也可以接受。
上表中的 pcb 占用面积是根据 KiCad 封装的 margin 层面积计算的。事实上,由于 RP2040 需要更多的布线空间,其面积劣势更加明显。表中采用了 W25Q16JVUXIQ 作为 flash,如果换成我们常用的 W25Q16JVSSIQ(SOIC-8 封装),面积还会进一步增大。
综上所述:RP2040 适合做独立调试器(如同 debugprobe 产品那样);而对于板载调试器,CH32V203 是更优选择。本文接下来设计两个项目,一个是板载 CH32V203 调试器的 Pi Pico 开发板,另一个是基于 RP2040 的独立调试器。
0x07 自带调试器的 Pico 开发板
现在设计一块 Pico 开发板,它仅通过一条 type c 线连接到电脑,便可以进行 swd 调试、使用串口,电脑还能访问 RP2040 的 usb。具体细节:
- 采用 CH334P 作为 usb hub,连接到 CH32V203G6U6 调试器和 RP2040。
尽管 CH334 的手册中说只有部分封装提供了免晶振能力,但根据沁恒人员在论坛上的说法,目前在售的 CH334 均可免晶振。笔者实验了一下,发现确实如此,烧录速率也未见降低。 - 板上要留下 CH32V203 的烧录接口。若采用 usb 烧录,则需要引出 BOOT0;若采用 swd 烧录,则需要引出 SWCLK 和 SWDIO。
- 引脚兼容树莓派官方 Pico 开发板。
原理图:
现在进行 layout 工作。从树莓派 Pico 手册中查到 Pico 外形:
由图可知,两侧排针之间距离为 17.78mm。我们不做半孔,所以比 Pico 略窄一些。最终效果:
0x08 自制 DAPLink
在本文的最后,我们来完成开头提出的目标——自制一个 DAPLink。这次仍然采用 RP2040 方案,提供 stdc14 和标准 10pin 调试端口。原理图:
layout:
成品如下图(右)所示。pcb 面积与 debugprobe 产品相近。
pyocd 正常识别: