Usb cdc stm32 зависает
Последние две недели я работаю над проектом, и отладка этого вопроса заняла всю эту неделю. Хотите знать, если кто-нибудь может помочь, я постараюсь быть максимально четким и ясным.
Я пытаюсь реализовать виртуальный порт USB на микроконтроллере на базе STM32F302K8 (Cortex M4). Я использовал STM32CubMX для генерации кода, необходимого для настройки устройства USB Full Speed, реализующего класс CDC. Мое устройство отображается как в Windows (диспетчер устройств), так и в Linux. Я могу реализовать простую функцию эха на основе примера кода, но когда я сейчас пытаюсь использовать функцию USBD_CDC_SetTxBuffer для отправки данных на ПК, это отключает Hard Fault Handler. Я сузил это до того факта, что поле UsbDeviceFS.pClass (которое требуется USBD_CDC_SetTxBuffer) никогда не инициализируется, потому что USBD_CDC_Init () никогда не вызывается при инициализации USB-устройства.
Я внес исправления в несколько ошибок (включая изменение размера кучи, исправление флага передачи в USBD_CDC_TransmitPacket и изменение размера CDC_DATA_HS_MAX_PACKET_SIZE на 256 из 512) в примере кода, как описано на форуме ST, но все еще получаю ту же ошибку.
Код настройки моего устройства
Прошло много времени с тех пор, как я последний раз работал с USB на STM, но я думаю, что USBD_CDC_Init () пытается выполнить malloc. Проблема заключалась в том, что в настройках по умолчанию недостаточно места в куче, и вам нужно его увеличить.
Привет! Я увеличил размер кучи до 0x600 и ничего не происходит. Какая функция вызывает malloc, потому что когда я ставлю на нее точку останова, оказывается, она никогда не вызывается.
Чтобы ответить на мой собственный вопрос, проблема в том, что мой код не дождался завершения инициализации USB и сразу начал отправлять данные. Вставка активного ожидания в логическое значение или добавление задержки (как указано @ramez) решает проблему.
ОБНОВЛЕНИЕ Эта ошибка была исправлена в последующих версиях драйвера USB CDC от ST. Теперь в настройках есть HAL_Delay. Предостережение заключается в том, что если по какой-либо причине Sys_Tick не работает / деактивирован / еще не инициализирован, ваш код зависнет.
Да, вы должны опубликовать это как отдельный вопрос. Оставьте в этом ответе только информацию, относящуюся к исходному вопросу.
Я использовал CubeMX для генерации кода для обнаружения STM32F4. Я использовал его как виртуальный COM-порт, как вы. Я не использовал функцию USBD_CDC_SetTxBuffer () напрямую. В файле usbd_cdc_if.c есть функция с именем CDC_Transmit_FS () . Произошла ошибка в сгенерированном коде, функция взяла в качестве параметра буфер и ничего с этим не сделала. Исправленный код функции следующий:
Инициализация в MX_USB_DEVICE_Init () у меня такая же, как и у вас.
Спасибо, Рамез. Я обнаружил проблему, я должен был проверить, завершил ли инициализацию виртуальный коммуникационный порт, я использовал логическое значение в CDC_Init_FS, которое основной цикл ожидал, чтобы быть истинным, прежде чем вызывать CDC_Transmit_FS. Я думаю, что HAL_DELAY в вашем коде достигает того же эффекта. Спасибо вам за помощь.
Во-первых, проверьте, является ли hUsbDevice_0 нулевым (отсутствует элемент в вашем решении):
Это предотвратит зависание вашего ОК и не потребует много времени на ожидание.
После того как я вдоволь наковырялся с STM32 и USB, решил что было бы неплохо поделитсья опытом с окружающими. Тем более, что все делалось аж под три разные платы и две разные линейки процессоров: High-Density (STM32F103RET6, STM32F103VET6) и Connectivity-Line (STM32F107VCT6).
Платы у меня в руках оказались следующие:
1) STM32 Development Board MINI (512K Flash 64K SRAM) 2.4-inch QVGA TFT module
(ссылка 1) (ссылка 2)
На ней стоит микроконтроллер STM32F103VET6
2) Embest EM-STM32C (EM-STM3210C)
(ссылка)
На ней стоит микроконтроллер STM32F107VCT6 — Connectivity Line
3) Встраиваемый модуль TE-STM32F103 — Махаон, от фирмы Terraelectronica.
(ссылка)
Соответственно, на ней стоит контроллер STM32F103RET6
Запустить проект из примеров, который использует USB, на любой из этих плат, задача не такая уж и сложная.
Куда сложнее встроить эти примеры в свои проекты, так как часто они бывают очень запутанно завязаны на конкретных платах. Еще сложнее собрать проект с нуля, используя библиотеки драйверов от STM — все равно без примеров обойтись сложно.
Поэтому я поставил перед собой задачу сделать универсальный проект, работающий со всеми имеющимися у меня в наличии платами, и, при необходимости, легко подстраиваемый под другие платы.
Между первой и третьей платой отличий мало: похожие контроллеры, отличающиеся лишь числом ног, у обоих выведен USART1. А вот второй отличается сильно: это контроллер Connectivity Line, с поддержкой USB On-The-Go, из-за чего работа с USB построена по-другому, а также вместо USART1 выведен USART2, да еще и с ремапом пинов на другие, отличные от дефолтных, ноги.
На всех платах есть светодиоды в разном количестве: 1, 4 и 3 соответственно.
Поэтому было принято решение сделать банальную вещь — устройтво, светодиоды которого управляются с компьютера по USB.
Прежде чем продолжать, рекомендую вкратце ознакомиться с тем, что же из себя представляет USB.
Самая лучшая, на мой взгляд, статья по этому вопросу — «USB in a NutShell». Ее перевод можно найти тут.
- Control. Endpoint такого типа, с номером 0, обязательно должен присутствовать в любом USB-устройстве.
- Interrupt. Название, в принципе, говорит само за себя. Более подробно читайте в статье.
- Isochronous. Гарантированные передачи через равные промежутки времени. Обычно используется для передачи аудио и видео.
- Bulk. Самый простой для реализации вариант. Применяется широко. Подробнее в статье. С ним мы и будем работать.
Проект для Keil
В результате некоторых ковыряний и копипасты с примеров, редактирования, кодинга и прочих мучений, получилось следующее:
USB_SampleSomeDevice_src.rar (зеркало 1)
Структура файлов такая же, как и во многих примерах:
\Libraries\ — папка с библиотеками (CMSIS, Standart Peripheral Driver, USB OTG Full speed Device Driver)
\Project\ — папка для проектов. Их может быть много и все они могут использовать одни и те же библиотеки. Но у нас проект один.
\Project\SampleSomeDevice\ — папка с проектом
\Project\SampleSomeDevice\Doc\ — краткие описания
\Project\SampleSomeDevice\driver\ — драйвер устройства для Windows (подробнее о драйверах и софте в ч.2, когда ее напишу)
\Project\SampleSomeDevice\inc\ — заголовки .h
\Project\SampleSomeDevice\src\ — файлы исходников .c
\Project\SampleSomeDevice\RVMDK\ — файлы проекта и выходные файлы
Распаковываем проект и открываем.
Смотрим на вкладку Project, видим там несколько групп:
User — Основные исходники проекта.
User_headers — Заголовочные файлы. Вынес в отдельную группу для быстрого и удобного доступа к ним.
USB-FS-Device_Driver — файлы библиотеки USB.
StdPeriph_Driver — файлы библиотеки стандарной периферии.
RVMDK — startup-файлы для каждой линейки контроллеров. Обратите внимание, что только один, соответствующий вашему контроллеру должен компилиться.
Doc — Краткие описания.
По умолчанию проект сконфигурирован под плату TE-STM32F103.
Конфигурируем проект
под другой контроллер и плату.
1) Надо знать название контроллера и его линейку. Поддерживаются практически все контроллеры 103 серии (кроме XL-density), а также 105 и 107 серия — Connectivity Line.
Даташиты, предварительно скачанные с сайта ST:
STM32F103x4x6.pdf (зеркало 1) — STM32 Low-density performance line (краткое обозначение LD)
STM32F103x8xB.pdf (зеркало 1) — STM32 Medium-density performance line (краткое обозначение MD)
STM32F103xCxDxE.pdf (зеркало 1) — STM32 High-density performance line (краткое обозначение HD)
STM32F105_F107.pdf (зеркало 1) — STM32 Connectivity line (краткое обозначение CL)
Все, что связано с линейкой контроллера, содержит в себе краткое обозначение.
Например, startup-файл для Medium-density performance line будет называться startup_stm32f10x_md.s
Или глобальный define для Connectivity line — STM32F10X_CL
В Keil правым кликом по Target заходим в опции, выбираем вкладку Device и ищем там свой контроллер.
Следующий шаг — выбираем используемый JTAG для прошивки и отладки.
Я использую TE-ARM-LINK, отечественный клон J-LINK.
Последний шаг в данном пункте — выбрать нужный startup-файл в группе RVMDK, соответствующий линейке контроллера, включить его в сборку проекта, отключив при этом все остальные:
Следующие пункты — настройка платы.
открываем файл platform_config.h, ищем кусок кода:
2) Узнаем куда выведен USART. Для общего развития полезно также знать, используется ли при этом ремап пинов. При включенном дефайне _DEBUG_ на него выводится различная информация, которая может быть полезна.
Открываем схему платы и смотрим. Предположим, выяснили, что выведен USART2, TX — PB5, RX — PB6.
Смотрим комментарии вначале файла platform_config.h:
Выбираем подходящий и заменяем в последнем дефайне. В данном случае это будет так:
USART сконфигурирован на скорость 115200, 8 бит, 1 стоп, No Parity.
4) Проверяем, есть ли на контроллере пин, отвечающий за программный коннект/дисконнект USB и где он расположен. Схема может выглядеть так:
Если пина нету, просто удаляем дефайны, отвечающие за него.
Вот, в принципе и все. Осталось залить прошивку в контроллер. Если есть JTAG — это не проблема.
Если оного нету, не все потеряно:
USB_DfuSe.part1.rar (зеркало 1) — Софт для прошивки STM32 Connectivity line по USB. Часть 1
USB_DfuSe.part2.rar (зеркало 1) — Часть 2
COM_FlashLoader.zip (зеркало 1) — Софт для прошивки STM32 (103 серия) по UART
Не забудьте перед прошивкой этим способом перевести девайс в DFU-Mode, корректно выставив джамперы BOOT0 — BOOT1.
О софте и драйверах я напишу в другой раз, однако уже можно скачать программу, которая общается с любым количеством подключенных девайсов с данной прошивкой:
Ковыряемся в проекте
Поскольку каждую строчку кода расписывать долго, да и исходники полны в том числе и моих комментариев, приведу здесь список основных файлов проекта и их назначение.
User:
— main.c — очевидно.
— hw_config.c — конфигурация контроллера (периферия, прерывания, клоки и так далее)
— stm32f10x_it.c — обработчики прерываний
— usb_. c — конфигурация и работа USB посредством драйвера.
— user_usb.c — пользовательская работа с USB — разбор пакетов с данными и обработка команд.
— led.c — работа со светодиодами. Включение, выключение, непрерывное мигание.
User_headers:
— platform_config.h — конфигурация платы.
Для более удобного поиска я добавил в код комментарии следующего вида:
Проект собран так, что USB-устройство, помимо нулевой контрольной, содержит 4 оконечных точки типа bulk, попарно на прием и передачу.
В проекте используются первые две, по которым при помощи несложного протокола передаются команды управления светодиодами на плате.
Краткое описание протокола можно найти в Doc\protocol.txt
И что дальше?
Ну а дальше — куда приведет фантазия. Ковырять USB рекомендую начинать с файла дескрипторов usb_desc.c, потом поиграться с ендпоинтами.
Если есть желание — можно попробовать реализовать один из стандартных классов USB-устройств, или не париться и сделать свой протокол под свои задачи.
На этом пока все. Если эта статья покажется кому-то интересной и полезной, во второй части немного напишу о драйверах и софте.
Файлы, используемые в статье собраны тут
P.S.: Хоть это и первый блин, конструктивная критика, естественно, принимается.
В прошлой части я рассказал как примерно должен выглядеть проект-заготовка для Keil девайса с USB, дал ссылку на мой проект и рассказал как его настроить под практически любую плату с STM32.
В проекте был реализован интерфейс с двумя bulk-ендпойнтами (in и out), с моим «кастомным» протоколом, при помощи которого можно включать, выключать и заставлять светоиоды мигать с нужными временами горения/не горения.
Ну и выложил небольшую программу для всего этого:
Пользователь Vga в комментариях справедливо заметил, что разработка своего драйвера под Windows — задача далеко не тривиальная, и что проще реализовать стандартный класс, например HID, под который драйверы есть.
В этой статье я проведу небольшой обзор средств, которыми можно воспользоваться, чтобы сильно упростить себе жизнь.
Итак, у нас на повестке следующие варианты:
1) Написать драйвер самому. Задача сложная, неблагодарная, долгая. Тут сразу можно отсылать, например к Windows Driver Foundation. Есть куча примеров, которые можно расковырять и адаптировать под свои задачи.
Несомненный плюс этого варианта — решение получится красивое и компактное. Но стоит ли это потраченного времени…
Извините, если разочаровал тех, кто полагал, что я опишу именно этот вариант :)
2) Реализовать на девайсе один из стандартных классов, например тот же HID.
Но давайте предположим что мы вот буквально вчера наконец-то заставили наш девайс определяться по USB как самостоятельное устройство, и все что мы умеем — это читать и писать в ендпойнты. Нам нужно что-то еще проще.
3) Использовать готовые средства. Вот на этом «варианте для начинающих и не только» мы остановимся.
Но вначале небольшое отступление.
Как Windows опознает наши USB-устройства?
В большинстве случаев это «зашитые» в девайс VID (Vendor ID) и PID (Product ID). Есть исключения, например HID и Mass Storage, там Windows опознает класс устройства и подсовывает уже приготовленный драйвер.
Если мы воткнем наш девайс в компьютер, система определит его, но, очевидно, будет ругаться на отсутствие драйвера, и предложит выбрать .inf- файл.
Именно в этом файле и прописываются, помимо прочего, наши VID и PID, а также путь к драйверу.
Есть, конечно, и более хитрые варианты, но мы на них пока останавливаться не будем.
Наши VID и PID можно подсмотреть в файле usb_desc.c проекта. И обязательно прочтите комментарий ;-)
Итак, встречаем: libusb-win32 и Jungo WinDriver.
Jungo WinDriver
Весьма удобная штука.
Запускаем Wizard, выбираем по VID-PID наш девайс:
Генерим inf-файл, сохраняем, устанавливаем тут же драйвер, и вуаля. Вот они наши два bulk-ендпойнта + управляющий, нулевой ендпойнт:
А вот наш девайс определился в диспетчере устройств:
Но и это не все. Жмем на волшебную кнопку Generate Code:
И получаем воистину огромный набор вариантов на любой вкус:
Генерим проект, запускаем. Вот — готовая программа для работы с нашим девайсом:
Можем отправлять данные в ендпойнты и читать их оттуда.
Например, отправка пакета на скриншоте согласно моему протоколу зажжет светодиод номер два. (См protocol.txt в проекте Keil)
Ну а теперь поговорим о недостатках.
1) Jungo WinDriver — штука очень уж платная. Кто хочет расстроиться — цены лежат тут.
2) У некоторых USB-девайсов бывает несколько конфигураций. Такое встречается редко, но встречается.
WinDriver с такими работать не умеет, а функция смены конфигурации помечена как Not Implemented Yet.
libusb-win32
Распаковываем и в папочке bin лежит программка inf-wizard.exe. Тоже визард для генерации инф-файла, а заодно и всех остальных файлов, необходимых для установки драйвера.
Запускаем, выбираем наш девайс, сохраняем inf и прочее в отдельную папочку:
Ну и сразу инсталлируем.
Теперь, чтобы создать свой проект, необходимо собрать все нужные от libusb файлы в папках lib, include, подсмотреть как работать с устройством, в папке exampes. А работать — проще простого (см bulk.c)
LibUsbDotNet
Именно при помощи этой либы я и написал программу из предыдущей статьи. Работать с ендпойнтами так же просто.
Ну а дальше реализуем небольшой протокол, пишем-читаем ендпойнты и радуемся мигающим светодиодам :)
Только не забываем одну тонкость: весь обмен с USB-девайсом происходит по инициативе хоста. Поэтому, данные не попадут в хост до тех пор пока хост сам не захочет их прочитать.
Вот и все.
Итого
Вот, в принципе, все необходимое для того чтобы создать на STM32 примитивный USB-девайс. Весь обмен сводится к чтению и записи в ендпойнты и разбору того, что же туда все-таки пришло и что с этим делать.
Лично мне кажется этот вариант проще, чем реализация на девайсе стандатного класса.
Как всегда, файлы с проектом находятся тут.
Ну а в следующий раз, когда дойдут руки, будем поднимать USB Mass Storage, причем поверх уже сделанного интерфейса для светодиодов, т.е. составное USB-устройство :)
Постановка задачи
Исходный код
Tiva C — слоеный пирог с багами
Комментарии ( 48 )
LibUsb штука хорошая, но стандартные классы тоже не плохи. Например, класс CDC видится в системе как виртуальный КОМ порт, что не может не радовать, а реализуется очень просто. Всё-таки работа на любой системе без установки своих драйверов много стоит.
Ну да :) Только CDC-класс все равно потребует inf-файла, а также своих VID-PID. Ну линукс не в счет.
Вообще задача стояла такая: организовать USB-обмен максимально быстро и просто, чтением и записью в ендпойнты. А реализация даже CDC это уже хоть какое, но углубление :)
За статьи спасибо, интересно и познавательно. Сейчас помаленьку тоже ковыряю USB. Помаленьку — потому, что пока я не вижу особого смысла креативить свои USB-утройства, ибо есть FT232. VCP — наше все! :)
Есть. FTDI дубовая и железная. В ней все уже реализовано и намертво протестировано. Если ничего кроме не требуется, то зачем что либо еще?
Зачастую требуется хоть что-то еще. И тогда цена вырастает вдвое.
А если только усб-уарт, то конечно проще использовать железное решение
Мне FTDI (а именно FT232RL) не понравился тем что он от малейшего шума зависает. Поставил AT90USB82 (которая как минимум в 2 раза дешевле) — стало все отлично.
ORLY. Может плата кривая? Я так наоборот так и не смог повесить FTDI. А цена… FTDI стоит от 90 до 130 рублей. Может у вас кривой поставщик? ;)
Поставщик терраэлектроника, цены у них на FT232RQ-R 215.31р, а AT90USB82 87.15р. На сколько кривой этот поставщик не знаю, ну уж явно лучше чип и дипа. У нас тут в Караганде FT232R в некоторых местах если переводить на ваши деньги вообще за 400р. продают, а AT90USB82 за 120р., но на заказ. А какой поставщик не «кривой». Схему для платы брал из даташита. Питаетcя от USB, за ней стоит ATmega644, которая не висла не разу. Затем поискал в инете, проблема с зависанием FT232 общая, посоветовали посадить все неиспользуемые ножки на землю. Так и сделал, но все равно иногда подвисала. Заменил на AT90USB — все стало ок.
Для штучного производства пофигу.
Для серии совсем другие цены будут. Зато FTDI не надо прошивать и не надо париться по поводу возможных глюков в прошивке.
PLL и RC цепочка. У меня STM32F103 тоже без кварца на ура работал с USB и тоже пробовали вынести ему мозг и сбить частоту — не получилось.
Доброго всем дня, сегодняшняя статья будет посвящена реализации виртуального COM-порта для микроконтроллеров STM32. Эта тема уже поднималась на нашем сайте, но, в отличие от всех предыдущих случаев, сегодня мы будем производить все настройки при помощи STM32CubeMx. Собственно, стартуем.
И начинаем с действий, ставших привычными - создаем новый проект в CubeMx. Я буду использовать отладочную плату STM32F4Discovery и, соответственно, выбираю контроллер STM32F407VG при создании проекта.
С этим никаких сложностей возникнуть не должно, все-таки мы уже много раз проделывали это в предыдущих статьях, посвященных STM32CubeMx (статьи про STM32CubeMx), поэтому давайте сформулируем задачу, которую будем сегодня решать.
А задача проста - реализуем поддержку USB Virtual Com Port и отправим тестовый набор данных с платы на ПК. Таким образом, мы должны добиться определения нашей отладочной платы в системе как виртуального COM-порта. А открыв этот порт в какой-нибудь терминальной программе мы должны будем увидеть правильно принятые данные. Просто и наглядно, приступаем к осуществлению.
Первым делом включим поддержку USB в проекте. Для этого посещаем вкладку "Pinout & Configuration":
Кроме того, задействуем внешний тактовый генератор (8 МГц), установленный на плате - ровно так же как мы делали в статье про тактирование:
Осталось выбрать режим работы USB для нашего устройства:
На этом первый этап настроек проекта закончен, переходим в окно настроек тактирования (вкладка "Clock Configuration"). Здесь нам необходимо обеспечить подачу ровно 48 Мгц для тактирования модуля USB, привожу полную схему с выставленными значениями:
Среди многочисленных настроек USB давайте поменяем только PRODUCT_STRING , исключительно теста ради, проверим, как это сработает. Остальное пока не трогаем:
В принципе, на этом все, можно смело переходить к генерации проекта и исходного кода.
Немного обождав, пока CubeMx завершит генерацию, получаем готовый проект, в котором выполнена вся необходимая инициализация. Давайте не будем вносить никаких изменений, а просто соберем проект и запрограммируем контроллер. После этого подключаем USB-кабель к плате и в диспетчере устройств видим новое устройство:
Если мы зайдем в свойства, то можем увидеть там значения VID и PID, которые были установлены в настройках STM32CubeMx, а кроме того измененную нами строку PRODUCT_STRING :
Никаких сомнений в правильной работе сгенерированного проекта не остается 👍 Но это только часть задачи, нужно реализовать отправку данных. А необходимые для этого функции находятся в файле usbd_cdc_if.c:
- CDC_Receive_FS() - для приема данных.
- CDC_Transmit_FS() - для передачи данных.
Итак, давайте в основном цикле нашей программы будем раз в секунду отправлять 8 тестовых байт. Кстати для реализации простейших временных задержек в HAL присутствует функция HAL_Delay() . В качестве аргумента мы должны передавать количество миллисекунд. В общем, получаем такой код:
Теперь остается только прошить контроллер, открыть терминал и убедиться, что данные принимаются верно. Так и делаем:
Все работает отлично, как и планировалось. Собственно, на этой мажорной ноте мы заканчиваем сегодняшнюю статью, а вместе с ней и обсуждение реализации USB Virtual COM Port при помощи STM32CubeMx. В следующих статьях мы будем работать уже с другими режимами USB, так что оставайтесь на связи 🤝
Наличие USB порта в современных микроконтроллерах открывает широкие возможности для самостоятельного изготовления разнообразных управляемых с компьютера устройств. На практике, однако, выясняется, что поставляемые производителем библиотеки для работы с USB нуждаются в доработке. Если вам интересен опыт подобной доработки для двух популярных семейств МК — добро пожаловать под кат.
Комментарии ( 48 )
LibUsb штука хорошая, но стандартные классы тоже не плохи. Например, класс CDC видится в системе как виртуальный КОМ порт, что не может не радовать, а реализуется очень просто. Всё-таки работа на любой системе без установки своих драйверов много стоит.
Ну да :) Только CDC-класс все равно потребует inf-файла, а также своих VID-PID. Ну линукс не в счет.
Вообще задача стояла такая: организовать USB-обмен максимально быстро и просто, чтением и записью в ендпойнты. А реализация даже CDC это уже хоть какое, но углубление :)
За статьи спасибо, интересно и познавательно. Сейчас помаленьку тоже ковыряю USB. Помаленьку — потому, что пока я не вижу особого смысла креативить свои USB-утройства, ибо есть FT232. VCP — наше все! :)
Есть. FTDI дубовая и железная. В ней все уже реализовано и намертво протестировано. Если ничего кроме не требуется, то зачем что либо еще?
Зачастую требуется хоть что-то еще. И тогда цена вырастает вдвое.
А если только усб-уарт, то конечно проще использовать железное решение
Мне FTDI (а именно FT232RL) не понравился тем что он от малейшего шума зависает. Поставил AT90USB82 (которая как минимум в 2 раза дешевле) — стало все отлично.
ORLY. Может плата кривая? Я так наоборот так и не смог повесить FTDI. А цена… FTDI стоит от 90 до 130 рублей. Может у вас кривой поставщик? ;)
Поставщик терраэлектроника, цены у них на FT232RQ-R 215.31р, а AT90USB82 87.15р. На сколько кривой этот поставщик не знаю, ну уж явно лучше чип и дипа. У нас тут в Караганде FT232R в некоторых местах если переводить на ваши деньги вообще за 400р. продают, а AT90USB82 за 120р., но на заказ. А какой поставщик не «кривой». Схему для платы брал из даташита. Питаетcя от USB, за ней стоит ATmega644, которая не висла не разу. Затем поискал в инете, проблема с зависанием FT232 общая, посоветовали посадить все неиспользуемые ножки на землю. Так и сделал, но все равно иногда подвисала. Заменил на AT90USB — все стало ок.
Для штучного производства пофигу.
Для серии совсем другие цены будут. Зато FTDI не надо прошивать и не надо париться по поводу возможных глюков в прошивке.
PLL и RC цепочка. У меня STM32F103 тоже без кварца на ура работал с USB и тоже пробовали вынести ему мозг и сбить частоту — не получилось.
Доброго всем дня, сегодняшняя статья будет посвящена реализации виртуального COM-порта для микроконтроллеров STM32. Эта тема уже поднималась на нашем сайте, но, в отличие от всех предыдущих случаев, сегодня мы будем производить все настройки при помощи STM32CubeMx. Собственно, стартуем.
И начинаем с действий, ставших привычными - создаем новый проект в CubeMx. Я буду использовать отладочную плату STM32F4Discovery и, соответственно, выбираю контроллер STM32F407VG при создании проекта.
С этим никаких сложностей возникнуть не должно, все-таки мы уже много раз проделывали это в предыдущих статьях, посвященных STM32CubeMx (статьи про STM32CubeMx), поэтому давайте сформулируем задачу, которую будем сегодня решать.
А задача проста - реализуем поддержку USB Virtual Com Port и отправим тестовый набор данных с платы на ПК. Таким образом, мы должны добиться определения нашей отладочной платы в системе как виртуального COM-порта. А открыв этот порт в какой-нибудь терминальной программе мы должны будем увидеть правильно принятые данные. Просто и наглядно, приступаем к осуществлению.
Первым делом включим поддержку USB в проекте. Для этого посещаем вкладку "Pinout & Configuration":
Кроме того, задействуем внешний тактовый генератор (8 МГц), установленный на плате - ровно так же как мы делали в статье про тактирование:
Осталось выбрать режим работы USB для нашего устройства:
На этом первый этап настроек проекта закончен, переходим в окно настроек тактирования (вкладка "Clock Configuration"). Здесь нам необходимо обеспечить подачу ровно 48 Мгц для тактирования модуля USB, привожу полную схему с выставленными значениями:
Среди многочисленных настроек USB давайте поменяем только PRODUCT_STRING , исключительно теста ради, проверим, как это сработает. Остальное пока не трогаем:
В принципе, на этом все, можно смело переходить к генерации проекта и исходного кода.
Немного обождав, пока CubeMx завершит генерацию, получаем готовый проект, в котором выполнена вся необходимая инициализация. Давайте не будем вносить никаких изменений, а просто соберем проект и запрограммируем контроллер. После этого подключаем USB-кабель к плате и в диспетчере устройств видим новое устройство:
Если мы зайдем в свойства, то можем увидеть там значения VID и PID, которые были установлены в настройках STM32CubeMx, а кроме того измененную нами строку PRODUCT_STRING :
Никаких сомнений в правильной работе сгенерированного проекта не остается 👍 Но это только часть задачи, нужно реализовать отправку данных. А необходимые для этого функции находятся в файле usbd_cdc_if.c:
- CDC_Receive_FS() - для приема данных.
- CDC_Transmit_FS() - для передачи данных.
Итак, давайте в основном цикле нашей программы будем раз в секунду отправлять 8 тестовых байт. Кстати для реализации простейших временных задержек в HAL присутствует функция HAL_Delay() . В качестве аргумента мы должны передавать количество миллисекунд. В общем, получаем такой код:
Теперь остается только прошить контроллер, открыть терминал и убедиться, что данные принимаются верно. Так и делаем:
Все работает отлично, как и планировалось. Собственно, на этой мажорной ноте мы заканчиваем сегодняшнюю статью, а вместе с ней и обсуждение реализации USB Virtual COM Port при помощи STM32CubeMx. В следующих статьях мы будем работать уже с другими режимами USB, так что оставайтесь на связи 🤝
Наличие USB порта в современных микроконтроллерах открывает широкие возможности для самостоятельного изготовления разнообразных управляемых с компьютера устройств. На практике, однако, выясняется, что поставляемые производителем библиотеки для работы с USB нуждаются в доработке. Если вам интересен опыт подобной доработки для двух популярных семейств МК — добро пожаловать под кат.
STM32 — просто добавь кода
Итак, берем за основу готовый пример, который перекладывает данные из USB в последовательный порт микроконтроллера и обратно. Последовательный порт нам не понадобится, а данные мы будем просто перекладывать из входного потока в выходной, то есть реализуем эхо. С помощью PuTTY убеждаемся, что оно работает. Но этого недостаточно. Для обмена данными с устройством нам понадобится слать много больше одного символа за раз. Пишем тестовую программу на питоне, которая шлет посылки случайной длины и вычитывает ответ. И тут нас ждет сюрприз. Тест работает, но недолго, после чего очередная попытка чтения либо зависает навсегда, либо завершается по таймауту, если он выставлен. Исследование проблемы с помощью отладчика показывает, что МК таки отослал все полученные данные, причем последняя посылка имела длину 64 байта. Что же произошло?
USB-стек на хост-системе имеет многослойную структуру. На уровне драйвера данные получены, но остались у него в кэше. Драйвер передает закэшированные данные приложению тогда, когда приходят новые данные и вытесняют старые, либо когда драйвер узнает, что новых данных пока ожидать не следует. Откуда же он может получить это знание? USB шина передает данные пакетами. Максимальный размер пакета в нашем случае как раз 64 байта. Если в очередном пакете данных пришло меньше, значит новых данных пока можно не ждать, и это является сигналом для того, чтобы передать приложению все полученные данные. А если данных пришло ровно 64 байта? На этот случай в протоколе предусмотрена посылка пакета нулевой длины (ZLP), который и является сигналом прерывания потока. Получив его, драйвер понимает, что новых данных пока ожидать не следует. В нашем случае он его не получил потому, что разработчики USB стека для STM32 про ZLP просто ничего не знали.
Вторая проблема, которую разработчики USB-стека незаслуженно обошли вниманием — что делать с данными, которые были получены по USB, если их некуда девать, т.к. входной буфер переполнен. По большому счету, их вообще не волновала проблема входного буфера — они предполагали, что все полученные данные немедленно обрабатываются, что, конечно-же, не всегда может быть выполнено. В USB протоколе на случай, если данные не могут быть получены, предусмотрен ответ NAK — отрицательное подтверждение. После такого ответа хост просто посылает данные еще раз. Если мы хотим избежать переполнения входного буфера, нам нужно в случае, если в нем нет места для полной посылки (64 байта), переводить канал в состояние NAK, что обеспечивает автоматический ответ NAK на все входящие пакеты.
Читайте также: