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
- stD
27520
Поддержать автора
Комментарии (0)