arduino-sega-controller/

В детстве я много времени проводил за игровой приставкой Sega Mega Drive , также известной, как Sega Genesis. Одна из интересных особенностей этой приставки заключается в том, что джойстики подключаются к ней через обычный DE9-порт. Теоретически, сигнал от джойстика должно быть достаточно просто декодировать. Джойстик от ретро-приставки видится мне интересным примитивом для использования в будущих DIY проектах, в связи с чем я решил попробовать подружить его с Arduino.

Достаточно толковая информация о декодировании сигнала от джойстика была найдена по следующим ссылкам:

Сам джойстик, если у вас его нет, можно найти на Avito. Вот как он выглядит:

Джойстик от Sega Genesis (Mega Drive)

На фотографии показан оригинальный джойстик Sega SJ-6000, произведенный в Японии. Учтите, что бывают и клоны. Они тоже работают, но выполненны менее качественно.

Следующая иллюстрация объясняет, какой пин DE9-порта каким образом используется джойстиком:

Распиновка джойстика от Sega Genesis (Mega Drive)

Пины 5 и 8 используются для питания. Пин 7 называется SEL и используется приставкой для выбора режима считывания. Например, если на SEL подано высокое напряжение, пин 6 отражает состояние кнопки B джойстика. Если же на SEL подано низкое напряжение, тот же пин отражает состояние кнопки A. Таким образом, переключая напряжение на SEL, можно считать состояние кнопок вверх, вниз, влево и вправо, кнопок A, B, С, а также кнопки Start.

Старые джойстики имели только эти кнопки. Однако в новых джойстиках, вроде того, что изображен выше, были добавлены еще три кнопки X, Y, Z, а также кнопка Mode, располагаемая под указательным пальцем правой руки. Считать эти кнопки можно через пины 1-4, если послать на SEL серию из трех низких и высоких сигналов, каждый из которых имеет продолжительность около 20 микросекунд.

Вооружившись этими знаниями, несложно написать прошивку для Arduino:

#include <OLED_I2C.h>

OLED oled ( SDA, SCL ) ;
extern uint8_t SmallFont [ ] ;

#define UP_OR_Z 1
#define DOWN_OR_Y 2
#define LEFT_OR_X 3
#define RIGHT_OR_MODE 4
#define B_OR_A 6
#define SEL 7
#define C_OR_START 9

bool sega_up = false ;
bool sega_down = false ;
bool sega_left = false ;
bool sega_right = false ;

bool sega_start = false ;
bool sega_mode = false ;

bool sega_a = false ;
bool sega_b = false ;
bool sega_c = false ;

bool sega_x = false ;
bool sega_y = false ;
bool sega_z = false ;

void segaRead ( ) {
digitalWrite ( SEL, HIGH ) ;
delayMicroseconds ( 20 ) ;

sega_up = ( digitalRead ( UP_OR_Z ) == LOW ) ;
sega_left = ( digitalRead ( LEFT_OR_X ) == LOW ) ;
sega_right = ( digitalRead ( RIGHT_OR_MODE ) == LOW ) ;
sega_down = ( digitalRead ( DOWN_OR_Y ) == LOW ) ;

sega_c = ( digitalRead ( C_OR_START ) == LOW ) ;
sega_b = ( digitalRead ( B_OR_A ) == LOW ) ;

digitalWrite ( SEL, LOW ) ;
delayMicroseconds ( 20 ) ;

sega_a = ( digitalRead ( B_OR_A ) == LOW ) ;
sega_start = ( digitalRead ( C_OR_START ) == LOW ) ;

digitalWrite ( SEL, HIGH ) ;
delayMicroseconds ( 20 ) ;
digitalWrite ( SEL, LOW ) ;
delayMicroseconds ( 20 ) ;
digitalWrite ( SEL, HIGH ) ;
delayMicroseconds ( 20 ) ;
digitalWrite ( SEL, LOW ) ;
delayMicroseconds ( 20 ) ;
digitalWrite ( SEL, HIGH ) ;
delayMicroseconds ( 20 ) ;

sega_x = ( digitalRead ( LEFT_OR_X ) == LOW ) ;
sega_y = ( digitalRead ( DOWN_OR_Y ) == LOW ) ;
sega_z = ( digitalRead ( UP_OR_Z ) == LOW ) ;
sega_mode = ( digitalRead ( RIGHT_OR_MODE ) == LOW ) ;

digitalWrite ( SEL, LOW ) ;
delayMicroseconds ( 20 ) ;

digitalWrite ( SEL, HIGH ) ;
delayMicroseconds ( 20 ) ;
}

