# STM32F103C8T6 USB CDC 在 72MHz 下停止模式唤醒后断开（HSE 8MHz）—— 求助

**URL:** <https://bbs.eeclub.top/t/topic/249>\
**Category:** 嵌入式系统 / Embedded Systems\
**Tags:** 问答, MCU, STM32, USB\
**Created:** [2025 年12 月 7 日 09:55 UTC](https://bbs.eeclub.top/t/topic/249 "2025-12-07T09:55:48Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

作者： ![小聪同学](https://bbs.eeclub.top/user_avatar/bbs.eeclub.top/%E5%B0%8F%E8%81%AA%E5%90%8C%E5%AD%A6/32/590_2.png) [@小聪同学](https://bbs.eeclub.top/u/%E5%B0%8F%E8%81%AA%E5%90%8C%E5%AD%A6)\
发布日期： [2025 年12 月 7 日 09:55 UTC](https://bbs.eeclub.top/t/topic/249/1 "2025-12-07T09:55:48Z")

</div>

大家好，

我正在调试 STM32F103C8T6 开发板上一个间歇性的 USB CDC 问题。从停止模式唤醒后，电脑有时会丢失虚拟串口（除非硬复位，否则不会重新枚举）。希望大家能帮忙检查我的时钟 / USB 初始化序列是否合理，以及是否有经过验证的解决方案。

## 硬件及器件

- MCU：STM32F103C8T6，基于 8MHz HSE 通过 PLL 倍频 9 倍运行在 72MHz。
- 开发板：“小蓝板”（Blue Pill）版型；LDO 将 5V 转为 3.3V，VDD 引脚附近配备 0.1µF+1µF 去耦电容，8MHz HC-49 晶振搭配 22pF 负载电容。
- USB：全速设备，D + 通过 1.5kΩ 电阻上拉至 3.3V；D+/D - 线路串联 22Ω 电阻。

## 故障现象

- 首次上电：USB CDC 在 Windows 10 和 Ubuntu 22 系统上均能正常枚举。
- 进入停止模式约 5-30 秒后唤醒，主机有时无法识别 CDC 设备（无新的 PID/VID 事件，串口消失）。插拔 USB 线不一定有效，按下 NRST 复位引脚总能恢复。复现率约 10%-30%。

## 已尝试的操作

1. 停止模式唤醒后，重新使能 HSE/PLL，再重启 USB 时钟。
2. 切换 USB 外设时钟，并执行 USBD\_DeInit ()→USBD\_Init () 序列。
3. 时钟切换后确保调用 SystemCoreClockUpdate () 函数。
4. 在 RCC 和 USB 初始化步骤之间延长 1-10ms 延时。
5. 用示波器检测 3.3V 电源纹波：唤醒期间纹波 \< 20mVpp；D + 空闲状态约 3.3V。

## 时钟 / RCC 配置序列（HAL 库）

```c
// 从停止模式唤醒：重新使能HSE/PLL，并将SYSCLK切换回PLL时钟
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

HAL_RCC_OscConfig(&(RCC_OscInitTypeDef){
  .OscillatorType = RCC_OSCILLATORTYPE_HSE,
  .HSEState = RCC_HSE_ON,
  .PLL.PLLState = RCC_PLL_ON,
  .PLL.PLLSource = RCC_PLLSOURCE_HSE,
  .PLL.PLLMUL = RCC_PLL_MUL9
});

HAL_RCC_ClockConfig(&(RCC_ClkInitTypeDef){
  .ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2,
  .SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
  .AHBCLKDivider = RCC_SYSCLK_DIV1,
  .APB1CLKDivider = RCC_HCLK_DIV2,
  .APB2CLKDivider = RCC_HCLK_DIV1
}, FLASH_LATENCY_2);

SystemCoreClockUpdate();

```

## USB 重新初始化代码片段

```c
// 时钟稳定后完全重启设备栈
USBD_DeInit(&hUsbDeviceFS);
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();

USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);

```

## 观察结果

- 若跳过停止模式，仅在运行模式↔睡眠模式之间切换，系统稳定。
- 唤醒后若将 SYSCLK 保持在 HSI（不启用 PLL），系统似乎更稳定，但我需要 72MHz 时钟满足时序要求。
- 通过 GPIO 将 D + 拉低约 10ms（模拟断开）可提高恢复概率，但无法达到 100%。

## 求助问题

1. 对于 F103 芯片，停止模式唤醒后，是否有标准的使能顺序 / 时序（HSE→PLL→USBFS）可避免 CDC 设备异常？
2. 唤醒后强制 USB “软断开”（D + 拉低）是否属于 F1 系列的最佳实践，或是否有更优的协议栈级复位方法？
3. 是否存在已知勘误，涉及停止模式后 HSE 启动 + PLL 锁定影响 USB SOF（帧同步信号）或 48MHz 时钟域？
4. 推荐在 USB 时钟恢复时先使用 HSI，再切换到 PLL，还是始终优先稳定 PLL？
5. 您遇到过哪些板级问题可能导致该现象：D + 电阻容差、ESD 二极管 / 泄漏、晶振负载过重、LDO 瞬态响应不良等？

---

<div class="post-metadata">

作者： ![CCC](https://bbs.eeclub.top/user_avatar/bbs.eeclub.top/ccc/32/595_2.png) [@CCC](https://bbs.eeclub.top/u/CCC)\
发布日期： [2025 年12 月 7 日 10:02 UTC](https://bbs.eeclub.top/t/topic/249/2 "2025-12-07T10:02:46Z")

</div>

你好，

这是一个F1系列设备在 **STOP模式** 后USB重新枚举时的经典且极其令人困扰的问题！你绝对没有发疯，而且已经尝试了所有标准的故障排除步骤。间歇性现象（10–30%复现率）表明在复杂的时钟重新配置过程中存在微妙的时序/亚稳态问题。

以下是本人的分析和建议重点，特别是围绕USB外设的时钟域和所需的48 MHz时钟稳定性。

* * *

## ⏰ 时钟序列完整性检查与48 MHz时钟域

你的 **RCC** 序列在重新启用HSE/PLL时看起来标准且正确。问题可能不在PLL本身，而在于USB外设（需要由 **PLL / 1.5** 分频得到的 **48 MHz** 时钟）如何处理从断电到全速运行的过渡。

1. **USB时钟源限制** ：USB全速时钟_必须_来自PLL，具体为 **PLLCLK / 1.5** 以达到$48\text{ MHz}$。即使PLL短暂不稳定，也可能导致设备发送的前几个\*\*帧起始（SOF）\*\*数据包损坏，使主机PC忽略设备（僵尸CDC）。
2. **复位时序** ：`__HAL_RCC_USB_FORCE_RESET()`和`__HAL_RCC_USB_RELEASE_RESET()`序列是正确的，但相对于唤醒后的 **PLL锁定时间** ，复位脉冲可能_不够长_或_时序不正确_。F103的数据手册显示HSE启动时间和PLL锁定时间是分开的。你当前的操作流程为：
  - 唤醒 → 启动HSE/PLL
  - … (PLL锁定) …
  - 使能USB时钟 + 复位脉冲
  - … (立即执行USBD\_Init) …

如果在释放USB复位时PLL_刚刚_稳定，初始时钟仍可能处于临界状态。 **2ms** 延迟可能不足以确保整个PLL/USB时钟链完全稳定。

## 🔨 推荐解决方案：软断开/重新连接操作

你观察到将D+拉低有助于恢复是关键。强制物理断开/重新连接通常是向主机PC发出设备消失并重新出现的唯一可靠方式。

**工作原理** ：USB枚举由主机处理。如果设备在唤醒周期时钟不稳定，主机可能看到无响应设备（或垃圾数据），从而直接丢弃设备，保持端口处于“暂停”或“错误”状态，等待 **物理** 复位（你的NRST提供）。软断开/重新连接会强制主机USB控制器重启端口枚举状态机。

建议在时钟稳定后执行以下序列：

1. **确保时钟稳定** ：确认HSE/PLL完全稳定且$\text{SYSCLK} = 72\text{ MHz}$（当前RCC配置）。
2. **USB禁用（软断开）** ：
  - 禁用内部D+上拉（若使用内部上拉）。对于Blue Pill/外部1.5k$\Omega$上拉到D+的情况，需 **使用GPIO主动将D+拉低** 一段时间。
  - 将D+引脚配置为 **GPIO输出低电平** 。
  - `HAL_Delay(10);`（10ms是常见的可靠最小断开时间）。

3. **USB重新初始化（重新连接）** ：
  - 将D+引脚重新配置为USB功能（或重新启用内部上拉）。
  - 执行完整的USB重新初始化序列：

```c
USBD_DeInit(&hUsbDeviceFS);
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(2); // 仍建议保留
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();

USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
// ... (其余初始化代码)
USBD_Start(&hUsbDeviceFS);

```

  - 主机现在应检测到D+上拉电阻并开始重新枚举。

**这是最佳实践吗** ？对于F1系列而言，是的——在从STOP模式唤醒后强制软断开以实现可靠的USB重新枚举是众所周知且实用的解决方案。

* * *

## 🔎 板级潜在问题

虽然你的电源轨看起来干净（$\lt20\text{ mVpp}$纹波极佳），但仍需考虑以下因素：

- **晶体负载电容（22 pF）** ：对于8 MHz HC-49晶体，$22\text{ pF}$属于较高值，可能略微增加HSE启动时间或影响唤醒后的频率稳定性。大多数8 MHz晶体推荐$10\text{ pF}$至$15\text{ pF}负载电容。虽然可能性较低，但建议查阅晶体手册确认推荐的\text{C}\_{\text{L}}$（负载电容）。
- **D+电阻容差** ：你的$1.5\text{ k}\Omega$上拉电阻正确。确保其阻值未显著偏离（例如$\text{+/-} 5%$），因为该阻值向主机表明设备为全速模式。
- **ESD二极管/漏电流** ：若D+/D-上使用外部ESD保护二极管，需确保其未引入显著电容或漏电流，这可能干扰$3.3\text{ V}$信号完整性。

请优先尝试D+ GPIO软断开/重新连接方案。它直接解决了主机与设备唤醒状态不同步的根本问题。

---

<div class="post-metadata">

作者： ![ABC](https://bbs.eeclub.top/user_avatar/bbs.eeclub.top/abc/32/101_2.png) [@ABC](https://bbs.eeclub.top/u/ABC)\
发布日期： [2025 年12 月 7 日 10:14 UTC](https://bbs.eeclub.top/t/topic/249/3 "2025-12-07T10:14:36Z")

</div>

你好。这在 F103 + Blue Pill 上是个经典坑，主要原因在于 **Host 端没能识别到设备断开** ，以及 Blue Pill 的硬件设计缺陷。

针对你的问题，直接给解决方案：

**1. 核心原因：D+ 上拉电阻与软连接**  
Blue Pill 开发板的 D+ 上拉电阻（R10）通常直接焊在 3.3V 上，而不是受 MCU GPIO 控制。

- **现象：** 当 MCU 进入停止模式或复位时，D+ 依然通过电阻被拉高。主机（PC）认为设备一直连接且空闲，不会重新枚举。
- **你的操作：** 你提到“拉低 10ms”，这 **时间太短** 了。PC 端的 USB 协议栈（尤其是 Windows）通常需要检测到 D+ 低电平持续 **100ms 以上** （建议 500ms-1s）才会判定为设备断开（SE0 状态）。

**2. 解决方案：强制重枚举序列**  
在 `SystemCoreClockUpdate()` 之后，USB 初始化之前，加入这段“暴力”重置代码：

```c
// 1. 强制配置 PA12 (D+) 为 GPIO 输出低电平，模拟拔线
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); // 拉低 D+

// 2. 延时足够长的时间让 Host 识别断开
HAL_Delay(600); // 建议 500ms - 1000ms，10ms 绝对不够

// 3. 释放引脚，交还给 USB 外设控制（此时上拉电阻会再次拉高 D+，触发 Host 枚举）
// 注意：后续的 USBD_Init 会重新配置引脚为复用功能，这里只要不再强制输出低即可

```

**3. 关于时钟配置的补充**  
F103 的 USB 时钟必须是 48MHz。

- **检查点：** 确保 RCC 配置中 `RCC_USBCLKSOURCE_PLL_DIV1_5` 被正确设置。
- 72MHz (SysClk) / 1.5 = 48MHz。如果这步错了，USB 会因为时序不对而无法通讯，即使枚举了也会报“无法识别的设备”。

**4. 针对你问题的快速回答**

- **时序顺序：** HSE -\> PLL -\> Flash Latency -\> SysClk -\> **强制 D+ 拉低 600ms** -\> USB Init。
- **最佳实践：** 对于没有三极管控制上拉电阻的板子，软件控制 PA12 拉低是唯一的“软断开”标准做法。
- **勘误：** F103 停止模式唤醒通常没有会导致 USB 彻底挂掉的硅片勘误，问题主要出在 Host 状态机没复位。

**建议下一步：**  
把那段 10ms 的拉低延时改成 600ms，问题应该就能解决。

---

<div class="post-metadata">

作者： ![海阔天空](https://bbs.eeclub.top/user_avatar/bbs.eeclub.top/%E6%B5%B7%E9%98%94%E5%A4%A9%E7%A9%BA/32/591_2.png) [@海阔天空](https://bbs.eeclub.top/u/%E6%B5%B7%E9%98%94%E5%A4%A9%E7%A9%BA)\
发布日期： [2025 年12 月 7 日 10:57 UTC](https://bbs.eeclub.top/t/topic/249/4 "2025-12-07T10:57:04Z")

</div>

针对你的问题，我遇到过类似情况。从停止模式唤醒后USB CDC异常，通常是时钟恢复时序和USB外设状态未完全复位导致的。以下是关键要点和建议：

**1. 时钟恢复顺序与勘误**  
F103在停止模式唤醒后，默认使用HSI（8MHz）作为系统时钟。你重新启用HSE和PLL的步骤是正确的，但存在一个关键时序： **必须等待PLL锁定后，再使能USB时钟** 。USB FS需要精确的48MHz时钟（由PLL提供）。如果USB时钟在PLL不稳定时被启用，会导致USB PHY工作异常，主机无法识别。

建议在`HAL_RCC_OscConfig`后，增加检查PLLRDY标志的等待：

```c
while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET);

```

然后再执行`HAL_RCC_ClockConfig`。

**2. USB外设的彻底复位**  
你执行的`USBD_DeInit`和强制复位序列是必要的，但顺序可以优化。ST的勘误表（针对某些F1型号）指出，从停止模式唤醒后，USB外设可能需要一个更长的复位脉冲。建议将USB时钟禁用、复位、再使能的延时增加到至少10ms。

更可靠的做法是，在唤醒后先 **保持USB时钟禁用** ，等系统时钟完全稳定（PLL锁定且SYSCLK切换完成）后，再执行以下硬复位序列：

```c
__HAL_RCC_USB_CLK_DISABLE();
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(10);
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();
// 然后再执行USBD_Init等初始化

```

**3. 关于“软断开”DP拉低**  
强制DP拉低10ms模拟断开是有效的“土办法”，也确实是F1系列常用的最佳实践之一。这能确保主机检测到物理断开并触发重新枚举。为了提高成功率，建议在拉低DP前，先确保USB外设时钟已停止，并在拉低后保持足够时间（10-20ms）。这不是协议栈级的方法，但硬件上最直接。

**4. 时钟源切换建议**  
**不要** 在USB工作时切换到HSI。正确的做法是：唤醒后，目标就是快速稳定地恢复原来的72MHz PLL时钟。应优先稳定PLL，确保其锁定，然后再处理USB。在PLL稳定前，不要让USB外设有任何时钟。

**5. 板级可能原因**  
根据你的描述，硬件问题可能性较低，但可以排查：

- **晶振负载电容** ：22pF对于8MHz HC-49是典型值，但唤醒时HSE启动慢可能导致PLL锁定时间变长。可尝试将电容减小到18-20pF，并检查晶振两端波形是否干净。
- **USB信号线** ：串联22Ω电阻是标准的，确保D+的上拉电阻（1.5kΩ）直接来自MCU的3.3V，且电源在唤醒瞬态能保持稳定。
- **电源** ：唤醒瞬间电流增大可能导致LDO轻微跌落。虽然你测的纹波很小，但可以在VDD与地之间增加一个10µF的钽电容，增强瞬态响应。

**总结与推荐步骤**

1. **唤醒后时钟恢复** ：启用HSE → 等待HSE就绪 → 启用PLL → **等待PLL锁定** → 切换SYSCLK到PLL → 更新`SystemCoreClock`。
2. **彻底复位USB** ：先禁用USB时钟，强制复位并保持10ms以上，再释放复位并重新使能时钟。
3. **重新初始化USB栈** ：调用`USBD_DeInit`和`USBD_Init`序列。
4. **可选“软断开”** ：如果上述步骤后问题仍间歇出现，在USB重新初始化前，将DP（PA12）配置为推挽输出并拉低至少15ms，然后再初始化和上拉。

按这个顺序操作，应该能大幅提高唤醒后USB CDC的恢复可靠性。

---

<div class="post-metadata">

作者： ![duch](https://bbs.eeclub.top/user_avatar/bbs.eeclub.top/duch/32/2088_2.png) [@duch](https://bbs.eeclub.top/u/duch)\
发布日期： [2026 年10 月 22 日 09:29 UTC](https://bbs.eeclub.top/t/topic/249/5 "2026-10-22T09:29:56Z")

</div>

值得检查的一点是，USB 外设在退出 STOP 模式后是否已完全重新初始化。在 STM32F103C8T6 上，STOP 模式唤醒后系统时钟会切回 HSI，因此在重新启动 USB 设备栈之前，应恢复 HSE 和 PLL，并确认 USB 的 48 MHz 时钟。另外，还要检查 USB 断开/重新连接时序，并确保主机有足够时间检测到重新枚举。供参考，你可以在 [Xecor 上找到 STM32F103C8T6 的规格和元器件信息](https://www.xecor.com/product/stm32f103c8t6)。
