Думаю многие из вас помнят те славные времена, когда мы часами рубились в танчики, черепашки ниндзя, батлтодс энд даблдрагон и еще пару десятков других суперских игр на Денди. Это было шикарное время! Большинство игр были чертовски сложные, но даже несмотря на это, неудача за неудачей, мы, все равно, шли к своей цели и начинали игру заного, чтобы уж сегодня-то пройти ее до конца.
Поностальгировав всласть, я решил, что очень хочу поиграть в Dendy и поиграть хочу на ноутбуке, но не на клавиатуре, а на старом добром джойстике.
Ниже я опишу, с какими проблемами я столкнулся и как их решил.
Немного поясню, почему именно так мне захотелось поиграть. Приставка у меня сохранилась и она отлично работает, но осталась всего пара картриджей(остальные раздал друзьям). А играть на клавиатуре в игры денди, ну это как-то совсем не айс.
Встала проблема, как подключить джойстик от Денди к ноутбуку.
Немного погуглив, я понял, что в основном это решается подключением через LPT порт и использованием готовых драйверов, но у меня ноутбук, и мне это не подходит. Тут я вспомнил, что у меня есть плата Arduino Uno и я решил, что пойду своим путем и буду использовать именно ее.
0. Определение распиновки на джойстике
Первая проблема оказалась в том, что коннекторы у джойстиков внутренние, а все описания распиновок найденные в интернете, были для внешних коннекторов.
Найти спецификацию на мою noname денди мне не удалось, поэтому я стал рассматривать внутреннюю плату, чтобы найти там питание и землю, и понять какие из пяти контактов на джойстике с ними соединяются. С этим я справился быстро. Оказалось это два крайних контакта.
К Ардуино, я подсоединил все напрямую, питание — питание 5В, земля — земля, остальные три контакта подсоединил к 2, 3 и 4 цифровому пину.
1. Описание протокола работы джойстика
Оказалось не так-то просто найти эту информацию. Лишь несколько сообщений на паре форумов. Лично мне помогла эта запись: code.google.com/p/avrtoys/wiki/joystick, а именно этот код: avrtoys.googlecode.com/svn/trunk/joystick/main.c. А вот ссылка на упомянутую в этой записи заметку в журнале Радио: ftp.radio.ru/pub/arhiv/1996/06-96/46-6-1996.gif. Из всего этого я сделал вывод, что нужно подать сигнал на Latch, а потом 8 раз подавать сигнал на Clock и каждый раз считывать значение с Data, в котором после каждого Clock-а будет содержаться информация о следующей кнопке джойстика.
2. Настройка среды
Если у вас уже все настроено или вас устраивает Arduino IDE, можете перейти к 3 пункту.
Не знаю, как вас, но лично меня официальная Arduino IDE ужасно угнетает, поэтому коротко расскажу, как использовать свою любимую IDE.
Первым делом я скачал исходники Arduino IDE и нашел там код отвечающий за компиляцию и аплоад прошивки. Потратив немного времени я выделил все команды, которые там используются:
#!/bin/bash
avr-gcc -c -g -Os -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000UL -I /usr/share/arduino/hardware/arduino/cores/arduino -I /usr/share/arduino/hardware/arduino/variants/standard $filename.cpp -o $filename.o
avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p -o $filename.elf $filename.o libArduino.a -lm
avr-objcopy -O ihex -R .eeprom $filename.elf $filename.elf.hex
avrdude -V -p m328p -b 115200 -c arduino -P /dev/ttyACM0 -U flash:w:$filename.elf.hex
В приведенной выше команде, при линковке, используется библиотека libArduino.a, ее можно получить скомпилировав все *.c и *.cpp файлы из папки с хэдэрами и собрав все объектники в один архив. А можно просто запустить Arduino IDE, скомпилировать любой проект и скопировать файл /tmp/build*.tmp/core.a. Это будет абсолютно эквивалентно.
Все эти команды актуальны для Arduino Uno, для других ардуин следует изменить некоторые параметры.
Теперь используя эти команды можно легко настроить свою IDE, на автоматическую компиляцию и загрузку прошивки.
3. Написание кода прошивки
Здесь нужно было лишь определить какие из 2, 3 и 4 пина отвечают за Latch, Clock и Data. Это я решил методом проб и ошибок. Предполагаем, что 4 это Data, и делаем Serial.println(digitalRead(4)), если при нажатии кнопок есть какая-то реакция, значит это оно. Остальные 2 контакта определились, когда уже была написана прошивка, если все работает значит угадали, нет — меняем местами.
Код, повторюсь, писался на основе avrtoys.googlecode.com/svn/trunk/joystick/main.c:
#include <Arduino.h>
const int data = 2;
const int latch = 3;
const int clock = 4;
const int TICK = 2;
void init_joystick(int data, int latch, int clock)
{
pinMode(data, INPUT);
pinMode(clock, OUTPUT);
pinMode(latch, OUTPUT);
digitalWrite(clock, HIGH);
}
int get_keys_state_joystick(int data, int latch, int clock)
{
digitalWrite(latch, HIGH);
delayMicroseconds(TICK);
digitalWrite(latch, LOW);
int keys_state = 0;
for (int i = 0; i < 8; ++i) {
delayMicroseconds(TICK);
digitalWrite(clock, LOW);
keys_state <<= 1;
keys_state += digitalRead(data);
delayMicroseconds(TICK);
digitalWrite(clock, HIGH);
}
return keys_state;
}
void setup()
{
init_joystick(data, latch, clock);
Serial.begin(57600);
}
void loop()
{
Serial.write(get_keys_state_joystick(data, latch, clock));
}
В результате я получил 1 байт содержащий в себе информацию о всех 8-ми кнопках джойстика, о каждой в соответствующем бите. У меня получилось следующее расположение: A, B, Select, Start, Up, Down, Left, Right. После этот байт отправляется на компьютер, где принимается и обрабатывается моим «драйвером».
Несложно заметить, что можно легко подключить второй джойстик.
4. Написание «драйвера»
Здесь нужно было принять этот самый байт от Arduino и эмулировать нажатия кнопок клавиатуры. Да-да, нажатия на джойстике будут обрабатываться, как нажатия на клавиатуре, хорошо это или плохо.
В качестве языка программирования я выбрал Python. Простой и эффективный, полагаю, он отлично подошел для этой задачи.
Единственная проблема, которая здесь возникла это дребезг контактов джойстика. Я ее решил путем введения временного интервала, наступающего после смены состояния кнопки, в течение которого состояния не изменяется. Хватило 0.05 с.
Для эмуляции нажатия кнопок клавиатуры я использовал утилиту xte, идущую в комплекте Xautomation. Она очень проста в использовании, вот пример: xte 'keydown Left'
. За дополнительной инфорацией смотрите man xte.
Для выхода из драйвера я использовал состояние, когда нажата вся крестовина целиком. Этому состоянию соответствует число 0xf0.
#!/usr/bin/python
import serial
import os
import time
def bool_to_updown(val):
if val:
return 'up'
else:
return 'down'
exit_keys = 0xf0
delta_time = 0.05
keys = [['Right', False, 0.0],
['Left', False, 0.0],
['Down', False, 0.0],
['Up', False, 0.0],
['s', False, 0.0], # START
['a', False, 0.0], # SELECT
['x', False, 0.0], # B
['z', False, 0.0]] # A
ser = serial.Serial('/dev/ttyACM0', 57600)
keys_state = 0
while keys_state != exit_keys:
keys_state = ord(ser.read())
for i in range(8):
if not bool(keys_state & (1 << i)) != keys[i][1] and time.time() - keys[i][2] > delta_time:
os.system("xte 'key{0} {1}'".format(bool_to_updown(keys[i][1]), keys[i][0]))
keys[i][1] = not keys[i][1]
keys[i][2] = time.time()
for i in range(8):
os.system("xte 'keyup {0}'".format(keys[i][0]))
ser.close()
print('Goodbye!')
5. Ура! Играем!
Все! Осталось лишь настроить эмулятор денди, я выбрал FCEUX.
Указываем в настройках те клавиши, которые указаны в драйвере ииии вспоминаем детство!
Заключение
На самом деле есть и другие, возможно, более эффективные способы использования Ардуино в данной ситуации. Например, если у вас Arduino Leonardo, то можно использовать объект Keyboard для прямой посылки команд клавиатуры на компьютер. Так же можно, как я понял, перепрошить Ардуино так, чтобы она отображалась, как джойстик/клавиатура/мышь и так же напрямую посылать команды. Вот туториал: http://mitchtech.net/arduino-usb-hid-keyboard/. Впрочем, мой способ меня полностью устраивает и результатом я более чем доволен.
Как ни крути, но Ардуино это замечательная платформа для подобных экспериментов.
Теперь осталось только собрать все это в виде отдельного устройства.
Ну и видео напоследок:
Автор: Elsedar
Привет, у меня такой же нонейм, дай плиз распиновку разъема. ( на мыло если не трудно)