Экспонируйте два устройства HID Joystick с одной платой RP2040 (Waveshare Pi Pico Zero)

usb joystick hid raspberrypi-pico

Я хочу использовать Waveshare Pi Pico Zero для подключения двух стандартных контроллеров NES к компьютеру через один порт USB.

Я использую определение платы Waveshare RP2040 Zero из https://github.com/earlephilhower /arduino-pico, который, похоже, предоставляет HID-библиотеку Joystick, совместимую с этим предназначенным для Teensy NES_to_USB скетч. Однако это работает только для одного контроллера NES.

Я нашел Arduino-USB-HID-RetroJoystickAdapter, который поддерживает два джойстика или контроллера NES на одной плате с использованием MHeironimus ArduinoJoystickLibrary. К сожалению, похоже, что конкретная библиотека джойстика поддерживает только платы Arduino на базе ATmega32u4 (например, Leonardo, Pro Micro).

Спросить

Решение для размещения двух устройств USB HID Joystick на одной плате Pi Pico Zero. Первый приз — это дополнительная библиотека, такая как MHeironimus ArduinoJoystickLibrary, совместимая с https://github.com/earlephilhower/arduino-pico Ядро RP2040. Также рад услышать о других решениях, сделанных своими руками, но не уверен, что смогу им следовать :)

, 👍1

Обсуждение

«Первый приз — бесплатная библиотека»… что это значит?, @jsotola

>Первый приз — это встраиваемая библиотека — возможность просто установить существующую библиотеку в Arduino, которая сделает второе устройство Joystick HID доступным для Pi Pico, как это делает ArduinoJoystickLibrary от MHeironimus для плат на базе ATMega32u4., @NeilenMarais

это может помочь... https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/, @jsotola

Я получил несколько [полезных советов о том, как напрямую использовать TinyUSB](https://github.com/earlephilhower/arduino-pico/discussions/1562) от сопровождающего arduino-pico. Если мне удастся заставить что-то работать, я напишу это как ответ., @NeilenMarais

Хорошо, у меня все заработало, короче говоря, выберите «Adafruit TinyUSB» в качестве стека USB, а затем измените Adafruit [hid_gamepad.ino](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/blob/master/examples/HID/ hid_gamepad/hid_gamepad.ino) для получения двух разных отчетов GP, аналогичных примеру hid_composite. Тогда сложнее всего разобраться с глупостью ядра Linux и необходимостью включить режим HID_QUIRK_MULTI_INPUT, чтобы обеспечить работу нескольких HID-отчетов одного и того же типа на одном устройстве. Немного поздно, завтра напишу как следует., @NeilenMarais


1 ответ


Лучший ответ:

1

При выборе опции USB-стека Adafruit TinyUSB (Инструменты->USB Stack->Adafruit TinyUSB) ядра arduino-pico вместо стандартного (Pico SDK) дескрипторы USB HID могут настроить напрямую для доступа к двум HID-геймпадам.

Недостаток этого подхода заключается в том, что простые в использовании предварительно настроенные устройства (например, Джойстик, Мышь и Клавиатура) не настраиваются, а это дает вам возможность использовать всю гибкость TinyUSB для настройки USB-устройств. Хотя я использовал его с платой Waveshare Pi Pico Zero, он должен работать с любой платой Arduino, которую поддерживает Adafruit TinyUSB. .

Вот тестовый скетч, в котором настраивается HID-устройство с двумя геймпадами, аналогично игре «Эй, как насчет двух игроков?» пример в https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/. Я изменил пример Adafruit TinyUSB hid_gamepad.ino, добавив второй геймпад. отчет и сокращение "демо" цикл для экономии места:

/********************************* *************************************
Adafruit инвестирует время и ресурсы, предоставляя этот открытый исходный код.
пожалуйста, поддержите Adafruit и оборудование с открытым исходным кодом, купив
продукция от Adafruit!

Лицензия MIT. Дополнительную информацию см. в разделе ЛИЦЕНЗИЯ.
Авторские права (c) 2021 NeKuNeKo для Adafruit Industries
Весь текст выше и заставка ниже должны быть включены в
любое перераспределение
**************************************************** *******************/

#include "Adafruit_TinyUSB.h"

// Внимание, внимание!!! Выберите «Adafruit TinyUSB». для USB-стека

// Дескриптор отчета HID с использованием шаблона TinyUSB
uint8_t const desc_hid_report[] =
{
  TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)), // идентификатор первого отчета геймпада 1
  TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(2))  // Идентификатор отчета второго геймпада 2
};

// USB-HID-объект. Для ESP32 эти значения не могут быть изменены после этого объявления.
// отчет по описанию, длина, протокол, интервал, использование конечной точки
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);

// Полезные данные отчета, определенные в src/class/hid/hid.h
// - Битовую маску кнопки геймпада см. hid_gamepad_button_bm_t
// - Битовую маску геймпада см. в hid_gamepad_hat_t
hid_gamepad_report_t   gp[2]; // Два дескриптора геймпада

void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
  // Ручное начало() требуется на ядре без встроенной поддержки TinyUSB, например mbed rp2040
  TinyUSB_Device_Init(0);
#endif

  Serial.begin(115200);
  usb_hid.begin();

  // ждем пока устройство смонтируется
  while( !TinyUSBDevice.mounted() ) delay(1);
  Serial.println("Adafruit TinyUSB HID multi-gamepad example");
}

uint8_t gp_i = 0;

void loop()
{
  if ( !usb_hid.ready() ) return;

  Serial.print("Testing gamepad nr: ");
  Serial.println(gp_i);

  // Кнопки сброса
  Serial.println("No pressing buttons");
  gp[gp_i].x       = 0;
  gp[gp_i].y       = 0;
  gp[gp_i].z       = 0;
  gp[gp_i].rz      = 0;
  gp[gp_i].rx      = 0;
  gp[gp_i].ry      = 0;
  gp[gp_i].hat     = 0;
  gp[gp_i].buttons = 0;
  // gp_i + 1 — идентификатор отчета HID, т.е. 1 для первого геймпада и 2 для
  // второй, как определено в desc_hid_report[] выше.
  usb_hid.sendReport(gp_i + 1, &gp[gp_i], sizeof(gp[gp_i]));
  delay(2000);

  // Случайное касание
  Serial.println("Random touch");
  gp[gp_i].x       = random(-127, 128);
  gp[gp_i].y       = random(-127, 128);
  gp[gp_i].z       = random(-127, 128);
  gp[gp_i].rz      = random(-127, 128);
  gp[gp_i].rx      = random(-127, 128);
  gp[gp_i].ry      = random(-127, 128);
  gp[gp_i].hat     = random(0,      9);
  gp[gp_i].buttons = random(0, 0xffff);
  usb_hid.sendReport(gp_i + 1, &gp[gp_i], sizeof(gp[gp_i]));
  delay(2000);

  // выбираем другой геймпад
  gp_i = (gp_i + 1) % 2;
}

Linux HID Malarkey

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

Эти инструкции подходят для Ubuntu 22.04, возможно, вам придется адаптировать их для вашего дистрибутива.

После программирования примера скетча я смог увидеть идентификатор моего USB-устройства Pi Pico с помощью `lsusb:

$ lsusb
...
Bus 001 Device 011: ID 239a:cafe Adafruit RP2040 Zero
...

Запишите идентификатор USB-устройства (239a:cafe) и создайте файл в /etc/modprobe.d для настройки usbhid модуль:

$ echo "options usbhid quirks=0x239a:0xcafe:0x040" | sudo tee /etc/modprobe.d/adafruit_hid_quirk.conf

$ sudo update-initramfs -u

# Requires a reboot to take effect, e.g.:

$ sudo shutdown -r now

0x239a:0xcafe — из вывода lsusb, а 0x040 — это магическое число для включения режима HID_QUIRK_MULTI_INPUT . Нам нужен update-initramfs, поскольку usbhid обычно загружается слишком рано в процессе загрузки, чтобы настройки из /etc/modprobe.d вступили в силу. без его присутствия в initrd.

После перезагрузки вы можете проверить, вступила ли в силу настройка usbhid:

$ cat /sys/module/usbhid/parameters/quirks
0x239a:0xcafe:0x040,(null),(null),(null)

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

ls /dev/input/js*
/dev/input/js0  /dev/input/js1

Тестирование

Простой способ протестировать — перейти на https://gamepad-tester.com/. Через некоторое время вы должны увидеть что-то вроде:

Геймпады игрока 1 и игрока 2

Вы должны увидеть переключение значений Player1, а затем значения Player2, пока вы не отключите пико.

,