8-900-374-94-44
[email protected]
Slide Image
Меню

Драйвер stm32 usb: STSW-STM32102 — STM32 Virtual COM Port Driver

Как подключить кучу старого RS232 оборудования по USB без регистрации и sms (STM32 + USB-HID)

Вместо вступления


Как обычно я предлагаю заняться странным — попробовать подключить несколько старых RS232 устройств, через один USB порт с помощью синей изоленты и смекалки. Статья не будет большой, скорее это описание что где взять и зачем вообще все это делать.



Зачем?


Нужно это бывает когда некий специальный аппаратный комплекс, состоящий из отдельных устройств и который выполняет какую-то единый функционал, вдруг начинают модифицировать. Разумеется что можно попробовать найти комплекс оборудования по новее, но в реальной жизнь на это идут очень редко. Начинают модифицировать то что есть. Иногда вдумчиво, но чаще как получится.

Как правило «мозгами» такого комплекса является компьютер у которого 100500 выходов RS232. Из примеров могу привести место кассира в супермаркете, банкоматы и тому подобное. С первым случаем столкнулся я.

Эти компьютеры не блещут мощностью, но отличаются космической стоимостью. Естественно он перестает отвечать требованиям современных технологий и многим приходит в голову идея заменить их на обычный ПК и получить приличную мощность по адекватной цене, однако быстро выясняется что RS232 на новых ПК вымер как класс. Теперь этот интерфейс стал сугубо специализированным. Соответственно надо или самостоятельно прикручивать кучу RS232 или искать уже готовое спец решение.

Разумеется что можно заменить само оборудование, но если вы посмотрите сколько стоит стационарный лазерный сканер приличной фирмы и помножите эту цену на их количество то передумаете.

Люди не посвященный в тему сразу радостно покупают пучок китайских USBtoRS232 переходников, а дальше все идет очень печально. Лучше этого не делать. Вторым вариантом является покупка специализированной платы расширения с кучей RS232. Этот вариант уже лучше и имеет право на жизнь, но тоже имеет свои недостатки. Например стоимость и проблемы с дровами если используется не Windows или не та версия. Так же не маловажным фактором является доступность в будущем, так как что-то выходит из строя и парк может расширятся в будущем. Потом оказывается что конкретная модель уже не выпускается или не постановляется в конкретную местность и т.д. В общем привязывать себя к конкретному устройству это всегда опасно, особенно если можно этого не делать.

Пробуем что-то сделать


Может показаться странным что примитивные RS232 устройства так сложно и дорого подключить по нормальному если по сути там обычно простые протоколы и примитивный физический уровень. А все потому что обычно подобные аппаратные комплексы используются в коммерческих доходных сферах и покупка оборудования по таким ценам оправдана, а само оборудование уже перешло в разряд специального. Спец оборудование = спец цена вопроса.

Однако все это не мешает попробовать собрать свой бюджетный велосипед. Бонусом получим возможность менять поведение такого своеобразного RS232 мультиплексора и полностью обойдем проблему написания USB драйверов. HID профиль поддерживается почти везде.

Однажды я прочел отличную статью автора RaJa До этого я интересовался USB, но до практики не доходило. В наличии у меня было несколько дешевых отладочных плат, китайских клонов Blue Pill на микроконтроллере Stm32f103c8t6. Сама история и идея создания этой платы очень интересная, стоит поинтересоваться.

Этот микроконтроллер отличается тем что имеет три UART и аппаратную поддержку USB. Это то что может быть нам интересно в разрезе нашей задумки, а вообще микроконтроллер на этой плате, работает на частоте 72Mhz и по характеристикам порвет любое Arduino схожего формфактора. Но самое главное преимущество это повсеместная распространенность этой платы. Я не знаю более простых и дешевых способов пощупать «железный» USB.

Добавив три дешевых преобразователя RS232toUART MAX2323 и немного «рассыпухи» можно собрать своеобразный конвертер интерфейса 3хRS232 <=> USB.

В моем случае нужно было подключить три RS232 устройства к Raspberry Pi 3. Если использовать обычные RS232 <=> USB переходники то в итоге сталкиваешься с тем что в системе куча одинаковых устройств к которым не понятно что подключено и все это дико глючит, а выглядит еще более печально.

Если вы немного капнете в сторону какие микросхемы USB to UART доступны повсеместно, то обнаружите что там полно подделок. Думаю не стоит объяснять как это все потом себя ведет с оригинальными драйверами. И никто не сможет гарантировать вам что даже брендовые переходники вдруг не закупят левую партию микросхем. Удешевление производства оно происходит повсеместно.

В итоге придумалась примерно такая простая схема коммутации ее даже можно изобразить в стиле Arduino:

Я отломал резистор который подтягивает D+ к питанию и сделал эту подтяжку управляемой с помощью транзистора. Она отвечает за опознание устройства на USB шине.

Распиновка разъемов RS232 в программе (Fritzing) где я набрасывал схему странноватая, но думаю никому не составит труда найти ее в интернете, контакты задействованы как обычно 2,3 и 5. А еще удобнее будет использовать платы конвертера UART to RS232 котором уже присутствует разъем RS232.

Low Level


Код для микроконтроллера я писал и отлаживал с помощью IDE EmBitz (если честно то я был удивлен как легко завелась эта IDE, особенно после танцев с бубном вокруг CooCox. ). За основу взял проект из статьи выше. Это первый пример который у меня заработал сразу после заливки.

Я добавил работу с тремя UART и изменил структуру HID репортов так чтобы обмен с ПК шел всегда по 64 байта с контрольной суммой (crc8).

Попытался организовать автопереподключение устройства если соединение по USB не прошло корректно. Не претендую на супер алгоритм, должен признать что не являюсь специалистом по USB. Отдельный транзистор, отображенный на схеме, управляемый отдельным пином B5 подтягивает одну из сигнальных линий USB к питанию что имитирует включение устройства и хост проводит инициализацию устройства. Если попытка не удачная то происходит переподключение.

Было замечено что в Linux (Raspbian) процент не успешных подключений значительно меньше чем в Win 10, возможно этот результат получился из-за моих локальных технических условий.

Общий алгоритм похож на примитивный маршрутизатор, принимая пакет по USB мы смотрим какому UART оно предназначено и пересылаем туда, в обратную сторону аналогично. Есть немного обработки самих пакетов но это относится к конкретному RS232 оборудованию которое я подключал. Это были: экран покупателя Datecs dpd-201, стационарный лазерный сканер штрихкодов Datalogic Magellan 8300 и весы Digi DS890.

На фото выше тестовый образец устройства содержит еще DC-DC шим преобразователь питания на микросхеме MP2307.

Это нужно для того чтобы питать устройство от напряжения 10-24В (входное) и иметь возможность подключить дисплей покупателя который питается от такого же напряжения. После DC-DC шим преобразователя поулчаем 3.3В для всех остальных модулей. Для этого дисплея так же установлен «телефонный» разъем вместо RS232, не хотелось перепаивать стоковый штекер.

Проект лежит тут.

High level


Вторая часть софта это примеры и тесты собранные в сумбурный проект на Java написанный IDE IDEA. Предполагается что работа с устройством интегрируется в софт высокого уровня используя различные обертки по работе с USB стеком в зависимости от языка на кортом этот софт пишется. Сейчас сложно найти такой ЯП чтобы под него не существовало таких оберток. Отдельно для староверов отмечу что java и usb совместимы если готовить правельно, это доказано практикой и используется в коммерческом проекте.

В процессе тестирования выяснилось что работа в Linux и Windows с USB HID несколько отличается, работа отлаживалась через две библиотеки usb4java и hid4java. Работа через последнюю используется в Linux (Raspberry Pi 3).

Разница заключается в том что в Windows можно обратится напрямую к USB устройству даже если оно зарегистрировано как HID и писать\читать его конечные точки (endpoint). В Linux же приходится работать с hid устройством. То есть устанавливается стандартный драйвер hid и всё, работайте только с ним пожалуйста. Работа таким образом происходит немного медленнее чем напрямую, но напрямую тоже возможно если убедить систему не ставить драйвера. Это реально.

Как и обещал не растягиваю статью и не привожу подробное описание кода, те пару человек кому интересно могут посмотреть проекты и поиграться в живую, а остальным думаю будет полезнее принять к сведению что есть такое решение и прибегнуть к более глубокому изучению в случае необходимости.

Заключение


Представленное устройство это лишь одни из примеров как довольно легко можно приобщится к процессу создания нативных USB устройств и перестать наконец использовать переходники.

Не забываем плюсовать RaJa автора статью в которых на пальцах объясняется как пощупать железный USB и сохранить желание разбираться дальше.

STM32 и USB. Часть 1. Проект для Keil. / STM32 / Сообщество EasyElectronics.ru
Связанные статьи:
STM32 и USB. Часть 2. Немного о драйверах и софте.

Введение

После того как я вдоволь наковырялся с 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». Ее перевод можно найти тут.

Если совсем упрощенно, то каждое USB-устройство имеет некоторое количество оконечных точек — Endpoint-ов, которые бывают следующих типов:

  • 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 и ищем там свой контроллер.

Далее, открываем файл Doc\sample_global_defines.txt и, в зависимости от линейки контроллера, платы и необходимости вывода отладочных сообщений, выбираем нужную строку и копируем.
Если ни одна из этих плат не используется, просто копируем любую строку, исправив define линейки процессора и выкинув define, содержащий _BOARD.
Вставляем строку в опции, во вкладку С/С++

Следующий шаг — выбираем используемый JTAG для прошивки и отладки.
Я использую TE-ARM-LINK, отечественный клон J-LINK.


Последний шаг в данном пункте — выбрать нужный startup-файл в группе RVMDK, соответствующий линейке контроллера, включить его в сборку проекта, отключив при этом все остальные:

Следующие пункты — настройка платы.
открываем файл platform_config.h, ищем кусок кода:

#else// Дефолтная конфигурация - сделано под TE-STM32F103_BOARD

	#define USB_DISCONNECT                      GPIOB  
	#define USB_DISCONNECT_PIN                  GPIO_Pin_5
	#define USB_DISCONNECT_LOG1		    DISABLE
	#define RCC_APB2Periph_GPIO_DISCONNECT      RCC_APB2Periph_GPIOB

	#define LED1				    0
	#define LED1_GPIO_PORT              	    GPIOA
	#define LED1_GPIO_CLK               	    RCC_APB2Periph_GPIOA  
	#define LED1_GPIO_PIN               	    GPIO_Pin_4

	#define LED2				    1
	#define LED2_GPIO_PORT       	            GPIOA
	#define LED2_GPIO_CLK              	    RCC_APB2Periph_GPIOA  
	#define LED2_GPIO_PIN         		    GPIO_Pin_5

	#define LED3				    2
	#define LED3_GPIO_PORT       	            GPIOA
	#define LED3_GPIO_CLK              	    RCC_APB2Periph_GPIOA  
	#define LED3_GPIO_PIN         		    GPIO_Pin_6

	#define USE_USART1_DEFAULT_PA9_PA10

#endif

2) Узнаем куда выведен USART. Для общего развития полезно также знать, используется ли при этом ремап пинов. При включенном дефайне _DEBUG_ на него выводится различная информация, которая может быть полезна.
Открываем схему платы и смотрим. Предположим, выяснили, что выведен USART2, TX — PB5, RX — PB6.
Смотрим комментарии вначале файла platform_config.h:

// Варианты конфигурации USART

// USE_USART1_DEFAULT_PA9_PA10
// USE_USART1_REMAP_PB6_PB7

// USE_USART2_DEFAULT_PA2_PA3
// USE_USART2_REMAP_PD5_PD6

// USE_USART3_DEFAULT_PB10_PB11
// USE_USART3_REMAP1_PC10_PC11
// USE_USART3_REMAP2_PD8_PD9

Выбираем подходящий и заменяем в последнем дефайне. В данном случае это будет так:

#define USE_USART2_REMAP_PD5_PD6

USART сконфигурирован на скорость 115200, 8 бит, 1 стоп, No Parity.

3) Смотрим сколько на плате светодиодов и куда они подключены. Правим соответствующие дефайны. Число светодиодов должно быть не более 4х, начинаться с

#define LED1			0
и идти по порядку. Также не забываем вместе с правкой порта, поправить и RCC_APB2Periph_GPIOx

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.

О софте и драйверах я напишу в другой раз, однако уже можно скачать программу, которая общается с любым количеством подключенных девайсов с данной прошивкой:

SomeUsbDev_1.0.0_src.rar (зеркало 1) — Проект для Visual Studio 2010
SomeUsbDev_1.0.0_bin.rar (зеркало 1) — Исполняемые файлы
.NET Framework 4.0

Ковыряемся в проекте

Поскольку каждую строчку кода расписывать долго, да и исходники полны в том числе и моих комментариев, приведу здесь список основных файлов проекта и их назначение.
User:
— main.c — очевидно.
— hw_config.c — конфигурация контроллера (периферия, прерывания, клоки и так далее)
— stm32f10x_it.c — обработчики прерываний
— usb_???.c — конфигурация и работа USB посредством драйвера.
— user_usb.c — пользовательская работа с USB — разбор пакетов с данными и обработка команд.
— led.c — работа со светодиодами. Включение, выключение, непрерывное мигание.

User_headers:
— platform_config.h — конфигурация платы.

Для более удобного поиска я добавил в код комментарии следующего вида:

#define BTABLE_ADDRESS      (0x00)

// $USBCONFIG - при изменении числа ендпойнтов, нужно подправить эту таблицу
// и задать верные адреса для буферов с учетом максимального размера пакета.

/* EP0  */
/* rx/tx buffer base address */
//#define ENDP0_RXADDR        (0x18)
#define ENDP0_RXADDR        (0x40)
#define ENDP0_TXADDR        (0x80)

Слово, начинающееся с $ — ключевое для поиска. При помощи поиска по всем файлам можно найти важные куски кода, которые надо проверить, при внесении в проект изменений.
$BOARDSPECIFIC — код, который зависит от того, какая плата выбрана
$USBCONFIG — код, который нужно проверить поправить для изменения конфигурации USB-устройства.

Проект собран так, что USB-устройство, помимо нулевой контрольной, содержит 4 оконечных точки типа bulk, попарно на прием и передачу.
В проекте используются первые две, по которым при помощи несложного протокола передаются команды управления светодиодами на плате.
Краткое описание протокола можно найти в Doc\protocol.txt

И что дальше?

Ну а дальше — куда приведет фантазия. Ковырять USB рекомендую начинать с файла дескрипторов usb_desc.c, потом поиграться с ендпоинтами.
Если есть желание — можно попробовать реализовать один из стандартных классов USB-устройств, или не париться и сделать свой протокол под свои задачи.

На этом пока все. Если эта статья покажется кому-то интересной и полезной, во второй части немного напишу о драйверах и софте.

Файлы, используемые в статье собраны тут

P.S.: Хоть это и первый блин, конструктивная критика, естественно, принимается.

STM32F4 USB RNDIS драйвер (управление устройством через Web-интерфейс) / Хабр
Доброе время суток, дорогие друзья!

Первым делом хотелось бы с лучшими пожеланиями поздравить всех с минувшими новогодними праздниками.

Ранее в статье была анонсирована разработка RNDIS USB драйвера для контроллеров серии STM32F4. С тех пор библиотека постепенно развивалась и нынче доросла до первой release-версии. Библиотека под названием LRNDIS (LWIP + RNDIS) позволяет нам создавать на базе контроллера STM32F4 как устройства класса USB «модем», так и любые другие устройства с управлением через web-интерфейс. Пример управления платой stm32f4-discovery из web-браузера на Android-планшете представлен на видео:

На странице видеоролика представлена ссылка на исходные коды и HEX-файл прошивки для платы discovery, с которым вы сможете повторить данный эксперимент. В статье рассказано о том, как и когда технология доступа через WEB-интерфейс полезна, а также — как работает библиотека LRNDIS для контроллеров STM32F4. Также присутствует обучающий материал о работе USB и устройстве Ethernet-сетей.

Предыстория создания библиотеки

Предыстория проекта весьма типовая. Стоял тёплый летний день. Гхм… Для заказчика стояла задача разработать устройство с сервисным интерфейсом управления.

ПодробностиПо мере развития прошивки было введено несколько команд управления по VCP-интерфейсу. Это значит, что после подключения USB-устройства в ОС создавался виртуальный COM-порт. Используя его, из пользовательского терминала передавались команды управления и диагностики. В ответ от устройства принимался статус выполнения и его текущее состояние.

