Люблю слушать музыку, чтобы сконцентрироваться или расслабиться. Но вот управлять громкостью и переключать треки было ой как неудобно на моей китайской механике. В один прекрасный день работа не сильно давила на голову и я решил сделать себе примочку к компу похожую на те, которые я видел на Алике.
В первую очередь, определимся, что нам нужно от готового устройства:
- Поворотная голова для регулировки громкости
- Кнопки Пуск/Пауза, Следующий трек, Предыдущий
- Какая-нибудь световая индикация - что устройство вообще еще живое
- Питание от USB
- Отсутствие драйверов
- Небольшой размер - по желанию
До глобального кризиса микросхем (2020-2021) я закупал пачками STM32F103 (в народе "BluePill"). Весной 2021 цена BluePill'ы стала на 30% выше, чем BlackPill'ы. Которая сделана намного более аккуратно, с TypeC и с STM32F4 на борту.
Но у меня завалялось много F103, даже в виде чипов, поэтому решил делать на этом камушке.
Итак. Вся суть платы - упрощенная BluePill с энкодером и какой-никакой защитой по USB.
На данном МК есть возможность аппаратной работы с энкодером. Выбирается тут:
https://www.youtube.com/watch?v=xqzWQgpqHmI
Более подробное видео по подключению энекодера
Оставим энкодеры и кнопки, т.к. это самая неинтересная часть данного поста. Для тестов нам нужна будет лишь одна кнопка на вход. Приступим к USB устройству. USB во вкладке Connectivy просто включается буз изменений. Далее подключаем библиотеку, которая как раз и содержит в себе дескриптор устройства. Выберем класс устройства HID. Такие устройства не требуют установки дополнительных драйверов и работают на практически любых машинах.
Во время разводки первой платы проворонил некоторый момент с энкодером и пришлось писать свой, достаточно специфичный, обработчик сигналов с него. Но - еще раз - энкодер в данном случае не проблема, самое сложное, обычно, разобраться в дескрипторах и найти пример с которого можно начать эксперименты.
Посе успешной генерации кода кубом начинаем его редактировать. Нас интересуют следующие файлы
Куб генерирует рабочий дескриптор устройства - мышь, который можно сразу же использовать. Мы будем делать клавиатуру. Причем, добавим нормальную клавиатуру + спец-клавиши. Таким образом можно совмещать несколько устройств в одном - например мышь+клавиатура и т.д.
В файле usbd_hid.c найдем строку
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END = {
Именно в данном массиве описывается что у нас за устройство такое. Очень подробно разбирать каждую строку не буду - это уже сделано на многих ресурсах(например, хабр). Стоит лишь обратить внимание что РАЗНЫЕ УСТРОЙСТВА ОТЛИЧАЮТСЯ ПО Report ID. И описано более 1 устройства, то добавляется самым первым байтом его идентификатор.
Для составления собственного дескриптора имеет смысл воспользоваться программой HID Descriptor Tool.
Мой дескриптор следующий (спасибо доброму человеку, который его сделал и дописал комментарии. В первый раз такое было тяжело переварить):
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_CUSTOM_REPORT_DESC_SIZE] __ALIGN_END = {
// 78 bytes
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, // Report Count (3)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x05, 0x0C, // Usage Page (Consumer)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x09, 0xB5, // Usage (Scan Next Track)
0x09, 0xB6, // Usage (Scan Previous Track)
0x09, 0xB7, // Usage (Stop)
0x09, 0xB8, // Usage (Eject)
0x09, 0xCD, // Usage (Play/Pause)
0x09, 0xE2, // Usage (Mute)
0x09, 0xE9, // Usage (Volume Increment)
0x09, 0xEA, // Usage (Volume Decrement)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
Тут стоит остановиться и внимательно посмотреть на комментарии. Видно, что тут есть Report ID 1 и Report ID 2. Это значит, что при вызове функции отправки данных в USB, я добавлю первым байтом этот ID. Устройство Report ID 1 - это очень усечённая клавиатура.
Я не стал менять название массива, а лишь поменял дефайн размера массива на соответствующий данному дескриптору.
Везде в данном файле меняем все HID_MOUSE_REPORT_DESC_SIZE на HID_CUSTOM_REPORT_DESC_SIZE.
Так же стоит найти данные строчки (их 3)
0x00, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
И выставить в 0x01. У нас жеж клавиатура.
В файле usbd_hid.h добавляем новый дефайн, который отвечает за размер нового массива
define HID_CUSTOM_REPORT_DESC_SIZE 78U
Так же имеет смысл увеличить максимальный размер пакета. (Максимум 0x40)
#define HID_EPIN_SIZE 0x08U
Кстати, все ваши изменения CubeMX сотрёт при перегенерации проекта. Так что аккуратней с этим. На этом работа с модификацией HID устройства закончена. Можно приступать непосредственно к коду. Переходим в main.c. После #include'ов добавляем описание структуры.
struct mediaHID_t {
uint8_t id; // Report ID
uint8_t keys; //Key
};
Так, с Report ID, более менее понятно, а как понять что писать в key? Добавим дефайнов.
#define USB_HID_SCAN_NEXT 0x01
#define USB_HID_SCAN_PREV 0x02
#define USB_HID_STOP 0x04
#define USB_HID_EJECT 0x08
#define USB_HID_PAUSE 0x10
#define USB_HID_MUTE 0x20
#define USB_HID_VOL_UP 0x40
#define USB_HID_VOL_DEC 0x80
Как можно заметить, каждый из дефайнов для управления звуком - это 1, которая сдвигается влево (если смотреть сверху вниз). То есть функциональное назначение действия кнопки это единичка в массиве. Если записать две рядом - то и будут нажаты 2 кнопки.
Я думаю, следующая картинка поможет пониманию того, что я написал.
Добавим еще дефайнов для нашей мини-клавиатуры
// USB keyboard codes
#define USB_HID_MODIFIER_LEFT_CTRL 0x01
#define USB_HID_MODIFIER_LEFT_SHIFT 0x02
#define USB_HID_MODIFIER_LEFT_ALT 0x04
#define USB_HID_MODIFIER_LEFT_GUI 0x08 // (Win/Apple/Meta)
#define USB_HID_MODIFIER_RIGHT_CTRL 0x10
#define USB_HID_MODIFIER_RIGHT_SHIFT 0x20
#define USB_HID_MODIFIER_RIGHT_ALT 0x40
#define USB_HID_MODIFIER_RIGHT_GUI 0x80
#define USB_HID_KEY_L 0x0F
Обязательно стоит указать, что хэндлер usb уже где-то есть.
extern USBD_HandleTypeDef hUsbDeviceFS;
А он объявлен в файле usbdevice.c
В мэйне создаем объявленную ранее структуру и выставляем id в 2. Ща будем звуком управлять
int main(void)
{
/* USER CODE BEGIN 1 */
struct mediaHID_t mediaHID;
mediaHID.id = 2;
mediaHID.keys = 0;
В цикле делаем следующее:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET){
mediaHID.keys = USB_HID_VOL_UP;
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&mediaHID, sizeof(struct mediaHID_t));
HAL_Delay(20);
mediaHID.keys = 0;
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&mediaHID, sizeof(struct mediaHID_t));
HAL_Delay(2000);
}
}
Что тут? Если нажата кнопка - отправим сообщение " нажата кнопка прибавить звук", чуть ждем, отправим сообщение "кнопка прибавить звук отжата".
Заливаем, подключаем. Если всё сделано правильно, то в диспетчере устройств будет добавлено новое HID-устройство. Пробуем нажать кнопку - звук должен увеличится.
Далее ваш полет фантазии ничем не ограничен. У меня получилось такое
Ссылка на GitHub: (Только как пример - у меня был энкодер с приколом, пришлось делать всякие гадости, чтобы он заработал)
Литература:
Лучшее, что есть по дескрипторам человеческим языком
https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/
Если ссылка отвалится - сохранил в pdf
https://programel.ru/files/Tutorial about USB HID Report Descriptors _ Eleccelerator.pdf
Отличный конфигуратор устройств HID для STM32
https://github.com/FreeJoy-Team/FreeJoy
Замечательная литература для более глубокого изучения работы USB
http://microsin.ru/content/view/1107/44/https://www.usbmadesimple.co.uk/