DAPLinkV2 设备协议栈
本文档描述 XRUSB 的 CMSIS-DAP v2(Bulk) 设备类实现:LibXR::USB::DapLinkV2Class<SwdPort>。测试可用的VID:PID:0x0D28:0x2040,BCD版本:0x0201。
该设备类面向通用 CMSIS-DAP v2 主机工具链(如 pyOCD、OpenOCD 的 CMSIS-DAP backend、DAPLink 兼容客户端等)的 USB Bulk 传输方式,采用 1 个 Vendor Interface + 2 个 Bulk 端点(1 IN + 1 OUT) 的传输模型,实现 DAP v2 常用命令子集(以 SWD 为主),并提供 Windows 侧即插即用的 WinUSB(MS OS 2.0)能力宣告。
由于 DAPLink 通常会声明自身为复合设备,因此需要搭配其他 USB Class 使用(例如 CDC 虚拟串口等)。上位机识别设备时也常会查找带有 CMSIS-DAP 字符串的设备,因此建议的 LanguagePack 为:
static constexpr auto USB_FS_LANG_PACK =
LibXR::USB::DescriptorStrings::MakeLanguagePack(
LibXR::USB::DescriptorStrings::Language::EN_US, "XRobot", "CMSIS-DAP",
"XRUSB-DEMO-XRDAP-");
支持能力:
- CMSIS-DAP v2 Bulk transport
- SWD-only(
DAP_Connect仅支持 SWD;JTAG 未实现) - 可选 nRESET 控制(通过
GPIO* nreset_gpio注入,缺省为不支持) - SWJ_Pins shadow 语义(SWDIO/SWCLK 以 shadow 状态对主机表现;nRESET 若连线可返回真实电平)
- WinUSB(MS OS 2.0)BOS 平台能力(CompatibleID="WINUSB" + DeviceInterfaceGUIDs)
- DAP_Transfer / DAP_TransferBlock(含 AP posted-read pipeline;Transfer 支持 match / timestamp 约束检查)
1. 类与构造方式
1.1 LibXR::USB::DapLinkV2Class<SwdPort>
该类为模板类,SWD 后端类型由模板参数 SwdPort 指定。SwdPort 需提供本类用到的 SWD 能力(进入/关闭 SWD、DP/AP 读写事务、序列读写、设置时钟与传输策略等)。
构造函数:
template <typename SwdPort>
explicit DapLinkV2Class(
SwdPort& swd_link,
LibXR::GPIO* nreset_gpio = nullptr,
Endpoint::EPNumber data_in_ep_num = Endpoint::EPNumber::EP_AUTO,
Endpoint::EPNumber data_out_ep_num = Endpoint::EPNumber::EP_AUTO);
参数说明:
swd_link:SWD 链路对象引用(SwdPort实例)。nreset_gpio:可选 nRESET GPIO;若为空则 reset 相关命令以 best-effort 方式处理。data_in_ep_num/data_out_ep_num:Bulk IN/OUT 端点号;支持自动分配(EP_AUTO)。
常用接口:
SetInfoStrings(info):覆盖DAP_Info字符串。GetState():获取内部 DAP 状态结构(读)。IsInited():是否已完成绑定初始化。
1.2 InfoStrings
DAP_Info 字符串集合:
struct InfoStrings {
const char* vendor;
const char* product;
const char* serial;
const char* firmware_ver;
const char* device_vendor;
const char* device_name;
const char* board_vendor;
const char* board_name;
const char* product_fw_ver;
};
返回约定:
- 字符串返回包含末尾 NUL。
- 截断时保证以 NUL 结尾。
2. USB 接口与端点
2.1 接口描述符
DapLinkV2Class 贡献 1 个接口,不使用 IAD:
GetInterfaceCount() = 1HasIAD() = false
接口 class 固定为 0xFF(Vendor Specific),并暴露 2 个 Bulk 端点。
2.2 Bulk 端点
- Bulk OUT:Host → Device(DAP 请求包)
- Bulk IN:Device → Host(DAP 响应包)
端点分配与配置发生在 BindEndpoints():
- 从
EndpointPool分配 OUT/IN 端点(支持指定/自动端点号)。 - 端点类型为 BULK;最大传输长度以
UINT16_MAX作为上限,底层会选择合法值。 - 配置描述符中的
wMaxPacketSize来自端点对象的MaxPacketSize()。
3. WinUSB(MS OS 2.0)支持
该类在 BOS 中声明 MS OS 2.0 平台能力,并提供 MS OS 2.0 descriptor set:
- BOS Capability:MS OS 2.0 Platform Capability(数量 1)
- Vendor code:
0x20 - Compatible ID:
"WINUSB" - DeviceInterfaceGUIDs(REG_MULTI_SZ,UTF-16LE):
{CDB3B5AD-293B-4663-AA36-1AAE46463776}(单 GUID + 双 NUL 结束)
接口号在绑定时确定,因此 function subset 的 bFirstInterface 会在 BindEndpoints() 时更新,以确保 Windows 枚举一致。
4. 传输模型(Bulk 请求/响应)
本类实现 CMSIS-DAP v2 over Bulk 的同步请求/响应模型:
- 主机向 Bulk OUT 发送一帧请求。
- 设备在 OUT 完成回调中解析请求并生成响应(写入 IN 端点缓冲)。
- 设备通过 Bulk IN 发送响应。
- IN 完成后,设备重新 arm OUT 接收下一帧请求。
5. 生命周期:Bind / Unbind
5.1 BindEndpoints(endpoint_pool, start_itf_num)
要点:
- 记录
interface_num_ = start_itf_num,并更新 WinUSB function subset 的接口号字段。 - 分配并配置 Bulk OUT/IN 端点,注册回调。
- 生成并提交配置描述符块(Interface + 2x Endpoint)。
- 运行时默认值:
debug_port = DISABLEDtransfer_abort = falseswj_clock_hz = 1MHz并同步到swd_link- SWJ shadow 默认:SWDIO=1、nRESET=1、SWCLK=0
- 置
inited_=true,arm OUT 接收。
5.2 UnbindEndpoints(endpoint_pool)
要点:
- 清运行态标志并关闭 SWD 后端。
- 关闭并释放 IN/OUT 端点,归还给端点池。
- 复位 shadow 默认值(SWDIO=1、nRESET=1、SWCLK=0)。
6. 运行时状态与默认值
6.1 DAP 状态
内部状态结构 LibXR::USB::DapLinkV2Def::State 关键字段包括:
debug_port:默认 DISABLED;CONNECT 后为 SWDtransfer_abort:TransferAbort 标志transfer_cfg:TransferConfigure 解析后的策略(idle_cycles / retry_count / match_retry)
6.2 SWJ 时钟
- 默认:
1,000,000 Hz DAP_SWJ_Clock更新内部变量并调用swd_link.SetClockHz(hz)。
6.3 SWJ shadow 语义
类内部维护 SWJ pin shadow:
- 默认:SWDIO=1、nRESET=1、SWCLK=0
DAP_SWJ_Pins会对被选择的 pin 更新 shadow- nRESET:
- 若
nreset_gpio存在,则会驱动 GPIO;读取时返回真实电平 - 若
nreset_gpio不存在,则按 shadow 表现
- 若
7. 命令集(实现概览)
命令分发逻辑位于 ProcessOneCommand(),以请求首字节 CMD 决定处理器;未识别命令返回单字节 0xFF。
7.1 已实现命令列表
| Command | ID | 行为概述 |
|---|---|---|
DAP_Info | INFO | 返回字符串/数值信息(含 CAPABILITIES / PACKET_COUNT / PACKET_SIZE / TIMESTAMP_CLOCK 等) |
DAP_HostStatus | HOST_STATUS | 返回 OK |
DAP_Connect | CONNECT | 仅支持 SWD;成功返回端口 SWD |
DAP_Disconnect | DISCONNECT | 关闭 SWD 并回到 DISABLED |
DAP_TransferConfigure | TRANSFER_CONFIGURE | 设置 idle_cycles / retry / match_retry,并映射到 SWD policy |
DAP_Transfer | TRANSFER | DP/AP 读写;支持 match / timestamp;AP posted-read pipeline |
DAP_TransferBlock | TRANSFER_BLOCK | DP/AP block 读写;AP read 使用 posted pipeline;不支持 match/timestamp |
DAP_TransferAbort | TRANSFER_ABORT | 置 abort 标志,下一次 Transfer/Block 返回错误并清标志 |
DAP_WriteABORT | WRITE_ABORT | 写 ABORT,按 ack/EC 返回 OK/ERROR |
DAP_Delay | DELAY | 微秒延时 |
DAP_ResetTarget | RESET_TARGET | 若有 nRESET 则执行脉冲并 Execute=1;否则 Execute=0;始终返回 DAP_OK |
DAP_SWJ_Pins | SWJ_PINS | 更新 shadow 并 best-effort 控制 nRESET;支持 PinWait |
DAP_SWJ_Clock | SWJ_CLOCK | 更新 SWJ clock |
DAP_SWJ_Sequence | SWJ_SEQUENCE | 写入 SWJ bit 序列(LSB-first),并更新 shadow(SWDIO=last bit, SWCLK=0) |
DAP_SWD_Configure | SWD_CONFIGURE | best-effort 解析,返回 OK |
DAP_SWD_Sequence | SWD_SEQUENCE | 多段输入/输出序列;输入数据追加在响应尾部(LSB-first) |
DAP_QueueCommands | QUEUE_COMMANDS | 返回 <CMD, DAP_ERROR> |
DAP_ExecuteCommands | EXECUTE_COMMANDS | 返回 <CMD, DAP_ERROR> |
注:具体数值 ID 取决于 DapLinkV2Def::CommandId 的定义;本文以枚举名表示。
7.2 DAP_Info 关键字段
CAPABILITIES:DAP_CAP_SWDPACKET_COUNT:127PACKET_SIZE:返回 IN 端点的MaxTransferSize()TIMESTAMP_CLOCK:1,000,000(与微秒时间基准匹配)
8. 使用示例
8.1 设备侧初始化(示意)
#include "daplink_v2.hpp"
#include "usb/device.hpp"
#include "debug/swd.hpp"
LibXR::Debug::Swd swd(/* ... init ... */);
LibXR::GPIO nreset(/* ... optional ... */);
LibXR::USB::DapLinkV2Class<LibXR::Debug::Swd> dap(swd, &nreset);
// 可选:覆盖 DAP_Info 字符串
LibXR::USB::DapLinkV2Class<LibXR::Debug::Swd>::InfoStrings info;
info.vendor = "XRobot";
info.product = "DAPLinkV2";
info.serial = "00000001";
info.firmware_ver = "2.0.0";
dap.SetInfoStrings(info);
// USB device class list: {{&dap}}
// usb_dev.Init();
// usb_dev.Start();
8.2 Windows/WinUSB 侧访问
该类通过 BOS/MS OS 2.0 描述符集声明 WinUSB 与 DeviceInterfaceGUIDs,Windows 通常可在无需自定义 INF 的情况下枚举为 WinUSB 设备,并可通过 GUID 在用户态进行枚举与打开。