Система вполне типовая с сервисной точки зрения: есть последовательный порт и набор команд для управления и диагностики.

Всё изменилось в короткий срок. По объективным причинам рос требуемый набор команд. Также понадобилась интерактивность вывода: некоторые параметры стало необходимо отображать в динамике. Как, например, показания магнитного датчика при проносе мимо него ферромагнетика. Для этого были введены дополнительные команды, которые, оперируя управляющими последовательностями, печатали информацию в пользовательском терминале с высокой периодичностью. Это создавало необходимое ощущение риалтаймовости наблюдения. Интерактивные команды оказались настолько удобными для инженеров, что некоторая часть команд позже была добавлена в соответствии с концепцией. И тут раздался треск. Требовалось поддержать сразу несколько групп команд: интерактивные, диагностические, команды управления. При этом периодический рефакторинг кода был связан с трудоёмкой правкой в большом количестве обрабатываемых команд. Ясно стало, что ещё должна быть пользовательская группа команд — для менее квалифицированного персонала, который будет просто следовать инструкциям по эксплуатации. Для них возникла идея написания клиентского терминала с кнопочками и флажочками… И вот тут возникли сомнения: стало ясно, что мы занимаемся сервисной частью, уделяя всё меньше внимания функциональности! А ведь пользовательская программа, запускаемая на клиентской ЭВМ, также должна обладать своими требованиями: кроссплатформенность и LTS (длительностью поддержки).

Устройство, предположим, мы закончили, а пользовательское ПО мы должны портировать и тестировать с каждой версией выпускаемых операционных систем! А как долго?

Так и родился вопрос — как избавиться от дополнительных трудозатрат?


Было принято решение использовать стандарты гарантированно длительной поддержки. Те стандарты, которые нам позволят создать клиентскую программу управления устройством, которая будет поддерживаться максимально полным набором операционных систем в настоящем и будущем времени. На первых парах были найдены недостатки популярных кроссплатформенных фрэймворков:
— java: необходимость наличия в ОС JVM, и вытекающий из противного предположения необходимость дистрибьюции виртуальной машины
— qt: периодическая необходимость версионного портирования и нюансы запуска под Android.

Нет, эти сложности пугать не должны. Вопрос, пожалуй, только в трудочасах, которые мы, бывает, недооцениваем с учётом фактора длительной поддержки.

Так родилась идея сделать WEB-интерфейс для управления устройством. Он не требует разработки стороннего ПО, браузеры для отображения управляющей страницы есть во всех требуемых ОС. Потенциал оформления интерфейса огромный. Длительность поддержки в части стандартов http/html/js тоже не вызывает сомнений.

По замыслу, управление должно было работать следующим образом.
1. Подключаемое по USB устройство представляется сетевой картой
2. Клиентская ЭВМ (ПК или гаджет) получает IP-адрес для работы в сети нашего устройства
3. По запросу web-браузера на клиентской ЭВМ наше устройство отдаёт страницу
4. На странице присутствует информация о текущем состоянии и доступные элементы управления
5. При активации клиентом элементов управления, из браузера передаются соответствующие HTTP-запросы.
По сути, между браузером и устройством, ходят те же самые текстовые команды, но только в формате HTTP протокола.
Надо понимать, что это всего лишь один из вариантов из большого набора возможных решений. Он имеет свои плюсы и минусы. Так случилось, что сейчас к использованию web-интерфейсов прибегают производители в основном сугубо сетевых устройств: настройка модемов и роутеров. Посыл данной статьи — давайте применять то что, действительно, удобно. И давайте не бояться сложностей на пути: их преодоление сейчас нам сэкономит куда больше времени в будущем!

Сфера применения библиотеки

К сожалению, первый анонс в полной мере успешным не был, т.к. рассказ о сфере применения был упущен.
Попробуем немного наверстать упущенное и раскрыть эту тему.
Если мы находимся на этапе системного проектирования устройства, то следующие соображения могут склонить нас в сторону использования web-интерфейсов (вне зависимости от физического канала, Ethernet или USB):
1. Устройство должно иметь интерфейс управления и/или диагностики
2. Средства управления могут использоваться не только на этапе разработки, но и на этапе эксплуатации (ПО пользователя)
3. Квалификация пользователя может быть недостаточно высокой, что требует дружественный интерфейс управления
4. Способ «дружественного» управления должен быть доступен из под разных платформ и ОС
5. Соответствующие средства требуется поддерживать в рабочем состоянии длительное время
Дополнительным критерием может являться то, разрабатываем ли мы изначально сетевое устройство. А также: не будет ли (в противном случае) добавление в прошивку сетевого стека и web-сервера являться избыточным на фоне куда менее богатого функционала устройства. Иными словами, добавление web-интерфейса в контроллер управления лампочкой — очевидно, избыточное решение.

Если мы поверили в web-интерфейс, то следующие соображения, возможно, нам помогут в выборе физического канала связи (из Ethernet и USB перспективы).

Тип Внутрисхемное подключение Типовое применение
Ethernet Ethernet PHY контроллер — Промышленные устройства
— Бытовые устройства с сетевой функцией и доп. питанием
USB ULPI контроллер или прямое подключение к МК Бытовые и часть промышленных устройств. В особенности, если:
— устройства имеют не гарантированный источник питания (питание от батареи, например)
— устройства потенциально подключаемые к хосту только с USB интерфейсом (например, планшет)
— миниатюрный класс устройств

От себя добавлю — не смотря на все прелести, не посоветовал бы применять USB в промышленных узлах с требованием повышенной надёжности: часто встречается негативный опыт. Если альтернативы нет — то вопрос устойчивости требуется изучить досконально.
Исходя из приведённых пунктов, становится ясна сфера применения библиотеки: бытовые и часть промышленных устройств, которые:
— работают на базе МК STM32F4
— должны обладать дружественным интерфейсом управления
— должны управляться из под разного аппаратного и программного набора
— могут не иметь гарантированного источника питания
— должны иметь длительный период поддержки ПО управления
Возможных примеров использования технологии много даже вне области сугубо сетевых устройств.
К примеру, на данный момент есть планы по превращению stm32f4-discovery в инструмент любительской разработки с функциями портативного генератора/анализатора сигналов и осциллографа. Подключите такой помощник к телефону и посмотрите в динамике что происходит в интересующей вас цепи. Из бесплатных плюсов — не требуется собирать или устанавливать ПО; достаточно прошить HEX-файл и открыть браузер — в нём будут присутствовать все прелести GUI-интерфейса. На мой привередливый вкус — то что нужно. Конечно, инструмент не для профессиональной разработки, но известный интерес к нему присутствует.

Итак, надеюсь, разобрались. А теперь о том как работает библиотека.

Как оно работает

При ответе на этот вопрос спешить не будем. Человек, имеющий небольшой опыт взаимодействия с сетями, может вполне справедливо смутиться. Поэтому, касаясь того или иного протокола взаимодействия я буду также давать его краткое техническое описание на том уровне… которого когда-то не хватало самому.

Шаг 1. Подключаем USB-устройство.
Как говорилось раньше, на этом этапе наше устройство говорит хосту «я — сетевая карта!».
Хост (т.е. клиентская ЭВМ) после подключения к нему нашей поделки, начинает отправлять запросы.

Хосту требуется получить следующую информацию — как изделие называется
— какой у изделия VID и PID (идентификаторы производителя и изделия, см. список)
— к какому классу и подклассу относится устройство
— по каким endpoint точкам и какими блоками следует обмениваться данными
Ну, и некоторую другую информацию. Конфигурационные пакеты при этом передаются по точке endpoint 0. Ответные пакеты от устройства с информацией о себе обычно называют «дескрипторы USB устройства».

Подробно ознакомиться о процессе опроса (энумерации) можно здесь.

Вообще, протокол USB достаточно богат… иногда даже кажется, что избыточно. Однако, это богатство вот уже много лет позволяет подключать совершенно разные устройства, даёт возможность передавать изохронные потоки, блоки данных, прерывания. В общем, всё необходимое, что может потребоваться широкому набору современных устройств. Обратная сторона медали — высокий порог входа в разработку USB-устройств.


