Usb hid что это

Обновлено: 06.07.2024

В качестве подопытного устройства для тестов я буду использовать как раз USB HID устройство на микроконтроллере stm32 из статьи, ссылка на которую дана выше.

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

Выполнить этот квест помогут API-функции, описанные в таблице ниже (в среднем столбце указано из какой библиотеки соответствующая функция вызывается):

имя API-функции библиотека предназначение
HidD_GetHidGuid hid.dll получает GUID интерфейса класса HID
SetupDiGetClassDevs setupapi.dll получает список интерфейсов с указанным GUID и набор информации о каждом из них (грубо говоря кусок реестра со списком нужных интерфейсов и информацией о каждом из них)
SetupDiDestroyDeviceInfoList setupapi.dll освобождает ресурсы, используемые функцией SetupDiGetClassDevs
SetupDiEnumDeviceInterfaces setupapi.dll получает из полученного ранее набора информации об интерфейсах информацию об одном конкретном интерфейсе
SetupDiGetDeviceInterfaceDetail setupapi.dll получает подробную информацию о контретном интерфейсе, включая путь для подключения к нему

Кстати, в созданном нами ранее HID-устройстве на stm32 (ссылка на которое приводится в начале статьи) мы сами не создавали дескрипторы репортов и не прописывали Usages, они были прописаны где-то на уровене middleware. Если бы мы хотели сделать это самостоятельно, то нужно было бы создавать HID-устройство на основе класса Custom.

Кстати, мы в приведённой в качестве примера программе сможем увидеть USB мышь и клавиатуру, но не сможем к ним подключиться, поскольку к этим устройствам операционка подключается в режиме монопольного доступа и откажет в таком доступе функции CreateFile. Но это так, к слову.

Для обмена данными с устройствами USB HID могут использоваться следующие API-функции:

имя API-функции предназначение
HidD_GetFeature Читает из устройства Feature-репорт, используя управляющую передачу.
HidD_SetFeature Отправляет устройству Feature-репорт, используя управляющую передачу.
HidD_GetInputReport 1 Читает Input-репорт, используя управляющую передачу. Данные читаются в обход буфера, прочитанного драйвером HID
HidD_SetOutputReport 1 Отправляяет устройству Output-репорт, используя управляющую передачу.
ReadFile 1 Читает Input-репорт, используя передачу типа прерывание. Данные на самом деле читаются из буфера, заполняемого драйвером HID, который с заданным периодом опрашивает устройство. Если буфер пуст, функция просто ждёт, пока в него придут данные.
WriteFile 1 Отправляет Output-репорт, используя, если возможно, передачу типа прерывание, в противном случае используется управляющая передача.

Ниже показана структурная схема взаимодействия ПО хоста (в том числе API-функций) и HID-устройства:

Класс USB HID - это стандартный класс USB-устройств, который включает в себя множество устройств. Устройство HID определяет его как устройство для взаимодействия человека с компьютером, используемое для управления некоторыми аспектами работы компьютера, такими как USB-мышь, USB-клавиатура, игровой USB-джойстик и т. Д. Однако оборудование HID не обязательно требует интерфейса человек-машина, если оборудование, соответствующее спецификации категории HID, является оборудованием HID.

Одним из преимуществ устройств USB HID является то, что операционная система поставляется с драйверами HID, и пользователям не нужно разрабатывать драйверы, если они используют системные вызовы API для завершения взаимодействия.

Он содержит две наиболее важные инструкции:

1. 《Device Class Definition for human interface device (HID)》

2. 《Universal Serial Bus HID Usage Tables》

Документ 1 описывает базовый состав и формат HID, а документ 2 является дополнением к документу 1, в котором перечислены основные компоненты различных устройств HID.

Основываясь на введении базового протокола USB в предыдущей главе, в этой главе основное внимание уделяется классам взаимодействия человека и компьютера HID. (Базовый протокол USB-связи доступен по умолчанию)

2.1 Канал связи USB HID устройства

Все устройства HID связываются с хостом через канал управления USB (канал по умолчанию, конечная точка 0) и канал прерывания (конечная точка 1 или конечная точка 2).

Описание запроса конвейера

Выход прерывания Дополнительно Передача выходных данных с хоста на устройство

Примечание. USB-хост - это ПК и USB-устройства, такие как мышь. В описании конечной точки прерывания указан временной интервал опроса хоста. То есть хост будет «запрашивать» данные через определенные промежутки времени.

Конвейер управления в основном используется в следующих трех аспектах

  • Получение / ответ на запрос управления USB-хостом и связанные данные
  • Передавать данные, когда USB-хост запрашивает (например, отвечает на запрос Get_Report и т. Д.)
  • Получать данные с USB-хоста

Конвейер прерывания в основном используется в следующих двух аспектах

  • USB-хост получает данные асинхронной передачи от USB-устройства
  • USB-хост отправляет данные в реальном времени на USB-устройство.

Передача выходных данных прерывания от USB-хоста к USB-устройству является необязательной.Когда выходные данные прерывания не поддерживаются, USB-хост передает данные на USB-устройство через канал управления.

2.2 Дескрипторы, относящиеся к USB HID устройствам

В дополнение к пяти стандартным дескрипторам USB (дескриптор устройства, дескриптор конфигурации, дескриптор интерфейса, дескриптор конечной точки, строковый дескриптор), дескрипторы устройства HID также включают три дескриптора, зависящих от класса устройства HID: Дескриптор HID, дескриптор отчета (Отчет), дескриптор объекта (Физический) 。

Иерархическая взаимосвязь между ними показана на рисунке:


Можно видеть, что на уровне Interface desc указано HID desc.

В дополнение к трем конкретным дескрипторам HID, которые составляют интерпретацию устройств HID, к устройствам HID относятся пять стандартных дескрипторов:

  1. Дескриптор устройства : bDeviceClass, bDeviceSubClass, bDeviceProtocolТри значения должны быть 0
  2. Дескриптор интерфейса : bInterfaceClass Значение должно быть 0x03,bInterfaceSubClass Имеет значение 0 или 1 , 1 указывает, что HID-устройство является загрузочным устройством (BootDevice, обычно значимым для ПК, что означает, что BIOS может распознать HID-устройство, которое вы используете при загрузке, и только стандартную мышь или клавиатуру можно назвать BootDevice), 0 указывает устройство HID. Операционная система может распознавать используемое устройство только при загрузке. bInterfaceProtocol Смысл значения следующий:

0 — No Sub Class

1 — Boot Interface SubClass

2.2.1 Дескриптор HID

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

Дескриптор HID
Смещение площадь Размер (байт) ценность описание
0 bLength 1 цифровой Длина этого дескриптора в байтах
1 bDescriptorType 1 постоянный Тип дескриптора (здесь 0x21 Категория HID)
2 bcdHID 2 цифровой Номер версии спецификации HID (код BCD) с использованием 4-х шестнадцатеричных кодов формата BCD
4 bCountryCode 1 цифровой Идентификационный код страны назначения оборудования
5 bNumDescriptors 1 цифровой Количество поддерживаемых вспомогательных дескрипторов
6 bDescriptorType 1 постоянный Типы дескрипторов, связанных с HID, см. В таблице ниже
7 wDescriptorLength 2 цифровой Сообщите общую длину дескриптора
9 bDescriptorType 1 постоянный Константа, используемая для определения типа дескриптора, используйте устройство с более чем одним дескриптором
10 wDescriptorLength 2 цифровой Общая длина дескриптора, используемого в устройствах с более чем одним дескриптором.
Определение типа дескриптора, связанного с HID

Тип дескриптора, связанный с HID

В стандартном запросе USB, когда используется дескриптор конфигурации get, он будет возвращен в следующем порядке, то есть дескриптор HID также будет возвращен.


Дескриптор HID также содержит тип и длину присоединенного к нему дескриптора (например, дескриптора отчета), а затем хост запрашивает соответствующий дескриптор на основе информации в дескрипторе HID. Другими словами, хост знает, что устройство является HID-устройством, получая дескриптор.

Обычно в процессе запроса дескрипторов конфигурации и получения всех дескрипторов выбирают " Class 0x01 ”:



Значение Request - это содержание выше.

2.2.2 Дескриптор отчета

Дескриптор отчета является наиболее сложным из всех описаний USB, потому что он отличается от других и не имеет фиксированной длины и таблицы. Это переменный и разнообразный дескриптор.

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

Чтобы понять дескриптор отчета, необходимы два официальных USB HID-данных:

《Device Class Definition for human interface device (HID)》

《Universal Serial Bus HID Usage Tables》

Один - это протокол, описывающий дескриптор HID, а другой - определение содержания дескриптора отчета HID.

Дескриптор отчета состоит из одного элемента. Элемент делится на два типа. Имеются следующие форматы:

1. Short Item


2. Long Item


Из вышеперечисленного здесь в основном речь идет оShort Item, Наиболее используемым является Short Item 。

Короче говоря, первый 1 байт представляет цель этого элемента:


bSize: представляет следующие данные размером до 4 байтов.

bType: показывает, какой тип элемента является данным элементом. Существует три основных типа: основной (0x00), глобальный (0x01) и локальный (0x02).

bTag: представляет более подробную классификацию по соответствующему элементу.

| —— Основное разделено на: ввод, вывод, функция, коллекция, конечная коллекция.

| —— Глобальное разделение на: страницу использования, логический минимум, логический максимум и т. Д.

| —— Локальное разделение на: использование, минимум использования, максимум использования, строку и т. Д.

ДляbSize + bType + bTag Комбинация из 1 байта показана в таблице ниже:


В таблице перечислены все взаимодействия. Знак "?" Представляет значение младших 4 битов. Например, короткий элемент:

0x05(Prefix), 0x01(Data) , // USAGE_PAGE (Generic Desktop)

Среди них 0x05 представляет префикс, а 0x01 - часть данных.

0x05 =》 0000 01 01 Можно разбить:bSize = 1;bType = 1(Global);bTag = 0(Usage Page)

Приведенная выше интерпретация исходит из 《Device Class Definition for human interface device (HID)》 Файл, следующий за 0x01 Это 1 байт данных. Необходимо выяснить конкретное значение этих данных:《Universal Serial Bus HID Usage Tables》 Может быть найдено в, что означает: Generic Desktop 。


