Предисловие
Недавно обнаружил у себя два давно забытых старых геймпада от 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 |
Библиотека
Для удобства я написал простенькую библиотеку для работы с геймпадом.
#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
#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 <TimerOne.h>
#include <SMDjoystick.h>
SMDjoystick j(2,3,4,5,6,7,8);
JoyState_t s;
uint16_t data;
void setup()
{
Timer1.initialize(10000);
Timer1.attachInterrupt( timerIsr );
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()
{
s.buttons=data;
Joystick.setState(&s);
}
void timerIsr()
{
data=j.read();
}
Все, теперь можно использовать геймпад сеги как любой USB геймпад.
Источники:
www.hardwarebook.info/Mega_Drive_Joystick
applause.elfmimi.jp/md6bpad-e.html
Автор: AsGreyWolf