После получения информации об устройстве, ОС хоста производит поиск подходящего драйвера для взаимодействия. В типовом случае, вроде flash-носителей (USB класс MSC) или клавиатуры с мышкой (HID класс), загружается стандартный для класса драйвер. В более «тяжёлом» случае, вроде нашей USB сетевой карты (CDC класс с RNDIS подклассом), операционная система поступает по усмотрению:
— ОС linux/android/mac, как правило, успешно пытается наладить типовой обмен
— ОС windows просит установить внешний драйвер
Наше устройство в первом случае работает сразу.
В случае ОС windows (позднее XP) можно установить стандартный драйвер фирмы Microsoft. Для Windows XP необходимо поставить inf-файл, доступный в репозитории библиотеки LRNDIS.

Шаг 2. Драйвер инициализирует RNDIS-устройство
На данной картинке изображён принцип связи с RNDIS устройством (ОС Windows).

Более подробно о нём можно почитать тут и там.

Если вкратце, то RNDIS протокол — это расширение NDIS для внешних устройств. Роль протокола — обеспечить поддержку PnP и обмен сетевыми пакетами. По сути своей, RNDIS — самостоятельный сетевой интерфейс, информационной нагрузкой которого являются кадры канального/сетевого уровней (Ethernet или IP кадры, опционально).

На приведённой схеме это реализует кубик «Минипорт Remote NDIS», который отвечает за:
— сервис общения (спросить у сетевого устройства его MAC-адрес, размер пакета, скорость работы и прочее)
— оборачивает отправляемые хостом сетевые пакеты в RNDIS заголовок
— транслирует принимаемые от устройства пакеты, выбрасывая RNDIS заголовок
Кубик «Минипорт Remote NDIS USB» отвечает за транзит RNDIS посылок, работая с драйвером USB шины.
На стороне контроллера STM32 за поддержку RNDIS протокола и работу с USB отвечает файл usbd_rndis_core.c. Он делает то же самое, что и «кубик» хоста «Минипорт Remote NDIS» — занимается приклеиванием/отклеиванием заголовков, а также отвечает на вопросы драйвера. Ответы, вроде MAC-адреса и скорости он берёт из файла usbd_rndis_core.h.
После успешной инициализации RNDIS драйвер Windows создаёт сетевой интерфейс, который в последствии отображается в «Центре управления сетями» и в области трей-индикатора.

Шаг 3. Получение IP-адреса
Итак, для чего нужна служба получения динамического адреса. Эта служба называется DHCP (протокол динамической настройки узла).
После того как хост инициализирует наше устройство, он создаёт сетевой интерфейс.

Сетевой интерфейс (если кто не знает)Сетевой интерфейс — это программная сущность, предоставляющая доступ к ресурсам физической или виртуальной сети.
Чаще всего каждому сетевому интерфейсу хоста соответствует конкретный сетевой адаптер. Но есть множество других интерфейсов, вроде локальной петли или те, что служат для взаимодействия с виртуальной машиной. В их случае в сигнальном виде из хоста «ничего не выходит» — обмен происходит программным способом.

Каждому сетевому интерфейсу хоста должен быть сопоставлен хотябы один IP-адрес. По нему «жители сети» может обратиться к хосту.

Если «на проводе» адресовано несколько сетей (например, устройства с IP адресами 10.4.1.xx и 192.168.1.xx), то интерфейсу может назначаться два «личных» IP-адреса. Они могут выглядеть так: 10.4.1.151 и 192.168.1.200. Узнать набор сетевых интерфейсов и сопоставленных им IP-адресов в ОС Windows можно с помощью команды ipconfig и с помощью ifconfig в ОС Linux.

Для описания сетей/подсетей используется маска. Например, правильное описание сети 10.4.1.xx такое: сеть 10.4.1.0, маска 255.255.255.0. Либо, если 4-ёх байтовое число маски представить в двоичном виде и посчитать число ведущих едениц, то получится значение 24. Тогда сеть можно описать так: 10.4.1.0/24.

Подробнее об этом можно почитать в соответствующих источниках.


Известны две основные стратегии назначения IP-адреса интерфейсу: статический способ (когда пользователь сам прописывает адрес интерфейсу) и динамический (с помощью DHCP-службы).

Последний заключается в том, что при создании интерфейса на хосте активизируется служба DHCP-клиента. Она начинает посылать в сеть (конфигурация которой пока не известна) широковещательные пакеты по протоколу UDP, в надежде на то, что в сети присутствует DHCP-сервер.

Функция DHCP-сервера в общем, и в частности на нашем контроллере — ответить клиенту. В ответе контроллер «говорит»: клиент, ты в такой-то сети, держи такой-то IP-адрес, а ещё у нас имеется DNS-сервер с таким-то адресом.

После этого хост «чувствует себя» намного лучше: он назначает интерфейсу выданный IP-адрес и запоминает IP-адрес DNS-сервера.

Инициализация закончилась, теперь можно вводить имя страницы (run.stm) в браузере хоста.

Надо сказать, что поведение библиотеки LRNDIS настраивается. Службу DHCP-сервера можно исключить из сборки. Тогда на хосте придётся прописывать любой адрес, принадлежащий диапазону 192.168.7.(2-254). Такая сеть создаётся по умолчанию. Её параметры (192.168.7.0/24) также настраиваются. В примере клиенту выдаются адреса в диапазоне 192.168.7.2… 192.168.7.4 с временем лизинга 24 часа.
Более подробно по вопросу настройки библиотеки можно посмотреть в предыдущей статье.

Шаг 4. Загрузка страницы
Для загрузки страницы пользователь может ввести адрес нашего устройства 192.168.7.1 напрямую.
Однако, запоминать цифры не требуется, т.к. помимо DHCP-сервера, есть возможность собрать библиотеку с поддержкой DNS-сервера, функция которого — разрешать сетевые имена. В публикуемом примере DNS-сервер обучен разрешать имя ресурса «run.stm».

Поэтому, если в браузере написать run.stm, сетевая служба хоста отправит нашему (и не только) DNS-серверу запрос, в ответ на который сервер услужливо сообщит: доменному имени «run.stm» соответствует IP-адрес 192.168.7.1. Далее браузер хоста по известному адресу совершит TCP подключение с целью отправить HTTP запрос на получение корневой страницы.

Запрос и ответ между браузером Firefox и контроллером:

История запросов при загрузке страницы:

Из истории мы видим, что, после загрузки корневого HTML-документа браузер также загружает из контроллера другие два файла: discovery.svg и zepto.min.js. Первый — это изображение платы discovery. SVG формат выбран, т.к., являясь изображением векторной графики, мало занимает места в ПЗУ микроконтроллера. Скриптовый файл zepto.min.js включён, т.к. является урезанным аналогом знаменитого JQUERY. Надо заметить, что скрупулёзной экономии места в ПЗУ не производилось, т.к. не смотря на жертву в 35 Кб на все статические ресурсы, памяти контроллера ещё вполне достаточно. К тому же данный размер с дальнейшим увеличением сложности интерфейса обещает расти заметно медленней. Если же интерфейс разросся существенно — всегда есть выход хранить и отдавать статические ресурсы в сжатом виде — все известные браузеры на данный момент поддерживают декомпрессию «на лету».

Ещё один запрос, который отправляет браузер — это запрос /state.cgi. Он формируется скриптом из корневого HTML-документа с периодичностью 5 раз в секунду. Нужен запрос для получения в динамике текущего состояния устройства.
При приёме данного запроса, контроллер формирует и отвечает следующей строкой в JSON формате:

{ "systick": 9528746, "button": 0, "acc": [54, -288, 936], "leds": { "g": 0, "o": 0, "r": 0 } } 

Она и содержит все данные о текущем состоянии устройства, которые впоследствии отображаются на странице средствами JavaScript кода.

Ну и, пожалуй, последний момент в общении с браузером — способ управления.

В примере происходит управление тремя светодиодами. Например, при щелчке на флажок красного светодиода средствами JavaScript отправляется HTTP GET запрос с передачей параметра «r» и значения 0 или 1. Полностью запрос выглядит так: /ctl.cgi?r=1

Таким или альтернативными способами можно передавать любой набор данных, будь то логическое состояние 0/1, или значение текстового поля, или уведомление о нажатии кнопки. Красота подхода заключается в том, что программная логика вовсе может не знать о элементах управления, ибо получает строго формализованные управляющие сообщения. Также можно менять и отлаживать интерфейсную часть (HTML+JS) локально со всеми удобствами, после чего однократно заливать в составе прошивки в контроллер. Локальной web-разработкой, что не мало важно, может заниматься соответствующий специалист.

