Подключаем джойстики от Dendy к PC через Arduino

в 17:10, , рубрики: arduino, dendy, diy или сделай сам, joystick, Nes, Программинг микроконтроллеров, метки: , , ,

Думаю многие из вас помнят те славные времена, когда мы часами рубились в танчики, черепашки ниндзя, батлтодс энд даблдрагон и еще пару десятков других суперских игр на Денди. Это было шикарное время! Большинство игр были чертовски сложные, но даже несмотря на это, неудача за неудачей, мы, все равно, шли к своей цели и начинали игру заного, чтобы уж сегодня-то пройти ее до конца.

Поностальгировав всласть, я решил, что очень хочу поиграть в Dendy и поиграть хочу на ноутбуке, но не на клавиатуре, а на старом добром джойстике.

Ниже я опишу, с какими проблемами я столкнулся и как их решил.

Немного поясню, почему именно так мне захотелось поиграть. Приставка у меня сохранилась и она отлично работает, но осталась всего пара картриджей(остальные раздал друзьям). А играть на клавиатуре в игры денди, ну это как-то совсем не айс.

Встала проблема, как подключить джойстик от Денди к ноутбуку.

Немного погуглив, я понял, что в основном это решается подключением через LPT порт и использованием готовых драйверов, но у меня ноутбук, и мне это не подходит. Тут я вспомнил, что у меня есть плата Arduino Uno и я решил, что пойду своим путем и буду использовать именно ее.

0. Определение распиновки на джойстике

Первая проблема оказалась в том, что коннекторы у джойстиков внутренние, а все описания распиновок найденные в интернете, были для внешних коннекторов.
Подключаем джойстики от Dendy к PC через Arduino

Найти спецификацию на мою noname денди мне не удалось, поэтому я стал рассматривать внутреннюю плату, чтобы найти там питание и землю, и понять какие из пяти контактов на джойстике с ними соединяются. С этим я справился быстро. Оказалось это два крайних контакта.
Подключаем джойстики от Dendy к PC через Arduino

К Ардуино, я подсоединил все напрямую, питание — питание 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

  1. Дмитрий:

    Привет, у меня такой же нонейм, дай плиз распиновку разъема. ( на мыло если не трудно)

* - обязательные к заполнению поля