Skip to content

Commit 289dcbf

Browse files
committed
docs: rename Init/Deinit to BindEndpoints/UnbindEndpoints; add DAPLinkV2 doc; config filename update
1 parent 36234a1 commit 289dcbf

14 files changed

Lines changed: 580 additions & 31 deletions

File tree

docs/basic_coding/core/core-op.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ read_port(buffer, op_cb);
8282
### 轮询方式查询完成状态
8383

8484
```cpp
85-
OperationPollingStatus status = OperationPollingStatus::READY;
85+
auto status = LibXR::ReadOperation::OperationPollingStatus::READY;
8686
ReadOperation op_poll(status);
8787
read_port(buffer, op_poll);
8888

8989
// 后续通过 status 查询是否完成
90-
if (status == OperationPollingStatus::DONE) {
90+
if (status == LibXR::ReadOperation::OperationPollingStatus::DONE) {
9191
// 数据已读取完成
9292
}
9393
```

docs/code_gen/stm32/uart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ USB:
188188

189189
## 生成代码命令
190190

191-
修改 `.config.yaml` 后,可使用以下任一命令重新生成代码:
191+
修改 `libxr_config.yaml` 后,可使用以下任一命令重新生成代码:
192192

193193
```bash
194194
# 重新生成整个工程

docs/code_gen/stm32/watchdog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Watchdog:
5757
5858
## 生成代码命令
5959

60-
修改 `.config.yaml` 后,重新生成代码:
60+
修改 `libxr_config.yaml` 后,重新生成代码:
6161

6262
```bash
6363
xr_gen_code_stm32 -i ./.config.yaml -o ./User/app_main.cpp

docs/xrusb/dev_stack/cdc.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ CDC ACM 设备以 **两接口(Communication + Data)** 的方式呈现,并
8080
8181
### 端点(Endpoint)
8282
83-
`CDCBase::Init()` 从 `EndpointPool` 申请并配置以下端点:
83+
`CDCBase::BindEndpoints()` 从 `EndpointPool` 申请并配置以下端点:
8484
8585
| 端点 | 方向 | 类型 | 典型用途 |
8686
| -------- | ---- | --------- | --------------------------- |
@@ -197,7 +197,7 @@ struct SerialStateNotification
197197

198198
### Init 行为
199199

200-
`CDCBase::Init(endpoint_pool, start_itf_num)` 的关键行为:
200+
`CDCBase::BindEndpoints(endpoint_pool, start_itf_num)` 的关键行为:
201201

202202
- 清零 `control_line_state_`
203203
- 通过 `EndpointPool` 申请三个端点并完成 `Configure`
@@ -214,15 +214,15 @@ struct SerialStateNotification
214214

215215
### Deinit 行为
216216

217-
`CDCBase::Deinit(endpoint_pool)` 的关键行为:
217+
`CDCBase::UnbindEndpoints(endpoint_pool)` 的关键行为:
218218

219219
- `inited_ = false`
220220
- 清零 `control_line_state_`
221221
- 关闭三端点、清零 active length
222222
- 将端点归还给 `EndpointPool`
223223
- 置端点指针为空
224224

225-
派生类或上层适配类在 `Deinit()` 时应确保:
225+
派生类或上层适配类在 `UnbindEndpoints()` 时应确保:
226226

227227
- 终止所有依赖端点对象的异步操作
228228
- 对外完成或失败掉未完成的读写请求,避免上层永久等待

docs/xrusb/dev_stack/dap.md

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
---
2+
id: xrusb-dev-stack-daplinkv2
3+
title: DAPLinkV2
4+
sidebar_position: 5
5+
---
6+
7+
# DAPLinkV2 设备协议栈
8+
9+
本文档描述 XRUSB 的 **CMSIS-DAP v2(Bulk)** 设备类实现:`LibXR::USB::DapLinkV2Class`
10+
11+
该设备类面向通用 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)能力宣告。
12+
13+
支持能力:
14+
15+
- **CMSIS-DAP v2 Bulk transport**
16+
- **SWD-only**`DAP_Connect` 仅支持 SWD;JTAG 未实现)
17+
- **可选 nRESET 控制**(通过 `GPIO* nreset_gpio` 注入,缺省为不支持)
18+
- **SWJ_Pins shadow 语义**(SWDIO/SWCLK 以 shadow 状态对主机表现;nRESET 若连线可返回真实电平)
19+
- **WinUSB(MS OS 2.0)BOS 平台能力**(CompatibleID="WINUSB" + DeviceInterfaceGUIDs)
20+
- **DAP_Transfer / DAP_TransferBlock**(含 AP posted-read pipeline;Transfer 支持 match / timestamp 的约束检查)
21+
22+
---
23+
24+
## 1. 类与构造方式
25+
26+
### 1.1 `LibXR::USB::DapLinkV2Class`
27+
28+
构造函数:
29+
30+
```cpp
31+
explicit DapLinkV2Class(
32+
LibXR::Debug::Swd& swd_link,
33+
LibXR::GPIO* nreset_gpio = nullptr,
34+
Endpoint::EPNumber data_in_ep_num = Endpoint::EPNumber::EP_AUTO,
35+
Endpoint::EPNumber data_out_ep_num = Endpoint::EPNumber::EP_AUTO);
36+
```
37+
38+
参数说明:
39+
40+
- `swd_link`:SWD 后端实现(由 `LibXR::Debug::Swd` 提供具体 SWD 事务、序列读写等)。
41+
- `nreset_gpio`:可选 nRESET GPIO;若为空则与 reset 相关命令以“best-effort”方式处理。
42+
- `data_in_ep_num` / `data_out_ep_num`:Bulk IN/OUT 端点号;支持自动分配(`EP_AUTO`)。
43+
44+
### 1.2 可选的 InfoStrings
45+
46+
可通过 `SetInfoStrings()` 覆盖默认字符串集合,用于 `DAP_Info` 返回:
47+
48+
```cpp
49+
struct InfoStrings {
50+
const char* vendor;
51+
const char* product;
52+
const char* serial;
53+
const char* firmware_ver;
54+
55+
const char* device_vendor;
56+
const char* device_name;
57+
const char* board_vendor;
58+
const char* board_name;
59+
const char* product_fw_ver;
60+
};
61+
```
62+
63+
注意:`DAP_Info` 的字符串返回包含 **末尾 NUL**,并在容量不足时保证以 NUL 结尾(截断安全)。
64+
65+
---
66+
67+
## 2. USB 接口与端点
68+
69+
### 2.1 接口描述符
70+
71+
`DapLinkV2Class` 贡献 **1 个接口**,不使用 IAD:
72+
73+
- `GetInterfaceCount()` 返回 `1`
74+
- `HasIAD()` 返回 `false`
75+
76+
接口描述符的 class 固定为 `0xFF`(Vendor Specific),并暴露 **2 个 Bulk 端点**
77+
78+
### 2.2 Bulk 端点
79+
80+
- **Bulk OUT**:Host → Device(DAP 请求包)
81+
- **Bulk IN**:Device → Host(DAP 响应包)
82+
83+
端点配置要点:
84+
85+
- `Configure(max_len = UINT16_MAX, double_buffer = false)`
86+
其中 `UINT16_MAX` 仅作为上限;底层会选择一个不超过该值的合法最大包长(由 `Endpoint` 实现决定)。
87+
- `double_buffer = false` 用于保持严格的 **请求/响应串行**,并配合 `tx_busy_` 防止 `tx_buf_` 被覆盖。
88+
89+
---
90+
91+
## 3. WinUSB(MS OS 2.0)支持
92+
93+
为便于 Windows 侧免驱访问,该类在 BOS 中声明 MS OS 2.0 平台能力,并提供 MS OS 2.0 descriptor set:
94+
95+
- BOS Capability:MS OS 2.0 Platform Capability
96+
- Vendor code:`0x20``WINUSB_VENDOR_CODE`
97+
- Compatible ID:`"WINUSB"`
98+
- DeviceInterfaceGUIDs(REG_MULTI_SZ,UTF-16LE):`{CDB3B5AD-293B-4663-AA36-1AAE46463776}`(单 GUID + 双 NUL 结束)
99+
100+
接口号在绑定时确定(`start_itf_num`),因此 MS OS 2.0 function subset 中的 `bFirstInterface` 会在 `BindEndpoints()` 时根据实际接口号补丁更新,以确保 Windows 枚举一致。
101+
102+
---
103+
104+
## 4. 传输模型(Bulk 请求/响应)
105+
106+
本类实现的是 **CMSIS-DAP v2 over Bulk** 的同步请求/响应模型:
107+
108+
1. 主机向 **Bulk OUT** 发送一帧请求(request)。
109+
2. 设备在 OUT 完成回调中解析请求并生成响应(response)。
110+
3. 设备通过 **Bulk IN** 发送响应。
111+
4. IN 发送完成后,设备再次 arm OUT 接收下一帧请求。
112+
113+
---
114+
115+
## 5. 生命周期:Bind / Unbind
116+
117+
### 5.1 `BindEndpoints(endpoint_pool, start_itf_num)`
118+
119+
初始化流程要点:
120+
121+
1. 记录 `interface_num_ = start_itf_num`,并更新 WinUSB MS OS 2.0 function subset 的接口号字段。
122+
2.`EndpointPool` 分配 Bulk OUT 与 Bulk IN 端点。
123+
3. 配置端点(BULK,`max_len=UINT16_MAX``double_buffer=false`)。
124+
4. 注册端点回调:
125+
- OUT 完成 → `OnDataOutComplete()`
126+
- IN 完成 → `OnDataInComplete()`
127+
5. 生成并提交配置描述符块(Interface + 2x Endpoint),通过 `SetData()` 提交给上层拼装。
128+
6. 复位运行时状态:
129+
- `dap_state_` 清零后设置默认 debug_port=DISABLED,transfer_abort=false
130+
- `swj_clock_hz_` 设为 1 MHz 并同步到 `swd_`
131+
- SWJ shadow 默认:SWDIO=1、nRESET=1、SWCLK=0(详见 6.3)
132+
7. 标记 `inited_=true`,并调用 `ArmOutTransferIfIdle()` 保持 OUT 端点处于挂起接收状态。
133+
134+
### 5.2 `UnbindEndpoints(endpoint_pool)`
135+
136+
释放流程要点:
137+
138+
-`inited_``tx_busy_`,并将 `dap_state_.debug_port` 置为 DISABLED。
139+
- 关闭并释放 IN/OUT 端点,归还到 `EndpointPool`
140+
- 调用 `swd_.Close()` 关闭 SWD 后端。
141+
- 复位 shadow 默认值(SWDIO=1、nRESET=1、SWCLK=0)。
142+
143+
---
144+
145+
## 6. 运行时状态与默认值
146+
147+
### 6.1 DAP 状态
148+
149+
`GetState()` 返回内部状态结构 `LibXR::USB::DapLinkV2Def::State`,关键字段包括:
150+
151+
- `debug_port`:当前连接端口(默认 DISABLED;CONNECT 后为 SWD)
152+
- `transfer_abort`:TransferAbort 标志(由 `DAP_TransferAbort` 设置;见 8.6)
153+
- `transfer_cfg`:TransferConfigure 解析后的传输策略(idle_cycles / retry_count / match_retry)
154+
155+
### 6.2 SWJ 时钟
156+
157+
- 默认:`1,000,000 Hz`
158+
- `DAP_SWJ_Clock` 会写入 `swj_clock_hz_` 并调用 `swd_.SetClockHz(hz)`
159+
160+
### 6.3 SWJ shadow 语义
161+
162+
本实现并不在 USB 类内部直接 bit-bang SWDIO/SWCLK;这些由 `LibXR::Debug::Swd` 后端承担。为保持 CMSIS-DAP 兼容性,类内部维护一个 SWJ pin shadow:
163+
164+
- 绑定后默认:SWDIO=1、nRESET=1、SWCLK=0
165+
- `DAP_SWJ_Pins` 对 SWDIO/SWCLK 的设置会更新 shadow(即便该引脚不可物理驱动)
166+
- 对 nRESET:若 `nreset_gpio_` 存在则会驱动 GPIO,并在读取时返回真实电平;否则同样使用 shadow 表现
167+
168+
---
169+
170+
## 7. Host → Device 数据路径(Bulk OUT)
171+
172+
### 7.1 OUT 完成回调:`OnDataOutComplete()`
173+
174+
高层流程:
175+
176+
1. 校验初始化状态与端点有效性。
177+
2.`tx_busy_ == true`,出于安全直接返回。
178+
3. 读取 OUT 数据:
179+
- 若空包或空指针:立即重新 arm OUT。
180+
- 否则调用 `ProcessOneCommand(req, req_len, tx_buf_, MAX_RESP, out_len)` 生成响应。
181+
4. 设置 `tx_busy_ = true`,并通过 Bulk IN 发送响应:`TransferMultiBulk(tx_buf_[0..out_len))`
182+
5. 若 IN 提交失败,则清 `tx_busy_` 并重新 arm OUT。
183+
184+
### 7.2 OUT 端点保持挂起:`ArmOutTransferIfIdle()`
185+
186+
为保持主机端请求连续性,设备会尽可能让 Bulk OUT 处于接收状态,但受以下条件约束:
187+
188+
- `inited_ == true`
189+
- `tx_busy_ == false`(上一条响应已发送完成)
190+
- OUT 端点状态为 `IDLE`
191+
192+
满足条件后,提交接收缓冲:`TransferMultiBulk(rx_buf_, MAX_REQ)`
193+
194+
---
195+
196+
## 8. 命令集(实现概览)
197+
198+
命令分发逻辑位于 `ProcessOneCommand()`,以请求首字节 `CMD`(命令 ID)决定处理器;未识别命令返回单字节 `0xFF`
199+
200+
### 8.1 已实现命令列表
201+
202+
| Command | ID | 行为概述 |
203+
| --- | ---: | --- |
204+
| `DAP_Info` | `INFO` | 返回字符串/数值信息(含 PACKET_SIZE / TIMESTAMP_CLOCK 等) |
205+
| `DAP_HostStatus` | `HOST_STATUS` | 返回 OK(简化实现) |
206+
| `DAP_Connect` | `CONNECT` | 仅支持 SWD;成功时进入 SWD 并返回端口 SWD |
207+
| `DAP_Disconnect` | `DISCONNECT` | 关闭 SWD 后端并回到 DISABLED |
208+
| `DAP_TransferConfigure` | `TRANSFER_CONFIGURE` | 设置 idle_cycles / retry 等,并映射到 SWD policy |
209+
| `DAP_Transfer` | `TRANSFER` | 支持 DP/AP 读写、match、timestamp 约束检查、AP posted-read pipeline |
210+
| `DAP_TransferBlock` | `TRANSFER_BLOCK` | DP/AP block 读写;AP read 使用 posted pipeline;不支持 match/timestamp |
211+
| `DAP_TransferAbort` | `TRANSFER_ABORT` | 置 abort 标志,下一次 Transfer/Block 将返回错误并清标志 |
212+
| `DAP_WriteABORT` | `WRITE_ABORT` | 调用 SWD 后端写 ABORT,按 ack 返回 OK/ERROR |
213+
| `DAP_Delay` | `DELAY` | 微秒延时(Timebase) |
214+
| `DAP_ResetTarget` | `RESET_TARGET` | 若有 nRESET,则执行拉低/释放脉冲并标记 execute=1;否则 execute=0,但仍返回 DAP_OK |
215+
| `DAP_SWJ_Pins` | `SWJ_PINS` | 更新 shadow 并 best-effort 控制 nRESET;支持 PinWait 轮询 |
216+
| `DAP_SWJ_Clock` | `SWJ_CLOCK` | 更新 SWJ clock,并写入 SWD 后端 |
217+
| `DAP_SWJ_Sequence` | `SWJ_SEQUENCE` | 写入 SWJ bit 序列(LSB-first),并更新 shadow(SWDIO=last bit, SWCLK=0) |
218+
| `DAP_SWD_Configure` | `SWD_CONFIGURE` | best-effort 解析(可选),直接返回 OK |
219+
| `DAP_SWD_Sequence` | `SWD_SEQUENCE` | 多段输入/输出序列;输入数据追加在响应尾部(LSB-first) |
220+
| `DAP_QueueCommands` | `QUEUE_COMMANDS` | 固定返回 DAP_ERROR(未实现) |
221+
| `DAP_ExecuteCommands` | `EXECUTE_COMMANDS` | 固定返回 DAP_ERROR(未实现) |
222+
223+
注:具体数值 ID 取决于 `DapLinkV2Def::CommandId` 的定义;本文以枚举名表示。
224+
225+
---
226+
227+
## 9. 错误与兼容性约定
228+
229+
- 未识别命令:返回单字节 `0xFF`(unknown command response)。
230+
- 对部分命令采用“transport-level OK、语义 ERROR”的策略:即响应包仍按协议返回,但状态位标记错误,避免主机因丢包/短包失步。
231+
- `QUEUE_COMMANDS` / `EXECUTE_COMMANDS`:固定返回 `DAP_ERROR`(未实现)。
232+
- `SWD_CONFIGURE`:best-effort 解析(可选),始终返回 OK 以保持兼容。
233+
234+
---
235+
236+
## 10. 使用示例
237+
238+
### 10.1 设备侧初始化(示意)
239+
240+
```cpp
241+
#include "daplink_v2.hpp"
242+
#include "usb/device.hpp"
243+
#include "debug/swd.hpp"
244+
245+
// 此处使用基类示例,实际应为派生类
246+
LibXR::Debug::Swd swd(/* ... init ... */);
247+
LibXR::GPIO nreset(/* ... optional ... */);
248+
249+
LibXR::USB::DapLinkV2Class dap(swd, &nreset);
250+
251+
// 可选:覆盖 DAP_Info 字符串
252+
LibXR::USB::DapLinkV2Class::InfoStrings info;
253+
info.vendor = "XRobot";
254+
info.product = "DAPLinkV2";
255+
info.serial = "00000001";
256+
info.firmware_ver = "2.0.0";
257+
dap.SetInfoStrings(info);
258+
259+
// USB device class list: {{&dap}}
260+
// usb_dev.Init();
261+
// usb_dev.Start();
262+
```
263+
264+
### 10.2 Windows/WinUSB 侧访问
265+
266+
该类通过 BOS/MS OS 2.0 描述符集声明 WinUSB 与 DeviceInterfaceGUIDs,Windows 通常可在无需自定义 INF 的情况下枚举为 WinUSB 设备,并可通过 GUID 在用户态进行枚举与打开。
267+
268+
### 11. SWD实现
269+
270+
#### 11.1 SwdGeneralGPIO
271+
272+
使用两个普通GPIO分别作为 SWDIO/SWCLK,实现通用 SWD 通信,无需任何特殊配置。推荐为每个IO串联 33Ω 电阻,同时为 SWDIO 添加10KΩ上拉。