О стеке LWIP
Никакого сетевого обмена устроить бы не получилось, если бы не данный сетевой стек, который и был встроен в библиотеку.
Поскольку библиотека работает под «голым» железом (без ОС и динамической аллокации памяти), то надстройка в виде сокетов для использования недоступна. Написание сетевых приложений поэтому происходит с использованием сырого API стека. По этой теме, к счастью, в сети много информации.

Также в составе пакета contributions есть множество уже готовых решений. В том числе оттуда и был использован HTTP-сервер.

В прошлой статье я давал краткое описание стека и его настройки. На данный момент был уточнён набор важных для стека определений в файле:

lwipopts.h
#define NO_SYS                          1
#define MEM_ALIGNMENT                   4
#define LWIP_RAW                        1
#define LWIP_NETCONN                    0
#define LWIP_SOCKET                     0
#define LWIP_DHCP                       0
#define LWIP_ICMP                       1
#define LWIP_UDP                        1
#define LWIP_TCP                        1
#define ETH_PAD_SIZE                    0
#define LWIP_IP_ACCEPT_UDP_PORT(p)      ((p) == PP_NTOHS(67))

#define MEM_SIZE                        10000
#define TCP_MSS                         (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF                     (2 * TCP_MSS)

#define ETHARP_SUPPORT_STATIC_ENTRIES   1

#define LWIP_HTTPD_CGI                  1
#define LWIP_HTTPD_SSI                  1
#define LWIP_HTTPD_SSI_INCLUDE_TAG      0

Также была решена проблема с mem_malloc. Хоть текущая версия прошивки и не использует динамическую аллокацию, аппаратный крах при вызове mem_malloc держал настороже. Разрешилось добавлением определения MEM_ALIGNMENT, который раньше был обойдён вниманием.

По этой же причине стабильно заработал поддерживаемый сообществом HTTP-сервер, что и побудило отказаться от планов создания собственного.

Нерешённые вопросы
1. Ньюансы релицензирования стека lwip, который может иметь свои условия включения в состав другого ПО;
2. Доработка DNS-сервера для обработки «многовопросных» пакетов;

Вместо заключения
Благодарю читателя за терпение и надеюсь, что данная статья окажется для него полезной. Опубликованная в исходных кодах библиотека LRNDIS доступна для использования на правах MIT-лицензии. Считаю замечательным, если работа, на которую было уделено ощутимое время и запас сил, окажется полезной ещё кому-то. На худой конец, без использования открытых библиотек не получилось бы и этой.

В данный момент планируется поддержка библиотеки, поэтому за вопросами можно обращаться по адресу электронной почты [email protected]

Доработка USB-стека в микроконтроллерах STM32 и TivaC / Хабр
Наличие USB порта в современных микроконтроллерах открывает широкие возможности для самостоятельного изготовления разнообразных управляемых с компьютера устройств. На практике, однако, выясняется, что поставляемые производителем библиотеки для работы с USB нуждаются в доработке. Если вам интересен опыт подобной доработки для двух популярных семейств МК — добро пожаловать под кат.

Постановка задачи


Итак, мы хотим сделать устройство, которое обменивается с компьютером сообщениями произвольной длины через USB порт. Самый простой способ сделать это — воспользоваться USB классом символьных устройств (CDC), известным также под названием 'виртуальный последовательный порт'. Тогда на хост-системе, к которой вы подключите ваше устройство, автоматически будет создан последовательный порт, через который вы сможете обмениваться данными с устройством, работая с ним как с обычным файлом. На практике, однако, выясняется, что некоторые необходимые для этого функции в USB-стеке производителя либо не реализованы вовсе, либо реализованы с ошибками. Мы начнем с рассмотрения микроконтроллеров STM32 (первый случай) и закончим другим популярным семейством — Texas Instruments Tiva C (второй случай). Оба семейства имеют архитектуру ARM Cortex M4.

STM32 — просто добавь кода


Микроконтроллеры STM обычно имеют богатый функционал при весьма демократичной цене. Производитель поставляет широкий спектр библиотек на все случаи жизни. Среди них есть и библиотеки для поддержки USB, и библиотека для работы с прочей периферией, имеющейся на кристалле. В последнее время все эти библиотеки были объединены в один мега-пакет под названием STM32Cube. При этом, однако, о совместимости особо не заботились и поменяли все, что только смогли поменять, включая названия полей в структурах, описывающих конфигурацию портов ввода-вывода, при том, что само название структуры осталось прежним. Интресно, что есть еще и третий вариант примеров и библиотек, который можно найти на сайте stm32f4-discovery.com. Однако, автор этого варианта очень любит переименовывать файлы, позаимствованные у STM, дабы увековечить свои инициалы, что тоже не добавляет совместимости со всем остальным кодом. Учитывая все вышеизложенное, я решил взять за основу последний до-кубический вариант библиотек, поставляемых STM. Сейчас их можно найти в комплекте поставки компиляторов (я использую IAR). Чтобы потом долго не искать, библиотеки включены в состав проекта, который вы можете взять из гита по ссылке внизу. Для экспериментов я использовал плату STM32F4DISCOVERY www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF252419. Если у вас другая плата и код сразу не заработал, дело скорее всего в частоте внешнего кварцевого генератора. Хотя библиотеки изобилуют всяческими макроопределениями, и в последней версии библиотек среди них появился и макрос для внешней тактовой частоты, в коде этот параметр по-прежнему прописан в виде числа без всяких комментариев, видимо, чтобы разработчики не теряли форму и не забывали читать мануал. Вы можете найти это число — тактовую частоту в мегагерцах — в файле system_stm32f4xx.c в определении макроса PLL_M.

Итак, берем за основу готовый пример, который перекладывает данные из USB в последовательный порт микроконтроллера и обратно. Последовательный порт нам не понадобится, а данные мы будем просто перекладывать из входного потока в выходной, то есть реализуем эхо. С помощью PuTTY убеждаемся, что оно работает. Но этого недостаточно. Для обмена данными с устройством нам понадобится слать много больше одного символа за раз. Пишем тестовую программу на питоне, которая шлет посылки случайной длины и вычитывает ответ. И тут нас ждет сюрприз. Тест работает, но недолго, после чего очередная попытка чтения либо зависает навсегда, либо завершается по таймауту, если он выставлен. Исследование проблемы с помощью отладчика показывает, что МК таки отослал все полученные данные, причем последняя посылка имела длину 64 байта. Что же произошло?

USB-стек на хост-системе имеет многослойную структуру. На уровне драйвера данные получены, но остались у него в кэше. Драйвер передает закэшированные данные приложению тогда, когда приходят новые данные и вытесняют старые, либо когда драйвер узнает, что новых данных пока ожидать не следует. Откуда же он может получить это знание? USB шина передает данные пакетами. Максимальный размер пакета в нашем случае как раз 64 байта. Если в очередном пакете данных пришло меньше, значит новых данных пока можно не ждать, и это является сигналом для того, чтобы передать приложению все полученные данные. А если данных пришло ровно 64 байта? На этот случай в протоколе предусмотрена посылка пакета нулевой длины (ZLP), который и является сигналом прерывания потока. Получив его, драйвер понимает, что новых данных пока ожидать не следует. В нашем случае он его не получил потому, что разработчики USB стека для STM32 про ZLP просто ничего не знали.

Вторая проблема, которую разработчики USB-стека незаслуженно обошли вниманием — что делать с данными, которые были получены по USB, если их некуда девать, т.к. входной буфер переполнен. По большому счету, их вообще не волновала проблема входного буфера — они предполагали, что все полученные данные немедленно обрабатываются, что, конечно-же, не всегда может быть выполнено. В USB протоколе на случай, если данные не могут быть получены, предусмотрен ответ NAK — отрицательное подтверждение. После такого ответа хост просто посылает данные еще раз. Если мы хотим избежать переполнения входного буфера, нам нужно в случае, если в нем нет места для полной посылки (64 байта), переводить канал в состояние NAK, что обеспечивает автоматический ответ NAK на все входящие пакеты.

Tiva C — слоеный пирог с багами


Для экспериментов была взята плата 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. Первый просто посылает все полученные данные обратно, второй реализует пакетный протокол, который вы можете легко адаптировать под свои нужды. В папке tools — тестовые скрипты на питоне.
USB HID интерфейс для STM32 в STM32IDE / Как это устроено / iXBT Live

