Главная » Настройки » Hid протокол описание. Приобщение к миру USB-устройств на примере микроконтроллеров от Silicon Laboratories. Подробнее: откуда ноги растут и как оно, собственно, работает

Hid протокол описание. Приобщение к миру USB-устройств на примере микроконтроллеров от Silicon Laboratories. Подробнее: откуда ноги растут и как оно, собственно, работает

Устройства от Silicon Laboratories не пользуются широкой популярностью в любительских кругах, им далеко до таких флагманов, как Atmel. Однако у них есть и вполне доступные простому смертному микроконтроллеры основных линеек в корпусе TQFP, и стартовые комплекты USB ToolStick (о чем совсем недавно упоминалось на хабре). Я сам начал свое знакомство с микропроцессорной техникой, работая с «силабсами», и вполне успешно.
В данной статье я расскажу, каким образом можно организовать связь компьютера с МК, используя USB-интерфейс, и как Silabs попытались сделать это простым для разработчика.
В качестве испытуемого будем использовать плату С8051F320DK, с микроконтроллером соответственно F32x серии, поддерживающей USB аппаратно, и Keil"овскую среду разработки uVision4.

Перед тем, как начинать копать в сторону реализации связи по USB необходимо определиться с некоторыми базовыми аспектами протокола: какое место занимает устройство в топологии (хост или ведомое устройство) и какой характер будет иметь информация, передаваемая по интерфейсу.

Архитектура USB допускает четыре базовых типа передачи данных:

  • Управляющие посылки (control transfers ) – используются для конфигурирования устройств во время их подключения и для управления устройствами в процессе работы. Протокол обеспечивает гарантированную доставку данных.
  • Передачи массивов данных (bulk data transfers ) – это передачи без каких-либо обязательств по задержке доставки и скорости передачи. Передачи массивов могут занимать всю полосу пропускания шины, свободную от передач других типов. Приоритет этих передач самый низкий, они могут приостанавливаться при большой загрузке шины. Доставка гарантированная - при случайной ошибке выполняется повтор. Передачи массивов уместны для обмена данными с принтерами, сканерами, устройствами хранения и т. п.
  • Прерывания (interrupt transfers ) – короткие передачи, которые имеют спонтанный характер и должны обслуживаться не медленнее, чем того требует устройство.
    Предел времени обслуживания устанавливается в диапазоне 10-255 мс для
    низкой, 1-255 мс для полной скорости, на высокой скорости можно заказать и 125 мкс. При случайных ошибках обмена выполняется повтор. Прерывания используются, например, при вводе символов с клавиатуры или для передачи сообщения о перемещении мыши.
  • Изохронные передачи (isochronous transfers ) – непрерывные передачи в реальном времени, занимающие предварительно согласованную часть пропускной способности шины с гарантированным временем задержки доставки. Позволяют на полной скорости организовать канал с полосой 1,023 Мбайт/с (или два по 0,5 Мбайт/с), заняв 70 % доступной полосы (остаток можно заполнить и менее емкими каналами). На высокой скорости конечная точка может получить канал до 24 Мбайт/с (192 Мбит/с). В случае обнаружения ошибки изохронные данные не повторяются - недействительные пакеты игнорируются. Изохронные передачи нужны для потоковых устройств: видеокамер, цифровых аудиоустройств (колонки USB, микрофон), устройств воспроизведения и записи аудио- и видеоданных (CD и DVD).
В случае подключения МК к компьютеру контроллер, очевидно, будет ведомым устройством.

Создание USB совместимого HID-устройства типа джойстик

Наиболее распространенным и просто реализуемым типом USB-устройства является HID (Human Interface Devices). Используемый тип передачи, штатный для подобных устройств, - прерывания. Типичными представителями этого класса являются USB-клавиатуры, мыши, джойстики, панели настройки мониторов, считыватели штрих-кодов, карт-ридеры и т.п.
Преимуществами HID устройств является:
  • простота реализации;
  • компактный код;
  • поддержка Windows (не нужны дополнительные драйвера).
Итак, реализуем простейший джойстик-манипулятор . Например, понадобится нам ручка газа с двумя (или больше) кнопками для боевого меха(!), который мы собираем в гараже. На демонстрационной плате C8051F320DK имеется один переменный резистор и 2 кнопки - для минимума достаточно.

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

  1. конфигурацию дескриптора HID-устройства;
  2. процедуры передачи данных;
  3. дескриптор имени HID-устройства.
Начинаем с дескриптора устройства
Дескриптор нам необходим в нижеследующем виде:
code const hid_report_descriptor HIDREPORTDESC =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)

0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x02, // USAGE_PAGE (Simulation Controls)
0x09, 0xbb, // USAGE (Throttle)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)

0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x02, // USAGE_MAXIMUM (Button 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0 // END_COLLECTION
}
Теперь подробно разберем, что там к чему. Самая важная часть в описании будущего устройства - это типы данных. Необходимо описать раздел Simulation Controls (симуляция органа управления), в котором как раз есть Throttle (ручка газа), для этого указываем:
  • диапазон значений, в котором будет действовать Throttle – LOGICAL_MINIMUM(0) и LOGICAL_MAXIMUM(255),
  • задаем размер этого диапазона(один байт) – REPORT_SIZE (8) и
  • количество органов управления данного типа – REPORT_COUNT (1).
С кнопками аналогичная история(USAGE_PAGE (Button )):
  • диапазон значений - LOGICAL_MINIMUM(0) и LOGICAL_MAXIMUM(1);
  • размер диапазона(один бит) - REPORT_SIZE (1);
  • количество кнопок больше одной, поэтому тут уже необходимо использовать поле байтовой длины, значит REPORT_COUNT (8);
Все это нужно для операционной системы, теперь она будет знать, как обращаться с 2-мя байтами, которые она получит от контроллера, воспользовавшись дескриптором как ключом к расшифровке.
Да, и еще, в.h есть такие строки, сразу перед объявлением hid_report_descriptor:
#define HID_REPORT_DESCRIPTOR_SIZE 0x002C
#define HID_REPORT_DESCRIPTOR_SIZE_LE 0x2C00 //LITTLE ENDIAN

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