docs/xrusb/dev_stack/gsusb.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ FD DLC 表(实现内置):
130130

131131
---
132132

133-
## 4. 生命周期:Init / Deinit
133+
## 4. 生命周期:BindEndpoints / UnbindEndpoints
134134

135-
### 4.1 `Init(endpoint_pool, start_itf_num)`
135+
### 4.1 `BindEndpoints(endpoint_pool, start_itf_num)`
136136

137137
初始化流程要点:
138138

@@ -150,7 +150,7 @@ FD DLC 表(实现内置):
150150
- FD:订阅 STANDARD/EXTENDED(FD pack)
151151
6. `inited_ = true`,并调用 `MaybeArmOutTransfer()` 保持 OUT 端点处于挂起接收状态
152152

153-
### 4.2 `Deinit(endpoint_pool)`
153+
### 4.2 `UnbindEndpoints(endpoint_pool)`
154154

155155
- 关闭端点并归还到 `EndpointPool`
156156
- 清空关键状态并复位开关位

docs/xrusb/dev_stack/hid.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ sidebar_position: 2
6060

6161
### Endpoints(端点)
6262

63-
基类在 `Init()` 中从 `EndpointPool` 申请并配置端点:
63+
基类在 `BindEndpoints()` 中从 `EndpointPool` 申请并配置端点:
6464