Ряд микроконтроллеров STM32 имеют на борту USB интерфейс для связи с компьютерами. Как правило, удобнее всего использовать предоставляемый компаний ST Microelectronics драйвер класса CDC (Communication Device Class ). Он позволяет использовать на стороне компьютера UART через USB и не требует установки драйверов. Со стороны STM32 при этом требуется только поменять операции вывода данных, остальное делается самостоятельно. Причём скорость такого соединения может быть практически любой, поддерживаемой компьютером.

Однако ряд разработок, особенно, когда приходишь в другую компанию, где используется HID Class (Human Interface Device), в случае разработки новой версии устройства требуется поддерживать ранее выбранный интерфейс. Что, собственно, и случилось. Примеры проектов от самой ST, которые они дают при загрузке STM32 Cube MX и IDE, как обычно, дали только минимальное понимание, но не раскрыли, что и как надо делать. Я когда-то разбирался с USB, даже писал собственный драйвер, но это было так давно… Остались только общие воспоминания. Посему пришлось искать дополнительную информацию, чтобы получить стартовую точку.

Первое найденное было видеороликом на youtube в стиле HID за 5 минут 🙂 Автор даёт доступ к своему коду на GitHub. Всё, типа круто, красиво, просто вставляйте к себе и всё будет чудесно. Судя по отзывам под роликом, некоторым этого хватило.  Изучив исходники понял, что минимального прозрения не наступило, да и уровень полученной информации мал для того, чтобы решить поставленную задачу.  Но закомство с этим материалом было явно полезным. Решение вопроса с использованием кубика (STM32Cube MX) мне лично импонирует больше, чем другие подходы, поскольку позволяет отвлечься от ряда низкоуровневых операций и генерация проекта всегда происходит в одном стиле. Соответственно, изучение этого примера показало, на какие файлы надо обратить внимание, где и что надо поменять или добавить, какие функции использовать для получения и отправки данных именно для нашей выбранной среды программирования.

Следующий поиск оказался весьма удачным. Хабр — известный сайт, на котором можно найти много полезного по разной электронной тематике. Нашлась там и статья STM32 и USB-HID — это просто. Я не являюсь постоянным клиентом Хабра и не знаю автора этой статьи RaJa, но на мой взгляд это очень хорошая статья, описывающая основные положения работы HID интерфейся. Без её прочтения читать дальше здесь бессмысленно, поскольку далее будут, в основном,  комментарии для адаптации кода к среде разработки STM32IDE/STM32CubeMX + Atollic TrueStudio. (Далее STM32IDE). Да и столь популярный в 2014 году и реально очень неплохой проект EmBlocks, увы, умер.

Первое, что необходимо решить — как тестировать вновь создаваемое устройство. Лет… дцать назад я использовал для этого анализатор и синтезатор трафика USB — очень полезные, но дорогие игрушки 🙂 Сейчас у меня такой возможности нет, да и должен же быть более простой путь. Тем более для простого стандартного интерфейса без написания собственного драйвера. Авторы обоих рассмотренных выше проектов пошли самы простым для них путём — написание простой программы на известных им языках. Но автор статьи на Хабре сделал очень правильный шаг — он написал свой проект, совместимый с программой ST HID Demonstrator (ссылка есть в статье), позволяющей поуправлять нашим устройством, как графически, так и послать свои данные и посмотреть, что пришло от нашего устройства. Фактически программа может использоваться и в дальнейшем для отладки будущей программы на выбранном микроконтроллере.

Своё ознакомление с проектом для HID я осуществлял с платой STM32L476 Discovery. Плата, вообще говоря, может быть любой, где USB интерфейс микроконтроллера физически подключён к отдельному разъёму USB. Есть у меня и Nucleo 32 с STM32L4, но там один разъём USB тспользуется и для программирования/отладки, и для связи с хостом, что добавляет интриги в интерфейс и может служить источником дополнительных непоняток. Оно нам надо?

Итак, комментарии и дополнения к статье по привязке HID к STM32IDE примерно по тем же шагам, как и в хабровской статье.

Структура проекта

В STM32IDE структура всех проектов задаётся при генерации проекта из среды назначения функциональности пинов и пользователю о том заботиться не надо. В частности, в кубике (что отдельном STM32Cube MX, что в встроенном в STM32IDE)  активируем USB, как Device, и добавляем Middleware USB Custom HID.

Рис.1 Выбор USB интерфейсаРис.2 Выбор и первичная настройка MiddlewareНадо заметить, что несмотря на установку размера буфера в 64 байта, эта величина не вносится по #define. Видимо баг текущей версии кубика. Далее покажем, где надо пофиксить. Указанный резмер дескриптора равный 79 — это значение для данного конретного стартового проекта

Заходим в Clock Configuration. Вполне вероятно, что могут быть проблемы с системными частотами, которые маркируются малиновым цветом.

Рис. 3 Возможные проблемы по установке частот

Если так, нажимаем Resolve Clock Issues и, скорее всего, всё будет настроено на максимальные частоты. Главное — USB Clock будет выставлен на 48 МГц. Надо заметить, что в семействе STM32L4 генератор на 48МГц имеет автоподстройку по SOF (Start Of Frame), что позволяет создавать USB устройства без внешнего кварца/генератора. Если, конечно, остальной дизайн допускает использование некварцованных генераторов. Для других семейств не проверял, поскольку для моего текущего проекта был выбран именно L4. Только надо отметить, что при использовании USB есть некоторая минимальная частота работы микроконтроллера. Я делал прикидку для другого проекта, где надо общаться с хостом и при этом потреблять минимум тока. Задачи простые, не требуют большой скорости и я хотел запустить МК на 8МГц. Оказалось, что меньше 14МГц при подключении USB ставить не могу, RCC не позволяет. Пришлось остановиться на следующем круглом значении 16МГц.

Собственно, настройка аппаратной части USB и выбор файлов, отвечающих за базовую функциональность этого интерфейса на на этом закончены.  Вся остальная периферия, находящаяся на выбранной плате настраивается автоматически при её выборе на старте проекта. Сохраняем, генерим проект и переходим к «программированию» в сравнении с описанным на Хабре проектом.

Это страшное слово Descriptor

Стандартные массивы данных для передачи информации хосту, с чем он будет иметь дело. Для интереса можно посмотреть дескрипторы устройства и конфигурации.  Сейчас их можно оставить такими, как получились, но в дальнейшем они наверняка потребуют редактирования. Впрочем, не исключено, что они будут генериться по тем параметрам, что ставятся в кубике. Что не может не радовать. А вот Report Descriptor стоит изучить получше — это фактически основное, что придётся в дальнейшем править ручками. Не знаю, откуда RaJa взял его дескрипторы, в нашём случае они генерируются кубиком и располагаются в следующих файлах проекта:

Дескриптор от RajaДескриптор от STФайл в проекте
RHID_DeviceDescriptorUSBD_FS_DeviceDescusbd_desc.c
RHID_ConfigDescriptorUSBD_CUSTOM_HID_CfgFSDescusbd_customhid.c
RHID_ReportDescriptorCUSTOM_HID_ReportDesc_FSusbd_custom_hid_if.c

Поскольку для простоты сейчас будем работать только с ST HID Demonstrator, то не мудрствуя лукаво я просто скопировал содержимое  RHID_ReportDescriptor в соответствующее место моего проекта. Только подставил свои константы на место длины. Надо отметить, что надо точно посчитать количество байтов в этом дескрипторе (в этом проекте 79) и убедиться, что именно это значение стоит в Class Parameters. Не больше и не меньше. Иначе хост не опознает подключённое устройство. Проверено 🙂

Далее заходим в файл usbd_customhid.h  и меняем значения CUSTOM_HID_EPIN_SIZE и CUSTOM_HID_EPOUT_SIZE на 0x40U. Честно говоря, немного напрягает то, что ST не даёт альтернатив смене значения по умолчанию 2 на другое значение и далее в коде с использованием этих констант стоит комментарий, что не более 2х байт. Но,  с другой стороны,  это было рекомендовано в первом найденном описании и, вообще говоря, установка такого значения выглядит достаточно логично. Иначе в чём отличие CustomHID от обычного? Проблема в том, что при регенерации проекта из кубика, что на этапе первичного кода происходит довольно часто, это значение не сохраняется и его надо восстанавливать ручками. Для этого я себе в main вывел строку warning, чтобы не забывать проверить эти константы. Возможно я ошибаюсь, и в дальнейшем всё окажется проще. Но в такой конфигурации работает 🙂

