Используем джойстик от Sega Genesis в проектах на Arduino
11 января 2018
В детстве я много времени проводил за игровой приставкой Sega Mega Drive, также известной, как Sega Genesis. Одна из интересных особенностей этой приставки заключается в том, что джойстики подключаются к ней через обычный DE9-порт. Теоретически, сигнал от джойстика должно быть достаточно просто декодировать. Джойстик от ретро-приставки видится мне интересным примитивом для использования в будущих DIY проектах, в связи с чем я решил попробовать подружить его с Arduino.
Достаточно толковая информация о декодировании сигнала от джойстика была найдена по следующим ссылкам:
Сам джойстик, если у вас его нет, можно найти на Avito. Вот как он выглядит:
На фотографии показан оригинальный джойстик Sega SJ-6000, произведенный в Японии. Учтите, что бывают и клоны. Они тоже работают, но выполненны менее качественно.
Следующая иллюстрация объясняет, какой пин DE9-порта каким образом используется джойстиком:
Пины 5 и 8 используются для питания. Пин 7 называется SEL и используется приставкой для выбора режима считывания. Например, если на SEL подано высокое напряжение, пин 6 отражает состояние кнопки B джойстика. Если же на SEL подано низкое напряжение, тот же пин отражает состояние кнопки A. Таким образом, переключая напряжение на SEL, можно считать состояние кнопок вверх, вниз, влево и вправо, кнопок A, B, С, а также кнопки Start.
Старые джойстики имели только эти кнопки. Однако в новых джойстиках, вроде того, что изображен выше, были добавлены еще три кнопки X, Y, Z, а также кнопка Mode, располагаемая под указательным пальцем правой руки. Считать эти кнопки можно через пины 1-4, если послать на SEL серию из трех низких и высоких сигналов, каждый из которых имеет продолжительность около 20 микросекунд.
Вооружившись этими знаниями, несложно написать прошивку для Arduino:
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);
}
Внешний вид получившегося прототипа:
Плату с 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.
Метки: AVR, Винтаж, Электроника.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.