Я являюсь владельцем Nintendo Wii, и недавно мне стало интересно, как же работает «expansion port» на виимоуте, который создан для подключения различных аксессуаров. При этом я не мог не попытаться создать своё собственное устройство для подключения к Wii.
Предыстория
Началось всё с того, что так называемом «звёздном каталоге» Nintendo я увидел это:
Это контроллер для Wii в виде геймпада от SNES. Для тех, кто не в курсе, расскажу, что на Wii можно официально покупать и играть в игры от NES (у нас больше известна как «Денди»), SNES (aka Super Nintendo), Nintendo 64 и других классических консолей. Чтобы полноценно играть в такие игры, нужно дополнительно покупать «классический контроллер», который представляет из себя обычный геймпад.
Так вот, этот геймпад в виде оригинального контроллера SNES можно «купить» в звёздном каталоге только за звёздочки, получаемые при покупке игр. Проще говоря, его нельзя так просто купить в магазине как обычный классический геймпад, из-за этого эта штука весьма ценится в узких кругах.
Мне же в голову сразу пришло — почему бы не сделать переходник, который позволял бы подключать к Wiimote (именно к нему подключается классический контроллер, а не напрямую к консоли) настоящий геймпад от SNES, а заодно от NES, Nintendo 64 и чего-нибудь ещё. Так встал вопрос о том, как же устроен порт расширения виимоута.
Теория
После небольшого гугления выяснилось, что там обычный протокол I²C! В разъёме используется пять контактов: земля, питание, clock, data и определение подключения (туда надо просто подать питание).
Подключаемое устройство должно иметь адрес 0x52. Передаваемые данные зачем-то шифруются. При чём используется 512-битный ключ, который передаёт сам виимоут. Для дешифрации используется простая формула:
decrypted_byte = (encrypted_byte ^ table1[address%8]) + table2[address%8]
Зачем там шифрование? Ума не приложу. К счастью, вскоре выяснилось, что есть уже готовая библиотека для создания своих устройств для Wii:
code.google.com/p/circle-of-current/wiki/WiiExtensionLibrary
Она реализует работу с I²C и шифрование-дешифрование для микроконтроллеров AVR. Как раз то, что мне нужно! Остаётся только подставить ID устройства и заполнить массив передаваемыми данными. Вот ID некоторых устройств:
(Информация с wiibrew.org)
Меня интересовал Classic Controller. Далее нужно было разобраться, в каком формате посылать данные. А передаются там шесть байт:
LX и LY — это левый аналоговый стик (0-63), RX и RY — это правый аналоговый стик (0-31), LT и RT — это аналоговые шифты (0-31), BD{L,R,U,D} — крестовина, B{ZR,ZL,A,B,X,Y,+,H,-,LT,RT} — кнопки, где 1 — отжата, а 0 — нажата.
Всё несколько запутанно… Ещё стоит брать во внимание, что при включении происходит калибровка устройства и первое полученное значение с аналогов считается центральным.
Реализация
Таким образом, в спокойном положении геймпада массив из шести байт данных будет выглядеть так:
unsigned char but_dat[6]; // struct containing button data
but_dat[0] = 0b01011111; // RX<4:3> LX<5:0>
but_dat[1] = 0b11011111; // RX<2:1> LY<5:0>
but_dat[2] = 0b10001111; // RX<0> LT<4:3> RY<4:0>
but_dat[3] = 0b00000000; // LT<2:0> RT<4:0>
but_dat[4] = 0b11111111; // BDR BDD BLT B- BH B+ BRT 1
but_dat[5] = 0b11111111; // BZL BB BY BA BX BZR BDL BDU
Старшие биты у стиков равны нулю, всё остальное равно единице. Так будут передаваться данные, в которых стики находятся по центру, и все кнопки отжаты.
Остаётся только изменять эти значения на необходимые и посылать их, когда виимоут опрашивает наше устройство. Первым делом я решил попробовать подключить игровой контроллер от Денди. Т.е. читать нажимаемые на нём кнопки и посылать их в виимоут, имитирую нажатии на классическом контроллере.
Не буду рассказывать о том, как читать данные с геймпада от Денди, эта тема очень избитая, и делается это очень легко. Можете почитать, например, эту статью: habrahabr.ru/post/191936/ авторства KurilkaRymin. Впрочем, приведу тут код для этого, он совсем небольшой:
uint8_t get_nes_gamepad()
{
uint8_t gamepad_data = 0;
NES_PORT &= ~(1<<LATCH_PIN); // Latch
int b;
for (b = 0; b < 8; b++)
{
NES_PORT &= ~(1<<CLOCK_PIN); // Clock
_delay_us(10);
gamepad_data |= (((NES_PORT_PIN>>DATA_PIN)&1)<<b);
NES_PORT |= 1<<CLOCK_PIN; // Clock
_delay_us(10);
}
NES_PORT |= 1<<LATCH_PIN; // Latch
_delay_us(10);
return gamepad_data;
}
Всё заработало без каких-либо задержек и проблем, если не считать, что дендивские контроллеры время не пощадило, пришлось активно оттирать контакты спиртом. Кстати, как ни странно, им хватает питания в 3.3 вольта, хотя везде пишут, что они питаются от пяти вольт. Я уже морально готовился делать конвертер уровней, но не пришлось.
Теперь я могу играть на Wii в игры от NES на контроллере от Денди. Или на родном контроллере от NES, если достану таковой. Впрочем, они отличаются только наличием читерских кнопок турбо.
В планах:
- Сделать поддержку контроллеров SNES и N64
- Вытравить плату и оформить всё в красивом корпусе
Исходный код проекта: github.com/ClusterM/nes2wii
Основной источник информации: wiibrew.org/wiki/Wiimote/Extension_Controllers
Автор: ClusterM