Цикл обмена (пишем/читаем)

Для выдачи данных на хост всё достаточно аналогично описанию на Хабре. Только название функции другое: USBD_CUSTOM_HID_SendReport(). Все остальные реомендации из той статьи подходят по полной программе.

А вот чтение здесь интереснее, чем на Хабре.  И на самом деле несколько проще. Обработка принятого массива происходит в usbd_custom_hid_if.c / static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state). 

В этом тестовом проекте я не заморачивался с обработкой входных параметров и следуя своей обычной практике минимальности времени обработки прерываний, просто копирую полученные данные в заранее определённый массив и устанавливаю флаг готовности данных от USB

Ну, а собственно «сбор данных» (нажатие кнопок джойстика) и реакция на полученные от хоста данные в этом прото проекте делаю внутри бесконечного цикла в main.c Всё просто 🙂 В этом прото проекте нет разделения реакции на SET_FEATURE и SET_REPORT,  с этим надо будет разобраться далее, в реальном проекте. Компилируем, запускаем, подключаем к хосту и там должен появиться новый CustomHID от STMicroelectronics.

Звпускаем на хосте  USB HID Demonstrator. На плате,  с которой я запускал этот проект, не имеет органов для работы с Variable Inputs/Outputs, поэтому в разделе Graphic customization были убраны соответствующие назначениями, оставлено 5 кнопок и назначены ID, определённые в проекте: 1, 2 для Output report (входные данные для ST) и 4 для Input Report (выход от ST).

Рис. 4 Настройка демонстратора

Моей задачей для этого проекта было управлять парой светодиодов на плате, что стало работать сразу, как эта программа обнаружила подключенную плату, и включать «лампочки» этой платы при нажатии различных кнопок джойстика на плате, а вот здесь сразу не получилось. При указанных настройках все пять лампочек одновременно зажигались  при нажатии на центр джойстика. Остальные кнопки не отображались. При этом, если перейти на Input/Otput transfer, то данные были вполне ожидаемы. Т.е. сам интерфейс работает, но отображение в программе на хосте не отвечает моим запросам. Слава богу ST предоставляетс исходники,  а в соседнем кубике сидит программист нашей группы, пишущий в том числе и софт для компьютеров. В общем, он подправил одну функцию и сгенерил исполняемую программу. Всё стало работать, как хотелось. Конечно, можно было бы на каждую кнопку создать свой report с уникальным номером, что исходно и предусмотрено. В этом случае было бы достаточно посылать по одному байту для каждой кнопки, но мой проект предусматривает многобайтный отчёт.  Исходник подправленной функции и подправленный исполняемый файл можно скачать по ссылке ниже. 

 На этом, пожалуй, всё.  Если у Вас есть такая же плата 32L476GDISCOVERY, то для начала можно просто скачать мой прото проект, адаптированный для него демонстратор и исходник изменённой функции по этой ссылке. Исходный USB HID Demonstrator скачивается с сайта STM, инсталлируется и его исполняемый файл заменяется моим. Импортируете в STM32IDE мой проект, компилируете и должны получить работающую базу для своих проектов. Если у Вас другая плата, то адаптируете «сбор информации» и включение светодиодов под свою плату.

Для дальнейшей работы обязательно прочтите указанную статью RaJa с Хабра. Она даст понимание того, что и как должно быть сделано для других проектов с USB HID интерфейсом. А ещё лучше начать с неё 🙂

И при выборе класса устройства для Вашего проекта надо учитывать следующее: минимальный период опроса HID устройств — 1ms. И если я правильно помню, это скорее пожелание системе от внешнего устройства. В стандартном HID устройстве за один кадр (frame) передаётся только два байта, т.е. скорость обмена не более 2 кбайт/с. В Custom HID на 
Full Speed (12 мбит/с) объём данных отчёта (report) -  не более 64 байт, т.е. скорость обмена с Вашим HID не более 64 кбайт/с. Для High Speed (480 мбит/с) — максимальный объём данных 512 байт (512 кбайт/с). Не будь у меня ограничения совместимости с предыдущим софтом, используемым в компании, использовал хотя бы CDC.

У меня изучение статей и адаптация под мои хотелки заняло три дня. Описание заняло больше 🙂 Надеюсь, что у тех, кто воспользуется этой статьёй, аналогичный процесс займёт не более одного дня. Комментируйте, спрашивайте. Что смогу — отвечу. Что не смогу, вместе поищем решение.