void setup ( ) {
oled. begin ( ) ;
oled. setFont ( SmallFont ) ;

pinMode ( SEL, OUTPUT ) ;
digitalWrite ( SEL, HIGH ) ;

pinMode ( UP_OR_Z, INPUT ) ;
pinMode ( DOWN_OR_Y, INPUT ) ;
pinMode ( LEFT_OR_X, INPUT ) ;
pinMode ( RIGHT_OR_MODE, INPUT ) ;
pinMode ( B_OR_A, INPUT ) ;
pinMode ( C_OR_START, INPUT ) ;
}

void loop ( ) {
char temp [ 16 ] ;

segaRead ( ) ;
oled. clrScr ( ) ;

sprintf ( temp, » %s   %s  %s%s%s» ,
sega_up ? «U» : «u» ,
sega_mode ? «M» : «m» ,
sega_x ? «X» : «x» ,
sega_y ? «Y» : «y» ,
sega_z ? «Z» : «z» ) ;
oled. print ( temp, CENTER, 8 * 4 ) ;

sprintf ( temp, «%s%s%s  %s  %s%s%s» ,
sega_left ? «L» : «l» ,
sega_down ? «D» : «d» ,
sega_right ? «R» : «r» ,
sega_start ? «S» : «s» ,
sega_a ? «A» : «a» ,
sega_b ? «B» : «b» ,
sega_c ? «C» : «c» ) ;
oled. print ( temp, CENTER, 8 * 5 ) ;

oled. update ( ) ;
delay ( 1 ) ;
}

Внешний вид получившегося прототипа:

Использование джойстика от Sega в Arduino

Плату с DE9-портом вы могли узнать по заметке Травим плату перекисью водорода с лимонной кислотой . Состояние кнопок джойстика отображается на OLED-экранчике на базе чипа SSD1306 с диагональю 0.96 дюйма (24.5 мм), имеющем I2C-интерфейс. На eBay такой экранчик можно приобрести менее, чем за 3$.

Экранчик питается от 5 В. К Arduino он подключается так: пин SDA подключаем к A4, а пин SCL — к A5. Это если мы говорим про Arduino Uno, у прочих же Ардуин I2C пины могут быть другими. Работа с экранчиком осуществляется через библиотеку OLED_I2C . Написал ее тот же человек, что является автором библиотеки для экранчиков от Nokia 5110 , поэтому эти библиотеки имеют похожий интерфейс. При небольших размерах и используя всего лишь два пина микроконтроллера, экранчик позволяет отобразить шрифтом SmallFont 7 строк, каждая из которых содержит до 21 символа. Также в библиотеке есть шрифт TinyFont, который позволяет вывести еще больше информации, хотя ее будет и труднее прочитать.

Полную версию исходников к этой статье вы найдете на GitHub . Как видите, работать с джойстиком от приставки 1988 года выпуска оказалось не так уж и сложно. Если вам понравился этот проект, могу предложить вам улучшить его, взяв микроконтроллер ATmega32U4 и сделав с его помощью адаптер джойстика к современным компьютерам. Кое-какие подробности на эту тему можно найти в заметке Декодируем сигнал с OOK-модуляцией и паяем кликер .

Дополнение: См также заметку Микроконтроллеры STM32: работа с OLED-экранчиками на базе SSD1306 по I2C и SPI .

EnglishRussianUkrainian