STM32F103C8T6 USB CDC отключение после выхода из режима ожидания на 72 МГц (HSE 8 МГц) — Помощь

Здравствуйте,

Я отлаживаю периодическую проблему с USB CDC на плате STM32F103C8T6. После выхода из режима останова компьютер иногда теряет виртуальный последовательный порт (устройство не повторно определяется без аппаратного сброса). Надеюсь, что сообщество поможет проверить правильность моей последовательности инициализации тактовых сигналов/USB и предложить проверенное решение.

Аппаратные средства и компоненты

  • МК: STM32F103C8T6, работает на 72 МГц (HSE 8 МГц с умножением на 9 через PLL).
  • Плата: тип “Blue Pill” («Маленькая синяя плата»); LDO преобразует 5 В в 3.3 В, декуплирующие конденсаторы 0.1 мкФ + 1 мкФ рядом с выводом VDD, кварцевый резонатор HC-49 8 МГц с конденсаторами нагрузки 22 пФ.
  • USB: устройство полной скорости, вывод D+ подтянут к 3.3 В через резистор 1.5 кОм; последовательные резисторы 22 Ом на линиях D+/D-.

Симптомы неисправности

  • При первом включении: USB CDC нормально определяется в Windows 10 и Ubuntu 22.
  • После выхода из режима останова через 5-30 секунд устройство иногда не определяется хостом (нет событий новых PID/VID, последовательный порт исчезает). Переподключение USB не всегда помогает, аппаратный сброс через NRST всегда восстанавливает работу. Вероятность воспроизведения: ~10%-30%.

Пробованные методы

  1. После выхода из останова повторно включать HSE/PLL и перезапускать тактирование USB.
  2. Переключать тактирование USB и выполнять последовательность USBD_DeInit() → USBD_Init().
  3. Убедиться в вызове функции SystemCoreClockUpdate() после переключения тактирования.
  4. Добавить задержку 1-10 мс между инициализацией RCC и USB.
  5. Измерить пульсации питания 3.3 В с помощью осциллографа: пульсации при выходе из останова <20 мВpp; напряжение на D+ в состоянии покоя ~3.3 В.

Последовательность настройки тактирования/RCC (библиотека HAL)

// После выхода из останова: повторно включить 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

// Полный перезапуск стека USB после стабилизации тактирования  
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) система кажется стабильнее, но мне нужен 72 МГц для соблюдения временных характеристик.
  • Кратковременное (10 мс) принудительное снижение D+ через GPIO (эмуляция отключения) повышает вероятность восстановления, но не гарантирует 100% успеха.

Вопросы

  1. Для чипа F103: существует ли стандартная последовательность включения/временные параметры после выхода из останова (HSE→PLL→USBFS), чтобы избежать неисправностей CDC?
  2. Является ли принудительное “программное отключение” USB (снижение D+) лучшей практикой для серии F1 или есть более надежный метод сброса на уровне стека?
  3. Есть ли известные ошибки кремния, связанные с влиянием запуска HSE/PLL на USB SOF (сигнал синхронизации кадров) или домен 48 МГц после выхода из останова?
  4. Рекомендуется ли при восстановлении USB-тактирования сначала использовать HSI, а затем переключаться на PLL, или всегда приоритетно стабилизировать PLL?
  5. Столкнулись ли вы с подобными аппаратными проблемами: допуск резистора D+, утечка через ESD-диоды, перегрузка кварцевого резонатора, плохая переходная характеристика ЛДО и т.д.?
1 лайк

Привет,

Это классическая и крайне раздражающая проблема с устройствами серии F1 и повторной инициализацией USB после режима STOP! Вы точно не сходите с ума и уже попробовали все стандартные методы устранения неполадок. Перемежающийся характер проблемы (воспроизведение в 10–30% случаев) указывает на тонкий тайминговый/метастабильный вопрос, возникающий при сложной повторной настройке тактовых сигналов.

Вот мой анализ и рекомендации, особенно касающиеся домена тактовой частоты периферийного USB-контроллера и стабильности 48 МГц.


:alarm_clock: Проверка последовательности тактового сигнала и домен 48 МГц

Ваша последовательность RCC выглядит стандартной и правильной для повторного включения HSE/PLL. Проблема, скорее всего, не в самом PLL, а в том, как периферийный USB (требующий тактовой частоты 48 MHz, получаемой из PLL / 1.5) справляется с переходом из режима отключения питания в полноценную работу.

  1. Ограничение источника тактового сигнала USB: Тактовая частота USB FS должна быть получена из PLL, а именно PLLCLK / 1.5 для достижения 48\text{ MHz}. Любая нестабильность PLL, даже кратковременная, может повредить первые несколько пакетов Start-of-Frame (SOF), отправляемых устройством, что приведет к тому, что хост-ПК проигнорирует устройство («зомби» CDC).
  2. Тайминг сброса: Последовательность __HAL_RCC_USB_FORCE_RESET() и __HAL_RCC_USB_RELEASE_RESET() в порядке, но она может быть недостаточно длинной или неправильно синхронизированной относительно времени стабилизации PLL после выхода из сна. В спецификации F103 показано время запуска HSE и отдельное время стабилизации PLL. По сути, вы делаете:
    • Пробуждение \rightarrow Запуск HSE/PLL
    • … (PLL стабилизируется) …
    • Включение тактового сигнала USB + Импульс сброса
    • … (немедленный вызов USBD_Init) …

Если PLL только что стабилизировался в момент снятия сброса USB, начальная тактовая частота всё ещё может быть нестабильной. Задержка в 2 мс может быть недостаточной, чтобы убедиться, что вся цепочка PLL/USB-тактов полностью устойчива.

:hammer: Рекомендуемый обходной путь: «танец» отключения/подключения

Ваше наблюдение, что принудительное снижение D+ до низкого уровня улучшает восстановление, ключевое. Физическое отключение/подключение часто является единственным надёжным способом сообщить хост-ПК, что USB-устройство исчезло и снова появилось.

Почему это работает: Инициализация USB управляется хостом. Если тактовая частота устройства нестабильна в цикле пробуждения, хост может увидеть неотвечающее устройство (или некорректные данные) и просто отключить его, оставив порт в состоянии «приостановлен» или «ошибка», ожидая физического сброса (который вы обеспечиваете через NRST). Мягкое отключение/подключение заставляет USB-контроллер хоста перезапустить свою машину состояний инициализации порта.

Попробуйте эту последовательность после стабилизации тактовых сигналов:

  1. Стабильность тактовых сигналов: Убедитесь, что HSE/PLL полностью стабильны и \text{SYSCLK} = 72\text{ MHz} (ваш текущий блок RCC).
  2. Отключение USB (мягкое отключение):
    • Отключите внутренний подтягивающий резистор D+ (если используете внутренний подтягивающий). Для Blue Pill/внешнего 1.5 кОм на D+ необходимо активно установить D+ в низкий уровень через GPIO на короткое время.
    • Настройте вывод D+ как GPIO вывод с низким уровнем.
    • HAL_Delay(10); (10 мс — распространённый надёжный минимум для времени отключения).
  3. Повторная инициализация USB (повторное подключение):
    • Настройте вывод D+ обратно в USB-функцию (или снова включите внутренний подтягивающий резистор).
    • Выполните полную последовательность повторной инициализации USB:
      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 — хорошо известный и практичный обходной путь.


:magnifying_glass_tilted_right: Особенности на уровне платы