90000 90001 STSW-STM32102 - STM32 Virtual COM Port Driver 90002 90003 STSW-STM32102 90004 90005 STM32 Virtual COM Port Driver 90004 90005 1.5.0 90004 90005 ST 90004 90005 x 90012 90013 Please note, that cookies should be enabled for successful downloading the software. 90014 90015 x 90012 90013 Something went wrong with the server request. Please try again in a few moments. 90014 90015 90012 If you have an account on my.st.com, login and download the software without any further validation steps.90015 90012 If you do not want to login now, you can download the software by simply providing your name and e-mail address in the form below and validating it. 90015 90012 This allows us to stay in contact and inform you about updates of this software. 90015 90012 90027 For subsequent downloads this step will not be required for most of our software. 90028 90015 90012 90013 To validate your e-mail and start the download, please click on the link inside the e-mail that has been sent to you.This link will be valid for 24 hours. Please check your spam filters in case you did not receive the e-mail. 90014 90015 x 90012 Please indicate your acceptance or NON-acceptance by selecting "I ACCEPT" or "I DO NOT ACCEPT" as indicated below in the media. 90015 90012 BY INSTALLING COPYING, DOWNLOADING, ACCESSING OR OTHERWISE USING THIS SOFTWARE PACKAGE OR ANY PART THEREOF (AND THE RELATED DOCUMENTATION) FROM STMICROELECTRONICS INTERNATIONAL NV, SWISS BRANCH AND / OR ITS AFFILIATED COMPANIES (STMICROELECTRONICS), THE RECIPIENT, ON BEHALF OF HIMSELF OR HERSELF, OR ON BEHALF OF ANY ENTITY BY WHICH SUCH RECIPIENT IS EMPLOYED AND / OR ENGAGED AGREES TO BE BOUND BY THIS SOFTWARE PACKAGE LICENSE AGREEMENT.90015 90012 Under STMicroelectronics 'intellectual property rights and subject to applicable licensing terms for any third-party software incorporated in this software package and applicable Open Source Terms (as defined here below), the redistribution, reproduction and use in source and binary forms of the software package or any part thereof, with or without modification, are permitted provided that the following conditions are met: 90015 90040 90041 Redistribution of source code (modified or not) must retain any copyright notice, this list of conditions and the following disclaimer.90042 90041 Redistributions in binary form, except as embedded into microcontroller or microprocessor device manufactured by or for STMicroelectronics or a software update for such device, must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and / or other materials provided with the distribution. 90042 90041 Neither the name of STMicroelectronics nor the names of other contributors to this software package may be used to endorse or promote products derived from this software package or part thereof without specific written permission.90042 90041 This software package or any part thereof, including modifications and / or derivative works of this software package, must be used and execute solely and exclusively on or in combination with a microcontroller or a microprocessor devices manufactured by or for STMicroelectronics. 90042 90041 No use, reproduction or redistribution of this software package partially or totally may be done in any manner that would subject this software package to any Open Source Terms (as defined below).90042 90041 Some portion of the software package may contain software subject to Open Source Terms (as defined below) applicable for each such portion ( "Open Source Software"), as further specified in the software package. Such Open Source Software is supplied under the applicable Open Source Terms and is not subject to the terms and conditions of license hereunder. "Open Source Terms" shall mean any open source license which requires as part of distribution of software that the source code of such software is distributed therewith or otherwise made available, or open source license that substantially complies with the Open Source definition specified at www.opensource.org and any other comparable open source license such as for example GNU General Public License (GPL), Eclipse Public License (EPL), Apache Software License, BSD license and MIT license. 90042 90041 This software package may also include third party software as expressly specified in the software package subject to specific license terms from such third parties. Such third party software is supplied under such specific license terms and is not subject to the terms and conditions of license hereunder.By installing copying, downloading, accessing or otherwise using this software package, the recipient agrees to be bound by such license terms with regard to such third party software. 90042 90041 STMicroelectronics has no obligation to provide any maintenance, support or updates for the software package. 90042 90041 The software package is and will remain the exclusive property of STMicroelectronics and its licensors. The recipient will not take any action that jeopardizes STMicroelectronics and its licensors 'proprietary rights or acquire any rights in the software package, except the limited rights specified hereunder.90042 90041 The recipient shall comply with all applicable laws and regulations affecting the use of the software package or any part thereof including any applicable export control law or regulation. 90042 90041 Redistribution and use of this software package partially or any part thereof other than as permitted under this license is void and will automatically terminate your rights under this license. 90042 90063 90012 THIS SOFTWARE PACKAGE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW.IN NO EVENT SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.90015 90012 EXCEPT AS EXPRESSLY PERMITTED HEREUNDER AND SUBJECT TO THE APPLICABLE LICENSING TERMS FOR ANY THIRD-PARTY SOFTWARE INCORPORATED IN THE SOFTWARE PACKAGE AND OPEN SOURCE TERMS AS APPLICABLE, NO LICENSE OR OTHER RIGHTS, WHETHER EXPRESS OR IMPLIED, ARE GRANTED UNDER ANY PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF STMICROELECTRONICS OR ANY THIRD PARTY. 90015 90068 90004 90070.90000 90001 STSW-STM32121 - STM32F10x, STM32L1xx and STM32F3xx USB full speed device library (UM0424) 90002 90003 STSW-STM32121 90004 90005 STM32F10x, STM32L1xx and STM32F3xx USB full speed device library (UM0424) 90004 90005 4.1.0 90004 90005 ST 90004 90005 - 90004 90005 x 90014 90015 Please note, that cookies should be enabled for successful downloading the software. 90016 90017 x 90014 90015 Something went wrong with the server request. Please try again in a few moments.90016 90017 90014 If you have an account on my.st.com, login and download the software without any further validation steps. 90017 90014 If you do not want to login now, you can download the software by simply providing your name and e-mail address in the form below and validating it. 90017 90014 This allows us to stay in contact and inform you about updates of this software. 90017 90014 90029 For subsequent downloads this step will not be required for most of our software. 90030 90017 90014 90015 To validate your e-mail and start the download, please click on the link inside the e-mail that has been sent to you.This link will be valid for 24 hours. Please check your spam filters in case you did not receive the e-mail. 90016 90017 x 90014 Please indicate your acceptance or NON-acceptance by selecting "I ACCEPT" or "I DO NOT ACCEPT" as indicated below in the media. 90017 90014 BY INSTALLING COPYING, DOWNLOADING, ACCESSING OR OTHERWISE USING THIS SOFTWARE PACKAGE OR ANY PART THEREOF (AND THE RELATED DOCUMENTATION) FROM STMICROELECTRONICS INTERNATIONAL NV, SWISS BRANCH AND / OR ITS AFFILIATED COMPANIES (STMICROELECTRONICS), THE RECIPIENT, ON BEHALF OF HIMSELF OR HERSELF, OR ON BEHALF OF ANY ENTITY BY WHICH SUCH RECIPIENT IS EMPLOYED AND / OR ENGAGED AGREES TO BE BOUND BY THIS SOFTWARE PACKAGE LICENSE AGREEMENT.90017 90014 Under STMicroelectronics 'intellectual property rights and subject to applicable licensing terms for any third-party software incorporated in this software package and applicable Open Source Terms (as defined here below), the redistribution, reproduction and use in source and binary forms of the software package or any part thereof, with or without modification, are permitted provided that the following conditions are met: 90017 90042 90043 Redistribution of source code (modified or not) must retain any copyright notice, this list of conditions and the following disclaimer.90044 90043 Redistributions in binary form, except as embedded into microcontroller or microprocessor device manufactured by or for STMicroelectronics or a software update for such device, must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and / or other materials provided with the distribution. 90044 90043 Neither the name of STMicroelectronics nor the names of other contributors to this software package may be used to endorse or promote products derived from this software package or part thereof without specific written permission.90044 90043 This software package or any part thereof, including modifications and / or derivative works of this software package, must be used and execute solely and exclusively on or in combination with a microcontroller or a microprocessor devices manufactured by or for STMicroelectronics. 90044 90043 No use, reproduction or redistribution of this software package partially or totally may be done in any manner that would subject this software package to any Open Source Terms (as defined below).90044 90043 Some portion of the software package may contain software subject to Open Source Terms (as defined below) applicable for each such portion ( "Open Source Software"), as further specified in the software package. Such Open Source Software is supplied under the applicable Open Source Terms and is not subject to the terms and conditions of license hereunder. "Open Source Terms" shall mean any open source license which requires as part of distribution of software that the source code of such software is distributed therewith or otherwise made available, or open source license that substantially complies with the Open Source definition specified at www.opensource.org and any other comparable open source license such as for example GNU General Public License (GPL), Eclipse Public License (EPL), Apache Software License, BSD license and MIT license. 90044 90043 This software package may also include third party software as expressly specified in the software package subject to specific license terms from such third parties. Such third party software is supplied under such specific license terms and is not subject to the terms and conditions of license hereunder.By installing copying, downloading, accessing or otherwise using this software package, the recipient agrees to be bound by such license terms with regard to such third party software. 90044 90043 STMicroelectronics has no obligation to provide any maintenance, support or updates for the software package. 90044 90043 The software package is and will remain the exclusive property of STMicroelectronics and its licensors. The recipient will not take any action that jeopardizes STMicroelectronics and its licensors 'proprietary rights or acquire any rights in the software package, except the limited rights specified hereunder.90044 90043 The recipient shall comply with all applicable laws and regulations affecting the use of the software package or any part thereof including any applicable export control law or regulation. 90044 90043 Redistribution and use of this software package partially or any part thereof other than as permitted under this license is void and will automatically terminate your rights under this license. 90044 90065 90014 THIS SOFTWARE PACKAGE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW.IN NO EVENT SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.90017 90014 EXCEPT AS EXPRESSLY PERMITTED HEREUNDER AND SUBJECT TO THE APPLICABLE LICENSING TERMS FOR ANY THIRD-PARTY SOFTWARE INCORPORATED IN THE SOFTWARE PACKAGE AND OPEN SOURCE TERMS AS APPLICABLE, NO LICENSE OR OTHER RIGHTS, WHETHER EXPRESS OR IMPLIED, ARE GRANTED UNDER ANY PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF STMICROELECTRONICS OR ANY THIRD PARTY. 90017 90070 90004 90072.90000 MichaelTien8901 / STM32WINUSB: Convert STM32 USB CDC to WINUSB Device with HAL library 90001 Skip to content Sign up 90002 90003 Why GitHub? Features → 90002 90003 Code review 90006 90003 Project management 90006 90003 Integrations 90006 90003 Actions 90006 90003 Packages 90006 90003 Security 90006 90003 Team management 90006 90003 Hosting 90006 90021 90002 90003 Customer stories → 90006 90003 Security → 90006 90021 90006 90003 Team 90006 90003 Enterprise 90006 90003 Explore 90002 90003 Explore GitHub → 90006 90021 90038 Learn & contribute 90039 90002 90003 Topics 90006 90003 Collections 90006 90003 Trending 90006 90003 Learning Lab 90006 90003 Open source guides 90006 90021 90038 Connect with others 90039 90002 90003 Events 90006 90003 Community forum 90006 90003 GitHub Education 90006 90021 90006 90063 90021.90000 migite2232 / stm32_usb_audio: USB Audio Class with HAL Driver for STM32F103RC 90001 Skip to content Sign up 90002 90003 Why GitHub? Features → 90002 90003 Code review 90006 90003 Project management 90006 90003 Integrations 90006 90003 Actions 90006 90003 Packages 90006 90003 Security 90006 90003 Team management 90006 90003 Hosting 90006 90021 90002 90003 Customer stories → 90006 90003 Security → 90006 90021 90006 90003 Team 90006 90003 Enterprise 90006 90003 Explore 90002 90003 Explore GitHub → 90006 90021 90038 Learn & contribute 90039 90002 90003 Topics 90006 90003 Collections 90006 90003 Trending 90006 90003 Learning Lab 90006 90003 Open source guides 90006 90021 90038 Connect with others 90039 90002 90003 Events 90006 90003 Community forum 90006 90003 GitHub Education 90006 90021 90006 90021.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *