/*--------------------------------------------------------
メガドライブパッド→USB変換
注意:
このスケッチは、Arduino Leonardo/Pro Micro(ATmega 32U4)専用です。
パッド側のピン読み出しを最速で行うためポート(PinD)アクセスを利用しているため、
他マイコンと互換性がありません。
[参考資料]
このスケッチ作成には以下のURLを参考にしています。
JoystickMD
https://github.com/NibblesLab/JoystickMD
セガ メガドライブ 6ボタンパッドの読み取り方
http://applause.elfmimi.jp/md6bpad.html
メガドライブミニ を ジョイスティック で遊ぶ ジョイスティックアダプターの作成
https://tounosumura302.netlify.app/posts/201910191643/
Arduino Joystick Library
https://github.com/MHeironimus/ArduinoJoystickLibrary
2020.4.20 BLUE
--------------------------------------------------------*/
#include <Joystick.h>
// ライブラリ定義
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK,
10, 0, // Button Count, Hat Switch Count
true, true, false, // X, Y, and Z Axis
false, false, false, // Rx, but no Ry or Rz
false, false, // No rudder or throttle
false, false, false); // No accelerator, brake, or steering
#define AXIS_MIN 0 // アナログ方向キー最小値
#define AXIS_MAX 511 // アナログ方向キー最大値
#define AXIS_CENTER 255 // アナログ方向キーセンター位置
// PADキー番号定義
#define PAD_X 0 // Xボタン
#define PAD_A 1 // Aボタン
#define PAD_B 2 // Bボタン
#define PAD_Y 3 // Yボタン
#define PAD_C 4 // Cボタン
#define PAD_Z 5 // Zボタン
#define PAD_MODE 8 // MODEボタン
#define PAD_START 9 // STARTボタン
// メガドライブパッド用ポート番号
#define MD_D0 3 // PD0/Pin3
#define MD_D1 2 // PD1/Pin2
#define MD_D2 1 // PD2/Pin1(RX1)
#define MD_D3 0 // PD3/Pin0(TX1)
#define MD_D4 4 // PD4/Pin4
#define MD_D5 6 // PD7/Pin6
#define SELECT 5 // Select信号ポート番号
#define DELAY_TIME 10 // 遅延時間(10μs/10ms)
//メガドライブ3Bパッド定義
enum{
PAD_UP = 0 , // UP 方向キー
PAD_DOWN , // DOWN 方向キー
PAD_LEFT , // LEFT 方向キー
PAD_RIGHT , // RIGHT 方向キー
PAD_AB , // A/B Buton
PAD_DUMMY1 , // dummy
PAD_DUMMY2 , // dummy
PAD_CSTART , // C/Start Buton
PAD_MAX
};
//メガドライブ6Bパッド定義
enum{
PAD6B_Z = 0 , // Z Buton
PAD6B_Y , // Y Buton
PAD6B_X , // X Buton
PAD6B_MODE , // Mode Buton
PAD6B_MAX
};
//メガドライブSelect信号定義
enum{
SelLo = 0 , // Select = Low
SelHi , // Select = High
SelMAX
};
const int pad_map[SelMAX][PAD_MAX] =
{
// D0 D1 D2 D3 D4 - - D7
{ PAD_Z ,PAD_Y ,PAD_X ,PAD_MODE ,PAD_A ,-1,-1 ,PAD_START},//Lo(3B/6B)
{ PAD_UP ,PAD_DOWN,PAD_LEFT,PAD_RIGHT,PAD_B ,-1,-1 ,PAD_C } //Hi(3B)
};
//PAD状態を前回と最新状態で比較して、変化があった場合にコードを送信する
bool cur_3B_sts[SelMAX][PAD_MAX]; //PAD状態(3B/6B共通)
bool cur_6B_sts[SelMAX][PAD6B_MAX]; //PAD状態(6B用)
int cur_3B_PinD[SelMAX]; // 3B/6B共通PIND状態(LOW/HIGH)
int cur_6B_PinD[SelMAX]; // 6B用PIND状態(LOW/HIGH)
bool check_button(int sel ,int pad_num);
bool check_button_6b(int pad_num);
//////////////////////////////////////
// パッド制御処理(3B/6B共通)
// sel = LOW/HIGH
// pad_num = 3B PAD定義
//////////////////////////////////////
bool check_button(int sel ,int pad_num)
{
int pad;
int state;
// ボタン状態取得
state = ((cur_3B_PinD[sel]>>pad_num)&0x01) ? HIGH : LOW;
// ボタン状態変化あり
if (cur_3B_sts[sel][pad_num] != state)
{
// PAD制御
pad = pad_map[sel][pad_num];
// ボタン押下
if (state==LOW)
{
// 方向キーである
if ((sel==HIGH)&&(pad_num<=PAD_RIGHT))
{
switch(pad)
{
case PAD_UP:
// Y軸を最小
Joystick.setYAxis(AXIS_MIN);
break;
case PAD_DOWN:
// Y軸を最大
Joystick.setYAxis(AXIS_MAX);
break;
case PAD_LEFT:
// X軸を最小
Joystick.setXAxis(AXIS_MIN);
break;
case PAD_RIGHT:
// X軸を最大
Joystick.setXAxis(AXIS_MAX);
break;
default:
break;
}
}
else
{
// ボタン押下セット
Joystick.pressButton(pad);
}
}
// ボタン離した
else
{
// 方向キーである
if ((sel==HIGH)&&(pad_num<=PAD_RIGHT))
{
switch(pad)
{
case PAD_UP:
case PAD_DOWN:
// Y軸をセンターに戻す
Joystick.setYAxis(AXIS_CENTER);
break;
case PAD_LEFT:
case PAD_RIGHT:
// X軸をセンターに戻す
Joystick.setXAxis(AXIS_CENTER);
break;
default:
break;
}
}
else
{
// ボタン離す
Joystick.releaseButton(pad);
}
}
// カレントのPAD状態を更新する
cur_3B_sts[sel][pad_num] = state;
return true;
}
return false;
}
//////////////////////////////////////
// 6Bパッド制御処理
// pad_num = 6B PAD定義
//////////////////////////////////////
bool check_button_6b(int pad_num)
{
int pad;
int state;
// ボタン状態取得
state = ((cur_6B_PinD[SelHi]>>pad_num)&0x01) ? HIGH : LOW;
// ボタン状態変化あり
if (cur_6B_sts[SelHi][pad_num] != state)
{
// PAD制御
pad = pad_map[SelLo][pad_num];
// ボタン押下
if (state==LOW)
Joystick.pressButton(pad);
// ボタン離した
else
Joystick.releaseButton(pad);
// カレントのPAD状態を更新する
cur_6B_sts[SelHi][pad_num] = state;
return true;
}
return false;
}
//////////////////////////////////////
// 初期化
//////////////////////////////////////
void setup() {
// put your setup code here, to run once:
// メガドライブパッド読み取り用ポート初期化(INPUT_PULLUP)
pinMode(MD_D0, INPUT_PULLUP);
pinMode(MD_D1, INPUT_PULLUP);
pinMode(MD_D2, INPUT_PULLUP);
pinMode(MD_D3, INPUT_PULLUP);
pinMode(MD_D4, INPUT_PULLUP);
pinMode(MD_D5, INPUT_PULLUP);
// Select信号出力用ポート初期化(OUTPUT)
pinMode(SELECT, OUTPUT);
// Joystick Library初期化
Joystick.begin(false);
// PAD方向キー初期化
Joystick.setXAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setYAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setXAxis(AXIS_CENTER);
Joystick.setYAxis(AXIS_CENTER);
}
//////////////////////////////////////
// メインループ
//////////////////////////////////////
void loop() {
// put your main code here, to run repeatedly:
int cur_state;
int sel = 0;
bool change=false; //JoyStick状態変化なし(通信制御しない)
bool Pad6B = LOW; //6Bパッド未検出
/////////////////////////////////
// MEGA DRIVE パッド状態読み出し
/////////////////////////////////
// PAD情報取得(7シーケンス実施)
for( int i = 0 ; i < 7 ; i++ )
{
// Select信号出力
digitalWrite(SELECT, (sel==0) ? LOW : HIGH );
delayMicroseconds(DELAY_TIME);
// パッド状態読み出し
cur_state = PIND;
// 6Bパッド検出?
if ( !sel && !(cur_state & 0xf) )
{
// 6Bパッド検出セット
Pad6B = HIGH;
}
// 6Bパッド未検出
if(Pad6B == LOW)
{
// パッド情報(3B/6B共通)に格納する
cur_3B_PinD[sel] = cur_state;
}
else
{
// 6Bパッド情報を格納する
cur_6B_PinD[sel] = cur_state;
}
// Select状態(Low/High)を反転する
sel ^= 0x01;
}
////////////////////////////////
// Joystick制御
////////////////////////////////
// Startボタン制御
change |= check_button(SelLo,PAD_CSTART);
// Aボタン制御
change |= check_button(SelLo,PAD_AB);
// Bボタン制御
change |= check_button(SelHi,PAD_AB);
// Cボタン制御
change |= check_button(SelHi,PAD_CSTART);
// 6Bパッド検出
if(Pad6B)
{
// MODEボタン制御
change |= check_button_6b(PAD6B_MODE);
// Xボタン制御
change |= check_button_6b(PAD6B_X);
// Yボタン制御
change |= check_button_6b(PAD6B_Y);
// Zボタン制御
change |= check_button_6b(PAD6B_Z);
}
// UP 方向キー制御
change |= check_button(SelHi,PAD_UP);
// DOWN 方向キー制御
change |= check_button(SelHi,PAD_DOWN);
// LEFT 方向キー制御
change |= check_button(SelHi,PAD_LEFT);
// RIGHT 方向キー制御
change |= check_button(SelHi,PAD_RIGHT);
// Joystick情報送信
if(change)
{
Joystick.sendState();
}
// 次のシーケンスまで待つ(2ms以上)
delay(DELAY_TIME);
}