Андрей Смирнов
Время чтения: ~6 мин.
Просмотров: 0

STM32 — организация виртуального COM-порта

421b78.jpgРаз у камня есть аппаратный USB, то грех им не пользоваться. Один из способов плюнуть байтом в компьютер и чтобы он при этом не очень обиделся — это организация виртуального COM-порта. Все в железе пробовалось на камне STM32F103ZE, на аналогах тоже должно взлететь.

Забиваем.

Как я уже говорил, пока я сторонник библиотечных решений. Памяти хватает, байтовыцарапыванием заниматься рано. Так что пойдем по легкому пути. STM предоставила нам библиотеку STM32F10x and STM32L1xx USB full-speed device library, версия 3.3.0 Описание к ней: документ CD00158241 — UM0424 User manual Точные названия я привожу на случай если на сайте производителя будет какая-нибудь реорганизация и ссылки станут невалидными. Не забываем дрова виртуального ком-порта. У меня вообще-то оно само все нашлось, но я много чего ставил от STM, может там в комплекте и пришлись. Также нам пригодятся следующие стандарты USB 2.0 SpecificationUSB CDC В составе библиотеки есть пример как раз организации виртуального COM-порта. Правда там он сделан как мост USART-USB, но мы же не боимся трудностей? Цель этой статьи — разобрать приложение, делающее следующее:e94ffc.png

Поджигаем

Собираем проект из нужных нам библиотек: CMSIS, SPD, USB. Дерево проекта прилагаю.1e3b0a.png Как обычно, самое интересное в папочке /src. Вот её-то и будем разбирать.

Неторопливо курим

Начнем с раскуривания библиотеки от STM Эта библиотека предназначена и для connectivity-line устройств (это STM32F105/107), у них не просто USB FS, а USB OTG. Поскольку камней с OTG у меня пока нет, сконцентрируемся на простом USB FS. В примере, который мы взяли за основу, есть куча дефайнов, как раз на случай отличить connectivity-line от других демоплат. Я эти дефайны повырезал, чтобы глаза не мозолили. Cо всей картинки нас интересует High Layer — синенькие квадратики, которые и составляют, собственно, пользовательскую часть библиотеки. Их мы меняем под свои нужды, все остальное остается неизменным. Если заглянуть в папочку /src, то видно, что все эти файлики там и собраны.

Первая затяжка — usb_conf.h

Самые общие настройки библиотеки. Указано сколько у нас будет endpoints (а нам их надо 4 штуки — нулевой для начального конфигурирования устройства, один для команд, один для приема и один для передачи). Также расписаны, какие нам будут нужны коллбэки. Все взаимодействие библиотеки и пользовательской программы построено на обратных вызовах: в случае возникновения какого-либо события вызывается функция с заданным названием. А уже содержание этой функции — проблема разработчика. Нам будет нужен SOF_CALLBACK – сигнал начала фрейма (Start Of Frame). По нему будем выплевывать то, что накопилось в буфере передачи. Также резервируем еще два коллбэка — на прием и передачу. По приему символа будем мигать светодиодами, чтобы уж как-нибудь задействовать канал приема. Упс, кончился файл. Короткая получилась затяжка. Файл берем из примера, ничего не меняем.

Вторая затяжка — usb_desc.h / usb_desc.c

В этих файлах находятся дескрипторы устройства и эндпоинтов. Информация по стандартным дескрипторам есть в стандарте USB, в разделе 9.6 Standard USB Descriptor Definitions Специфические дескрипторы описаны в USB CDC, раздел 5 (так и называется Descriptors), Эти все тонны текста в стандартах для того, чтобы USB стала действительно Universal. Поэтому тщательно выведена классификация устройств и интерфейсов — чтобы глянув на дескриптор хост сразу понял, что с этим делать. Особо подробно разбирать смысла не вижу — это не характерно для STM32, это общая боль разработчиков USB устройств. Файл берем из примеров, ничего не меняем.

Продолжение дескрипторов — usb_prop.h / usb_prop.c

В этих файлах описана таблица реакции на запросы. Все запросы к устройству пронумерованы и стандартизованы. В этих файлах определяется массивы функций Device_Property, User_Standard_Requests и просто массивы String_Descriptor, например. Это все используется ядром. Когда в устройство приходит определенный запрос, например «Выдай мне дескриптор устройства», то ядро посмотрит в таблице, какая функция обрабатывает этот запрос, вызовет эту функцию, а оно уже отработает: Опять же, берем файл из примеров.

Прерываемся — usb_istr.h / usb_istr.c

Тоже два коротеньких файла. В них находится то, что надо вызывать при приходе прерывания от USB. Поскольку прерывание одно, то по значениям флагов определяется событие и вызывается соответствующий коллбэк. Прерывание будет настраиваться в файле hw_config.c, обработчик выглядит очень просто: void USB_LP_CAN1_RX0_IRQHandler(void) { USB_Istr(); } его можно разместить в файлах stm32f10x_it.*, а в данном проекте, чтобы не растекаться мысью по древу, я его включил в файл main.c И тут особо ничего не меняем, все как в примере.

Питание — usb_pwr.h / usb_pwr.c

Здесь все, что касается питания. По стандарту, устройство может быть переведено в режим спячки, тогда, если оно питается от порта, нужно позаботиться об отключении жрущей периферии. Поскольку у нас плата питается отдельно, то особого смысла в управлении питанием нет. Просто устройство формально переводится в состояние SUSPENDED. Ничего не меняем, файл из примеров.

Коллбэки — usb_endp.c

Этот файл я переписал, поскольку у нас не будет работы с USART, как это было в примере. Приведу код с комментариями:

Работа с железом — hw_config.h / hw_config.c

В этом файле собраны функции инициализации периферии, прерываний, функции управления светодиодами и работы с буфером отправки. USB-порт и подтяжки на моей плате сделаны по такой схеме: И USB_EN заведен на пин PF11. Значит надо не забыть его проинициализировать и дернуть вовремя. По сравнению с примером, выкинуты функции инициализации и работы с USART, добавлена функция мигания светодиодом. По приему символов ‘A’, ‘B’, ‘C’, ‘D’ – зажигаются соответствующие светодиоды, ‘a’, ‘b’, ‘c’, ‘d’ – гасятся. ‘1’ и ‘0’ — соответственно зажигают и гасят все светодиоды. Ну и все, что будет передано функции USB_Send_Data() попадает в буфер, а затем и через USB – в комп. Уфф. С USB вроде закончили. Теперь в головной программе можно просто вызывать USB_Send_Data() и посимвольно передавать данные в комп. Если мы на этом остановимся, то размер кода будет порядка 11 кБ: Более двух килобайт bss – это буферы приема и передачи. Понятное дело, их можно уменьшить.

Выдыхаем — printf()

Но мы же хотим, чтобы вывод функции printf() перенаправлялся в наш свежесозданный COM-порт. Подключаем и офигеваем от количества ошибок линковки: А тут все просто — стандартная библиотека ввода-вывода подразумевает работу с файлами, но в больших системах есть ОС, которая собственно и занимается организацией файлов для программ пользователя. А поскольку у нас нет никакой ОС, то библиотека вполне справедливо недоумевает «А что же мне делать-то?» Вот, чтобы это обойти, в проект включается файл newlib_stubs.c В нем находятся функции-заглушки ко всем вышеперечисленным ошибкам. Но и кроме этого, есть одна функция, которую нам и надо переписать, чтобы весь вывод шел в USB: Видим, что как раз и вызывается наша функция USB_Send_Data() Все, компилим, собираем, запускаем. Архив с проектом прилагаю — это на случай, если кто не заметит маленькие буковки внизу 🙂Используемые источники:

  • http://we.easyelectronics.ru/stm32/stm32-organizaciya-virtualnogo-com-porta.html

Рейтинг автора
5
Подборку подготовил
Максим Уваров
Наш эксперт
Написано статей
171
Ссылка на основную публикацию
Похожие публикации