Урок 33
Отладочную плату ипользуем ту же: STM32F4-DISCOVERY.
Проект создаём из проекта I2CLCD80. Назовем его USB_OTG_CDC. Запустим проект в Cube, включим USB_OTG_FS в режим Device_Only
В USB_DEVICE в разделе Class For FS IP выберем пункт Communication Device Class (Virtual Port Com).
Лапки портов PD4-PD7, PB8, PB9 отключим, это пережиток прошлых занятий
В Clock Configuration выберем следующие делители (нажмите на картинку для увеличения изображения)
В Configuration ничего не трогаем, т.к. прерывания там выставились сами.
Сгенерируем и запустим проект, подключим lcd.c и настроим программатор на автоперезагрузку.
Соберем проект. Прошьём контроллер. У нас появится неизвестное устройство, скачаем драйвер на наше виртуальное устройство usb. Для этого зайдем на сайт st.com, в строке поиска там вводим virtual com port, скачиваем и устанавливаем драйвер. Затем желательно зайти в папку с установленным драйвером, выбрать папку, соответствующую разрядности нашей операционной системы, и запускаем также установку и оттуда.
У нас скорей всего устройство установится с ошибкой (код 10)
Есть несколько типов решений, мне понравился именно этот, т.к. более простой: в файле usbd_cdc.h заменим размер пакета, вместо 512 напишем 256 в данной строке:
#define CDC_DATA_HS_MAX_PACKET_SIZE 256 /* Endpoint IN & OUT Packet size */
Соберём, прошьём и увидим, что ошибка исчезла.
Начнём писать код.
Сначала попытаемся передать данные на ПК.
Для этого мы сначала откроем файл usbd_cdc_if.c и исправим там в 2х строчках 4 на 64
/* It’s up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE 64
#define APP_TX_DATA_SIZE 64
В файле main.c закомментируем весь пользовательский код кроме инициализации и очистки дисплея
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
Также в main.c подключим файл usbd_cdc_if.h для видимости функций приема и передачи
/* USER CODE BEGIN Includes */
Немного изменим в главной функции строковую переменную, убавив в ней размер и добавив префикс tx
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
В файле usbd_cdc_if.c добавим прототип функции передачи, скопировав объявление из реализации данной функции в том же файле
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
В main() внесём данные в строку
/* USER CODE END 2 */
В бесконечном цикле попробуем эти данные отправить в порт USB, используя функцию, прототип которой мы добавили
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
/* USER CODE END WHILE */
Соберём код, прошьём контроллер и посмотрим результат в терминальной программе.
Вроде передать нам что-то удалось. Теперь попробуем что-нибудь принять. Здесь чуть посложнее, т.к. для этого используется уже обработчик прерывания, коим является в файле usbd_cdc_if.c функция CDC_Receive_FS.
Добавим ещё одну строковую глобальную переменную в main()
/* USER CODE BEGIN PV */
/* USER CODE END PV */
Объявим её также и в файле usbd_cdc_if.c
/* USER CODE BEGIN PRIVATE_VARIABLES */
extern char str_rx[21];
/* USER CODE END PRIVATE_VARIABLES */
В функцию CDC_Receive_FS в этом же файле добавим некоторый код и кое-что закомментируем
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
/* USER CODE BEGIN 6 */
Добавим переменную в main()
/* USER CODE BEGIN 1 */
Занесенные в наш буфер данные попробуем вывести на дисплей, для этого в бесконечном цикле в функции main() добавим определённый код
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
Соберём проект. Прошьём код и посмотрим результат, вводя в терминальной программе и отправляя в порт USB какие-нибудь строки.
17 комментариев на “ STM Урок 33. HAL. USB. Virtual Com Port ”
"Есть несколько типов решений, мне понравился именно этот, т.к. более простой: в файле usbd_cdc.h заменим размер пакета, вместо 512 напишем 256 в данной строке…."
Просто измените размер кучи (Minimum Heap Size) в настройка CubeMX. Вместо значения 0x200 задайте 0x400.
И комп увидит устройство без ошибок.
При инициализации структур компилятору элементарно не хватает места, заданного по умолчанию, для выделения памяти.
Пардон, очепятка вышла. Не компилятору, а функции malloc.
Спасибо, так действительно проще.
Спасибо огромное за ваши материалы по STM32 , подключил TFT 320×240 — все отлично работает . Вернулся к материалу для подключения флешки . Все отладочные средства у меня находятся на VirtualBox ( W7 ) . Скачал по вашей инструкции и поставил драйвер Virtual com port . Он поставился , но в диспетчере задач ничего не появилось ни в разделе com портов , ни в других . Может вы сталкивались с подобной проблемой ? Если нет — в любом случае еще раз спасибо за проделанную работу .
Сначала не смог реализовать данный пример на SystemWorkbench в части приёма данных и передачи их из функции приёма в main посредством массива str_rx с модификатором extern — компилятор ругается на использование неопределённых переменных, а если задать ему какие-нибудь значения, то только эти заданные значения и будут передаваться. Вышел из положения объявив массив обмена str_rx в заголовочном файле usbd_cdc_if.h
Спасибо.Я сделал так.В хидер usbd_cdc_if.h добавил две строчки
extern uint8_t UserRxBufferFS[1000];
uint8_t receiveBufLen;
В метод CDC_Receive_FS добавил перед return receiveBufLen = *Len;
И в main ловил данные просто одним условием
if(receiveBufLen > 0)// если получены данные от ПК
<
HAL_Delay(250);
CDC_Transmit_FS((uint8_t*) UserRxBufferFS,receiveBufLen);
// эхо для наглядности
receiveBufLen = 0;// сброс получения
>
Всё просто,а UserRxBufferFS чистить не нужно от мусора,он сам чистится.
Здравствуйте! Спасибо огромное за ваши уроки, тут пожалуй лучший ресурс с уроками по стм32!
Хочу спросить, а как использовать CDC_Receive_FS в main.c? Я проделал в usbd_cdc_if.c «эхо», но мне нужно принимать из него и гнать дальше. Наверное вопрос больше в целом по си чем по контроллеру, а то иначе мне получается надо много всего переносить в usbd_cdc_if.c.
Думаю, что следует добавить в main.c функцию, а в файле usbd_cdc_if.c — на неё прототип и вызвать её в CDC_Receive_FS, И весь свой пользовательский код затем писать в файле main.c.
Это именно СИ. Так что обязательно подтяните свои знания по языку.
Ох, видимо сперва надо читать коментарии, прочитал тот что выше.
При переходе на USB cтолкнулся с такой проблемой. Скажем, конструкция, приведённая в примере, а именно
sprintf(str_tx,»USB Transmit
»);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
работает без проблем. Но, если я делаю так
sprintf(str_tx,»USB Transmit»);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
CDC_Transmit_FS((unsigned char*)»
», 2);
то CDC_Transmit_FS((unsigned char*)»
», 2); не срабатывает (не успевает) и данные летят без переноса строки. Если ставить задержку, то работает как надо. По неопытности, может, это я и принял бы как должное, если бы перед этим не работал бы с UART где такая же конструкция работает без проблем. Для работы с UART уже написана довольно хорошая часть программы и менять её структуру очень не хочется, тем более, что данные передаются не в текстовом формате а в посылке имеется несколько меток. Что можно сделать, чтобы посылки могли идти подряд без задержки?
Скорей всего придется делать конкатенацию передаваемых строк с помощью strcat. Была аналогичная проблема при использовании CDC. Автор применял этот метод в одном из уроков.
Здравствуйте
А если я хочу передавать данные с микроконтроллера на компьютер?
Константин:
А мы их туда и передали.
Установил различные драйвера VCP от STM, но при этом плата не определяется при подключении её к компьютеру. только виден STLink Virtual COM Port. Кто уже сталкивался с такой проблемой.
Оказалась, что проблема с дровами. Надо их полностью сносить и устанавливать заново.
Hello, I’m new to STM32. How do I send int32_t value via usb CDC from ADC input ? or How to convert int32_t to char?
You can use(for example):
sprintf(str_tx,»ADC:%d
»,ADC_Data);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
Итак, у нас есть плата STM32F7 Discovery (например). На других платах всё должно быть аналогично.
Запускаем Stm32CubeMX, создаём новый проект, выбираем наш микроконтроллер из списка и производим некоторую настройку проекта.
1. Вкладка Pinout:
открываем Peripherials/USB_OTG_FS, выбираем Mode – Device Only
открываем Peripherials/RCC, выбираем High Speed Clock (HSE)- Crystal/Ceramic Resonator
открываем Peripherials/SYS, выбираем Debug – Serial Wire
открываем MiddleWares USB_DEVICE, выбираем Class For FS IP – Communication Device Class (Virtual Com Port)
2. Вкладка Clock Configuration:
устанавливаем HCLK = 216MHz (или другое значение, в зависимости от микроконтроллера)
CubeMX настраивает всё автоматически
3. Вкладка Configuration:
Нажимаем на кнопку USB_FS, ставим VBUS sensing = Disabled
В меню Project/Settings:
Вкладка Project: выбираем нужную IDE, например, EWARM, также настраиваем пути к папкам проекта
Вкладка Code Generator:
включаем опцию Add necessary library files.
включаем Generate peripherial initialization.
включаем Set all free pins as analog.
Закрываем окно, генерируем код (Project/Generate Code)
Генерируем код, открываем IDE.
В файле usbd_cdc_if.c пишем следующее:
в функции CDC_Transmit_FS:
в функции CDC_Receive_FS:
Этот код обеспечивает "эхо", при приеме символа в виртуальный com-порт он будет сразу передан обратно.
Компилируем и прошиваем контроллер. Подключаем плату (разъем USB_FS) к компьютеру.
И ту нас ожидают первые грабли. Windows видит, что подключено устройство, но драйвер не работает. Примерно так (кликабельно):
К счастью, задача программирования работы с портом 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.