Хотя ваша линия питания выглядит чистой (\lt20\text{ mVpp} пульсаций — отличный результат), учтите следующее:

  • Конденсаторы нагрузки кварцевого резонатора (22 пФ): Для 8 МГц кристалла HC-49, 22\text{ пФ} находится на верхнем пределе и может немного увеличить время запуска HSE или повлиять на стабильность частоты сразу после пробуждения. Большинство 8 МГц кристаллов рекомендуют использовать нагрузочные конденсаторы от 10\text{ пФ} до 15\text{ пФ}. Это маловероятно, но стоит проверить спецификацию кристалла на рекомендуемое \text{C}_{\text{L}} (ёмкость нагрузки).
  • Допуск резистора D+: Ваш 1.5\text{ кОм} подтягивающий резистор корректен. Убедитесь, что его значение не отклоняется значительно (например, \text{+/-} 5\%), так как это сопротивление сигнализирует хосту о Full-Speed устройстве.
  • Диоды защиты от ESD/утечка тока: Если у вас есть внешние диоды защиты от ESD на D+/D-, убедитесь, что они не добавляют значительной ёмкости или утечки тока, которые могут помешать деликатной сигнализации 3.3\text{ V}.

Попробуйте реализовать мягкое отключение/подключение D+ через GPIO. Это напрямую решает проблему, когда хост выходит из синхронизации с состоянием пробуждения устройства.

Привет. Это классическая проблема для F103 + Blue Pill, в основном связанная с тем, что хост-устройство не распознаёт отключение периферии, а также с аппаратными ограничениями Blue Pill.

Прямое решение для вашей ситуации:

1. Корневая причина: Резистор подтяжки D+ и программное управление

На плате Blue Pill резистор подтяжки D+ (R10) обычно напрямую подключён к 3.3В, а не управляется через GPIO контроллера.

  • Симптом: Когда МК переходит в режим ожидания или перезагружается, D+ остаётся подтянутым через резистор. Хост-компьютер (ПК) считает устройство постоянно подключённым и не инициирует повторное опросы устройств (re-enumeration).
  • Ваша операция: Вы упомянули “снижение на 10 мс” — этот интервал слишком мал. Стек USB-протоколов ПК (особенно Windows) обычно требует обнаружения низкого уровня на D+ в течение более 100 мс (рекомендуется 500 мс–1 с), чтобы определить отключение устройства (состояние SE0).

2. Решение: Принудительная последовательность повторной инициализации

После SystemCoreClockUpdate() и перед инициализацией USB добавьте следующий код “жёсткого сброса”:

// 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. Пауза достаточной длительности для распознавания отключения хостом
HAL_Delay(600); // Рекомендуется 500 мс - 1000 мс, 10 мс недостаточно

// 3. Освобождение вывода для передачи контроля периферийному USB (в этот момент резистор подтяжки снова потянет D+ вверх, инициируя опрос хостом)
// Примечание: Последующий USBD_Init повторно настроит вывод в режим альтернативной функции, здесь достаточно прекратить принудительный вывод низкого уровня

3. Дополнение по настройке тактирования

Частота USB-контроллера для F103 должна быть 48 МГц.

  • Контрольная точка: Убедитесь, что в конфигурации RCC установлено RCC_USBCLKSOURCE_PLL_DIV1_5.
  • 72 МГц (SysClk) / 1.5 = 48 МГц. При ошибке в этом разделе USB-интерфейс не будет работать из-за нарушения таймингов, даже если устройство определено, появится ошибка “Неопознанное устройство”.

4. Быстрый ответ на вашу проблему

  • Порядок инициализации: HSE --> PLL --> Flash Latency --> SysClk --> Принудительное снижение D+ на 600 мс --> Инициализация USB.
  • Рекомендуемая практика: Для плат без транзисторного управления резистором подтяжки программное снижение PA12 — единственный стандартный способ “мягкого отключения”.
  • Исправление: Проблемы с аппаратной реализацией F103 при выходе из режима ожидания обычно не приводят к полному сбою USB, основная сложность — сброс состояния хост-машины.

Рекомендуемое действие:

Измените задержку снижения D+ с 10 мс на 600 мс, это должно решить проблему.

Для вашего вопроса я сталкивался с похожей ситуацией. Аномалии USB CDC после выхода из режима останова обычно связаны с последовательностью восстановления тактового сигнала и неполной переинициализацией периферийного устройства USB. Ниже приведены ключевые моменты и рекомендации:

1. Последовательность восстановления тактового сигнала и уточнения
После выхода из режима останова F103 по умолчанию использует HSI (8 МГц) в качестве системного тактового сигнала. Ваши шаги по повторному включению HSE и PLL корректны, но существует критическая последовательность: только после стабилизации PLL следует включать тактовый сигнал USB. USB FS требует точного тактового сигнала 48 МГц (поставляемого PLL). Если тактовый сигнал USB включается до стабилизации PLL, PHY-устройство USB может работать некорректно, и хост-устройство не сможет его распознать.

Рекомендуется после HAL_RCC_OscConfig добавить ожидание установки флага PLLRDY:

while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET);

Затем выполнить HAL_RCC_ClockConfig.

2. Полный сброс периферийного устройства USB
Выполняемый вами USBD_DeInit и последовательность принудительного сброса необходимы, но порядок можно оптимизировать. В документации ST (для некоторых моделей F1) указано, что после выхода из режима останова периферийному устройству USB может потребоваться более длительный импульс сброса. Рекомендуется увеличить задержку при отключении тактового сигнала USB, сбросе и повторном включении до минимум 10 мс.

Более надежный подход: после выхода из спящего режима сначала оставить тактовый сигнал USB отключенным, дождаться полной стабилизации системного тактового сигнала (после стабилизации PLL и переключения SYSCLK), затем выполнить следующую последовательность жесткого сброса:

__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 на 10 мс для эмуляции отключения - эффективный “кустарный” метод, который действительно является одной из лучших практик для серии F1. Это гарантирует, что хост-устройство обнаружит физическое отключение и запустит повторную инициализацию. Для повышения надежности рекомендуется перед понижением DP сначала убедиться, что тактовый сигнал периферийного устройства USB остановлен, и сохранять низкий уровень на протяжении достаточного времени (10-20 мс). Это не протокольный метод, но самый прямой на уровне оборудования.

4. Рекомендации по переключению источника тактового сигнала
Не переключайтесь на HSI во время работы USB. Правильный подход: после выхода из режима останова приоритет - быстро и стабильно восстановить исходный 72 МГц PLL. Сначала стабилизируйте PLL, убедитесь в его стабилизации, затем обрабатывайте USB. До стабилизации PLL не включайте тактовый сигнал для периферийного устройства USB.

5. Возможные причины на уровне платы
Согласно вашему описанию, вероятность аппаратных проблем низка, но стоит проверить:

  • Конденсаторы нагрузки для кварца: 22 пФ - типичное значение для 8 МГц HC-49, но медленный запуск HSE после выхода из спящего режима может увеличить время стабилизации PLL. Можно попробовать уменьшить емкость до 18-20 пФ и проверить, чистый ли сигнал на выводах кварца.
  • Сигнальные линии USB: последовательные резисторы 22 Ом - стандартные. Убедитесь, что подтягивающий резистор 1.5 кОм на D+ подключен непосредственно к 3.3В от MCU, а питание стабильно во время переходных процессов при выходе из спящего режима.
  • Питание: внезапный скачок тока при выходе из спящего режима может вызвать небольшое падение напряжения на LDO. Хотя вы измерили низкий уровень пульсаций, можно добавить танталовый конденсатор 10 мкФ между VDD и землей для улучшения переходной характеристики.

Итог и рекомендуемая последовательность действий

  1. Восстановление тактового сигнала после выхода из спящего режима: включить HSE → дождаться готовности HSE → включить PLL → дождаться стабилизации PLL → переключить SYSCLK на PLL → обновить SystemCoreClock.
  2. Полный сброс USB: сначала отключить тактовый сигнал USB, затем принудительно сбросить и удерживать не менее 10 мс, после этого отпустить сброс и повторно включить тактовый сигнал.
  3. Повторная инициализация стека USB: вызвать последовательность USBD_DeInit и USBD_Init.
  4. Дополнительно “мягкое отключение”: если проблемы остаются после вышеуказанных шагов, перед повторной инициализацией USB настройте DP (PA12) как выход с открытым стоком и понизите его минимум на 15 мс, затем повторно инициализируйте и подтяните вверх.

Следуя этой последовательности, надежность восстановления USB CDC после выхода из спящего режима должна значительно повыситься.

2 лайка