Использование 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-память микроконтроллера.

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