Для упрощения задачи составления дескриптора можно воспользоваться программой, лежащей на www.usb.org (HID Descriptor Tool). В комплекте с программой предоставляются примеры конфигураций некоторых HID-устройств, которые можно корректировать под свою задачу или создавать собственное HID-устройство.
На этом описание джойстика заканчивается и нужно подготовить данные для передачи в PC.

Процедуры передачи данных
Находим в примере следующий код:
void IN_Report(void){

IN_PACKET = VECTOR;
IN_PACKET = BUTTONS;

// point IN_BUFFER pointer to data packet and set
// IN_BUFFER length to transmit correct report size
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Length = 2;
}

В этой процедуре идет составление отправляемого пакета, который после через хитрый указатель (на самом деле это просто структура из указателя и его длины) и передается нашим устройством. Главное аккуратно составить пакет, о чем нам и намекает комментарий, а дальше уже с ним сделают все без нашего участия.
Теперь расскажу о том, как и откуда мы берем переменные VECTOR и BUTTONS (обе, к слову, имеют тип unsigned char размером в байт).
Глобальной переменной VECTOR присваиваются значения с АЦП во время возникновения прерывания от него:
void ADC_Conver_ISR(void) interrupt 10
{
AD0INT = 0;

// индикация работы АЦП
if(VECTOR != ADC0H)
LED = 1;
else
LED = 0;

VECTOR = ADC0H;
}

Глобальная переменная BUTTONS аналогично изменяет значение в зависимости от нажатия кнопок. Кнопки опрашиваются по прерыванию от таймера. Таймер настраивайте в соответствии с личными предпочтениями.
void Timer2_ISR (void) interrupt 5
{
P2 &= ~Led_2;

If ((P2 & Sw1)==0) // Проверка нажатия кнопки #1
{
// pressed
BUTTONS = BUTTONS | (1<<0);
LED2 = 1;
}
else
{
// not pressed
BUTTONS = BUTTONS & 0xFE;
}

If ((P2 & Sw2)==0) // Проверка нажатия кнопки #2
{
// pressed
BUTTONS = BUTTONS | (1<<1);
LED2 = 1;
}
else
{
// not pressed
BUTTONS = BUTTONS & 0xFD;
}
TF2H = 0; // Очистка флага прерываний Timer2
}

Дескриптор имени HID-устройства
Напоследок можем скорректировать строковые данные, чтобы устройство имело то название, какое мы хотим (в моем примере «JOYSTICK-HABR»).
Ищем строковый дескриптор String2Desc , переписываем
#define STR2LEN sizeof ("JOYSTICK-HABR") * 2

Code const unsigned char String2Desc =
{
STR2LEN, 0x03,
"J", 0,
"O", 0,
"Y", 0,
"S", 0,
"T", 0,
"I", 0,
"C", 0,
"K", 0,
"-", 0,
"H", 0,
"A", 0,
"B", 0,
"R", 0,
};

Идентификация HID-устройства
После компиляции проекта и программирования микроконтроллера можно подключить устройство к USB-порту. Хост определяет, что устройство принадлежит к HID классу и передает управление устройством соответствующему драйверу.

Теперь в Windows идем в Панель управления->Игровые устройства и видим там нашего пассажира. Смотрим свойства и проверяем функциональность.

Низкая скорость передачи является главным ограничением HID-варианта построения устройства. Максимально возможная скорость передачи данных при такой организации обмена составляет 64 Кбит/сек. Такой показатель в сравнении с 12 Мбит/сек полной скорости USB-шины выглядит минусом HID-технологии в вопросе выбора конкретной USB-реализации. Однако для многих задач коммуникации указанной скорости вполне хватает и HID-архитектура как специализированный инструмент занимает достойное место среди способов организации обмена данными.

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

Создание полноценного USB-устройства с использованием инструментария Silabs USBXpress

Но вот наступает момент, когда вам необходимо использовать свой протокол работы с устройством на МК. При этом хотелось бы передавать много данных на большой скорости, и делать все это с помощью своего ноутбука, в котором много USB и ни одного COM, да еще и ваше устройство должно быть не больше спичечного коробка, и лепить на плату USB-UART на микросхеме FT232RL нет никакой возможности.
Тут-то ребята из Silabs и решили облегчить всем жизнь и показать “дорогу в будущее”, без тяжелого ломанья зубов об написание собственных дров и прошивок.
USBXpress Development Kit – это законченное решение для МК и хоста (PC), обеспечивающее простую работу с протоколом USB с помощью высокоуровневого API для обоих сторон. Не требуется особых знаний ни самого протокола USB, ни написания драйверов. Так пишут силабовцы в своем гайде.


Кстати о Programmer"s Guid : занимая всего 30 страниц, он крайне прост и доходчив. Примеры же лично мне не нравятся, часто встречаются очень кривые места, программы же под PC вообще лучше не смотреть, крайне нечитабельны.
USBXpress DK предоставляется к микроконтроллерам линеек C8051F32x, C8051F34x и для CP210x (USB-to-UART Bridge Controller). Библиотека USBXpress включает в свой состав библиотеку нижнего уровня, драйверы USB для ПК и DLL-библиотеку для разработки приложений на верхнем уровне. Ну и, конечно же, набор документации и примеров.
В библиотеке реализована передача данных только в режиме BULK. При использовании всех функций библиотеки, их реализация займет всего 3 Кбайта Flash-памяти микроконтроллера.
Firmware
Разберем один более или менее простой и понятный пример, схожий по функционалу с предыдущим примером по HID. В приложение для PC лезть не будем, с ним будет все кристально ясно после того, как закончим прошивку для МК.
Итак, суть примера TestPanel: принимаем от микроконтроллера показания АЦП (Potentiometer) и встроенного термометра (Temperature ), а так же от нажатия кнопок (Switch1State и Switch2State ), а сами можем мигать светодиодами (Led1 и Led2 ).
Теперь обязательные для выполнения этапы и тонкие места, которые мы рассмотрим:
  1. Написание USB-дескриптора;
  2. Инициализация устройства и USB на борту;
  3. Обработка входящих данных и формирование исходящего пакета;
  4. Обработка прерываний.
Но для начала при создании проекта не забываем включить в него файл хидеров USB_API.h и саму библиотеку USBX_F320_1.lib .
Написание USB-дескриптора
В отличие от HID с его хитро формализованной структурой тут все просто
code const UINT USB_VID = 0x10C4;
code const UINT USB_PID = 0xEA61;
code const BYTE USB_MfrStr = {0x1A,0x03,"S",0,"i",0,"l",0,"a,0,"b,0,"s,0};
code const BYTE USB_ProductStr = {0x10,0x03,"U",0,"S",0,"B",0,"X",0,"_",0,"A",0,"P",0};
code const BYTE USB_SerialStr = {0x0A,0x03,"H",0,"A",0,"B",0,"R",0};
code const BYTE USB_MaxPower = 15;
code const BYTE USB_PwAttributes = 0x80;
code const UINT USB_bcdDevice = 0x0100;

С VID, PID и именами думаю все понятно, плюс еще можно задавать максимальный ток параметром MaxPower (макс.ток = _MaxPower*2), PwAttributes - параметр отвечающий за удаленный wake-up хоста, и bcdDevice - номер релиза устройства.

Нюанс инициализации устройства и USB на борту
Теперь начнем собственно функцию main, в которой МК будет без устали выполнять прием и передачу данных.
void main(void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
USB_Clock_Start(); // Init USB clock *before* calling USB_Init
USB_Init(USB_VID,USB_PID,USB_MfrStr,USB_ProductStr,USB_SerialStr,USB_MaxPower,USB_PwAttributes,USB_bcdDevice);

Initialize();
USB_Int_Enable();
...

Здесь, как требует комментарий, в первую очередь необходимо инициализировать тактовый генератор для USB перед самой его инициализацией, только потом провести остальные стартовые операции для МК - Initialize(); - который настраивает порты, таймер и АЦП; затем разрешаем прерывания от USB.

Обработка входящих данных и формирование исходящего пакета
Вот подобрались к самому главному
//... продолжение main
while (1)
{
if (Out_Packet == 1) Led1 = 1;
else Led1 = 0;
if (Out_Packet == 1) Led2 = 1;
else Led2 = 0;

In_Packet = Switch1State;
In_Packet = Switch2State;
In_Packet = Potentiometer;
In_Packet = Temperature;
}
// конец main
}

Out_Packet – пакет, принятый от хоста;
In_Packet - пакет, отправляемый хосту;
Суть ясна, МК постоянно обновляет отправляемый пакет и считывает статус полученного.

Обработка прерываний
Теперь в 2-х словах о том, откуда получаем значения в отправляемый пакет. Как и в примере с HID, состояния кнопок получаем по прерываниям от таймера, а значения АЦП и термометра - по прерываниям от АЦП.
Вот здесь один тонкий момент - при инициализации АЦП настраиваем его так, чтобы конвертирование значений происходило по переполнению таймера (того же, который мы используем для кнопок), а само же прерывание от АЦП возникает по завершению конвертирования. И тут кроме получения значений преобразователя в конце процедуры вызываем API функцию
Block_Write(In_Packet, 8)
которая и отправляет собранные данные на компьютер.
Получение команд от компьютера происходит в процедуре обработки прерываний от USB:
void USB_API_TEST_ISR(void) interrupt 16
{
BYTE INTVAL = Get_Interrupt_Source();

If (INTVAL & RX_COMPLETE)
{
Block_Read(Out_Packet, 8);
}

If (INTVAL & DEV_SUSPEND)
{
Suspend_Device();
}

If (INTVAL & DEV_CONFIGURED)
{
Initialize();
}
}

Этот момент подробно расписан в Programmer"s Guid. Суть в том, что вызывается API-функция Get_Interrupt_Source(), возвращающая код причины возникновения API прерывания. Далее код анализируется и выполняется необходимое действие.

Программ на PC
Разбирать программу для компьютера я не буду. Силабовцы предоставили примеры на Visual Basic и на C, но, даже не заглядывая в исходники, подключить библиотеку в используемой вами среде разработки и прочитать пару страниц о функциях сложности вызвать не должно.
Поэтому я воспользуюсь уже скомпилированной программой из примера.

Итак, компилируем проект для МК, зашиваем, устанавливаем универсальные драйвера для USBXpress и подключаем отладочную плату. Система определит новое устройство и установит для него драйвера.
Посмотрим после установки, что творится в диспетчере устройств Винды:


Теперь запускаем программу:


Видим, что она правильно нашла устройство.


Все, теперь можно тут потыкать кнопки, поморгать диодами, погреть МК руками, увидеть как растет температура.

Заключение

В целом создание USB устройства с помощью библиотек USBXpress оказалось более быстрым и прозрачным процессом, нежели используя HID-архитектуру. Да и скорость будет однозначно выше. Наиболее тонким местом является то, что библиотека закрыта, и узнать насколько надежным является это решение невозможно, к тому же доступен только BULK режим передачи данных.
Использованные и полезные источники:
  1. Гук М., Аппаратные интерфейсы ПК. Энциклопедия. - СПб.: Питер, 2002. - 528 с.
  2. Курилин А.И. Микроконтроллеры компании Silicon Labs c интерфейсом USB. Журнал «Электронные компоненты» №5, 2007г

Mbed OS is an open-source operating system for platforms using Arm microcontrollers designed specifically for Internet of Things (IoT) devices: low-powered, constrained devices that need to connect to the internet. Mbed OS provides an abstraction layer for the microcontrollers it runs on, so that developers can focus on writing C/C++ applications that call functionality available on a range of hardware. Mbed OS applications can be reused on any Mbed-compatible platform.

On this page

Getting started

If you want to dive straight in:

  • The source code is available on GitHub and on our release page .

Source code and licensing

We release Mbed OS under an Apache 2.0 license, so you can confidently use it in commercial and personal projects. For more information about licensing, please see our licensing documentation .

Architecture diagram

This is the basic architecture of an Mbed board:

Mbed OS foundations

Mbed OS uses a hardware abstraction layer (HAL) to support the most common parts of a microcontroller, such as timers. This foundation facilitates writing applications against a common set of application programming interfaces (APIs); your device automatically includes necessary libraries and driver support for standard MCU peripherals, such as I2C, serial and SPI.

The HAL also serves as the starting point when adding support for new targets or features to existing targets. We work closely with our silicon Partners to port these new features to Mbed Enabled development boards.

Mbed OS has an RTOS core, so it supports deterministic, multithreaded, real-time software execution. The RTOS primitives are always available, allowing drivers and applications to rely on threads, semaphores, mutexes and other RTOS features.

The structure of Mbed OS enables matching applications and storage systems. In other words, where the block level storage options vary and are application dependent, you can choose the file system that best fits your IoT device. The FAT file system - backed by an SD card - provides compatibility with other operating systems, such as Windows, Mac OS or Linux. When high reliability and recovery from power failure are important, it makes sense to use our embedded file system, backed with a (Q)SPI NOR flash chip.

Finally, Mbed OS implements the retargeting layer and boot process integration of each supported toolchain for you, so application development feels similar to C or C++ development for any other operating system.

Connectivity

Arm works with its Partners to enable Bluetooth Low Energy, NFC, RFID, LoRa, 6LoWPAN-ND, Thread, Wi-SUN, Ethernet, Wi-Fi, cellular and mobile IoT (LPWA) across devices and system architectures running Mbed OS. Mbed OS offers a stable core of existing connectivity technologies. At the same time, it adds modern ones in quarterly feature releases, keeping you informed of industry trends so you can transition to new, innovative solutions that generate business value.

The networking and connectivity stacks are flexible enough to meet the needs of the most demanding IoT device designs, whether a combination of a single chip microcontroller and radio, or multiple chips connected across serial buses. System designers can have confidence in our certified connectivity stacks, such as our certified Thread stack, because of their maturity, interoperability and validated components.

Mbed OS fully supports our Pelion IoT Platform, so you can manage your deployed devices and their data. Together, Mbed OS and Pelion comprise a coherent ecosystem that meets the needs of most production-ready projects.

Security

The Pelion IoT Platform has built-in security at all levels, stressing both protection against violations and mitigation of their consequences. Alongside hardened cloud services, robust communication stacks and safe firmware updates, Mbed offers two security-specific embedded building blocks: Arm Mbed TLS  and a Secure Partition Manager (SPM) that meets industry best practices as part of Arm’s Platform Security Architecture. Mbed TLS secures communication channels between a device and gateway or server, and the use of a secure partition manager and isolated security domains for trusted system services reduces the attack surface. All together, this provides a unique chip-to-cloud security model, relying on the low-level capabilities the Arm ecosystem silicon Partners provide to secure the data and identity of cloud-connected devices.

Our approach to security is to leverage state-of-the-art industry standard protocols, ciphers and encryption suites following the recommendations from NIST and other related organizations. This gives us access to the latest work by the global security research community, rather than a limited in-house resource. We regularly verify the results of these efforts with code reviews, penetration exercises and other methods.

Remote firmware update

Mbed OS offers a seamless integration with the Pelion Device Management Update Services, so you can update your device"s application or version of Mbed OS. The Arm Mbed CLI development tool can build update payloads, generate their manifests and send them to your development device with two commands. If you want to update device groups, you can generate and upload your manifest and payload to the Device Management Portal and run a standard campaign from there. The Online Compiler also offers a quick integration for development flows.

Hardware

Arm, its Partners and the Arm Mbed developer community work together to develop the Mbed OS project. This thriving ecosystem means that Mbed OS includes drivers for a lot of different hardware, so you can concentrate on clean and portable application code.

Broadly speaking, the hardware you can see on our site is of three types:

  • Modules : include a microcontroller, IoT centric connectivity and required onboard memory. They are ideal for designing IoT products, from prototyping to mass production. Mbed Enabled Modules have full support for Mbed OS with all available connectivity drivers.
  • Boards : development boards are an inexpensive way to start developing with Mbed OS and other components.
  • Components : the Component Database hosts reusable libraries for different hardware, middleware and IoT services that you can use with Arm Microcontrollers. These components can be used as building blocks for quickly developing prototypes and products.

Tools

The Mbed product suite includes the tools you need to work with Mbed OS, whatever your skill level. If you are an experienced developer with a desktop setup, you may prefer working offline with Arm Mbed CLI, our Python-based command-line tool. You can use Mbed CLI with one of three supported toolchains: Arm Compiler 6, GCC and IAR. You can also export projects for other IDEs, such as Keil MDK. Mbed OS includes integration code for each supported toolchain to make it thread safe.

Управление

Информация о USB устройстве называется Descriptors и хранится в его ROM. Интерфейс дескриптора может определять устройство как принадлежащее к одному из конечного числа классов. Устройство HID-класса использует соответствующий драйвер для извлечения и маршрутизации всех данных.

Дескриптор устройства определяет, какие другие дескрипторы класса присутствуют. Например, Report Descriptor и Physical Descriptor.

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

Дескриптор устройства и конфигурации один для устройства. Дескрипторов интерфейса может быть несколько (например, в МФУ может быть интерфейс принтера, интерфейс сканера и т.д.).

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

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

Устройства HID делятся на классы (клавиатура, мышь, джойстик и т.д.). Это позволяет унифицировать репорт-дескрипторы. При этом некоторые классы могу содержать подклассы, например, подкласс загрузочного устройства.

Если устройство может использоваться в BIOS, то оно обозначается как загрузочное с помощью параметра

bInterfaceSubClass

0 - нет подкласса (обычное устройство);

1 - загрузочное устройство;

2-255 - зарезервировано.

Параметр bInterfaceProtocol имеет смысл в том случае, если устройство объявлено загрузочным.

1 - клавиатура;

3-255 - зарезервировано.

Определять принаждежность устройства к HID классу необходимо не по параметрам bDeviceClass и bDeviceSubClass, а по параметрам bInterfaceClass и bInterfaceSubClass.

Отчетные дескрипторы состоят из элементов информации (Item).

Элемент может включать в себя дополнительный элемент данных. Размер блока данных определяется его основным типом (короткий и длинный). Для короткого объем может быть 0, 1, 2 или 4 байта. Длинный элемент имеет значение bSize = 2.

Драйвер HID парсит в линейном порядке весь репорт от хоста в поисках элементов согласно репорт-дескриптору и сохраняет их таблице элементов.

Usages

Это части отчетного дескриптора, определяют что должно быть сделано с данными (например, ввод x, y, z).

Ориентация HID

По дефолту принято направление перемещения слева направо (X), сверху вниз (Y) и издалеко ближе (Z).

Возможна передача значений за пределами допустимого, в этом случае хост проигнорирует их и текущие значения не будут изменены. Это так называемы NULL значения. Рекомендуется 0 использовать тоже как NULL, т.е. диапозон допустимых координат начинать с 1.

HID Descriptor

Определяет длину и тип подчиненных дескрипторов для устройства. Состоит из следующих частей:

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

Вот три типа элементов: Maain, Global, Local. Например, типу Main соответствуют следующие тэги:

Input - относится к данным устройства, например, положение курсора, состояние кнопок и т.д.

Output - определяет данные для посылки на устройство от хоста.

Feature - описывает конфигурацию устройства, которая может быть послана на устройство.

Collection - смыслова группировка элементов Input, Output и Feature.

End Collection - указывает на конец набора элементов.

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

Local и Global типы определяют минимальные и максимальные значения данных и так далее.

Локальные элементы описывают только поля данных, определенные следующим Main элементом. Глобальные элементы сттановятся атрибутами по умолчанию для всех последующих полей данных в этом дескрипторе.

Пример:

Report Count (2)

Report Count (2)

Report Size (3) определяет размер элемента в битах (в данном случае 3);

Report Count (2) определяет количество таких элементов.

Если мы можем содержать и передавать данные о трех одновременно нажатых клавишах, то репорт будет следующий:

Report Size (8),

Report Coint (3),

Если репорт указывает состояние 8 кнопок, то он выглядит следующим образом:

Report Size (1),

Report Coint (8),

Отчетный дескриптор может содержать несколько Main элментов. Каждый из следующих элементов описания данных управления обязателен (остальные являются дополнительными):

  • Input (Output or Feature)
  • Usage
  • Usage Page
  • Logical Minimum
  • Logical Maximum
  • Report Size
  • Report Count

Пример на основе 3-кнопочной мыши:

Usage Page (Generic Desktop), ;общая usage page

Usage (Mouse),

Collection (Application), ;открыть наборь мышь

Usage (Pointer),

Collection (Physical), ;открыть указательный набор

Usage Page (Buttons),

Usage Minimum (1),

Usage Maximum (3),

Logical Minimum (0),

Logical Maximum (1), ;поля возвращают данные от 0 до 1

Report Count (3),

Report Size (1), ;создать 3 однобитных поля (кнопки 1, 2 и 3)

Input (Data, Variable, Absolute), ;создать поле для входных отчетов

Report Count (1),

Report Size (5), ;создать 5-битное поле констант

Input (Constant), ;добавить поле во входящий отчет

Usage Page (Generic Desktop),

Usage (X),

Usage (Y),

Logical Minimum (-127),

Logical Maximum (127), ;поле возвращает значения от -127 до 127

Report Size (8),

Report Count (2), ;создать 2 поля по 8 бит (X и Y)

Input (Data, Variable, Relative), ;добавить поля во входящий отчет

End Collection, ;закрыть набор указателя

End Collection ;закрыть набор мыши

Collection открывает набор данных:

  • Physical 0x00
  • Application 0x01
  • Logical 0x02
  • Report 0x03
  • Named Array 0x04
  • Usage Switch 0x05
  • Usage 0x06
  • Reserved 0x07 - 0x7F - для будущего использования
  • Reserved 0x80 - 0xFF - для вендора

Все элементы содержат 1-байтный префикс, который определяет основной тип элемента. HID определяет 2 основных формата элементов:

Короткие 1-5 байт общей длины, используются для наиболее часто встречающихся элементов.

Длинные 3-258 байт, используются для элементов, требующих больших объемов данных.

Короткий и длинный форматы содержат размер элемента, тип и тег в первом байте.

Короткий формат

Длинный формат

Logical Minimum & Logical Maximum

LMin & LMax связывают значения, возвращаемые устройство, а Physical Minimum и Physical Maximum дают смысл этим границам, позволяя значениям масштабироваться. Например, термометр имеет логические степени от 0 до 999, но физические степени от 32 до 212 градусов.

Рассмотрим на примере мыши с чувствительностью 400dpi:

Таким образом, формула для вычисления отношения должна быть

(127-(-127)) / ((3175-(-3175)) * 10^-4) = 400 dpi

Стандартные запросы

HID класс использует стандартные запросы Get_Descriptor. На запрос Get_Descriptor(Configuration) возвращается конфигурационный дескриптор, все дескрипторы интерфейса, дескрипторы Endpoint и все HID дескрипторы для каждого интерфейса. Не должен возвращаться String дескриптор, репорт дескриптор.

Таким образом, порядок должен быть следуюим:

1. Get_Descriptor

Структур:

Таблица, определяющая Descriptor Type (старший байт wValue в запросе Get_Descriptor):

2. Set_Descriptor

Структура:

3. Class-Specific запросы

Структура:

Доступные значения bRequest:

4. Get_Report

mValue определяет тип репорта в старшем байте и Report ID в младшем. Report ID устанавливается в 0, если не используется. Тип репорта определяется следующим образом:

5. Set_Report

Смысл полей аналогичен запросу Get_Report.

6. Get_Idle

Читает текущий процент бездействия системы для каждого Input Отчета.

7. Set_Idle

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

8. Get_Protocol

Запрос читает, какой протокол сейчас активен.

Поддерживается устройствами загрузочного класса.

9. Set_Protocol

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

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

В данном проекте рассматривается использование микроконтроллера PIC с поддержкой USB и компилятора для преобразования старого джойстика игрового порта так, чтобы его можно было использовать по USB. Одним из преимуществ компилятора mikroC является создание библиотек USB HID, которые позволяют легко создать USB HID устройство. При записи USB кода с использованием компилятора mikroC, изготовленное USB устройство является настоящим HID устройством, которое может использоваться для передачи и получения данных из микроконтроллера PIC. Однако существует возможность модифицировать USB дескриптор, генерируемый mikroC так, что можно получить специфическое USB HID устройство, наподобие клавиатуры, мыши, джойстика или планшета графического ввода.

Компилятор

В данном проекте используется компилятор mikroC v8 compiler. Используемые методы сходятся с большинством других компиляторов, которые генерируют HID код.

Джойстик

В данном проекте используется старый джойстик IBM 76H1571, фото показано ниже:

76H1571 – это 4-кнопочный джойстик с колесиком throttle и переключателем видов POV hat. Наиболее интересно, что вы не можете использовать колесико и переключатель одновременно – можно использовать либо один элемент, либо другой. Два ползунковых переключателя на передней стороне джойстика используются для включения и выключения колесика и переключателя POV, поэтому вы можете выбрать тот элемент, который хотите использовать в конкретный момент.

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

В заключении хочу указать, что переделанный джойстик имеет следующие характеристики:

  • 2-осевой джойстик
  • Колесико Throttle
  • 4 направленный переключатель видов POV
  • 6 кнопок

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

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

Создание дескриптора

На веб-сайте USB IF размещается полезная программа, которая значительно облегчает создание дескрипторов отчета HID. Она называется HID Descriptor Tool и может быть загружена бесплатно со страницы HID Tools page . После загрузки, распакуйте архив и запустите файл Dt.exe .

Используя данную программу можно создать ваш собственный дескриптор отчета для джойстика (или любого другого HID устройства), определить количество осей и кнопок, и указать другие характеристики (педали управления рулем направления, колеса и т.д.). Также данная программа поставляется с шаблонами дескрипторов, которые можно незамедлительно использовать или модифицировать для своих потребностей. Они находятся в той же папке, что и исполняемый файл, и имеют расширение .hid . Шаблон дескриптора джойстика называется joystk.hid , именно его я и использовал. После загрузки появится следующий экран:

На нем красной рамкой выделены важные секции. В них указываются следующие параметры:

  • 1 колесико, описываемое 8 битным значением от -127 до 127
  • Оси X и Y
  • 1 переключатель видов POV, имеющий 4 положения (0-3) и представляющий угол 0-270, описывается 4-битным значением
  • 4 кнопки, каждая из которых описывается 1 битом

Как можно заметить, показатель REPORT_SIZE определяет разрядность данных, которые используются для представления параметра и показатель REPORT_COUNT , который определяет количество отчетов, которые пересылаются для представления параметра.

Модификация дескриптора

Добавление дескриптора в ваш код

После создания дескриптора отчета вы должны экспортировать его в код на языке Си. Для этого, нажмите File->Save As (Файл-сохранить как) в меню программы HID Descriptor Tool. В появившемся диалоговом окне измените тип файла на Header File (*.h) .

При этом будет создан заголовочный файл C, который можно впоследствии добавить в ваш проект.

Интеграция mikroC

Для добавления заголовочного файла в дескриптор, генерируемый mikroC, потребуется выполнить некоторые действия. Если вы посмотрите на дескриптор mikroC, вы заметите, что каждый байт сопровождается символом ‘,0’ (игнорируя кавычки, это запятая-ноль ). Вам потребуется модифицировать дескриптор отчета, который был сгенерирован, включая данные дополнения. В конечном итоге вы должны получить дескриптор следующего вида:

0x05, 0, 0x01, 0, // USAGE_PAGE (Generic Desktop) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x09, 0, 0x04, 0, // USAGE (Joystick) 0xa1, 0, 0x01, 0, // COLLECTION (Application) 0x05, 0, 0x02, 0, // USAGE_PAGE (Simulation Controls) 0x09, 0, 0xbb, 0, // USAGE (Throttle) 0x15, 0, 0x81, 0, // LOGICAL_MINIMUM (-127) 0x25, 0, 0x7f, 0, // LOGICAL_MAXIMUM (127) 0x75, 0, 0x08, 0, // REPORT_SIZE (8) 0x95, 0, 0x01, 0, // REPORT_COUNT (1) 0x81, 0, 0x02, 0, // INPUT (Data,Var,Abs) 0x05, 0, 0x01, 0, // USAGE_PAGE (Generic Desktop) 0x09, 0, 0x01, 0, // USAGE (Pointer) 0xa1, 0, 0x00, 0, // COLLECTION (Physical) 0x09, 0, 0x30, 0, ///////////////////////////////////////// // USAGE (X) 0x09, 0, 0x31, 0, // USAGE (Y) 0x95, 0, 0x02, 0, // REPORT_COUNT (2) 0x81, 0, 0x02, 0, // INPUT (Data,Var,Abs) 0xc0, 0, // END_COLLECTION 0x09, 0, ///////////////////////////////////////////// 0x39, 0, // USAGE (Hat switch) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x25, 0, 0x03, 0, // LOGICAL_MAXIMUM (3) 0x35, 0, 0x00, 0, // PHYSICAL_MINIMUM (0) 0x46, 0, 0x0e, 0, 0x01, 0, // PHYSICAL_MAXIMUM (270) 0x65, 0, 0x14, 0, // UNIT (Eng Rot:Angular Pos) 0x75, 0, 0x04, 0, // REPORT_SIZE (4) 0x95, 0, 0x01, 0, // REPORT_COUNT (1) 0x81, 0, 0x02, 0, // INPUT (Data,Var,Abs) 0x05, 0, ///////////////////////////////////// 0x09, 0, // USAGE_PAGE (Button) 0x19, 0, 0x01, 0, // USAGE_MINIMUM (Button 1) 0x29, 0, 0x04, 0, // USAGE_MAXIMUM (Button 4) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x25, 0, 0x01, 0, // LOGICAL_MAXIMUM (1) 0x75, 0, 0x01, 0, // REPORT_SIZE (1) 0x95, 0, 0x04, 0, // REPORT_COUNT (4) 0x55, 0, 0x00, 0, // UNIT_EXPONENT (0) 0x65, 0, 0x00, 0, // UNIT (None) 0x81, 0, 0x02, 0, // INPUT (Data,Var,Abs) 0xc0, 0 // END_COLLECTION

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

Фактические данные дескриптора полностью хранятся в массиве DescTables . Нижние 50 или около того записей в массиве являются дескриптором отчета (строки 109-160). Удалите эти строки, и далее вставьте в новый дескриптор в этом месте. Теперь необходимо выполнить следующие модификации с файлом USBdsc.c:

  • Измените строку 23, чтобы она соответствовала незаполненному размеру дескриптора отчета (т.е. размеру дескриптора, сгенерированного программой HID tool, без дополнительных нулевых записей 0′, которые необходимо добавить для mikroC – 77 байт в случае дескриптора по умолчанию для джойстика):
    • unsigned char const HID_ReportDesc_len = 77;
  • Удалите границы массива для DescTables в строке 36:
  • unsigned char const DescTables = {

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

Пересылка данных в ПК

Если микроконтроллер PIC распознается как USB джойстик, тогда наиболее сложная часть проекта завершена. Пересылка данных джойстика в ПК выполняется простым способом. Когда мы ранее создали дескриптор, мы разработали удобный формат данных с точки зрения дескриптора. В результате экспериментов мы получили следующие параметры:

Колесико, значения X и Y представляют собой 8-битные значения. Однако значения переключателя видов POV и кнопок являются 4-битными, поэтому упаковываются в один байт. Формат данных указан ниже:

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

Аппаратное обеспечение

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

Настройка переключателей

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

Наиболее интересный элемент – это переключатель видов POV. Вместо 4 отдельных переключателей, переключатель видов POV подключен как аналоговая система, в которой сопротивление через зеленый и оранжевый провод определяет, какая из кнопок нажимается. В таблице ниже указано сопротивление каждого переключателя:

Поэтому для определения направления переключателя видов POV, его провода будут подключаться к схеме делителя напряжения, как показано ниже:

При подключении проводов к источнику питания напряжением 5 В, мы получим следующие напряжения:

Электрическая схема

После определения подключения проводов джойстика, мы спроектировали схему, которая будет соединяться со всеми кнопками и потенциометрами, и отправлять данные в ПК. Для этого был использован микроконтроллер PIC18F2550, работающий на частоте 20 МГц. Переключатели подключены к портам PORTB, потенциометры и переключатель видов POV к АЦП порта PORTA. Схема показана ниже:

Макетная плата показана ниже (имеется три проволочные перемычки, нажмите для увеличения изображения):

После создания платы и проведения тестирования, поместите плату в основание джойстика и замените кабель игрового порта на USB кабель. Фото переделанного джойстика показано ниже:

Устройство готово к работе! Соберите джойстик и подключите к свободному USB порту. При этом он должен правильно определиться ОС Windows. Вы можете откалибровать его и начать использовать для игр, которые поддерживают джойстик.

Заключение

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

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК PIC 8-бит

PIC18F2550

1

Разрабатывая USB или Bluetooth периферию, довольно часто требуется реализовать интерфейс HID . В этом посте мы будем делать это по-человечески, а не так, как все…

Вкратце: что такое HID и с чем его едят

Как следует из названия, рассматриваемый класс интерфейсов предназначен для реализации устройств взаимодействия с пользователем. Но в силу простоты и удобства зачастую через этот интерфейс работают и с другими типами периферии. Со стороны устройства реализация HID очень проста: не нужно ломать голову над низкоуровневыми протоколами обмена данными, описал отчёты и знай себе обрабатывай запросы. А со стороны хоста с HID устройствами можно работать в пространстве пользователя через стандартный драйвер операционной системы. Конечно, CDC ACM тоже позволяет обмениваться данными с устройством аналогичным образом, но этот класс сложнее в реализации на устройстве, он удобен для организации непрерывного поточного взаимодействия, и менее подходит для работы в режиме запрос-ответ.

Подробнее: откуда ноги растут и как оно, собственно, работает

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

Дескрипторы отчётов

Так вот, формирование этих самых дескрипторов отчётов и является самой сложной частью в реализации HID периферии. Чего только ни делают разработчики и всё получается как-то мимо кассы. Одни используют специальные программы (вроде официального HID Descriptor Tool ), которые позволяют формировать дескрипторы в графическом интерфейсе, и на выходе выдают готовый набор байтов. Другие использует конвертеры, преобразующие текстовое описание в код и обратно (например, hidrd). Всё бы хорошо, но дальнейшая поддержка такого сформированного сторонними инструментами кода будет значительно осложнена: при каждом исправлении необходимо будет снова воспользоваться соответствующей программой. Третьи тупо берут готовый дескриптор из примеров, правят размер и количество данных под свою задачу и обменивается пакетами в своём формате, не соответствующем фактическому содержимому дескриптора. Но этот подход тоже чреват многими нехорошими моментами, не всегда очевидными, как в общем-то и любое другое отклонение в сторону от буквы спецификации.

Мы же пойдём прямым путём: будем формировать дескриптор понятным читаемым образом, с использованием макросов препроцессора Си, так, как нам, сишникам, привычно и удобно. Этот подход не отменяет необходимость ознакомления со спецификацией HID 1.11 в целях понимания базовых принципов формирования отчётных дескрипторов, однако освобождает от необходимости знать подробности бинарного формата. Другими словами, мы будем описывать дескрипторы примерно так, как они описаны в примерах означенной выше спецификации.

Базовые принципы

Кому лень прямо сейчас лезть в спеку и вникать в суть, тот читает мой вольный текст ниже.

Описание отчёта состоит из так называемых элементов или пунктов, каждый из которых состоит из заголовка и, опционально, блока данных. Вообще, элементы бывают короткие и длинные, но здесь мы опишем только короткие, потому как работать будем только с ними. Заголовок элемента включает поле размера данных (2 бита), поле типа (2 бита) и тег (4 бита) - итого 8 бит. Поле размера задаёт длину данных в байтах: 0, 1, 2 или 4 байта. Поле типа задаёт тип: главный (main), глобальный (global), локальный (local). Поле тега задаёт собственно элемент. Элементов много разных, и описывать здесь их все мы не будем, остановимся лишь на некоторых основных по ходу описания.

Пример дескриптора

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

Usage Page (Generic Desktop), Usage (Mouse), Collection (Application), Usage (Pointer), Collection (Physical), Usage Page (Buttons), Usage Minimum (01), Usage Maximun (03), Logical Minimum (0), Logical Maximum (1), Report Count (3), Report Size (1), Input (Data, Variable, Absolute), Report Count (1), Report Size (5), Input (Constant), Usage Page (Generic Desktop), Usage (X), Usage (Y), Logical Minimum (-127), Logical Maximum (127), Report Size (8), Report Count (2), Input (Data, Variable, Relative), End Collection, End Collection

Что мы тут видим? А видим мы как раз таки наши элементы по одному элементу на строку. Каждый элемент в этом примере занимает 2 либо 1 байт. Первым идёт глобальный элемент Usage Page, который описывает предназначение нашего устройства (Generic Desktop). Далее идёт локальный элемент Usage, который определяет собственно тип устройства (Mouse), давая тем самым подсказку драйверу ОС. Затем при помощи главного элемента Collection начинается описание коллекции типа приложение (Application), и снова идёт элемент Usage Page, но на этот раз он определяет назначение коллекции как указатель (Pointer) тем самым все последующие элементы до элемента конца коллекции будут относиться к указателю.

Далее начинается коллекция типа Physical и уже внутри этой коллекции описывается элемент отчёта Buttons. Локальные элементы Usage Minimum и Usage Maximum связаны с конкретным вариантом использования, в данном случае они идентифицируют первую и последнюю кнопки мыши. Далее глобальные элементы Logical Minimum и Logical Maximum задают минимальное и максимальное значения состояния этих кнопок. Следующие глобальные элементы Report Count и Report Size задают количество значений в отчёте и размер каждого значения в битах. Главный элемент Input заканчивает описание кнопок и определяет описанные поля как часть дескриптора входного отчёта. В скобках приводятся флаги: Data - означает, что описанные поля следует трактовать как данные, а не как константы, Variable - описана переменная а не массив, Absolute - значения следует трактовать как абсолютные.

Часто бывает удобно выравнивать данные по байта или словам, однако в описании отчетов размеры задаются в битах, поэтому для выравнивания добавляют так называемые отступы. Следующие три строки как раз декларируют такой отступ после битов состояний трёх кнопок в отчёте количеством 1 и размером 5 бит, а чтобы драйвер проигнорировал эти биты, в элементе Input вместо Data используется флаг Constant.

Далее похожим образом описываются поля координат курсора. Поскольку мышь в отличии от дигитайзера обычно генерирует относительные координаты, то вместо Absolute в элементе Input указано Relative. А поскольку эти относительные координаты могут быть как положительными так и отрицательными, то указываются соответствующие предельные значения в Logical Minimum и Logical Maximum от -127 до 127. На каждое значение отводится до 8 бит (Report Size) и всего значений 2 (Report Count). Подсказка драйверу о порядке полей со значениями координат здесь даётся с использованием элементов Usage.

Описание дескриптора на Си

Итак, наша задача представить то же самое описание средствами языка Си, в чём нам поможет Си препроцессор. Вот как будет выглядеть теперь тот же самый пример:

#include /* Определения макросов нашего HID Report Descriptor DSL */ #include "hid_def.h" static const uint8_t hid_report_descriptor = { HID_USAGE_PAGE (GENERIC_DESKTOP), HID_USAGE (MOUSE), HID_COLLECTION (APPLICATION), HID_USAGE (POINTER), HID_COLLECTION (PHYSICAL), HID_USAGE_PAGE (BUTTONS), HID_USAGE_MINIMUM (1, 1), HID_USAGE_MAXIMUM (1, 3), HID_LOGICAL_MINIMUM (1, 0), HID_LOGICAL_MAXIMUM (1, 1), HID_REPORT_COUNT (3), HID_REPORT_SIZE (1), HID_INPUT (DATA, VARIABLE, ABSOLUTE), HID_REPORT_COUNT (1), HID_REPORT_SIZE (5), HID_INPUT (CONSTANT), HID_USAGE_PAGE (GENERIC_DESKTOP), HID_USAGE (X), HID_USAGE (Y), HID_LOGICAL_MINIMUM (1, -127), HID_LOGICAL_MAXIMUM (1, 127), HID_REPORT_SIZE (8), HID_REPORT_COUNT (2), HID_INPUT (DATA, VARIABLE, RELATIVE), HID_END_COLLECTION (PHYSICAL), HID_END_COLLECTION (APPLICATION), };

Как видим, описание максимально приближено к примеру из спецификации, но есть несколько отличий, связанных с реализацией макросов препроцессора, описывающих элементы. Поскольку размер данных некоторых элементов не фиксированный, то нам понадобилось как-то указать этот размер. Поэтому первым параметром у элементов, задающие границы minimum/maximum, идёт размер значения в байтах, а вторым само значение. Макросы, задающие главные элементы Input, Output, Feature, устроены так, что могут принимать на вход любое количество флагов, в том числе и ни одного, поэтому флаги Data и Absolute на самом деле не обязательны, поскольку установлены по-умолчанию.

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

Присоединяйтесь к вечеринке

Не буду скрывать, макро-определения для нашего предметно-ориентированного языка в файле hid_def.h содержат изрядную дозу препроцессорной магии, которая определена в другом заголовочном файле macro.h .

Этот проект я оформил в репозиторий github:katyo/hid_def , так что теперь самое время начать писать дескрипторы отчётов HID по-человечески.



Предыдущая статья: Следующая статья:

© 2015 .
О сайте | Контакты
| Карта сайта