Предисловие
Недавно обнаружил у себя два давно забытых старых геймпада от SMD, сама приставка была давно утеряна и геймпады все это время пылились без дела, но они рабочие и выкинуть «живые» устройства как-то рука не поднимается. Решил подключить их к компьютеру, хоть какое-то им применение, а в качестве переходника используем Arduino Leonardo.
Протокол геймпада SMD

В самом простом случае получение данных выглядит так:
| PIN | Направление | Select=LOW | Select=HIGH |
|---|---|---|---|
| 1 | IN | UP | UP |
| 2 | IN | DOWN | DOWN |
| 3 | IN | HIGH | LEFT |
| 4 | IN | HIGH | RIGHT |
| 5 | OUT | 5v | 5v |
| 6 | IN | A | B |
| 7 | OUT | Select | Select |
| 8 | OUT | GND | GND |
| 9 | IN | Start | C |
Показатели меняются в зависимости от изменения Select пина.
Как видите здесь не хватает 4 клавиш.
Их получение немного усложнено: необходимо сделать 2 изменения Select LOW->HIGH с интервалом 1.1-1.2 миллисекунд, после чего пины 1,2,3,4 становятся LOW, а при еще одном изменении Select LOW->HIGH на них появляются значения Z,Y,X,Mode соответственно.
| Select | PIN 1 | PIN 2 | PIN 3 | PIN 4 |
|---|---|---|---|---|
| LOW | UP | DOWN | LOW | LOW |
| HIGH | UP | DOWN | LEFT | RIGHT |
| LOW | UP | DOWN | LOW | LOW |
| HIGH | UP | DOWN | LEFT | RIGHT |
| LOW | LOW | LOW | LOW | LOW |
| HIGH | Z | Y | X | MODE |
| LOW | HIGH | HIGH | HIGH | HIGH |
| HIGH | UP | DOWN | LEFT | RIGHT |
| LOW | UP | DOWN | LOW | LOW |
Библиотека
Для удобства я написал простенькую библиотеку для работы с геймпадом.
SMDjoystick.h
#ifndef _SMDJOYSTICK_H_ #define _SMDJOYSTICK_H_ #include <Arduino.h> /** SegaMegaDrive gamepad arduino library by AsGreyWolf **/ enum{ SMD_A=0, SMD_B, SMD_C, SMD_EMPTY_1, SMD_X, SMD_Y, SMD_Z, SMD_MODE, SMD_EMPTY_2, SMD_START, SMD_EMPTY_3, SMD_EMPTY_4, SMD_UP, SMD_DOWN, SMD_LEFT, SMD_RIGHT, SMD_MAX_KEYS }; class SMDjoystick{ public: SMDjoystick(int upPin,int downPin,int leftPin,int rightPin,int aPin,int selectPin,int startPin); bool read(int key); uint16_t read(); private: int up; int down; int left; int right; int a; int select; int start; }; #endif
SMDjoystick.cpp
#include "SMDjoystick.h" SMDjoystick::SMDjoystick(int upPin,int downPin,int leftPin,int rightPin,int aPin,int selectPin,int startPin):up(upPin),down(downPin),left(leftPin),right(rightPin),a(aPin),select(selectPin),start(startPin){ pinMode(up,INPUT); pinMode(down,INPUT); pinMode(left,INPUT); pinMode(right,INPUT); pinMode(a,INPUT); pinMode(start,INPUT); pinMode(select,OUTPUT); } uint16_t SMDjoystick::read(){ uint16_t data=0; digitalWrite(select,LOW); data+=!digitalRead(a)<<SMD_A; data+=!digitalRead(start)<<SMD_START; data+=!digitalRead(up)<<SMD_UP; data+=!digitalRead(down)<<SMD_DOWN; delayMicroseconds(1200); digitalWrite(select,HIGH); data+=!digitalRead(left)<<SMD_LEFT; data+=!digitalRead(right)<<SMD_RIGHT; data+=!digitalRead(a)<<SMD_B; data+=!digitalRead(start)<<SMD_C; digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); digitalWrite(select,HIGH); data+=!digitalRead(up)<<SMD_Z; data+=!digitalRead(down)<<SMD_Y; data+=!digitalRead(left)<<SMD_X; data+=!digitalRead(right)<<SMD_MODE; digitalWrite(select,LOW); digitalWrite(select,HIGH); return data; } bool SMDjoystick::read(int key){ bool val=false; switch(key){ case SMD_UP: val=!digitalRead(up); break; case SMD_DOWN: val=!digitalRead(down); break; case SMD_LEFT: digitalWrite(select,HIGH); val=!digitalRead(left); break; case SMD_RIGHT: digitalWrite(select,HIGH); val=!digitalRead(right); break; case SMD_START: digitalWrite(select,LOW); val=!digitalRead(start); break; case SMD_A: digitalWrite(select,LOW); val=!digitalRead(a); break; case SMD_B: digitalWrite(select,HIGH); val=!digitalRead(a); break; case SMD_C: digitalWrite(select,HIGH); val=!digitalRead(start); break; case SMD_X: digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); digitalWrite(select,HIGH); val=!digitalRead(left); digitalWrite(select,LOW); digitalWrite(select,HIGH); break; case SMD_Y: digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); digitalWrite(select,HIGH); val=!digitalRead(down); digitalWrite(select,LOW); digitalWrite(select,HIGH); break; case SMD_Z: digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); digitalWrite(select,HIGH); val=!digitalRead(up); digitalWrite(select,LOW); digitalWrite(select,HIGH); break; case SMD_MODE: digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); delayMicroseconds(1200); digitalWrite(select,HIGH); digitalWrite(select,LOW); digitalWrite(select,HIGH); val=!digitalRead(right); digitalWrite(select,LOW); digitalWrite(select,HIGH); break; } return val; }
SMDjoystick.read(SMD_id) позволяет считать значение 1 кнопки.
SMDjoystick.read() возвращает uint16_t со значениями всех кнопок, пригодную для JoyState.
Пример
Подключаем к леонардо:
| Gamepad pin | Arduino pin |
|---|---|
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 4 | 5 |
| 5 | 5v |
| 6 | 6 |
| 7 | 7 |
| 8 | GND |
| 9 | 8 |
Пример эмуляции нажатия клавиш клавиатуры
#include <SMDjoystick.h> SMDjoystick j(2,3,4,5,6,7,8); uint16_t data=0; void setup() { Keyboard.begin(); } uint16_t lastdata; char keys[]={ 'f', 'r', ' ', 0, KEY_LEFT_SHIFT , KEY_TAB , KEY_BACKSPACE, KEY_ESC, 0, KEY_RETURN, 0, 0, 'w', 's', 'a', 'd' }; void loop() { data=j.read(); for(int i=0;i<SMD_MAX_KEYS;i++){ if(keys[i]==0) continue; bool c=(data>>i)&1; if(c!=((lastdata>>i)&1)){ if(c)Keyboard.press(keys[i]); else Keyboard.release(keys[i]); } } lastdata=data; delay(10); }
Пример эмуляции нажатия клавиш USB джойстика
#include <SMDjoystick.h> SMDjoystick j(2,3,4,5,6,7,8); JoyState_t s; uint16_t data; void setup() { s.xAxis=128; s.yAxis=128; s.zAxis=128; s.xRotAxis=128; s.yRotAxis=128; s.zRotAxis=128; s.throttle=128; s.rudder=128; s.hatSw1=0; s.hatSw2=0; } void loop() { data=j.read(); s.buttons=data; Joystick.setState(&s); }
Все, теперь можно использовать геймпад сеги как любой USB геймпад.
Репозиторий GitHub
Источники:
www.hardwarebook.info/Mega_Drive_Joystick
applause.elfmimi.jp/md6bpad-e.html
UPD:
Добавлен пример эмуляции нажатия клавиш клавиатуры.
Убран таймер
Добавлен репозиторий
