Pull to refresh

Arduino Leonardo как адаптер SegaMegaDrive Gamepad->USB

Reading time4 min
Views32K

Предисловие


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

Протокол геймпада SMD


image
В самом простом случае получение данных выглядит так:

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:
Добавлен пример эмуляции нажатия клавиш клавиатуры.
Убран таймер
Добавлен репозиторий
Tags:
Hubs:
Total votes 24: ↑21 and ↓3+18
Comments0

Articles