Использование USB в STM32 на примере Virtual COM port

К счастью, задача программирования работы с портом USB для контроллеров STM32 не отличается особой сложностью. Программа STM32CubeMX, предназначенная для конфигурирования периферии микроконтроллеров STM32, сделает самую грязную работу — скомпонует комплект файлов с исходным кодом, при работе которого ваш прибор уже будет определяться как готовое к работе устройство. Все что вам останется, это обеспечить работу USB-порта с точки зрения схемотехники плюс, разумеется, написать саму логику обработки принимаемой и отдаваемой по USB информации.

В этой статье будет рассмотрена реализация на базе STM32 виртуального COM порта, как одного из наиболее простых вариантов обеспечения совместной работы устройства на STM32 и персонального компьютера (вопросы, относящиеся к программированию ПК, более подробно освещены в статье «Обмен данными между STM32 и ПК через USB virtual COM port»).


Давайте сделаем четыре простых шага:
1. рассмотрим схемотехнические нюансы подключения;
2. при помощи STM32 Cube MX создадим минимальный программный проект с уже подключенным USB модулем;
3. дополним наш минимальный проект кодом, который будет возвращать ПК полученные от него данные (примерно как если бы мы замкнули пинцетом выводы RX и TX разъема RS-232);
4. ну и в конце добавим немного «нормального», рабочего кода, демонстрирующего работу с данными, полученными по USB.

Итак, схема.
В принципе, схемотехника подключения интерфейса USB к современному микроконтроллеру проста до безобразия. Один провод от контакта D+ USB к нужной ножке микроконтроллера, еще один провод для D-, да одна общая «земля». Но мы, в надежде на долгую жизнь разрабатываемого устройства, чуть-чуть усложним схемотехнику, добавив на шину USB пару защитных деталей. Общая схема показана на рисунке внизу (он кликабелен) и содержит следующие узлы:

 

1. Защитные резисторы R6, R7, последовательно включенные в линии D+, D- линий USB.
2. Защитная диодная сборка USBLC6-4SC6, включается параллельно с портом микроконтроллера.
3. Резистор R3 1.5 кОм, предназначен для обеспечения работоспособности конкретно STM32F102RBT6, который требует подключения внешнего резистора между D+ и питанием. Микроконтроллер, который будете применять вы, скорее всего, не будет требовать подобных внешних подключений, имея все необходимое «на борту».

Остальные узлы схемы имеют отношение не к USB, а к обеспечению минимальной работоспособности схемы:
1. Тактовый генератор на ZQ1, C7, C8 и R9. Кстати, если для работы последовательного порта подходит практически любая частота кварцевого резонатора, поскольку нужную нам скорость передачи можно «накрутить» при помощи самого UART, то USB гораздо менее гибок (подробности ниже, в разделе, посвященному STM32CubeMX).
2. Отладочный разъем XS1 с обвязкой для подключения к отладчику по SWD.
3. Фильтрующие конденсаторы, включенные в шину питания C1, C2, C4, C5, C6.

Далее, что касается STM32CubeMX, то последовательность создания проекта с интерфейсом USB (который, по общепринятой терминологии, относится к т. н. middleware) проще всего проиллюстрировать тремя кликабельными скриншотами, расположенными ниже. Полный мануал по использованию STM32CubeMX сюда, думаю, копировать не стоит, желающие могут погуглить последовательность действий, необходимую для создания вменяемого проекта, а мы ограничимся ключевыми моментами.

Сначала выберите нужный вам контроллер. На рисунке необходимая опция выделена красным прямоугольником. Не создавайте пробный проект на абы каком контроллере в надежде внести изменения позже. К сожалению, тип микроконтроллера после первоначального выбора изменить уже нельзя (такая вот небольшая «пасхалка» от программистов STMicroelectronics) и если вы, паче чаяния, набьете этот проект рабочей информацией, то вам придется править текстовый файл *.ioc с записанным проектом.

 

Самое «сложное» — включите собственно USB и выберите необходимый режим работы порта. Список доступных режимов несколько меняется в зависимости от контроллера, например, младшие модели STM32 не могут работать в качестве Mass Storage. В нашем конкретном случае для создания Virtual COM Port нужен Communication Device Class. Не пугайтесь кракозябры справа, это просто кусок рабочего проекта, у вас, разумеется, выводы контроллера будут названы иначе.

 

Перед тем, как создать заготовку с кодом из вашего проекта (Project -> Generate Code), обязательно зайдите в настройки (Project -> Settings -> Code Generation) и поставьте галочку у опции «Generate peripheral initialization as a pair of ‘.c/.h’ files per IP». Если этого не сделать, то кодогенератор свалит все исходники в main.c, что не только не очень хорошо с точки зрения структурированности кода, но и чревато ошибками (с кривой инициализацией АЦП сталкивался лично).

Теперь, когда вы при помощи STM32CubeMX удачно (я надеюсь) создали папку с подпапками с подпапками с рабочим кодом под ваш любимый компилятор, можно переключиться собственно на программирование поведения вашего USB-устройства. Давайте для начала реализуем простой «возвращатель байтов», хотя бы просто для того, чтобы найти в получившемся коде место, ответственное за работу с USB.

Итак, если роды кода при помощи STM32CubeMX прошли успешно, то в main.c вы увидите примерно следующее:

, а в «Мой компьютер -> Управление -> Диспетчер устройств -> Порты COM и LPT» (это для ОС Windows©®TM) у вас должен появится новый COM-порт (для этого, разумеется, надо подсоединить USB-кабель между ПК и вашим устройством).

Пришло время немного изменить код. Необходимый нам файл называется usbd_cdc_if.c, а нужные процедуры — static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len) и uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len). Процедура CDC_Transmit_FS уже содержит код наподобие

, а CDC_Receive_FS может вообще быть пустой.

Давайте добавим немного функционала. Во-первых, надо создать два буфера, ответственных за прием и передачу данных:

Во-вторых, модернизируем CDC_Transmit_FS:

Тут нужно заметить, что весь код, который вы добавляете в программную заготовку, созданную при помощи STM32CubeMX, нужно обязательно обрамлять тэгами /* USER CODE BEGIN БЛА-БЛА-БЛА */ и /* USER CODE END БЛА-БЛА-БЛА */. Рано или поздно вам захочется внести изменения в проект STM32CubeMX, поменяв названия выводов или подключив новый таймер и тогда при очередной генерации кода STM32CubeMX — сюрприз! — сотрет все, что вы написали, оставив девственно чистую инициализацию необходимой периферии.

Для некоторого уменьшения риска словить инфаркт также настоятельно рекомендуется включить в STM32CubeMX опцию «Backup previosly generated files when regenerating» в Project -> Settings -> Code Generator. Ну и стоит, наверное, начать уже делать бэкапы, хотя бы изредка…

В итоге такой несложной коррекции кода вы должны получить программное эхо — то, что отправлено из терминала на ваше устройство, будет возвращено обратно. Разумеется, эхо самого терминала надо отключить.

Под занавес давайте реализуем какой-нибудь дополнительный функционал, демонстрирующий работу с USB Virtual COM port на STM32. Давайте, например, полученный по USB пакет запишем во Flash-память микроконтроллера. Тогда полученная по USB информация не пропадет даже при отключении питания вашего устройства.

Для начала, создадим ссылку на кусок Flash-памяти вашего микроконтроллера в main.c:

Теперь основательно добавим кода в usbd_cdc_if.c:

В main.c добавим:

Всё! Теперь наш контроллер умеет по запросу с ПК принимать пакет данных, проверять CRC16 пакета и записывать его во Flash-память микроконтроллера.

Закладка Постоянная ссылка.

2 комментария: Использование USB в STM32 на примере Virtual COM port

  1. Denis пишет:

    А у вас есть проект для этой работы? Можно его посмотреть?

    • wiredlogic пишет:

      Да, разумеется, это часть проекта, но в этом проекте еще много чужого кода, так что всю работу целиком я выложить не могу. Но могу подсказать какие-то частности, если что-то непонятно.

Добавить комментарий

  • Несколько случайная, но довольно любопытная информация:

    Нынешний алюминий — достаточно дешевый металл, широко применяемый в промышленности и ежегодно выплавляемый в количестве многих миллионов тонн. Но мы знаем, что до изобретения Ч. Холлом и П. Эру электролиза окиси алюминия этот металл был страшно дорог (в частности, Д. И. Менделееву в знак признания его заслуг преподнесли драгоценный дар — кубок из чистого алюминия), так как даже все знания химиков первой половины XIX века не помогли найти способ добычи алюминия в сколь-либо значимых количествах. На этом фоне особенно интересными смотрятся два события, имевших место быть в древнем мире. Во-первых, римский историк Плутарх свидетельствует, что императору Тиберию неназванным ремесленником была изготовлена корона из металла, подобному серебру — белого цвета и серебристого — но значительно легче. Во-вторых, в гробнице китайского полководца IV века Чжао Чжу были обнаружены фрагменты орнамента, которые, как оказалось по результатам спектрального анализа, содержат 10 % меди, 5 % магния и… та-дам! 85 % алюминия. Неужели наши предки знали секрет безэлектролизной добычи алюминия? Например, такие как способ Х. К. Эрстеда от 1825 года «разлагаемость хлористого алумия посредством потассия» или рецепт 1863 года русского химика Н. А. Бекетова «Глиний восстанавливается магнием из своего фтористого соединения».