STM32 - Virtual COM Port








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

Коротенькая заметка про инициализацию USB у микроконтроллеров stm32.


Чтобы использовать VCP (Virtual COM port) без переподключения USB-кабеля, нужно при инициализации МК кратковременно прижать D+ к «земле». Суть заключается в том, что компьютер узнаёт о подключении нового usb-устройства когда видит высокий потенциал на линии D+. У usb-устройства эта линия подключена к «плюсу» через резистор 1.5КОм. Подключение может быть как напрямую, так и через управляющий транзистор (см. схемы ниже).

Как только комп увидел usb-устройство, то они «договариваются» между собой, а после начинается обмен данными.

Когда плата перезагружается (нажатие кнопки Reset или загрузка новой прошивки), то получается примерно следующее: для компьютера ничего не изменилось, он по прежнему думает что устройство подключено (ведь «плюс» то на линии D+ никуда не исчезал). Однако изначальная «договорённость» между ними пропадает, в результате чего возникает ошибка и обмен данными невозможен. Чтобы восстановить связь, нужно переподключить usb-устройство. Для того чтобы не перетыкать провод нужно просто кратковременно прижать линию D+ к «земле» (компьютер расценит это как отключение устройства), а потом подать на неё «плюс» (компьютер поймёт что подключилось новое устройство).

Если у платы нет управляющего транзистора, например как у BluePill или у Discovery STM32F303…



… тогда в программе это можно реализовать так: перед инициализацией USB инициализировать пин D+ как выход, записать в него «0», сделать паузу, и переинициализировать его для работы с USB.

Пример:

Добавляем код в функцию инициализации GPIO…

static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  // reset USB DP (D+)
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  // инициализируем пин DP как выход
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); // прижимаем DP к "земле"
  for(uint16_t i = 0; i < 10000; i++) {}; // немного ждём

  // переинициализируем пин для работы с USB
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  for(uint16_t i = 0; i < 10000; i++) {}; // немного ждём

}

Не забывайте про то, что если снова сгенерируете проект в Кубе, то всё затрётся.

Исходник — github.com/stDstm/Example_STM32F103/tree/master/usb_vcp

USB echo — github.com/stDstm/Example_STM32F103/tree/master/usb_echo


Если есть управляющий транзистор, например как у платы XNUCLEO-103-302-401…



… то лучше воспользоваться им. В данном случае подтяжка линии D+ управляется пином PD2 через PNP транзистор.

...
/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_SET);
  HAL_Delay(20);
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_RESET);
  ...

PNP транзистор открывается «минусом», NPN — «плюсом».


На некоторых фирменных платах, D+ подключён через резистор к пину МК, например как у Nucleo-144…



В данном случае надо кратковременно подавать «минус» на пин PG6…

...
/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
  HAL_Delay(20);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
  ...

У меня такой платы нет, поэтому не проверял.

И да, перед нажатием Reset на МК нужно закрывать терминал на компе. Если не закрывать, то естественно, что после ресета порт поменяется.

Читать про стандарт USB



Это всё, всем спасибо


Телеграм-чат istarik

Телеграм-чат STM32


  • 0
  • 23883
Поддержать автора


Telegram-чат istarik

Задать вопрос по статье
Telegram-канал istarik

Известит Вас о новых публикациях






Комментарии (0)

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.