Skip to content

Commit 93142eb

Browse files
committed
docs: expand CAN/FDCAN abstraction docs; add XRUSB device stack (CDC/HID/UAC/GSUSB)
1 parent 0432c31 commit 93142eb

15 files changed

Lines changed: 3593 additions & 77 deletions

File tree

docs/basic_coding/driver/can.md

Lines changed: 468 additions & 37 deletions
Large diffs are not rendered by default.

docs/xrusb/dev_stack/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
id: xrusb-dev-stack
3+
title: 设备协议栈
4+
sidebar_position: 2
5+
---
6+
7+
# 设备协议栈
8+
9+
本节介绍XRUSB支持的USB Device Class,包括CDC,HID,UAC等。

docs/xrusb/dev_stack/cdc.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
---
2+
id: xrusb-dev-stack-cdc
3+
title: CDC
4+
sidebar_position: 1
5+
---
6+
7+
# CDC 设备协议栈
8+
9+
本节介绍 XRUSB 的 **USB CDC ACM(虚拟串口)** 设备类实现,重点覆盖:
10+
11+
- 描述符组织方式(IAD + Communication Interface + Data Interface)
12+
- 端点资源申请、配置与回调分发
13+
- CDC ACM 标准类请求处理(Line Coding / Control Line State)
14+
- Serial State 通知的格式与发送策略
15+
- 上层适配(`CDCUart`)与吞吐测试类(`CDCWriteTest` / `CDCReadTest`
16+
17+
当前 CDC 协议栈由以下头文件构成(源码随仓库提供):
18+
19+
- `cdc_base.hpp`:CDC ACM 通用基类(描述符 / 类请求 / 端点管理 / 回调分发)
20+
- `cdc_uart.hpp`:CDC ↔ UART 语义适配(对上提供 `LibXR::UART` 的 Read/Write)
21+
- `cdc_test.hpp`:吞吐测试用类(持续写出 / 持续读入)
22+
23+
---
24+
25+
## 组件概览
26+
27+
### `LibXR::USB::CDCBase`
28+
29+
`CDCBase` 继承自 `DeviceClass`,实现 CDC ACM 的通用部分:
30+
31+
- 端点资源申请与配置(Data IN / Data OUT / Comm IN)
32+
- IAD + 通信接口 + 数据接口的配置描述符块填充
33+
- CDC ACM 标准类请求处理(`GET_LINE_CODING` / `SET_LINE_CODING` / `SET_CONTROL_LINE_STATE`
34+
- 通过回调将 **控制线变化(DTR/RTS)****线路参数变化(Line Coding)** 通知给上层
35+
- 提供收发完成钩子(`OnDataOutComplete` / `OnDataInComplete`)供派生类实现具体数据通路
36+
37+
`CDCBase` 不直接实现数据通路;派生类需要实现:
38+
39+
```cpp
40+
virtual void OnDataOutComplete(bool in_isr, ConstRawData& data) = 0;
41+
virtual void OnDataInComplete(bool in_isr, ConstRawData& data) = 0;
42+
```
43+
44+
回调由端点传输完成触发,并由 `CDCBase` 的静态 trampoline 做 `inited_` 防护后分发。
45+
46+
### `LibXR::USB::CDCUart`
47+
48+
`CDCUart` 在 `CDCBase` 的基础上再继承 `LibXR::UART`,对上层暴露典型串口语义:
49+
50+
- `Read()`:从主机发来的 OUT 数据中读取
51+
- `Write()`:向主机发送 IN 数据
52+
- `SetConfig()`:把 UART 配置映射到 CDC Line Coding,并发送一次 Serial State 通知
53+
54+
它内部使用 `LibXR::ReadPort` / `LibXR::WritePort` 做软件缓冲与写队列管理,并在端点回调中完成数据入队/出队。
55+
56+
### `LibXR::USB::CDCWriteTest` / `LibXR::USB::CDCReadTest`
57+
58+
两者均派生自 `CDCBase`,用于验证链路吞吐与驱动稳定性:
59+
60+
- `CDCWriteTest`:当主机发送任意数据时,持续通过 Data IN 回传数据(测试设备 → 主机通路)
61+
- `CDCReadTest`:持续预装 OUT 端点接收并在完成后立即重启(测试主机 → 设备通路)
62+
63+
---
64+
65+
## 接口与端点布局
66+
67+
### 接口(Interface)
68+
69+
CDC ACM 设备以 **两接口(Communication + Data)** 的方式呈现,并带 IAD(Interface Association Descriptor),便于主机将其识别为一个 CDC 复合功能。
70+
71+
- 通信接口(Communication Interface):包含 1 个 Interrupt IN 端点(Notification Endpoint)
72+
- 数据接口(Data Interface):包含 1 个 Bulk OUT + 1 个 Bulk IN(数据收发)
73+
74+
`CDCBase::GetInterfaceNum()` 固定返回 `2`,`HasIAD()` 固定返回 `true`。
75+
76+
说明:
77+
78+
- IAD 的 `bFirstInterface` 由 `start_itf_num` 偏移得到
79+
- Communication Interface 通常是 class request 的目标接口(`wIndex` 指向该接口号)
80+
81+
### 端点(Endpoint)
82+
83+
`CDCBase::Init()` 从 `EndpointPool` 申请并配置以下端点:
84+
85+
| 端点 | 方向 | 类型 | 典型用途 |
86+
| -------- | ---- | --------- | --------------------------- |
87+
| Data OUT | OUT | BULK | 主机 → 设备 数据接收 |
88+
| Data IN | IN | BULK | 设备 → 主机 数据发送 |
89+
| Comm IN | IN | INTERRUPT | CDC 通知(Serial State 等) |
90+
91+
Comm IN 端点最大包大小固定为 16 字节;Serial State 通知本身为 10 字节结构(见下文)。
92+
93+
端点号可在构造 `CDCBase` / `CDCUart` / 测试类时指定;默认使用 `Endpoint::EPNumber::EP_AUTO` 由端点池自动分配。
94+
95+
### 速度与最大包大小
96+
97+
Data IN/OUT 的描述符 `wMaxPacketSize` 取自端点对象的 `MaxPacketSize()`。
98+
99+
- Full-Speed Bulk 典型为 64 bytes/packet
100+
- High-Speed Bulk 典型为 512 bytes/packet(若平台支持 HS)
101+
102+
实际值以平台 USB 控制器与端点实现为准。协议栈在描述符中写入端点报告的值。
103+
104+
---
105+
106+
## CDCBase 关键能力
107+
108+
### DTR/RTS 控制线状态
109+
110+
`CDCBase` 内部维护控制线状态 `control_line_state_`,并提供:
111+
112+
```cpp
113+
bool IsDtrSet() const;
114+
bool IsRtsSet() const;
115+
```
116+
117+
当收到类请求 `SET_CONTROL_LINE_STATE` 时,行为为:
118+
119+
- 更新 `control_line_state_`(来自 `wValue`
120+
- 返回 ZLP(Zero-Length Packet)确认
121+
- 调用 `SendSerialState()` 尝试通过 Comm IN 上报当前串行状态
122+
- 触发用户回调 `SetOnSetControlLineStateCallback(cb)`,参数为 `(DTR, RTS)`
123+
124+
工程建议:
125+
126+
-**DTR** 视作“主机串口已打开/准备通信”的关键信号
127+
- DTR 断开时避免继续发送,避免上层阻塞或无意义的队列堆积
128+
129+
### Line Coding(波特率/校验/停止位/数据位)
130+
131+
CDC ACM 的 Line Coding 通过类请求 `SET_LINE_CODING` / `GET_LINE_CODING` 进行读写。
132+
133+
- `GET_LINE_CODING`:设备返回当前 `line_coding_`(7 字节)
134+
- `SET_LINE_CODING`:控制传输的数据阶段写入 7 字节 `line_coding_`,随后在 `OnClassData()` 中转换为 `LibXR::UART::Configuration` 并回调上层
135+
136+
`SET_LINE_CODING` 的数据长度必须为 7 字节,不符合则返回 `ErrorCode::ARG_ERR`
137+
138+
#### Line Coding 映射规则
139+
140+
当前 `CDCBase` 将 CDC Line Coding 映射到 `LibXR::UART::Configuration` 的规则如下:
141+
142+
| CDC 字段 | 取值 | 映射到 UART 配置 |
143+
| ------------- | ---------- | ---------------------------------------------------- |
144+
| `dwDTERate` | 任意 | `cfg.baudrate = dwDTERate` |
145+
| `bCharFormat` | 0 | `stop_bits = 1` |
146+
| `bCharFormat` | 2 | `stop_bits = 2` |
147+
| `bCharFormat` | 其他 | 降级为 `stop_bits = 1``1.5 stop bits` 目前未实现) |
148+
| `bParityType` | 1 | `parity = ODD` |
149+
| `bParityType` | 2 | `parity = EVEN` |
150+
| `bParityType` | 其他 | 降级为 `NO_PARITY`(Mark/Space 将降级) |
151+
| `bDataBits` | 5/6/7/8/16 | `data_bits = bDataBits`(透传) |
152+
153+
提示:
154+
155+
- USB CDC 的 Line Coding 在多数桌面 OS 上更多是“协商/提示”,是否真正影响主机侧串口参数取决于驱动策略
156+
- 如果用于桥接真实 UART 外设,请以回调参数为准并在外设侧做合法性校验
157+
158+
### Serial State 通知
159+
160+
`SendSerialState()` 通过 Comm IN(Interrupt IN)端点向主机发送 Serial State 通知。
161+
162+
通知结构为 10 字节:
163+
164+
- 8 字节 CDC Notification Header
165+
- 2 字节 UART state 位图 `serialState`
166+
167+
代码中结构体定义为:
168+
169+
```cpp
170+
#pragma pack(push, 1)
171+
struct SerialStateNotification
172+
{
173+
uint8_t bmRequestType; // 固定 0xA1
174+
uint8_t bNotification; // 固定 SERIAL_STATE (0x20)
175+
uint16_t wValue; // 固定 0
176+
uint16_t wIndex; // Interface number(Communication Interface)
177+
uint16_t wLength; // 固定 2
178+
uint16_t serialState; // UART state bitmap
179+
};
180+
#pragma pack(pop)
181+
```
182+
183+
---
184+
185+
## 回调与执行上下文
186+
187+
`CDCBase` 对外提供两类上层回调:
188+
189+
- `SetOnSetControlLineStateCallback(LibXR::Callback<bool, bool> cb)`
190+
- `SetOnSetLineCodingCallback(LibXR::Callback<LibXR::UART::Configuration> cb)`
191+
192+
这些回调的 `Run(in_isr, ...)` 由控制传输处理路径触发;`in_isr` 用于提示调用上下文。
193+
194+
---
195+
196+
## 初始化与资源释放行为
197+
198+
### Init 行为
199+
200+
`CDCBase::Init(endpoint_pool, start_itf_num)` 的关键行为:
201+
202+
- 清零 `control_line_state_`
203+
- 通过 `EndpointPool` 申请三个端点并完成 `Configure`
204+
- 填充 IAD、Communication Interface、Data Interface 与端点描述符块
205+
- 将描述符块通过 `SetData(RawData{...})` 交给设备框架拼入配置描述符
206+
- 注册 Data OUT / Data IN 端点传输完成回调
207+
- 设置 `inited_ = true`
208+
- 启动 Data OUT 预接收:`ep_data_out_->Transfer(ep_data_out_->MaxTransferSize())`
209+
210+
提示:
211+
212+
- OUT 端点预接收的长度取决于端点实现的 `MaxTransferSize()`,用于持续接收主机数据
213+
- `CDCBase` 不对收到的数据做缓存;派生类需在 `OnDataOutComplete` 中消费并重启 OUT 传输(或按自身策略重启)
214+
215+
### Deinit 行为
216+
217+
`CDCBase::Deinit(endpoint_pool)` 的关键行为:
218+
219+
- `inited_ = false`
220+
- 清零 `control_line_state_`
221+
- 关闭三端点、清零 active length
222+
- 将端点归还给 `EndpointPool`
223+
- 置端点指针为空
224+
225+
派生类或上层适配类在 `Deinit()` 时应确保:
226+
227+
- 终止所有依赖端点对象的异步操作
228+
- 对外完成或失败掉未完成的读写请求,避免上层永久等待
229+
230+
---
231+
232+
## 使用示例
233+
234+
### 作为 CDC 虚拟串口使用(推荐:`CDCUart`
235+
236+
```cpp
237+
#include "cdc_uart.hpp"
238+
239+
LibXR::USB::CDCUart cdc_uart(/*rx*/256, /*tx*/256, /*tx_queue*/8);
240+
241+
// 设备构造时把 &cdc_uart 放入 class 列表:{{&cdc_uart}}
242+
// usb_dev.Init();
243+
// usb_dev.Start();
244+
```
245+
246+
可选:监听主机对 Line Coding / DTR/RTS 的变化:
247+
248+
```cpp
249+
cdc_uart.SetOnSetLineCodingCallback(
250+
LibXR::Callback<LibXR::UART::Configuration>(
251+
[](bool in_isr, LibXR::UART::Configuration cfg) {
252+
(void)in_isr;
253+
// 可在此同步到真实 UART 外设(注意 ISR 场景下不要阻塞)
254+
}
255+
)
256+
);
257+
258+
cdc_uart.SetOnSetControlLineStateCallback(
259+
LibXR::Callback<bool, bool>(
260+
[](bool in_isr, bool dtr, bool rts) {
261+
(void)in_isr;
262+
(void)rts;
263+
// dtr=true 表示主机已打开串口,可开始发送
264+
}
265+
)
266+
);
267+
```
268+
269+
### 吞吐测试
270+
271+
写测试:
272+
273+
```cpp
274+
#include "cdc_test.hpp"
275+
LibXR::USB::CDCWriteTest cdc_write_test;
276+
```
277+
278+
读测试:
279+
280+
```cpp
281+
#include "cdc_test.hpp"
282+
LibXR::USB::CDCReadTest cdc_read_test;
283+
```
284+
285+
同样通过 USB Device 的 class 列表传入即可。

0 commit comments

Comments
 (0)