6565
| 端点 | 方向 | 类型 | `wMaxPacketSize` | 用途 |
6666
| ---------------- | ---- | --------- | ---------------: | ------------------------- |
@@ -78,7 +78,7 @@ sidebar_position: 2
7878

7979
## 初始化与资源释放
8080

81-
### Init 行为(`HID::Init(endpoint_pool, start_itf_num)`
81+
### Init 行为(`HID::BindEndpoints(endpoint_pool, start_itf_num)`
8282

8383
初始化的关键步骤:
8484

@@ -97,7 +97,7 @@ sidebar_position: 2
9797
7. 若启用 OUT:启动首次 OUT 接收 `ep_out_->Transfer(RX_REPORT_LEN)`(随后每次完成会自动 re-arm)
9898
8. 设置 `inited_ = true`
9999

100-
### Deinit 行为(`HID::Deinit(endpoint_pool)`
100+
### Deinit 行为(`HID::UnbindEndpoints(endpoint_pool)`
101101

102102
- `inited_ = false`
103103
- 关闭并归还 IN/OUT 端点给 `EndpointPool`
@@ -175,7 +175,7 @@ virtual ConstRawData GetReportDesc() = 0;
175175

176176
若启用 OUT 端点,基类默认行为是:
177177

178-
- 首次 `Init()` 后调用一次 `ep_out_->Transfer(RX_REPORT_LEN)`
178+
- 首次 `BindEndpoints()` 后调用一次 `ep_out_->Transfer(RX_REPORT_LEN)`
179179
- 每次 OUT 接收完成触发 `OnDataOutCompleteStatic()`
180180
1. 调用虚函数 `OnDataOutComplete(in_isr, data)` 让派生类消费数据
181181
2. 立即 `ep_out_->Transfer(RX_REPORT_LEN)` 重新挂载接收(持续接收)

0 commit comments

Comments
 (0)