То есть согласно [bSize + bType + bTag】 Информация знает конкретное значение тега. Комбинируя таблицу таблицы и конкретный тег, найдите соответствующее значение следующих данных, чтобы достичь цели интерпретации дескриптора отчета.

Разберем функцию и значение нескольких важных тегов.

Main

| —— Ввод: указывает режим ввода данных о работе устройства на хост. Этот формат данных формирует входной отчет.Хотя входной отчет может быть передан конвейером управляющего типа с получением отчета (входом), он обычно передается входным конвейером типа прерывания, чтобы гарантировать, что обновленный входной отчет может быть передан в каждый фиксированный период. Передайте хосту.

| —— Вывод: представляет формат данных, выводимых хостом для операции устройства. Этот формат данных формирует выходной отчет. Выходной отчет обычно не отправляется на устройство путем опроса, но выходной отчет должен быть отправлен прикладным программным обеспечением в соответствии с фактическими потребностями, поэтому большинство конвейеров контрольного типа используются для отправки отчета на устройство с установить команду отчета (вывода). Конечно, вы также можете использовать прерванный выходной конвейер для передачи, но обычно это не рекомендуется.

| —— Функция: указывает формат данных конфигурации, требуемых хостом для отправки на устройство. Этот образец данных формирует характерный отчет. Отчет о функциях может использовать конвейер управления только для получения и установки значения функции устройства с помощью команд получить отчет (функция) и установить отчет (функция) соответственно.

No Null Position

Данные / константа: данные основного элемента представляют собой значение переменной (установленное на Data) или фиксированное неизменяемое значение (установлено на Constant). Константы используются в отчете Feature или используются для заполнения, поэтому длина отчета указывается в байтах.

Массив / переменная: каждое поле данных основного элемента может указывать, что запускается одна из нескольких различных операций (установлено значение «Массив»), или каждое поле указывает только одну операцию (установлено значение «Переменная»). Если это переменная, значение данных Report Count равно количеству полей данных отчета. Если это массив, значение данных Report Count указывает максимальное количество операций, которые могут быть запущены одновременно.

Абсолютный / Относительный: данные основного элемента предоставляют абсолютные значения относительно фиксированной контрольной точки (установленной на Абсолютный) или предоставляют относительные значения относительно предыдущего отчета (установленный на Относительный).

Без переноса / переноса: когда значение данных основного элемента достигает экстремального значения, оно переключается на чрезвычайно низкое значение, и наоборот, что называется намоткой (установлено значение Wrap). Например, поворотную ручку можно повернуть на 360 °, а выходное значение находится в диапазоне от 0 до 10. Если установлено значение Wrap, значение достигает 10, а значение становится 0, если она вращается в том же направлении. В противном случае, если он достигнет 0, поверните его снова, чтобы получить 10.

Линейный / Нелинейный: данные основного элемента и шкалы операций являются линейными (для параметра "Линейный") или нелинейными (для параметра "Нелинейный").

Предпочтительное состояние / Нет предпочтений: операция, соответствующая основному элементу, автоматически вернется в исходное состояние (установленное на Предпочтительное состояние), когда она не запущена, или она не вернется в исходное состояние (установлено на Нет предпочтительного). Например, клавиши клавиатуры и самоцентрирующиеся джойстики являются предпочтительными состояниями.

Нет нулевой позиции / нулевое состояние: операция, соответствующая основному элементу, имеет состояние, при котором значимые данные не будут отправляться, то есть данные не будут находиться между логическим минимумом и логическим максимумом. Этот вид манипуляции должен быть отмечен как нулевой. Состояние, иначе это не нулевая позиция. Например, если имеется несколько клавиш, но в столбце Использование не указано, что ни одна клавиша не нажата, вы можете установить нулевое состояние в данных основного элемента, чтобы исключить состояние отсутствия нажатой клавиши из логического элемента. Минимальный и логический максимальный диапазон, см.Universal Serial Bus HID Usage Tables Пример в Приложении A.3 документа.

Non Volatile / Volatile: Данные основного элемента Feature не могут быть изменены хостом (установлен на Non Volatile) или разрешены для изменения хостом (установлен на Volatile). Обратите внимание на основные пункты Input и Output, эта установка метки бессмысленна, поэтому код бита 7 должен быть 0.

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

Сбор и завершение сбора. В случае с мышью, это фактически указатель, который используется только как компьютерная мышь; и этот указатель содержит три кнопки и две оси перемещения X и Y. Следовательно, отчет указателя состоит из данных в разных форматах, поэтому необходимо использовать Сборник и Завершить сборник, чтобы сгруппировать несколько элементов ввода в группу, которая используется в качестве указателя, а затем использовать Сбор и Завершить сборку, чтобы заключить указатель. чтобы указать, что он используется как мышь. Метка тома End Collection не соответствует никаким данным. Но метка тома Collection следует за одним байтом данных.Например, имя данных указателя - Physical, а данные мыши - Application. Имена данных и коды всех Коллекций показаны в таблице:

Ряд микроконтроллеров 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.

Заходим в 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

Значение типа дескриптора
Дескриптор от 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.

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


На дворе 2014 год, а для связи микроконтроллеров с ПК самым популярным средством является обычный последовательный порт. С ним легко начать работать, он до примитивности прост в понимании — просто поток байт.
Однако все современные стандарты исключили COM порт из состава ПК и приходится использовать USB-UART переходники, чтобы получить доступ к своему проекту на МК. Не всегда он есть под рукой. Не всегда такой переходник работает стабильно из-за проблем с драйверами. Есть и другие недостатки.
Но каждый раз, когда заходит разговор о том, применять USB или последовательный порт, находится множество поклонников логической простоты UART. И у них есть на то основания. Однако, хорошо ведь иметь альтернативу?

Меня давно просили рассказать как организовать пакетный обмен данными между ПК и МК на примере STM32F103. Я дам готовый рабочий проект и расскажу как его адаптировать для своих нужд. А уж вы сами решите — нужно оно вам или нет.

Выбор профиля HID

USB-HID — довольно обширный класс устройств, поэтому прежде всего придется выбрать какое именно устройство мы будем создавать.
Мы можем создать эмуляцию клавиатуры, мыши, джойстика и других устройств ввода, а можем создать свое устройство, чтобы не зависеть от довольно жестких рамок стандарта и свободно обмениваться данными с ПК.
Я расскажу как cделать Custom HID device. Это дает максимальную свободу. Чтобы не затягивать статью, постараюсь рассказать максимально кратко — описаний стандарта в сети и без меня много, но лично мне они слабо помогли, когда понадобилось решить конкретную задачу.

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

  • Папка USB-FS с библиотекой «STM32F10x, STM32L1xx and STM32F3xx USB-FS-Device Driver» версии 4.0.0.
  • В папках Inc и Src файлы:
    platform_config.h — здесь собраны определения, касающиеся конкретной платы и семейства МК
    stm32_it.h, stm32_it.c — здесь определены обработчики прерываний
    usb_conf.h, usb_endp.c — здесь определяются конечные точки (Endpoint), размеры и адреса их буферов, функции-обработчики
    usb_desc.h, usb_desc.c — здесь собрана информаци о самом устройстве — как оно будет определяться при подключении к ПК и определены размеры и формат пакетов данных
    hw_config.c — здесь собрана вся работа с отправкой данных на ПК
    hw_config.h, usb_istr.h, usb_prop.h, usb_pwr.h
    usb_istr.c, usb_prop.c, usb_pwr.c — нужны для работы USB-FS библиотеки, но лезть в них необязательно

Инициализация USB


Для корректной работы USB модуля важна частота работы МК. Далеко не все частоты позволяют правильно задать тактирование USB. В нашем случае используется кварцевый генератор на 8МГц и МК работает на частоте 72МГц, а USB модуль на 48МГц.
В main.c достаточно включить всего несколько строк кода

В функции Set_System() производится настройка пина подтяжки линии D+ к питанию для программного подключения/отключения устройства от ПК (в нашей плате не используется), настраивается прерывание и инициализируются светодиоды и кнопки для демонстрационного проекта.
В USB_Interrupts_Config() настраиваются прерывания в зависимости от семейства МК (поддерживаются F10x, F37x, L1x).
Функция USB_Init() запускает работу USB модуля. Если временно нужно отключить для отладки работу с USB, просто закомментируйте эту строку.
Далее в бесконечном цикле проверяется, удалось ли сконфигурировать USB модуль при подключении к ПК. Если все сработало верно и устройство успешно подключилось, ПК включен и не находится в режиме энергосбережения, то состояние будет CONFIGURED.
Далее проверяется, была ли закончена предыдущая передача данных в ПК и если да, то готовится к отправке новый пакет в функции RHIDCheckState()

Размер пакета и частота передачи


USB-HID девайс не может сам инициировать передачу, т.к. координацией шины занимается host устройство — ПК. Поэтому при подготовке USB дескриптора нашего устройства, мы пишем, как часто нужно опрашивать наше устройство. По спецификации максимальная частота опроса — 1кГц и максимальный размер передаваемого за раз пакета — 64 байта. Если этого недостаточно — придется использовать другие режимы работы — вроде USB bulk, но там уже без драйверов не обойтись.
За настройку взаимодействия с ПК отвечают 3 дескриптора:

В комментариях все довольно прозрачно. Обратите внимание на DEVICE_VER_L, DEVICE_VER_H — это константы из usb_desc.h, которые вы можете изменить для идентификации версии своего устройства.

Дескриптор конфигурации (описывает возможности устройства)

Здесь стоит обратить внимание на константу wMaxPacketSize — она определяет максимальный размер пакета, которым мы будем обмениваться с ПК. Проект так настроен, чтобы при ее изменении менялись и размеры буферов. Но не забывайте, что больше 0x40 по стандарту указывать не стоит. С этой константой будьте осторожны — если передаваемый пакет будет отличаться по размеру — будут проблемы!
Следующая за ним константа с комментарием bInterval — это период опроса устройства в миллисекундах. Для нашего устройства задано 32мс.

Это самый важный дескриптор — он описывает протокол обмена и функционал устройства. Его формирование — не самая простая задача. Если допустить ошибку при формировании дескриптора — устройство перестанет работать. Формат дескриптора очень жесткий. Есть даже специальная утилита HID Descriptor tool. А в корне проекта лежит файл «RHID.hid» с описанным выше дескриптором для редактирования в этой утилите. Но если вы не понимаете, что делаете, лучше не лезть.
Для простоты я сделал две константы:
RPT3_COUNT — размер OUTPUT буфера в байтах для передачи пакета в МК (в примере — 1 байт)
RPT4_COUNT — размер INPUT буфера в байтах для передачи пакета в ПК (в примере — 4 байта)
Размер любого из этих буферов не должен превышать wMaxPacketSize. Меньше — можно.
Кстати, превратить Custom HID в другой HID девайс, например, клавиатуру или джойстик можно фактически только переписав ReportDescriptor и изменив класс и подкласс устройства в дескрипторе конфигурации.

Что такое Report

  • REPORT_ID = 1 и 2 — команда МК включить/выключить LED1/LED2. Содержит поле размером 1 бит с желаемым состоянием светодиода и поддерживает отправку как методом SET_REPORT так и методом SET_FEATURE (об этом чуть позже).
  • REPORT_ID = 3 — передает один байт в МК. Просто, чтобы показать, как передать данные МК. Мы будем передавать положение ползунка.
  • REPORT_ID = 4 — это репорт для передачи данных ПК. Возвращает информацию о текущем состоянии светодиодов, кнопок (если они есть) и возвращает переданный в репорте с байт, чтобы показать, что данные приняты.

Цикл обмена

Массив uint8_t Buffer[RPT4_COUNT+1] определен как размер полезных данных входящего (рассматривается всегда с точки зрения хоста) пакета + байт ID. Это важно — если размер буфера будет отличаться — будут проблемы. Поэтому для изменения размеров буфера редактируйте значение константы в usb_desc.h.
В функции мы собираем данные в пакет, устанавливаем флаг PrevXferComplete = 0, говорящий о том, что данные отправляются и вызываем функциии библиотеки USB_SIL_Write и SetEPTxValid для отправки данных хосту.
Все, на этом передача данных хосту закончена.

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

Обработка SET_FEATURE

Данные, отправленные методом SET_FEAUTRE обрабатываются в usb_prop.c

Здесь мы проверяем первый байт в репорте и в соответствии с ним обрабатываем остаток пакета — управляем светодиодами или просто берем байт, отправленный нам хостом и кладем в пакет для последующей отправки обратно в функции RHIDCheckState.
Под Report_Buf зарезервировано wMaxPacketSize байт, чтобы влез любой пакет, который нам отправит хост.

Данные, отправленные методом SET_REPORT обрабатываются в usb_endp.c

Здесь почти то же самое, только нужно самостоятельно забрать данные вызовом USB_SIL_Read(EP1_OUT, Receive_Buffer) и в конце сообщить, что мы закончили вызовом SetEPRxStatus(ENDP1, EP_RX_VALID);

Настраивать устройство, передавать и принимать данные в пакетах нужного размера с нужной нам периодичностью мы научились.
Собираем проект и прошиваем в устройство.
Работать, это будет примерно так:


Также я написал маленькую демо-софтинку, которая автоматически определяет подключение к компу и отключение нашего девайса по его VID и PID, отображает статус — подключено/отключено индикатором рядом с чекбоксом Auto Connect

Радиокнока Send using позволяет выбрать метод отправки данных девайсу.
Report: отображает полученный от девайса пакет побайтно, начиная с ReportID.
Щелкая по светодиодам ниже — управляем светодиодами девайса. Их состояние отображает текущее состояние девайса. Считывается из репорта от девайса.
Перемещая ползунок, мы отправляем Report с и значением, соответствующим позиции ползунка. Девайс вернет это значение в 4 байте репорта.
В выпадающем комбобоксе отображаются HID девайсы, найденные в системе и если найден наш девайс, то отображается его название.

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

Установите драйверы HID Keyboard

Если вы недавно обновились до Windows 10, переустановили или обновили ее, возможно, вы обнаружили, что вам необходимо установить (или обновить) драйверы HID Keyboard.

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

Вот несколько способов обновить и переустановить драйверы клавиатуры и решить проблемы, с которыми вы можете столкнуться.

Чтобы установить / обновить драйверы клавиатуры, откройте RUN, нажав Winkey + R, введите «devmgmt.msc» и нажмите Enter.


В диспетчере устройств разверните ветку Клавиатуры.


Затем щелкните правой кнопкой мыши на клавиатуре и выберите «Обновить драйвер».


Выберите Автоматический поиск драйверов.


Драйверы будут установлены / обновлены автоматически. Перезагрузите компьютер, чтобы изменения вступили в силу.

Вы также можете просмотреть (свой) компьютер в поисках драйверов, если вы уже загрузили драйверы с вашего компьютера или веб-сайта производителя клавиатуры.


Нажмите «Обзор», найдите папку с файлом драйвера и нажмите «Далее».


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


Затем выберите один из предложенных вариантов и нажмите «Далее».


Если вы не можете найти вариант, снимите флажок «Показать совместимое оборудование» и получите полный список драйверов от разных производителей на выбор. Затем нажмите Далее.


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


Откройте диспетчер устройств, как показано ранее (Win + X). Вы также можете использовать для этого только мышь: щелкните правой кнопкой мыши кнопку «Пуск» и выберите «Диспетчер устройств».


Затем щелкните правой кнопкой мыши на клавиатуре в ветви «Клавиатуры» и выберите «Удалить устройство».


Снова нажмите Удалить.


Выберите Да и перезагрузите компьютер, чтобы удалить.


После перезапуска Windows автоматически установит драйверы клавиатуры по умолчанию и снова заставит клавиатуру работать.

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

HID Проблемы с клавиатурой

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

Исправления: клавиатура не работает

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

Убедитесь, что порт USB работает

  • Неисправная клавиатура.
  • Неисправный кабель.
  • Неисправный порт USB.

Чтобы проверить, работает ли порт USB, отключите клавиатуру HID и подключите другое устройство USB, например флэш-накопитель или мышь. Если он тоже не распознается, у вас проблема с оборудованием. Помимо исправления базовой инфраструктуры, вам также следует обойти любые USB-концентраторы, которые вы можете использовать, и подключить клавиатуру напрямую к ПК.

Конфликт обновления драйвера Windows 10

Некоторые пользователи сообщают о любопытной проблеме при перезагрузке Windows 10. Функциональная клавиатура перестает работать, как только драйверы устройства обновляются автоматически с помощью обновления Windows.

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

Отключить ключи фильтра

Лучше оставить ключи фильтров выключенными. Чтобы в этом убедиться, нажмите Win + I и откройте «Настройки». Щелкните «Удобство доступа».


Прокрутите вниз и выберите «Клавиатура» на левой панели.


Теперь справа в разделе «Использовать ключи фильтра» выключите кнопку и снимите флажок «Разрешить ярлыку запускать ключи файлов».


Вы также можете один раз обновить / переустановить драйверы, на всякий случай. Затем перезагрузите компьютер. Этот метод должен обновить драйверы и предотвратить вмешательство других функций во ввод данных.

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

Читайте также: