Arduino Leonardo (aka Pro Micro) в качестве USB-HID джойстика.

Или подключаем джойстики игровых консолей (NES, SNES, N64, SMD) к PC-компу через USB.

Почему Leonardo?

Интерес к вариации ардуины под названием Leonardo появился у меня совсем не давно, и очень предметно, т.е. я наконец-таки проникся в то узко специфичное назначение и задачу, которую более изящно, чем на леонарде не выполнишь, пожалуй, не на одной из ардуин. Весьма кстати сложилось и то, что незаюзанная леонарда (причем в двух исполнениях) была предусмотрительно (перед кризисом, еще в том году) припасена. Тогда, в конце 2014, я от души не понимал: нахрен было изобретать этакую «кастрированную солянку» (это я об леонарде так)…

Предадимся на чуток воспоминаниям: первая моя из ардуин (самая продвинутая на тот момент, если не считать мегу) была «даймиланов» (Duemilanove) и строилась она на базе «основного»  контроллера mega168|328 и конвертора-преобразователя USB-COM (FT232); далее появилась в продаже «уна» (UNO) причем (последовательно) аж три разных ревизии, где в качестве интерфейсного преобразователя был установлен также контроллер, но более скромный – mega8u2|16u2 … Но такой апгрейд не сразу получил поддержку кодом, да и сейчас не так уж много решений, позволяющих заюзать сею фичность. А причин этому несколько. Во-первых интерфейсный контроллер на первой ревизии UNO был не то чтоб сильно маленьким (в плане памяти – всего 8кб) – у него не было практически распаянных ног, за исключением коннектора ISP. Ревизия R2 получила более емкий 16u2 контроллер. И лишь последняя R3 заимела разведенными еще 4 ноги (помимо ISP) интерфейсного контроллера. А почему всего 4? – да контроллер такой, нету у него ног свободных больше. Ввиду этого, функционал, который можно возложить на интерфейсный контроллер совместно или вместо штатного USB-в-COM весьма ограничен. Плюс к этому софт под интерфейсный контроллер нужно писать и компиллировать не в среде Ардуино (а в штатной атмеловской «Студии») – что требует уже другого уровня квалификации. А если квалификация позволяет программить в AVR Studio (и есть соответствующие программаторы-отладчики) – то тогда зачем вообще ардуина? Короче «если» и «зачем» намного больше, чем реально полезных моментов. Из реализованного и доступного альтернативного функционала интерфейсного контроллера, пожалуй, наиболее востребованным является режим ISP-программатора (о чем я уже писал). Реализовывать же на уне компьютерные «приблуды» на USB-шину по крайней мере неудобно с точки зрения отладки (каждый раз шить оба контроллера, один через другой? Или внешними программаторами? – геморройно короче), да еще нужно и взаимный протокол обмена между двумя контроллерами продумывать …

Концепция платы Arduino Leonardo объединила два контроллера в одном. Если быть более точным – то «основной» контроллер у леонарды просто выкинули, оставили один интерфейсный. Но в качестве интерфейсного взяли не только более емкий (32кб), но и с большим количеством ног – mega32u4. У данного контроллера ног хватает чтобы «покрыть» все пины разъемов штатного коннектора щитов. Плюс к этому код, выполняющийся в интерфейсном контроллере, все также пишется и компиллится в среде разработки Ардуино. Но есть два недостатка: негусто памяти (часть из 32кб занимает загрузчик) и контроллер впаян на плату (если сожжешь, заменять геморройно), т.е. раньше все было просто: тестишь и отлаживаешь на уне, а конечные проекты (где нужна память и которые электрически отлажены) – реализуешь на меге (которую тоже перепаивать не захочешь) … в леонардо же встречаются эти два взаимоисключающих себя (для меня имхо) свойства. Вот и смотрел я на леонарду как на гадкого утенка.

Но если честно, то покупал я леонарду не совсем «прозапас», а с некоторым гипотетическим прицелом: к тому времени я уже успел приобрести раритетную консоль Nintendo 64 (далее – N64), собрал к ней комплект разных фишек, в том числе и с прицелом попрограммить. А на практике, помимо прочего, нашел я весьма эргономичным (тем более с учетом почти 20-ти летней давности) её джойстик. Вот соответственно и решил прикрутить его к компу (через USB) … Ардуино в помощь?.. или

«Открываем америку»?

Разумеется, чтобы не тратить зря время (хоть это иногда бывает и весьма увлекательно) я погуглил на предмет, а не существует ли уже готовых решений? Из готовых был только вариант купить на ебэе уже собранный (непонятно на чем) преобразователь джойстика N64 в USB-PC. Отметя данный вариант как полностью противоречащий инженерной и радиолюбительской традиции гуглю дальше … Благо удалось найти пример опроса джойстика N64 в виде ардуиновского скетча … осталась вторая половина задачи – реализация эмуляции USB-HID джойстика для компа и трансляции в него данных прочитанных с джоя N64. Тут сперва и навскидку копнув поверхностно я понял для себя – что нужны, походу, многие часы для «вкуривания» идеи концепции HID-профилей и иже с ним … Все отложилось в долгий ящик – до нового года. В каникулы была попытка вкурить еще разок – снова безрезультатно, почувствовал что или все крайне не просто, или я не понимаю какой то элементарщины, или просто уже старею и программистско-радиолюбительский скилл стал «рассасываться» за ненадобностью в профессиональной деятельности ;-(( Но хотелось не только «тряхнуть страной», но и тему до конца довести из практической целесообразности. А так как попутно столкнулся еще и с тем, что скетч по опросу джойстика N64 в неизменном виде не только не работал на леонарде но и тупо «заваливал» её - решено было для начала упростить задачу и реализовать …

Джойстик Dendy (NES) в USB-порт PC-компа.

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

  1. Среда разработки Ардуино штатными средствами не поддерживает создание и модификацию USB-HID-профилей на уровне скетчей. Посему необходимо «патчить» непосредственно исходники библиотек, входящих в комплект. При этом, не забывая бекапить старые версии – ведь получается, что в рамках одного проекта мы корректируем набор библиотек, для всего экземпляра среды разработки, фактически затачивая ее под компиляцию единственного скетча (в нашем случае в части HID-профилей). Править библиотеки не сложно – там такой же сишный код как и в скетчах ардуины.
  2. HID-профиль – это практически описание переменной типа «структура», хотя с другой стороны это массив, размер которого должен быть кратен целому числу байт. Описывая профиль, мы фактически описываем объекты  как по функциональному назначению (выбирая из достаточно большой коллекции предопределенных стандартом типов), так и указываем их количество и самое главное размер (в битах), которым будет закодировано состояние объекта. И если, например, для кнопки это один бит, то состояние оси джойстика может кодироваться разным числом битов: ведь структуру HID-профиля мы создаем произвольно и наша цель отразить «в цифре» состояние физического объекта (джойстика в нашем случае). Поэтому исходными данными для написания HID-профиля джойстика служат две вещи: во-первых формат представления информации (формат данных) о физическом состоянии элементов джойстика, которое мы читаем с него; во-вторых – то каким мы хотим его видеть в системе (и это не жестко связанные друг с другом вещи – вариантов можно придумать много).
  3. Как выглядит и чем создать HID-профиль? Фактически это бинарная структура  (как исходник на ассемблере в hex-code) – в случае ардуины описываем мы его как hex-массив в текстовом виде (файл HID.cpp), ввиду простоты и наличия исчерпывающей документации править HID-профиль можно и «в рукопашную». Но создавать с нуля ручками профиль не так удобно, займет много времени – ибо шарить по листингу документации долго и рутинно. Есть утилита позволяющая несколько оптимизировать данный процесс. Сразу скажу, что хоть она и претендует на некую «официальность» - но при этом очень сырая и неудобная, упрощает процесс создания профиля лишь в части мнемонического представления записей и трансляции их в hex-код. В остальном же жопь полная – нельзя даже поменять строчки местами, а вставить новую можно только на одну выше курсора, но не в конец списка! Вобщем плеваться при ее использовании придется много – но альтернатив как-то нет, благо нет никаких подводных камней и результат зачастую проще откорректировать потом вручную (сохранив в формат *.h)

Вернемся к дендевому джойстику: его состояние мы читаем в виде восьми бит данных, джойстик представляет собой 8 кнопок (крестовина направлений – 4 кнопки, A, B, Select, Start). Можно представить в профиле такое устройство просто как набор из 8 кнопок (их нумерацию определяем мы - от младших бит в байте HID-профиля) – но мне показалось логичным описать «эмуляцию осей» на основе сведений о состоянии 4-х кнопок крестовины (все же джойстик вроде как). Попутно, в том числе для простоты, не стал я отказываться и от описания всех восьми кнопок в виде «кнопок». Итого получаем, что нажатие кнопок крестовины генерирует для компа два события: нажатие кнопки и отклонение соответствующей оси. Разумеется, при такой конфигурации отклонение по осям может быть лишь только максимальным, либо отсутствовать вовсе. Что получилось в результате.

 

Скачать исходники.

И немного пояснений как по использованию исходников, так и по структуре (с целью понимания):

  1. Для компиляции применялась среда разработки Ардуино версии 1.0.3;
  2. Скетч (*.ino) кладем в предварительно созданную одноименную папку в папке отведенной под скетчи, либо же просто копи-пастим содержимое в новый скетч и сохраняем его;
  3. «Патченые» версии библиотек (HID.cpp и USBAPI.h) кладем с заменой (и предварительным бекапом) в \hardware\arduino\cores\arduino\ (относительно корня среды разработки);
  4. Компиллим, подключаем дендевый джойстик (согласно распиновке, описанной в начале скетча – три линии управления/данных, земля и +5В питания) …
  5. По HID-профилю: в нем описывается две оси и восемь кнопок. Именно в такой последовательности следует заполнять массив данными. Директива REPORT_ID (4) не относится напрямую к структуре, а лишь указывает на порядковый номер самого профиля среди остальных, эмулируемых устройством;
  6. Сам профиль находится в файле HID.cpp, также в нем (ниже) расписывается структура битового массива HID-профиля уже в виде функции в сишном коде;
  7. В файле USBAPI.h функция из предыдущего пункта уже описывается в составе класса;

PS. Для джойстика SNES интерфейс и протокол аналогичны, но считывать нужно уже не 8, а 16 бит данных. Но если крестовину не дублировать в виде кнопок – то конфигурации HID-профиля в 8 кнопок + крестовина вполне достаточно. Навскидку немного правок и скетч (как пример) готов ;-)

Скачать исходники.

Джойстик Nintendo 64 (N64) в USB-порт PC-компа.

Но вернемся к первоочередной задаче. С джойстиком N64 одновременно и проще и сложнее. Результат выглядит так.

 

Скачать исходники.

Также немного поясню:

  1. Джойстик N64 содержит полноценный «аналоговый джойстик» (две аналоговых оси), цифровую крестовину (4-кнопки, как и на дендевом джое) и еще 10 функциональных кнопок (A, B, два шифта, Z – снизу в центре, Старт – в центре наверху, а также группу из 4-х независимых кнопок «С» расположенных тоже в виде крестовины);
  2. Электрический интерфейс джойстика N64 весьма прост – одна линия данных (плюс питания и земля). Но протокол обмена, в отличие от джойстика Денди, весьма чувствителен к точному соблюдению таймингов. Линию данных подключаем к порту D2 (в терминологии ардуины), не забываем, что питание джойстика N64 +3.3В. Также обращу внимание, что при желании переназначить линию данных на другой порт ардуины – необходимо поменять не только номер порта в начале скетча, но и маски (следующие далее) при помощи которых происходит попеременное переключение линии то на вход, то на выход. Вместе с этим не забывает, что у разных плат Ардуино (Leonardo, UNO, Mega) – одни и теже порты, с точки зрения среды разработки (распайки ардуины), - подключены (в некоторых случаях) к разным линиям (портам) самого контроллера – что в конкретном примере требует маску, соответствующую типу контроллера. Проще говоря: приведенный скетч в части опроса состояния джойстика N64 не имеет непосредственной переносимости между платами UNO и Leonardo – при подключении джоя к порту D2 маска для уны - 0x04, для леонарды - 0x02 (см. карты портов UNO и Leonardo);
  3. Как представить цифровую крестовину? С аппаратной точки зрения – это просто 4 кнопки (ровно как и в Денди) – «запилить» их также в виде осей как в примере выше для дендевого джойстика? – но в данном примере у нас уже есть две полноценные оси … Вариант решения я «подглядел» в реализации моего древнего беспроводного PC-джойстика Logitech RumblePad 2 – там цифровая крестовина представлена в виде датчика угла поворота («Hat switch» - в терминологии HID-профиля или «POV» - в терминологии панели управления винды). В данном примере мы описываем объект с углом поворота от 0 до 315 градусов (360 - 45), дискретностью 45 градусов – итого 8 фиксированных положений (нумерация от 0 до 7). Но в такой модели отсутствует состояние, соответствующее не нажатым кнопкам крестовины – решение придумал экспериментально: присваиваем значение выходящие за рамки установленного диапазона (например 8);
  4. При описании HID-профиля не допускается оставлять недоопределенные биты в байтах структуры профиля (устройство не будет работать). В нашем же случае, если при описании объекта Hat switch несмотря на востребованные лишь 4 бита мы указали размер структуры в 8 бит – то для кнопок так просто не выкрутишься. У нас 10 кнопок, соответственно 6 бит остаются неиспользованными, но нам нужно их описать, при этом не создавать визуальных (логических) объектов в профиле, а также не изобретать чего либо лишнего, дабы потом не городить всяко разно для работы с этим (не по 5 же бит вместо одного отводить на кнопку, чтобы уложиться в размер кратный одновременно и 10-ти и 8-и, а потом заморачиваться с преобразованием данных при их «рассовывании» по такой структуре). Удалось все же найти вариант описания незаюзанных бит – который и был применен;
  5. Для компиляции проекта кладем соответствующие проекту «патченые» версии библиотек (HID.cpp и USBAPI.h) в \hardware\arduino\cores\arduino\ (относительно корня среды разработки);

Оптимизируем результат.

Как и любая из плат Ардуино – леонардо  удобна для разработки и отладки, но для использования в готовых решениях геометрический размер с разъемом под щит далеко не оптимален. И если макаронники по этому поводу болт клали на конечных пользователей, то китайцы спешат привнести рациональные модификации в массу. Так вот, леонардой в миниатюрном исполнение стала «платка» под названием Pro Micro – правда придумали ее не китайцы, корни видимо растут из этого проекта – но зато китайцы продают клон всего за 5 баксов (прямо-таки антикризисное решение), а не за 20 как хапуги на Sparkfun.

Для нашей задачи на Mini Pro не хватает 3-х вольтовой линии питания, необходимой для джоя N64 – решить это легко добавлением стабилизатора 78L33, никакой дополнительной обвязки в виде емкостей не требуется. Особенность модифицированного (в сравнении с леонардой) загрузчика Mini Pro заключается в том числе в том, что после записи скетча автоматического сброса (reset) не происходит – нужно либо передернуть питание, либо временно коротнуть  на землю соответствующую ногу.

ЗЫ. Видимо (по состоянию на начало 2015 года) – мой проект конвертора джойстика N64 в USB-HID на базе Arduino Leonardo является первым и уникальным во всем интернете ;-))

MiGeRA (февраль 2015)

Калибровка аналоговых осей оригинального джойстика N64.

Впринципе, еще на этапе тестирования проекта и разных джойстиков с ним был выявлен факт, что у оригинальных джойстиков N64 оси «короче» чем у китайского новодела. Если конкретнее, то максимальный «размах» осей современного китайского джойстика от -127 до +127 (причем точно, как в аптеке). У раритетных же оригинальных джойстиков оси доходят +/- до 80-ти. Хоть Nintendo 64 не та консоль, которую заигрывали «до дыр» (как Денди), но аналоговые «стики» на таких джоях весьма раздрочены при в целом весьма хорошем состоянии корпуса и кнопок (в том числе резиновых контактных пластинок). Посему точность позиционирования «нуля» и максимальных отклонений по осям крайне низкая: дважды подряд отклоненная ось до крайнего положения может показать результат и 65, и 82 … Но для игр «в железе» N64 такой расклад вовсе не мешает, скорее даже наоборот оригинальный джойстик воспринимается как более «мягкий». С точки же зрения использования джойстика Nintendo 64 в качестве PC-джойстика под виндой – есть возможность калибровки осей штатными средствами драйвера. Вроде все просто, и данном проекте (исходник выше) я описал размерность осей по максимуму (т.е. как бы под китайский джой ) - +/- 127, в расчете на то, что при использовании оригинального джойстика нужно будет его всего лишь откалибровать средствами драйвера (тривиальная задача). Можно конечно вообще не заморачиваться – но правда при этом оси не будут достигать максимального отклонения, что может оказаться не комфортным для некоторых игр.

На практике же все получилось несколько иначе: калибровка средствами драйвера хоть и расширяет диапазон покрытия «виртуальной поляны осей», но не обеспечивает полного ее покрытия «до упора» (странно, но факт).

Что делать? – Реализовать калибровку средствами ардуины!

Скачать исходники (с калибровкой).

Ну и чуток поясню:

  1. Вход в режим калибровки: Start + Z. Сработка при отпускании этой комбинации кнопок. Далее нужно отклонить джой до максимальных значений осей:  левый-верхний (-/-) и правый-нижний (+/+) углы – далее еще раз жмем Start + Z для выхода из режима калибровки;
  2. В коде сохранен активированным вывод отладочной информации в COM-порт, работе HID-устройства это не мешает (хотя можно и закомментировать, как в первом примере);
  3. Механически стик N64 сконструирован так, что максимальные отклонения по диагоналям меньше, чем по вертикальным осям – имейте это ввиду при калибровке. Т.е. имея цель максимально расширить диапазон нужно калибровать не по диагоналям, а вверх-вниз-влево-вправо, или просто вперемешку сделать несколько полных оборотов;
  4. Если же стоит цель максимизировать чувствительность – то преднамеренно в режиме калибровки отклоняем стик слегка и не до упора.

Далее в планах доработать проект на предмет сохранения настроек калибровки во флэш-памяти контроллера ардуины (чтобы не повторять ее при каждом подключении). Также «растиражировать» код в рамках одного контроллера для подключения через одну ардуину двух джойстиков N64.

MiGeRA (март 2015)

Джойстик Sega MegaDrive (SMD) в USB-порт PC-компа.

С появлением не так давно в моем арсенале настоящей аппаратной Сеги (из 2003 года), а стало быть и (отдельно приобретенных новодельных) джойстиков к ней – решил пополнить материал данной статьи конвертором джойстика и этой приставки (самому тоже пригодиться). Решил сразу использовать свои наработки (описанные выше), найти описание протокола оказалось пустяковым делом: аппаратно-программная модель джойстика Сеги, пожалуй, самая простая из всех описанных ранее (особенно трехкнопочного). Интерфейс имеет 9 пинов, разъем совместим с DB-9 (т.е. ответной частью для джойстика может служить старая коса от шторки COM-порта), помимо земли и питания (от приставки к джойстику) содержит 7 сигнальных двунаправленных линий! (роскошно, ничего не скажешь). Классический «трехкнопочный» (помимо крестовины и кнопки «старт» есть лишь три кнопки действий: A, B, C) джойстик использует 6 линий для передачи информации о состоянии кнопок (в приставку) и одну (из приставки) для получения сигнала управления мультиплексором (к555кп11 или 74ls157) – собственно на этом схемотехника джойстика заканчивается, а алгоритм опроса заключается в чтении состояния 6 информационных линий в каждом из состояний мультиплексора (0 или 1 на управляющей ноге). Но так как таким образом можно передать информацию о 12-ти кнопках (а их всего 8) то некоторые из линий «непереключаемые»: кнопки «вверх» и «вниз» читаются независимо от состояния мультиплексора, что почти аналогично кнопкам «влево» и «вправо» значение которых коммутируется лишь при 1 на управляющем входе, т.е. реально переключаются лишь две линии. В отличие от всех Nintendo-джойстиков, джойстик SMD имеет полнофункциональную крестовину! – есть возможность обеспечить и зафиксировать нажатие всех четырех направлений (нажатие на центр), или трех … но не двух как у NES, SNES, N64 … Минимальное количество времени, требуемое для опроса состояния джойстика (параллельный интерфейс против последовательного у Нинтендо) открывает дополнительные игровые возможности! Но сейчас не об этом. Реализация подключения к ардуине самая простейшая – вешаем сигнальные ноги на произвольные пины ардуины:

Существует и «шестикнопочный» джойстик – добавился верхний ряд кнопок действий: X, Y, Z (а считают получается именно и только их – т.е. к трем добавили еще три) и кнопка Mode (не в счет) - у оригинальных моделей она находилась на правом-верхнем торце, у новодела рядом с кнопкой «старт», аля как «селект» у Денди, что имхо более логично. Интереса ради отмечу что игр, требующих шестикнопочного джойстика не так уж и много (по-любому они 1993 года выпуска или новее) – верхний ряд кнопок используют как правило файтинги; при этом не встретил ни одной игры которая бы задействовала кнопку Mode (хотя это обычная «программная» кнопка как и три в верхнем ряду). Опрашиваются добавленные кнопки несколько экзотическим образом (что имхо странно, с учетом наличия «задвоений» не полностью использованного мультиплексора) – нужно послать два коротких строба, после чего при 1 на управляющей ноге на определенные входы мультиплексора будут выставлено состояние четырех «новых» кнопок, еще один строб – возвращает джойстик в исходное состояние (см. скетч).

За основу решил взять HID-профиль созданный для NES-джойстика: крестовина и 8 копок – как раз соответствует конфигурации «шестикнопочного» сега-джойстика.

Для сборки, как и прежде (дабы не лопатить и не патчить потроха новых IDE) нужна версия 1.0.3 среды разработки, в которую нужно подложить «дендевые» профили и лишь после компилить сеговский скетч (все в одном архиве).

Скачать исходники.

MiGeRA (сентябрь 2019)

Заглавная » Радиоэлектроника » Arduino - Высокоуровневая платформа устройств на микроконтроллерах » Подключаем джойстики игровых консолей к компьютеру через USB