Skip to content

Commit ad06071

Browse files
committed
docs: add SWD debug docs and refresh XRUSB stack documentation
1 parent 8d1a9f8 commit ad06071

21 files changed

Lines changed: 1447 additions & 1161 deletions

File tree

docs/about.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: about
33
title: 关于
4-
sidebar_position: 11
4+
sidebar_position: 12
55
---
66

77
# 关于本项目

docs/adv_coding/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: adv-coding
33
title: 进阶编程
4-
sidebar_position: 9
4+
sidebar_position: 10
55
---
66

77
# 进阶编程

docs/con_guide/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
id: adv-coding
2+
id: con-guide
33
title: 贡献指南
4-
sidebar_position: 10
4+
sidebar_position: 11
55
---
66

77
# 贡献指南

docs/debug/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
id: debug
3+
title: 调试接口
4+
sidebar_position: 8
5+
---
6+
7+
# 调试接口
8+
9+
本章将介绍如何使用本项目的调试接口,包括 SWD、JTAG 等。

docs/debug/swd/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
id: debug-swd
3+
title: SWD调试接口
4+
sidebar_position: 1
5+
---
6+
7+
# SWD调试接口
8+
9+
本章将介绍如何使用本项目的 SWD 调试接口实现。

docs/debug/swd/swd-gpio.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
id: debug-swd-gpio
3+
title: SWD GPIO实现
4+
sidebar_position: 2
5+
---
6+
7+
# SWD GPIO实现
8+
9+
本文档描述 `LibXR::Debug::SwdGeneralGPIO<SwclkGpioType, SwdioGpioType>`:一个基于 GPIO 轮询(bit-bang)的 SWD 探针实现。它继承自 `LibXR::Debug::Swd`,提供 SWD 链路层能力,通常由上层(如 CMSIS-DAP 处理器/调试器)调用。
10+
11+
本文重点放在“如何使用”和“延时参数(loops_per_us)的选择/标定”,不展开实现细节。
12+
13+
---
14+
15+
## 1. 总览
16+
17+
`SwdGeneralGPIO` 使用两根 GPIO 完成 SWD:
18+
19+
- SWCLK:由 `SwclkGpioType` 驱动输出时钟。
20+
- SWDIO:由 `SwdioGpioType` 承担双向数据,运行时在“输出驱动”和“输入采样”之间切换。
21+
22+
时序由软件生成:通过目标时钟 `hz` 和延时标定系数 `loops_per_us` 计算“半周期应空转的循环次数”,从而逼近指定 SWCLK。
23+
24+
推荐外围电路(强烈建议按此做):
25+
- SWCLK/SWDIO 串联 33Ω(限流/抑制振铃)
26+
- SWDIO 端接 10k 上拉
27+
28+
---
29+
30+
## 2. 模板参数与 GPIO 依赖能力
31+
32+
类定义:
33+
34+
```cpp
35+
template <typename SwclkGpioType, typename SwdioGpioType>
36+
class SwdGeneralGPIO final : public Swd;
37+
```
38+
39+
期望 GPIO 类型提供的最小能力(抽象描述):
40+
- `SetConfig({Direction, Pull}) -> ErrorCode`
41+
- `Write(bool)`
42+
- `Read() -> bool`
43+
44+
其中 SWDIO 需要支持两种配置:
45+
- 输出驱动:`OUTPUT_PUSH_PULL`
46+
- 输入采样:`INPUT + PULL_UP`
47+
48+
---
49+
50+
## 3. 快速开始
51+
52+
构造函数:
53+
54+
```cpp
55+
explicit SwdGeneralGPIO(SwclkGpioType& swclk,
56+
SwdioGpioType& swdio,
57+
uint32_t loops_per_us,
58+
uint32_t default_hz = DEFAULT_CLOCK_HZ);
59+
```
60+
61+
典型使用流程:
62+
1) 初始化 GPIO 对象与探针实例(先给一个“保守”的频率与已标定的 `loops_per_us`)。
63+
2) 对目标执行 `EnterSwd()`(不确定目标当前处于何种调试模式时建议调用)。
64+
3) 上层用 `Swd` 基类的“带重试”接口进行传输(处理 WAIT、插入 idle clocks 等)。
65+
66+
示例:
67+
68+
```cpp
69+
using Probe = LibXR::Debug::SwdGeneralGPIO<MyGpio, MyGpio>;
70+
71+
MyGpio swclk, swdio;
72+
73+
// loops_per_us 建议先标定;default_hz 从保守值起步(如 100k~500k)
74+
Probe probe(swclk, swdio, /*loops_per_us=*/calibrated, /*default_hz=*/500000);
75+
76+
probe.EnterSwd();
77+
78+
// 上层建议统一走带重试的路径(示意,具体看你的 Swd 基类封装)
79+
uint32_t idcode = 0;
80+
LibXR::Debug::SwdProtocol::Ack ack;
81+
probe.ReadIdCode(idcode, ack);
82+
```
83+
84+
---
85+
86+
## 4. 时钟与延时参数(重点)
87+
88+
### 4.1 `hz` 的含义与行为
89+
90+
`SetClockHz(hz)` 用于设置“期望的 SWCLK 频率”。
91+
92+
- `hz` 会在实现支持的范围内被钳制(低于最小值会被抬高,高于最大值会被压低)。
93+
- 频率是否“真的等于设定值”,取决于 `loops_per_us` 是否合理、GPIO 翻转速度、CPU 负载等。
94+
- `hz == 0` 会使内部进入“无延时路径”(不主动插入 BusyLoop 延时),此时 SWCLK 实际频率由 CPU 与 GPIO 翻转速度决定,通常是“尽可能快”。
95+
96+
### 4.2 `loops_per_us` 是什么
97+
98+
`loops_per_us` 是一个“延时标定系数”:表示 BusyLoop 大约每 1 微秒需要的循环迭代次数。
99+
100+
它不是通用常量,通常会随以下因素显著变化:
101+
- CPU 主频
102+
- 编译优化等级(`-O0/-O2/-Os` 等)
103+
- 是否开启 LTO、不同编译器版本
104+
- 指令/总线等待状态(某些 MCU 在不同电源/时钟域下也可能变化)
105+
106+
只要上述条件变化,就应重新标定 `loops_per_us`。
107+
108+
### 4.3 何时会进入“无延时路径”
109+
110+
实际工作时有两种情况会进入“无延时路径”:
111+
- 你显式把 `loops_per_us` 设为 0;或
112+
- 在某个较高的 `hz` 下,计算出来“半周期需要的循环次数 < 1”,内部会把半周期循环数置 0,从而自动走无延时路径。
113+
114+
无延时路径的意义:
115+
- 优点:开销更低,吞吐更高。
116+
- 代价:SWCLK 频率不再受 `hz` 精准约束,而是“尽可能快/尽可能稳定”,以平台极限为准。
117+
118+
工程建议:
119+
- 需要“可控且可复现”的 SWCLK:让 `half_period_loops_` 明确大于 0(即 `loops_per_us` 标定合理,且 `hz` 不要设得过高)。
120+
- 只追求“能跑、尽量快”:可以把 `loops_per_us=0`,或把 `hz` 调到足够高让系统自然进入无延时。
121+
122+
---
123+
124+
## 5. `loops_per_us` 的选择与标定方法
125+
126+
下面给出两种常用标定方法。原则是:标定应在与你最终固件“相同编译选项 + 相同主频”条件下进行。
127+
128+
### 方法 A:用硬件计时器/周期计数器标定
129+
130+
思路:执行一次已知循环次数的 BusyLoop,测出耗时,再反推 `loops_per_us`。
131+
132+
操作要点:
133+
1) 选择足够大的循环次数 N(例如 1e6 级别),保证测量时间明显大于计时器分辨率。
134+
2) 记录开始时间 t0,执行 BusyLoop(N),记录结束时间 t1。
135+
3) 计算耗时 `dt_us`(微秒)。
136+
4) `loops_per_us ≈ N / dt_us`(取整即可)。
137+
138+
优点:不依赖 SWD 外设、也不依赖目标板状态;标定完成后频率可预期。
139+
140+
### 方法 B:用逻辑分析仪/示波器对 SWCLK 实测回归
141+
142+
思路:先给一个粗略的 `loops_per_us`,设置一个低到中等的 `hz`(例如 100 kHz 或 500 kHz),测 SWCLK 实际频率,再调整 `loops_per_us` 使之贴合。
143+
144+
操作要点:
145+
1) 先用“可确保进入有延时路径”的配置(例如较低 `hz`)。
146+
2) 测量 SWCLK 频率 `f_meas`。
147+
3) 按比例调整:`loops_per_us_new ≈ loops_per_us_old * (f_meas / f_target)`,反复迭代 1~2 次即可收敛。
148+
149+
优点:不需要访问计时器资源;同时把 GPIO 翻转开销一并纳入拟合。
150+
注意:当你已经进入无延时路径时(频率很高),这种方法会失效,因为再调 `loops_per_us` 也未必能“拉回”到有延时路径。
151+
152+
---
153+
154+
## 6. 频率选择建议(实践导向)
155+
156+
建议从保守到激进逐步上调,避免一上来就把链路推到边界:
157+
158+
- 初始 bring-up:100 kHz ~ 500 kHz(更容易排查连线/电气问题)
159+
- 稳定后提速:1 MHz、2 MHz、4 MHz 分档尝试
160+
- 如果需要更高:优先确认走线长度、阻尼电阻、地参考与目标板 SWD 引脚驱动能力;然后再上调
161+
162+
当出现以下症状,通常意味着频率过高或信号完整性不足:
163+
- ACK 频繁出现 WAIT/FAULT/NO_ACK
164+
- 读回数据 parity 失败
165+
- 只在某些线长/某些板子上不稳定
166+
167+
处理顺序建议:
168+
1) 先降频;
169+
2) 确认 33Ω 串阻与 SWDIO 上拉;
170+
3) 缩短线缆/改善接地;
171+
4) 再考虑重新标定 `loops_per_us`(尤其在改了优化选项之后)。
172+
173+
---
174+
175+
## 7. 关闭与安全态
176+
177+
调用 `Close()` 会把探针引脚恢复到更安全的状态(用于退出调试或切换复用功能时):
178+
- SWCLK 置高
179+
- SWDIO 切到输入上拉
180+
181+
---

0 commit comments

Comments
 (0)