从零手搓CMSIS-DAP调试器:裸板固件包实战指南

发布时间:2026/10/20 21:44:44
从零手搓CMSIS-DAP调试器:裸板固件包实战指南
简介这份资源是面向嵌入式开发者与硬件调试爱好者的CMSIS-DAP裸板实现方案基于ARM的CMSIS-DAP标准帮助使用者在无专用调试器的情况下通过USB接口对目标芯片进行JTAG或SWD调试。压缩包共164个文件约6.38MB以c与h源码、Keil工程文件、编译产物及原理图、PCB生产文件为主另含少量图片、PDF与说明文档覆盖从固件到硬件的完整链路。资源按“技小新”整理的第三版组织代码已整理至V1.4可直接烧录到STM32F103等微控制器配套原理图与Gerber文件便于自行打板验证。目前已有831人学习下载适合想低成本搭建调试环境、理解USB通信与SWD协议实现细节的工程师参考也能为自制调试器项目提供可复用的软硬件基础。1. 从零手搓 CMSIS-DAP 调试器一份裸板固件包能省下多少事手里只有一块带 USB 接口的 Cortex-M 开发板想把它变成能给别的板子下载、调试的 CMSIS-DAP 调试器最省事的路径不是从零写 USB 协议栈而是直接拿一份已经调通的裸板固件包烧进去。CMSIS_DAP_裸板.zip 就是干这个的它把 DAP 固件、USB 描述符、SWD/JTAG 时序、以及常见的 LED 状态指示都打包好了目标平台是常见的 STM32F103 一类带 USB 的 M3 芯片。你不需要装 Keil、不需要买现成的调试器只要有一根 USB 线和一个能烧录的工具半小时内就能让一块吃灰的板子变成能识别进 IDE 的调试探头。适合谁适合手头有多余开发板、想低成本搭一套离线调试环境的人也适合想搞清楚 CMSIS-DAP 到底怎么跑起来的嵌入式新手。2. 拆包与选型为什么是裸板固件而不是成品调试器2.1 裸板固件包到底装了什么拿到 CMSIS_DAP_裸板.zip 之后先别急着烧。解压出来通常能看到几个关键部分一个是预编译的 .bin 或 .hex 固件对应某款具体芯片的引脚映射一个是源码目录里面是 DAP 的 USB 描述符、HID 或 WinUSB 接口实现、SWD 时序驱动还有一个配置文件用来改 LED 引脚、USB VID/PID、以及目标芯片的复位方式。裸板的意思是不带板载调试芯片全靠主控自己跑 DAP 协议。常见做法是主控用 STM32F103C8T6USB 走 PA11/PA12SWD 输出走 PB13/PB14 这类组合固件包里一般已经把这些引脚写死了你只需要确认自己板子的接线和它一致。选型上要盯住三个参数主控型号、USB 接口类型、以及是否支持目标复位。主控型号决定你能不能直接烧预编译固件如果型号对不上就得改源码重新编译。USB 接口类型决定驱动安装方式HID 免驱但速度慢WinUSB 需要装驱动但吞吐高。目标复位引脚决定你能不能调试那些复位后立刻跑飞的板子。这三点在固件包的 README 或 config 文件里通常有说明没有的话就翻源码里的宏定义。2.2 为什么不用成品调试器成品调试器比如某宝上几十块的那种优点是即插即用缺点是固件黑匣子出了问题只能换。裸板固件的价值在于你能看到每一行 USB 请求怎么处理、SWD 时钟怎么分频、DAP 的包怎么组。对于要批量产测或者要集成到自动化流水线的场景裸板方案可以改 VID/PID 避免和别的设备冲突可以加自定义命令可以把调试口和串口复用在同一块板上。另一个现实原因是成本一块 STM32F103 最小系统板不到十块钱烧上固件就能当调试器用坏了直接换板子不心疼。2.3 烧录前的硬件确认清单动手之前先确认几件事能省掉后面一堆玄学问题。第一主控的 USB 引脚是不是直接引到 USB 座子中间有没有串电阻或者电平转换有些板子为了省事没焊 USB 座你得自己飞线。第二SWD 输出引脚有没有被板载外设占用比如 PB13/PB14 如果接了 SPI 屏就得改引脚定义。第三供电方式调试器本身要从 USB 取电目标板最好单独供电否则大电流目标板会把调试器拉挂。第四复位引脚如果你要调试的目标板复位电路是低电平复位固件里的复位输出极性要对应改。# 以常见的 st-flash 工具为例先确认芯片能被识别 st-flash --version # 读取芯片 ID确认连接正常 st-flash read out.bin 0x8000000 0x100 # 擦除并烧录裸板固件 st-flash erase st-flash write CMSIS_DAP.bin 0x8000000上面这段命令里read用来验证 SWD 链路通不通读出来的 0x100 字节如果全是 0xFF 或者全 0说明芯片没连上或者被读保护了。erase是整片擦除注意这会清掉原有程序。write的地址 0x8000000 是 STM32 的 Flash 起始地址如果你的芯片是别的系列地址可能不同。烧完之后拔插 USB系统应该能识别出一个 HID 设备或者一个未知设备具体看固件用的是哪种接口。3. 让电脑认出它USB 描述符与驱动安装的实操3.1 改 VID/PID 避免设备冲突裸板固件默认的 VID/PID 往往是开发板厂商的测试 ID直接插上可能和系统里已有的调试器冲突导致 IDE 认错设备。稳妥做法是改成一组不常见的组合比如 VID 用 0x1209开源项目常用PID 自己编一个。改的地方在源码的 usb_desc.c 或者 usb_config.h 里找到idVendor和idProduct两个宏改完重新编译。如果你不会编译也可以找固件包里附带的配置工具有些包会提供一个 Python 脚本直接改 bin 文件里的描述符偏移。// usb_desc.c 里设备描述符的关键字段 const uint8_t DeviceDescriptor[] { 0x12, // bLength 0x01, // bDescriptorType 0x0200, // bcdUSB 2.0 0x00, // bDeviceClass 0x00, // bDeviceSubClass 0x00, // bDeviceProtocol 0x40, // bMaxPacketSize0 0x09, 0x12, // idVendor 0x1209 0x34, 0x56, // idProduct 0x5634 0x00, 0x01, // bcdDevice 0x01, // iManufacturer 0x02, // iProduct 0x03, // iSerialNumber 0x01 // bNumConfigurations };这段结构体里idVendor和idProduct是小端存放所以0x09, 0x12实际读出来是 0x1209。改完这两个字节Windows 和 Linux 就会把它当成一个新设备不会和旧驱动打架。iManufacturer这些字符串索引指向字符串描述符表想改设备名就改对应的字符串数组。注意改完要重新计算校验有些工具链会自动算手动改 bin 的话要自己补 CRC。3.2 Windows 下装 WinUSB 驱动的两种方式如果固件用的是 WinUSB 接口Windows 不会自动认需要手动装驱动。第一种方式是用 Zadig 这类工具选中设备后替换成 WinUSB 驱动优点是快缺点是每次换 USB 口可能要重来。第二种方式是写一个 inf 文件把 VID/PID 和 WinUSB 服务绑定然后右键安装。inf 文件里关键的是[Manufacturer]段和[DeviceList]段把硬件 ID 写成USB\VID_1209PID_5634下面挂 WinUSB 的安装节。装完之后在设备管理器里应该能看到一个没有黄色感叹号的通用串行总线设备。; cmsis_dap.inf 关键片段 [Manufacturer] %MFGNAME%DeviceList, NTamd64 [DeviceList.NTamd64] %DEVNAME%USB_Install, USB\VID_1209PID_5634 [USB_Install] Includewinusb.inf NeedsWINUSB.NT [USB_Install.Services] Includewinusb.inf NeedsWINUSB.NT.Services这个 inf 的逻辑是告诉系统遇到指定 VID/PID 的设备就加载 WinUSB 作为功能驱动。NTamd64表示 64 位系统32 位系统要改成NT。装的时候如果提示签名问题临时禁用驱动签名强制或者用测试签名模式。装完最好换个 USB 口再插一次确认驱动跟着硬件 ID 走而不是跟着端口走。3.3 Linux 和 macOS 下的权限与识别Linux 下通常不需要装驱动内核自带 HID 和 WinUSB 支持但普通用户没有权限直接访问 USB 设备需要加 udev 规则。规则文件放在/etc/udev/rules.d/下内容是指定 VID/PID 的设备赋予0666权限或者归到plugdev组。加完规则执行sudo udevadm control --reload再重新插拔。macOS 下一般免驱但如果 IDE 用的是 libusb 后端可能需要用system_profiler SPUSBDataType确认设备树里能看到看不到就检查 USB 线是不是只供电不传数据。# /etc/udev/rules.d/99-cmsis-dap.rules SUBSYSTEMusb, ATTR{idVendor}1209, ATTR{idProduct}5634, MODE0666 # 重新加载规则 sudo udevadm control --reload-rules sudo udevadm trigger这段规则的意思是只要 USB 子系统里出现 VID 0x1209、PID 0x5634 的设备就把设备节点权限设成所有人可读写。MODE0666对开发环境够用生产环境建议改成GROUPplugdev再把用户加进组。udevadm trigger是让规则立即生效不用重启。如果加了规则还是认不到用lsusb -v看设备有没有枚举成功枚举失败通常是固件里 USB 时钟配置不对。4. 避坑与排查烧完不认、认了连不上、连上就断4.1 现象插上 USB 完全没反应电脑连未知设备都不弹原因通常是三类USB 时钟没配好、DP/DM 接反、或者芯片本身没跑起来。STM32F103 的 USB 需要 48MHz 时钟一般由 PLL 分频得到如果固件里时钟树配错USB 外设根本不工作。DP/DM 接反的话主机检测不到上拉自然没反应。芯片没跑起来可能是复位电路或者 BOOT 引脚状态不对。解决先用万用表量 DP 对地电压正常应该有 3.3V 左右的上拉。没有的话检查固件里 USB 初始化代码确认RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE)这类使能语句存在。DP/DM 接反只能改线或者改固件里的引脚重映射。BOOT0 要接地BOOT1 随便但有些板子 BOOT1 拉高会影响 USB 引脚。4.2 现象设备管理器里能看到但 IDE 里选不到调试器原因一般是驱动没装对或者 IDE 的调试器后端不支持当前接口类型。比如固件用的是 HID 接口但 IDE 只认 WinUSB那就互相看不见。另一种可能是 VID/PID 被系统里别的驱动抢了设备管理器里看属性如果驱动是某个串口或者厂商工具就说明被占用了。解决先确认固件用的是 HID 还是 WinUSBHID 的话在 IDE 里选 CMSIS-DAP HIDWinUSB 就选 CMSIS-DAP WinUSB。被占用的话在设备管理器里卸载设备并勾选删除驱动然后重新插。还不行就改 VID/PID 换一组避开常见厂商的 ID 段。4.3 现象能识别也能连但下载几秒后就断开原因多半是供电不足或者 SWD 时钟太快。调试器本身从 USB 取电如果目标板也从调试器取电总电流超过 500mA 就会触发 USB 过流保护表现就是下载到一半掉线。SWD 时钟太快的话长排线或者目标板电源不稳会导致时序错乱DAP 包重传几次后超时断开。解决目标板单独供电调试器只连 SWDIO、SWCLK、GND 三根线不要连 VCC。SWD 时钟在 IDE 设置里降到 1MHz 或者 500kHz 试试稳定后再往上加。排线尽量短超过 20cm 的话中间加地线隔离。如果还断在固件里把 DAP 的超时重传次数调大或者加一个看门狗防止 USB 栈卡死。4.4 现象能下载但无法调试断点停不住原因通常是复位方式不对或者目标芯片开了读保护。有些目标板复位后立刻进入低功耗模式调试器还没来得及接管就跑了。读保护的话SWD 能连但 Flash 操作被拒断点自然无效。解决在 IDE 里把复位方式改成Connect under reset让调试器在复位期间就接管 SWD。读保护的话只能先整片擦除解除保护但会丢程序。另外检查目标芯片的调试接口有没有被选项字节关闭比如 STM32 的DBGMCU_CR寄存器里要允许调试。4.5 现象Linux 下普通用户没权限每次都要 sudo原因就是没加 udev 规则或者规则里的 VID/PID 和实际设备对不上。有些固件会在运行时改 PID导致规则匹配不到。解决用lsusb确认实际枚举的 VID/PID然后写进规则文件。如果 PID 会变可以用ATTRS{idVendor}1209只匹配 VID但这样会放开所有同厂商设备不太安全。稳妥做法是固定固件里的 PID不要动态改。规则加完记得udevadm control --reload-rules和udevadm trigger然后重新插拔设备。5. 进阶把裸板调试器塞进自动化产测流水线5.1 用命令行工具替代 IDE 做批量烧录产测场景下不可能每块板子都开 IDE 点下载得用命令行。CMSIS-DAP 固件烧好之后可以用pyocd或者openocd这类工具通过 DAP 接口给目标板烧录。pyocd 的好处是 Python 生态容易和测试脚本集成openocd 的好处是配置灵活支持几乎所有的 Flash 算法。我一般会先用 pyocd 跑通单板确认 SWD 速率和复位方式再写成批量脚本。# batch_flash.py 用 pyocd 批量烧录 from pyocd.core.helpers import ConnectHelper from pyocd.flash.file_programmer import FileProgrammer import glob # 枚举所有 CMSIS-DAP 调试器 probes ConnectHelper.get_all_connected_probes(blockingFalse) for probe in probes: print(找到调试器:, probe.product_name) with ConnectHelper.session_with_chosen_probe(unique_idprobe.unique_id) as session: # 目标芯片选型按实际改 session.options[target_override] cortex_m session.options[frequency] 1000000 # SWD 1MHz FileProgrammer(session).program(firmware.bin, base_address0x8000000) print(烧录完成:, probe.unique_id)这段脚本先枚举所有已连接的 DAP 调试器然后逐个建立会话设置目标芯片和 SWD 频率最后烧录指定 bin 文件。target_override如果写cortex_m是通用 M 核具体芯片最好写明确型号比如stm32f103rc这样 Flash 算法才对。frequency设 1MHz 是保守值产测线上如果排线短可以提到 4MHz 甚至 8MHz。批量的时候注意每个调试器的 unique_id 不同脚本里用 unique_id 区分避免串台。5.2 加自定义命令做目标板电压监测裸板固件的好处是你可以往 DAP 协议里塞自定义命令。CMSIS-DAP 协议本身有DAP_Transfer和DAP_TransferConfigure这些标准命令但厂商可以扩展。常见做法是在固件里加一个 ADC 采样把目标板的 VCC 电压通过自定义 DAP 命令返回给上位机。上位机在烧录前先读电压低于阈值就报警避免烧到一半掉电变砖。// 在 DAP 命令处理里加一个自定义 ID #define ID_DAP_GetTargetVoltage 0x80 uint8_t DAP_ProcessCommand(uint8_t *request, uint8_t *response) { uint8_t id request[0]; if (id ID_DAP_GetTargetVoltage) { uint16_t adc ADC_Read(ADC_Channel_0); uint16_t mv adc * 3300 / 4096; // 12位ADC3.3V参考 response[0] ID_DAP_GetTargetVoltage; response[1] mv 0xFF; response[2] (mv 8) 0xFF; return 3; } // 其他标准命令走原逻辑 return DAP_ExecuteCommand(request, response); }这段代码在 DAP 命令分发里插了一个 0x80 的自定义 ID读 ADC 通道 0 的电压转成毫伏后小端返回。上位机发[0x80]就能拿到两字节电压值。ADC 参考电压要用芯片的 VDDA如果 VDDA 不是精确 3.3V得先校准。这个功能在产测里很实用能提前发现电源不良的板子省掉烧录到一半断线的麻烦。5.3 验证固件是否真的在跑 DAP 协议有时候设备枚举成功了但 DAP 命令没响应IDE 里能看到调试器却连不上目标。验证方法是直接用 USB 抓包或者用 pyocd 的底层命令发一个DAP_Info请求看返回的字符串对不对。DAP_Info的 ID 是 0x00请求格式是[0x00, info_id]返回的是字符串。如果返回空或者超时说明固件的 DAP 处理循环没跑起来可能是 USB 接收回调没挂上或者主循环被别的中断卡死了。# 用 pyocd 的命令行工具读 DAP 信息 pyocd list --probes # 输出里应该能看到 CMSIS-DAP 的型号和 unique_id # 再用 commander 连一下 pyocd commander -u unique_id -t cortex_m # 进去之后敲 show probe 看固件版本pyocd list能列出所有 DAP 探头如果列表是空的说明 USB 层就没通。commander进去之后show probe会打印固件版本和厂商字符串这些信息来自固件里的DAP_Info响应。如果版本号是乱码检查字符串描述符的编码CMSIS-DAP 要求 ASCII。这一步过了基本就能确认固件是活的剩下的就是目标板接线和 SWD 参数问题。从那以后我每次拿到新的裸板固件包都先烧一块最小系统板用pyocd list确认枚举再用commander读一遍 DAP 信息最后才接到目标板上。这个习惯帮我排掉了好几次 USB 描述符写错和时钟配置翻车的问题。希望帮到你。本文还有配套的精品资源点击获取