USB-джойстик - стандартный пример реализации HID-устройства. В дескрипторе HID-устройства описывается формат посылки с джойстика, по которому драйвер в ОС должен разобрать посылку на стороне компьютера. Для джойстиков там есть специальные поля, причем есть как просто абстрактные оси X/Y, так и определенные Rudder/Aileron, предназначенные для целей привязки к управлению летающей техникой.
Основной этап конструирования такого джойстика именно формирование верного HID-дескриптора. В остальном отличий нет никаких: все реализуется на основе любого стандартного примера HID-устройства.
Данный преобразователь повторяет по своим функциям то, что предоставляет обычный USB-шнурок для аппаратуры радиоуправления. На вход он получает PPM сигнал управления, а для компьютера представляется обычным HID устройством типа джойстик. Одна особенность: PPM сигнал в моем случае берется не с разъема пульта управления, а с приемника. Это позволяет обойтись без проводов, что в моем случае показалось очень удобным.
В качестве основы была взята плата управления на LPC134x. Было внесено минимальное изменение: соединено питание, поступающее с USB с линией питания разъемов сервомашинок. Это сделано простым замыканием выводов 1 и 2 диода Шоттки BAT54C.
В HID-дескрипторе, который описывает структуру посылки компьютеру были заданы аналоговые оси Aileron, Elevator, Throttle, Rudder. На каждую из них выведен соответствующий канал передатчика. Но здесь и начались танцы: Газ и руль направления работали, а еще 2 канала (идущих в посылке первыми) упорно оказывались видется. В начале я их задал как X-Y оси, затем попытался перевести на Slider/Dial. Но это не дало результата, так же обменял их с 2мя видимыми, что не дало результата.
Вот мой сгенерированный дескриптор:
0x05, 0x01, // USAGE_PAGE (Generic Desktop) //0x15, 0x00, // LOGICAL_MINIMUM (0) 0x09, 0x04, // USAGE (Joystick) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x02, // USAGE_PAGE (Simulation Controls) 0x09, 0xb0, // USAGE (Aileron) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0xb8, // USAGE (Elevator) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 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) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x09, 0xba, // USAGE (Rudder) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0xc0, // END_COLLECTION
Заставить его работать у меня так и не получилось. В итоге пришлось взять дескриптор из проекта MJoy, урезав до необходимых мне 4 каналов. С ним все завелось без переделок. Рабочий дескриптор:
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) 0x75, 0x08, // REPORT_SIZE (8) 0x09, 0x04, // USAGE (Joystick) 0xa1, 0x01, // COLLECTION (Application) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x95, 0x02, // REPORT_COUNT (2) 0x81, 0x82, // INPUT (Data,Var,Abs,Vol) 0xc0, // END_COLLECTION 0xa1, 0x00, // COLLECTION (Physical) 0x09, 0x32, // USAGE (Z) 0x09, 0x33, // USAGE (Rx) 0x95, 0x02, // REPORT_COUNT (2) 0x81, 0x82, // INPUT (Data,Var,Abs,Vol) 0xc0, // END_COLLECTION
Когда я случайно наткнулся на беспроводной джойстик Nunchuck для приставки Wii, мне сразу понравилась эта концепция. Во-первых встроенный датчик положения (в данном случае акселерометр, во-вторых 2 кнопки и мини-джойстик под палец. В третьих: беспроводной интерфейс (но это не относится к фирменным нунчакам).
Финальным аккордом стало использование обычного I2C для связи с внешним миром и наличие документации о всех регистрах устройства. По этой документации функции получения данных с джостика, а так же переходник на свое устройство делаются за вечер. Остается только найти применение.
К моей плате джойстик подключается через стандартный разъем внешних I2C устройств. Код для работы с джойстиком по документации был написан заранее. Оставалось только проверить на живую. Вот здесь-то и начались проблемы.
Проблема первая: джойстик отказался работать в I2C Fast Mode, хотя везде утверждают что с этим проблем нет. Пришлось опустить частоту шины I2C до 100кГц.
Проблема вторая: довольно быстро получилось прочитать данные с джойстика, однако, данные аналогового стика упорно были нулевыми. Потратил пол дня выискивая хитрые последовательности инициализации, но результатов это не дало. Стал уже думать, где бы проверить джойстик на реальной приставке. В итоге, решил еще посмотреть внутренности, как оказалось не зря. На потенциометры банально не подавалось напряжение. Как оказалось оно подается через ключ (чтобы можно было заснуть) и перед ключом стоит резистор. В моем случае, он оказался не пропаян с одной стороны.
Резистор R10 на фотографии, так же чуть выше виден транзистор-ключ). После пропайки резистора все заработало. Стоит заметить, данные оказались не зашифрованными (хотя везде указывается, что нужно применять простенькую функцию декодирования), видимо, сказывается то, что джойстик не родной. При записи в память джойстика ключей, данные начинают идти в шифрованном виде и уже не поддаются использованию.
В качестве конечного применения было реализовано 2 программы: