Наличие USB-порта в современных микроконтроллерах открывает широкие возможности для самостоятельного изготовления различных устройств с компьютерным управлением.
Однако на практике оказывается, что поставляемые производителем библиотеки для работы с USB нуждаются в доработке.
Если вас интересует опыт подобных модификаций для двух популярных семейств МК, добро пожаловать под кат.
Постановка задачи
Итак, мы хотим сделать устройство, которое обменивается сообщениями произвольной длины с компьютером через USB-порт. Самый простой способ сделать это — использовать класс символьных устройств USB (CDC), также известный как «виртуальный последовательный порт».Затем на хост-системе, к которой вы подключаете ваше устройство, автоматически будет создан последовательный порт, через который вы сможете обмениваться данными с устройством, работая с ним как с обычным файлом.
Однако на практике оказывается, что некоторые необходимые для этого функции в USB-стеке производителя либо не реализованы вообще, либо реализованы с ошибками.
Начнем с рассмотрения микроконтроллеров STM32 (первый случай) и закончим еще одним популярным семейством — Texas Instruments Tiva C (второй случай).
Оба семейства имеют архитектуру ARM Cortex M4.
STM32 — просто добавьте код
Микроконтроллеры STM обычно обладают богатым функционалом по очень доступной цене.Производитель поставляет широкий ассортимент библиотек на все случаи жизни.
Среди них есть библиотеки для поддержки USB, а также библиотека для работы с другой периферией, имеющейся на чипе.
Недавно все эти библиотеки были объединены в один мегапакет под названием STM32Cube. При этом, однако, о совместимости особо не заботились и поменяли все, что можно было изменить, включая названия полей в структурах, описывающих конфигурацию портов ввода/вывода, при этом имя самой структуры осталось такой же.
Интересно, что есть и третья версия примеров и библиотек, которую можно найти на сайте.
Однако автор этого варианта очень любит переименовывать файлы, заимствованные из STM, чтобы увековечить свои инициалы, что также не добавляет совместимости со всем остальным кодом.
Учитывая все вышесказанное, я решил взять за основу последнюю докубическую версию библиотек, поставляемую компанией STM. Сейчас их можно найти в пакетах компиляторов (я использую IAR).
Чтобы потом долго не искать, в проект включены библиотеки, которые вы можете взять по ссылке на git ниже.
Для экспериментов я использовал плату STM32F4DISCOVERY. www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF252419 .
Если у вас другая плата и код не работает сразу, проблема, скорее всего, в частоте внешнего кварцевого генератора.
Хоть библиотеки и изобилуют всевозможными определениями макросов, и в последней версии библиотек среди них появился макрос для внешней тактовой частоты, в коде этот параметр все равно прописан в виде числа без каких-либо комментариев, видимо, для того, чтобы разработчики не теряют форму и не забывают читать инструкцию.
Найти это число — тактовую частоту в мегагерцах — можно в файле system_stm32f4xx.c в определении макроса PLL_M. Итак, возьмем за основу готовый пример, который передает данные с USB на последовательный порт микроконтроллера и обратно.
Последовательный порт нам не нужен, и мы просто будем передавать данные из входного потока в выходной, то есть реализуем эхо.
Используя PuTTY, мы убеждаемся, что это работает. Но этого недостаточно.
Для обмена данными с устройством нам нужно будет отправлять более одного символа за раз.
Мы пишем тестовую программу на Python, которая отправляет сообщения произвольной длины и считывает ответ. И тут нас ждет сюрприз.
Тест работает, но недолго, после чего следующая попытка чтения либо виснет навсегда, либо заканчивается таймаутом, если он установлен.
Исследование проблемы с помощью отладчика показывает, что МК действительно отправил все полученные данные, а длина последнего сообщения составила 64 байта.
Что случилось? Стек USB в хост-системе имеет многоуровневую структуру.
На уровне драйвера данные были получены, но остались в его кэше.
Драйвер передает кэшированные данные приложению, когда поступают новые данные и замещают старые данные, или когда драйвер знает, что новых данных пока ожидать не следует. Где он может получить эти знания? Шина USB передает данные пакетами.
Максимальный размер пакета в нашем случае составляет ровно 64 байта.
Если в следующем пакете будет получено меньше данных, то новых данных пока ожидать нельзя, и это сигнал о передаче всех полученных данных приложению.
Что, если поступило ровно 64 байта данных? В этом случае протокол предусматривает отправку пакета нулевой длины (ZLP), который является сигналом о прерывании потока.
Получив его, водитель понимает, что новых данных ждать пока не стоит. В нашем случае он его не получил, потому что разработчики USB-стека для STM32 просто ничего не знали о ZLP. Вторая проблема, которую незаслуженно проигнорировали разработчики USB-стека, — что делать с данными, полученными по USB, если их некуда девать, потому что… входной буфер переполнен.
По большому счету, проблема входного буфера их совершенно не волновала — они предполагали, что все полученные данные сразу обрабатываются, что, конечно, не всегда можно было сделать.
В протоколе USB в случае невозможности получения данных предусмотрен ответ NAK – отрицательное подтверждение.
После такого ответа хост просто отправляет данные еще раз.
Если мы хотим избежать переполнения входного буфера, нам нужно, если в нем нет места для полной отправки (64 байта), перевести канал в состояние NAK, обеспечивающее автоматический ответ NAK на все входящие пакеты.
Тива С - слоеный торт с жуками
Для экспериментов мы взяли плату EK-TM4C123GXL. www.ti.com/tool/ek-tm4c123gxl .Для компиляции требуется пакет библиотеки TivaWare. www.ti.com/tool/sw-ek-tm4c123gxl .
Исследование библиотек показывает, что разработчики не обошли вниманием ни ZLP, ни проблему буферизации — во входных и выходных каналах имеются готовые кольцевые буферы.
Однако автоматический тест дает тот же результат — обмен данными внезапно прекращается.
С помощью отладчика выясняется, что на этот раз данные застряли в кольцевом буфере передачи, и проблема не связана с размером последнего пакета, а значит и с ZLP. Выявить проблему можно только внимательно изучив библиотечные источники.
Оказывается, для отправки ZLP нужно поставить специальный флажок, который по умолчанию не установлен.
Возможно, это обстоятельство побудило других разработчиков добавить код, отправляющий ZLP еще в одно место — на более низкий уровень USB-стека, причем без флага.
Это изменение привело к ошибке, из-за которой передача прекращалась.
Проблема возникает следующим образом.
Передатчик получает следующий пакет, когда заканчивается передача предыдущего, или если предыдущего не было, и приложение добавило данные в буфер передачи.
Код, инициирующий передачу, получает уведомление о завершении передачи предыдущего пакета с нижнего уровня стека USB. Проблема в том, что если нижний уровень стека инициировал передачу ZLP, то он не отправляет уведомление о завершении, потому что он сам инициировал передачу.
Верхний уровень не начинает передачу данных, пока передатчик занят передачей ZLP-пакета, и не начинает передачу после его завершения, так как не получает уведомления — процесс передачи останавливается.
Исправить очень просто: удалите низкоуровневый код, отправляющий ZLP, и оставьте его на верхнем уровне стека.
Вторая проблема, которую необходимо решить, заключается в том, что процедура, начинающая передачу, может быть вызвана либо из контекста обработчика прерывания (после завершения передачи), либо из контекста приложения при добавлении данных в буфер передачи.
Чтобы сериализовать вызовы этой процедуры из разных контекстов, нужно отключить прерывания во время ее выполнения.
Источник
Лежит здесь github.com/olegv142/stm32tivc_usb_cdc .В папках stm и ti находится по 2 тестовых проекта — usb_cdc_echo и usb_cdc_api. Первый просто отправляет все полученные данные обратно, второй реализует пакетный протокол, который вы можете легко адаптировать под свои нужды.
В папке инструментов лежат тестовые скрипты на Python. Теги: #usb #stm32 #tm32f4 #stm32f4discovery #Texas Instruments #tiva-c #Launchpad #tm4c123g #последовательный порт #открытый исходный код
-
Военно-Космическая Деятельность
19 Oct, 24 -
Сборка 486 – Подбор Комплектующих
19 Oct, 24 -
Модерация Вакансий
19 Oct, 24 -
Виды Знаний Программиста И Как Их Улучшить
19 Oct, 24