/*--------------------------------------------------------------------------
メガドライブパッド+プレイステーションパッド+PCEパッド+SFCパッド
→ USB変換(+メガドライブミニ/プレイステーションクラッシック対応)
メガドライブ用、プレイステーション用(デジタルパッド)、PCエンジン用パッド
および スーパーファミコン用パッドを、PCおよびメガドライブミニ等で使えるように
USBデバイスに変換します。
注意:
このスケッチは、Arduino Leonardo/Pro Micro(ATmega 32U4)専用です。
パッド側のピン読み出しを最速で行うためポート(PinD、PinF)を直接アクセスしているため、
他マイコンとピン位置の互換性がありません。
メガドライブ用パッドと他社用(プレイステーション、PCE、SFC)を同時に接続ができますが、
全て同じ1コン側として認識されます。いずれかのパッドを2コン側として利用はできませんので
ご注意ください。(同時に操作すると動作がバッティングします)
[参考資料]
このスケッチ作成には以下の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
Arduino PSX Library
https://playground.arduino.cc/Main/PSXLibrary/
SONY PLAYSTATIONR CONTROLLER INFORMATION
https://gamesx.com/controldata/psxcont/psxcont.htm
プレイステーション デュアルショック で痺れちゃう
https://applause.elfmimi.jp/dualshock.shtml
NEC PC Engine - Tech Wiki
https://console5.com/wiki/NEC_PC_Engine
TurboGrafx/Duo/PC-Engine Controller Data
https://gamesx.com/controldata/turbocont.htm
Arduino Project: Super Nintendo Entertainment System (SNES)
https://jfrmilner.wordpress.com/2016/07/17/arduino-project-super-nintendo-entertainment-system-snes-controllergamepad-to-windowslinux-retropie-usb/
スーパーファミコンコントローラの解析
https://sites.google.com/site/hardware007laboratory/home/denshi-kousaku/sfc_cont
2020.6.21 BLUE ( qze12045@nifty.com )
-----------------------------------------------------------------------------*/
#include <Joystick.h>
// コンパイルオプション
#define __Debug 0 // 1:シリアルデバッグON
#define __FFBtest 0 // 1:デュアルショック振動テスト
#define __HORI_ADP 1 // 1:HORI PS4-100模擬(VID=0x0F0D、PID=0x00EE)
#define __ELECOM_ADP 0 // 1:ELECOM USB変換アダプタ模擬(VID=0x0925、PID=0x8888)
#define __SEGA_ADP 0 // 1:SEGA メガドラミニ用パッド模擬(VID=0x0CA3、PID=0x0024)
#define __BugFix 0 //メガドライブミニに接続時に方向キーが入りっぱなしになる場合に1にする
#define DELAY_TIME 6 // 遅延時間(6μs)<--5より小さくするとPSX動作がNG
#define SCAN_TIME 10 // パッド読み取りスキャン時間(10ms) <-- 7だと100回/秒程度。2以下(200回以上/秒)はお勧めできない
//PSXコントローラー制御ピン定義
#define PS_DATA 7 // データ信号
#define PS_CMD 8 // Command送信
#define PS_ATT 9 // セレクト信号
#define PS_CLK 10 // 制御クロック信号
// PSX送受信コマンド種別
enum{
PS_READ = 0 , // パッド状態読出し(READ_DATA)
PS_CONFIG_MODE , // コンフィグモード遷移(CONFIG_MODE_ENTER)
PS_CONFIG_EXIT , // コンフィグモード終了(CONFIG_MODE_EXIT)
PS_SET_MODE , // 動作モード/モード変更可否設定(SET_MODE_AND_LOCK)
PS_QUERY , // 機器種別/動作モード取得(QUERY_MODEL_AND_MODE)
PS_VIBRATION_ENABLE , // 振動モード許可(VIBRATION_ENABLE)
PS_CMD_MAX
};
#define PS_ID_MAX 2 // PSXコマンドID長(2バイト)
#define PS_DATA_MAX 30 // PSXコマンドデータ最大長(30バイト以下)
typedef struct Psxid {
byte id; // 接続機器ID
byte config; // 初期化要求フラグ(0x5a=通常、0=再初期化あり)
} PSXID;
// PSX送信コマンドテーブル
const byte ps_cmd[PS_CMD_MAX][PS_ID_MAX] =
{
// PLAYSTATION Command Table
{ 0x42 ,0x00 }, // パッド状態読出し(READ_DATA)
{ 0x43 ,0x00 }, // コンフィグモード遷移(CONFIG_MODE_ENTER)
{ 0x43 ,0x00 }, // コンフィグモード終了(CONFIG_MODE_EXIT)
{ 0x44 ,0x00 }, // 動作モード/モード変更可否設定(SET_MODE_AND_LOCK)
{ 0x45 ,0x00 }, // 機器種別/動作モード取得(QUERY_MODEL_AND_MODE)
{ 0x4D ,0x00 } // 振動モード許可(VIBRATION_ENABLE)
};
// メガドライブパッド用ピン番号
#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 MD_SEL 5 // Select信号ポート番号
// PCEコントローラ制御ピン定義
#define PC_D0 18 // PD0/Pin18
#define PC_D1 19 // PD1/Pin19
#define PC_D2 20 // PD2/Pin20
#define PC_D3 21 // PD3/Pin21
#define PC_SEL 15 // Select信号
#define PC_EN 14 // /Enable信号
//Super Famicom コントローラ制御ピン定義
#define SF_CLOCK 8 // PB4/Pin8
#define SF_LATCH 10 // PB6/Pin10
#define SF_DATA 16 // PB2/Pin16
#if __BugFix
// 方向キー移動量(MEGA DRIVE mini向け)
#define AXIS_MIN 0 // アナログ方向キー最小値
#define AXIS_MAX 511 // アナログ方向キー最大値
#define AXIS_CENTER 255 // アナログ方向キーセンター位置
#else
// 方向キー移動量(PC向け)
#define AXIS_MIN 0 // アナログ方向キー最小値
#define AXIS_MAX 255 // アナログ方向キー最大値
#define AXIS_CENTER 127 // アナログ方向キーセンター位置
#endif
// Joystick PAD番号定義
#if __HORI_ADP
// HORI PS4-100模擬(VID=0x0F0D、PID=0x00EE)
#define PAD_A 1 // A / ×ボタン
#define PAD_B 2 // B / ○ボタン
#define PAD_C 5 // C / R1ボタン
#define PAD_X 0 // X / □ボタン
#define PAD_Y 3 // Y / △ボタン
#define PAD_Z 4 // Z / L1ボタン
#define PAD_MODE 8 // MODE / SELECTボタン
#define PAD_START 9 // STARTボタン
#define PAD_L2 6 // - / L2ボタン(感圧:X回転/右)
#define PAD_R2 7 // - / R2ボタン(感圧:Y回転/右)
#define PAD_L3 10 // - / L3ボタン
#define PAD_R3 11 // - / R3ボタン
#elif __ELECOM_ADP
// ELECOM USB変換アダプタ模擬(VID=0x0925、PID=0x8888)
#define PAD_A 2 // A / ×ボタン
#define PAD_B 1 // B / ○ボタン
#define PAD_C 6 // C / R1ボタン
#define PAD_X 3 // X / □ボタン
#define PAD_Y 0 // Y / △ボタン
#define PAD_Z 7 // Z / L1ボタン
#define PAD_MODE 9 // MODE / SELECTボタン
#define PAD_START 8 // STARTボタン
#define PAD_L2 4 // - / L2ボタン
#define PAD_R2 5 // - / R2ボタン
#define PAD_L3 10 // - / L3ボタン
#define PAD_R3 11 // - / R3ボタン
#elif __SEGA_ADP
// SEGA 純正パッド模擬(VID=0x0CA3、PID=0x0024)
#define PAD_A 2 // Aボタン
#define PAD_B 1 // Bボタン
#define PAD_C 5 // Cボタン
#define PAD_X 3 // Xボタン
#define PAD_Y 0 // Yボタン
#define PAD_Z 4 // Zボタン
#define PAD_MODE 8 // MODEボタン
#define PAD_START 9 // STARTボタン
#define PAD_L2 6 // - / L2ボタン
#define PAD_R2 7 // - / R2ボタン
#define PAD_L3 10 // - / L3ボタン
#define PAD_R3 11 // - / R3ボタン
#endif
// 接続パッド種別
enum{
tMD = 0 , // MegaDrive PAD
tPSX , // Play Station PAD
tPCE , // PC Engine/TG16 PAD
tSFC , // Super Famicom/Super Nintendo Entertainment System PAD
tMAX
};
// パッドボタン定義(ここに定義されたタイプのみ有効)
enum{
pUP = 0 , // UP
pDOWN , // DOWN
pLEFT , // LEFT
pRIGHT , // RIGHT
pA , // A / ×
pB , // B / ○
pC , // C / R1
pSTART , // START
pMODE , // MODE / SELECT
pX , // X / □
pY , // Y / △
pZ , // Z / L1
pSEL , // SELECT(PSX/PCE)
pIII , // III(PCE 6B)
pL2 , // L2(PSX)
pR2 , // R2(PSX)
pL3 , // L3(PSX)
pR3 , // R3(PSX)
pMAX
};
// 機種別ボタンチェック値(該当Bit位置をマスクする値)
const unsigned int pad_mask[tMAX][pMAX] =
{
// UP ,DOWN ,LEFT ,RIGHT ,A ,B ,C ,START ,MODE ,X ,Y ,Z ,SELECT ,III ,L2 ,R2 ,L3 ,R3(MEGA DRIVE用ボタン名)
// UP ,DOWN ,LEFT ,RIGHT ,× ,○ ,R1 ,START ,SELECT ,□ ,△ ,L1 ,SELECT ,III ,L2 ,R2 ,L3 ,R3(PLAY STATION用ボタン名)
{ 0x0001 ,0x0002 ,0x0004 ,0x0008 ,0x0040 ,0x0010 ,0x0020 ,0x0080 ,0x0800 ,0x0400 ,0x0200 ,0x0100 ,0x0000 ,0x0000 ,0x0000 ,0x0000 ,0x0000 ,0x0000},//MEGA DRIVE(tMD)
{ 0x0010 ,0x0040 ,0x0080 ,0x0020 ,0x4000 ,0x2000 ,0x0800 ,0x0008 ,0x0001 ,0x8000 ,0x1000 ,0x0400 ,0x0001 ,0x8000 ,0x0100 ,0x0200 ,0x0002 ,0x0004},//PLAY STATION(tPSX)
{ 0x0001 ,0x0004 ,0x0008 ,0x0002 ,0x0040 ,0x0020 ,0x0010 ,0x0080 ,0x0040 ,0x0200 ,0x0400 ,0x0800 ,0x0040 ,0x0100 ,0x0000 ,0x0000 ,0x0000 ,0x0000},//PC ENGINE/TG16(tPCE)
{ 0x0010 ,0x0020 ,0x0040 ,0x0080 ,0x0001 ,0x0100 ,0x0800 ,0x0008 ,0x0004 ,0x0002 ,0x0200 ,0x0400 ,0x0000 ,0x0000 ,0x0000 ,0x0000 ,0x0000 ,0x0000} //Super Famicom/SNES(tSFC)
};
// パッドボタン定義とJoystickライブラリ送信値変換用
const int pad_map[pMAX] =
{ -1,-1,-1,-1,PAD_A,PAD_B,PAD_C,PAD_START,PAD_MODE,PAD_X,PAD_Y,PAD_Z,PAD_MODE,PAD_A,PAD_L2,PAD_R2,PAD_L3,PAD_R3};
//PAD状態を前回と最新状態で比較して、変化があった場合にコードを送信する
unsigned int cur_sts[tMAX]; // 前回のボタン状態を保持する
// Joystickライブラリ定義
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK,
12, 1, // Button Count, Hat Switch Count
true, true, true, // X, Y, and Z Axis
false, false, true, // Rx, but no Ry or Rz
false, false, // No rudder or throttle
false, false, false); // No accelerator, brake, or steering
PSXID psx_id; // PSX機種ID情報
byte psx_data[PS_DATA_MAX]; // PSX受信データ(最新値)
byte cur_psx_data[4]; // PSX受信データ(前回のアナログ値4バイト)
bool psx_config_flag = false;
byte psx_mode; // 0:デジタル、1:アナログ、2ジョグコン、3:デュアルショック
#if __Debug
///////////////////////////////////////////////////////////////////////////////
void disp_psx(void)
{
Serial.print("ID:0x");
Serial.print(psx_id.id,HEX);
Serial.print(" 0x");
Serial.print(psx_id.config,HEX);
int len = (psx_id.id&0xf)<<1;
len=(len == 0xf) ? 2 : len;
for ( int i=0; i<len ;i++)
{
Serial.print(" 0x");
Serial.print(psx_data[i],HEX);
}
Serial.print("\n");
}
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
/////////////////////////////////////////////////
// プレイステーションパッド送受信処理
// 入力: cmd :送付コマンド値
// 戻り: true=読出し正常、false=失敗(未接続など)
/////////////////////////////////////////////////
byte ps_trx(int cmd)
{
byte data = 0;
// 8Bit送受信処理
for (int i = 0 ; i < 8 ; i++ )
{
//コマンド出力(1bit)
digitalWrite(PS_CMD,(cmd &(1<<i)) ? HIGH : LOW );
// Clock信号 LOW出力
digitalWrite(PS_CLK, LOW);
//4μs以上待つ
delayMicroseconds(DELAY_TIME);
//データ読出し(1bit)
data |= digitalRead(PS_DATA)<<i;
// Clock信号 HIGH出力
digitalWrite(PS_CLK, HIGH);
//4μs以上待つ
delayMicroseconds(DELAY_TIME);
}
return data;
}
/////////////////////////////////////////////////
// プレイステーションパッド送受信
// 入力: type :コマンド種別(PS_READ 他)
// 出力構造体:PSXID psx_id、psx_data[]
// 戻り: true=正常、false=失敗
/////////////////////////////////////////////////
bool psx_cmd(int type)
{
byte send[PS_DATA_MAX];
bool sts = false;
//送信データ初期化
memset(send,0,PS_DATA_MAX);
//送信データ編集
switch(type)
{
case PS_CONFIG_MODE:
send[0]=0x01;
break;
default:
break;
}
//デバイス選択
digitalWrite(PS_ATT, LOW);
//コマンド送付
ps_trx(0x01);
psx_id.id = ps_trx(ps_cmd[type][0]);
psx_id.config = ps_trx(ps_cmd[type][1]);
// 受信IDが読めている場合(=0xff以外なら)、パッド状態を読み出す
if ( psx_id.id != 0xff )
{
//データ長取り出し
int data_len = (psx_id.id & 0xf)<<1;
//パッド状態データ読出し
for (int i = 0 ; i < data_len ; i++ )
{
psx_data[i]= ps_trx(send[i]);
}
//モード状態変更検知
if ( psx_id.config == 0 )
{
//パッド未初期化とする
psx_config_flag = false;
}
// アナログ→デジタル変化検知(アナログモードでデジタルパッドを検知した)
if (( psx_mode>0 ) && ( type==PS_READ ) && ( psx_id.id==0x41 ))
{
//パッド未初期化とする
psx_config_flag = false;
}
//正常完了
sts = true;
}
else
{
//パッド未初期化とする
psx_config_flag = false;
}
//デバイスアクセス終了
digitalWrite(PS_ATT, HIGH);
delayMicroseconds(DELAY_TIME);
#if __Debug
///////////////////////////////////////////////////////////////////////////////
if(type!=PS_READ)
{
Serial.print("MODE");
Serial.print(psx_mode,HEX);
Serial.print(":CMD(");
Serial.print(type,DEC);
Serial.print(")0x");
Serial.print(ps_cmd[type][0],HEX);
Serial.print(" ");
disp_psx();
}
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
return sts;
}
/////////////////////////////////////////////////
// プレイステーションパッド初期化
// 入力
// type :機種種別(tMD、tPSX、tPCE)
// state = 方向キー状態
// 戻り
// true=ボタン状態変化あり、false=変化なし
/////////////////////////////////////////////////
bool psx_config(void)
{
bool sts;
//デジタルモードとする(初期値)
psx_mode = 0;
//コンフィグモード遷移(CONFIG_MODE_ENTER)
if (sts = psx_cmd(PS_CONFIG_MODE))
{
//コマンド受理待ち
delay(1);
// 機器種別/動作モード取得(QUERY_MODEL_AND_MODE)
sts |= psx_cmd(PS_QUERY);
delay(1);
// アナログモードである
if((psx_id.id==0xf3)&&(psx_data[2]==0x01))
{
//アナログモード設定
psx_mode = 1;
//デュアルショックまたは、ジョグコンである
if((psx_data[3]==0x01)||(psx_data[3]==0x02))
{
//ジョグコン・デュアルショックモード設定
psx_mode += psx_data[3];
}
}
// コンフィグモード終了(CONFIG_MODE_EXIT))
sts |= psx_cmd(PS_CONFIG_EXIT);
}
return sts;
}
/////////////////////////////////////////////////
// 方向キー制御処理(機種共通)
// 入力
// type :機種種別(tMD、tPSX、tPCE)
// state = 方向キー状態
// 戻り
// true=ボタン状態変化あり、false=変化なし
/////////////////////////////////////////////////
bool set_axis(int type ,unsigned int state )
{
bool change = false;
#if __ELECOM_ADP || __HORI_ADP
///////////////////
// HAT Switch制御
///////////////////
int HatSts = -1;
// 方向キーチェック用マスク値作成
unsigned int mask = (pad_mask[type][pUP]|pad_mask[type][pDOWN]|pad_mask[type][pLEFT]|pad_mask[type][pRIGHT]);
// 状態変化あり
if (( cur_sts[type] & mask )!=( state & mask ))
{
change = true;
// KEY押下状態比較用データ作成
unsigned int key = (~state) & mask;
// 上キー押下
if (pad_mask[type][pUP] == key)
{
HatSts = 0;
}
// 上+右キー押下
else if ((pad_mask[type][pUP] | pad_mask[type][pRIGHT]) == key)
{
HatSts = 45;
}
// 右キー押下
else if (pad_mask[type][pRIGHT] == key)
{
HatSts = 90;
}
// 右+下キー押下
else if ((pad_mask[type][pDOWN] | pad_mask[type][pRIGHT]) == key)
{
HatSts = 135;
}
// 下キー押下
else if (pad_mask[type][pDOWN] == key)
{
HatSts = 180;
}
// 左+下キー押下
else if ((pad_mask[type][pDOWN] | pad_mask[type][pLEFT]) == key)
{
HatSts = 225;
}
// 左キー押下
else if (pad_mask[type][pLEFT] == key)
{
HatSts = 270;
}
// 左+上キー押下
else if ((pad_mask[type][pUP] | pad_mask[type][pLEFT]) == key)
{
HatSts = 315;
}
// HAT Switch制御
Joystick.setHatSwitch(0, HatSts);
// ボタン状態を変更する
cur_sts[type] = (cur_sts[type] & ~mask)|(state & mask);
}
#else
int axis = AXIS_CENTER;
///////////////////
// Y軸制御
///////////////////
// 上下キー状態チェック用マスク値取得
unsigned int mask = pad_mask[type][pUP]|pad_mask[type][pDOWN];
//上下キー状態変化あり
if (( cur_sts[type] & mask )!=( state & mask ))
{
change = true;
// 上キーを離している
if (state & pad_mask[type][pUP])
{
// 下キーを離している
if (state & pad_mask[type][pDOWN])
{
// Y軸をセンターに戻す
axis = AXIS_CENTER;
}
else
{
// 下キーを押下している
// Y軸を最大
axis = AXIS_MAX;
}
}
// 上キーを押下している
else
{
// Y軸を最小
axis = AXIS_MIN;
}
// Y軸値セット
Joystick.setYAxis(axis);
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("Joystick.setYAxis(");
Serial.print(axis,DEC);
Serial.print(")\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
// 方向キー状態を最新に変更
cur_sts[type] = (cur_sts[type] & ~mask)|(state & mask);
}
///////////////////
// X軸制御
///////////////////
// 左右キー状態チェック用マスク値取得
mask = pad_mask[type][pLEFT]|pad_mask[type][pRIGHT];
// 左右キー状態変化あり
if (( cur_sts[type] & mask )!=( state & mask ))
{
change = true;
// 左キーを離している
if (state & pad_mask[type][pLEFT])
{
// 右キーを離している
if (state & pad_mask[type][pRIGHT])
{
// X軸をセンターに戻す
axis = AXIS_CENTER;
}
// 右キーを押下している
else
{
// X軸を最大
axis = AXIS_MAX;
}
}
// 左キーを押下している
else
{
// X軸を最小
axis = AXIS_MIN;
}
// X軸値セット
Joystick.setXAxis(axis);
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("Joystick.setXAxis(");
Serial.print(axis,DEC);
Serial.print(")\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
// 方向キー状態を最新に変更
cur_sts[type] = (cur_sts[type] & ~mask)|(state & mask);
}
#endif //__ELECOM_ADP || __HORI_ADP
return change;
}
/////////////////////////////////////////////////
// パッド制御処理(機種共通)
// 入力
// type :機種種別(tMD、tPSX、tPCE)
// state = 方向キー状態
// button = ボタン定義
// 戻り
// true=ボタン状態変化あり、false=変化なし
/////////////////////////////////////////////////
bool set_button(int type ,unsigned int state ,int button )
{
// ボタン状態チェック用マスク値取得
unsigned int mask = pad_mask[type][button];
// ボタン状態変化あり
if (( cur_sts[type] & mask )!=( state & mask ))
{
// Joystickボタン値取得
int pad = pad_map[button];
// ボタン押下?
if( !(state & mask) )
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print(pad,DEC);
Serial.print(" Press\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
// ボタン押下セット
Joystick.pressButton(pad);
}
// 離した
else
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print(pad,DEC);
Serial.print(" Release\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
// ボタン離す
Joystick.releaseButton(pad);
}
// ボタン状態を変更する
cur_sts[type] = (cur_sts[type] & ~mask)|(state & mask);
// 変化あり
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);
// MD Select信号出力用ポート初期化(OUTPUT)
pinMode(MD_SEL, OUTPUT);
//プレイステーション制御用I/O初期化
//DATA信号初期化
pinMode(PS_DATA, INPUT_PULLUP);
//CMD信号初期化
pinMode(PS_CMD, OUTPUT);
//ATT信号初期化(HIGH=非選択状態)
pinMode(PS_ATT, OUTPUT);
digitalWrite(PS_ATT, HIGH);
//Clock信号初期化(HIGH=初期値)
pinMode(PS_CLK, OUTPUT);
digitalWrite(PS_CLK, HIGH);
// PCEパッド読み取り用ポート初期化(INPUT_PULLUP)
pinMode(PC_D0, INPUT_PULLUP);
pinMode(PC_D1, INPUT_PULLUP);
pinMode(PC_D2, INPUT_PULLUP);
pinMode(PC_D3, INPUT_PULLUP);
// PCE SEL信号出力用ポート初期化(OUTPUT)
pinMode(PC_SEL, OUTPUT);
// PCE /EN信号出力用ポート初期化(OUTPUT:HIGH)
pinMode(PC_EN, OUTPUT);
// /EN=HIGH(パッド無効)セット
digitalWrite(PC_EN, HIGH);
//SFC制御クロック信号初期化(OUTPUT:HIGH)
pinMode(SF_CLOCK, OUTPUT);
digitalWrite(SF_CLOCK, HIGH);
//SFC制御ラッチ信号初期化(OUTPUT:LOW)
pinMode(SF_LATCH, OUTPUT);
digitalWrite(SF_LATCH, LOW);
//SFC制御データ信号初期化(INPUT_PULLUP)
pinMode(SF_DATA, INPUT_PULLUP);
// Joystick Library初期化
Joystick.begin(false);
// アナログ軸データ初期化
Joystick.setXAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setYAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setZAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setRzAxisRange(AXIS_MIN, AXIS_MAX);
Joystick.setXAxis(AXIS_CENTER);
Joystick.setYAxis(AXIS_CENTER);
Joystick.setZAxis(AXIS_CENTER);
Joystick.setRzAxis(AXIS_CENTER);
//前回軸データ初期化
memset(cur_psx_data,AXIS_CENTER,4);
// パッド状態初期化
cur_sts[tMD] = 0xffff;
cur_sts[tPSX]= 0xffff;
cur_sts[tPCE]= 0xffff;
cur_sts[tSFC]= 0xffff;
#if __Debug
Serial.begin(38400);
#endif //__Debug
}
//////////////////////////////////////
// メインループ
//////////////////////////////////////
void loop() {
// put your main code here, to run repeatedly:
unsigned int cur_state = 0;
/////////////////////////////////
// MEGA DRIVE パッド状態読み出し
/////////////////////////////////
bool Pad6B = LOW; //6Bパッド未検出
unsigned int state = 0xffff;
int sel = 0;
// PAD情報取得(読取りシーケンス7回実行)
for( int i = 0 ; i < 7 ; i++ )
{
// Select信号出力
digitalWrite(MD_SEL, !sel ? LOW : HIGH );
delayMicroseconds(DELAY_TIME);
// パッド状態読み出し
cur_state = PIND;
// 6Bパッド検出?
if ( !sel && !(cur_state & 0xf) )
{
// 6Bパッド検出セット
Pad6B = HIGH;
}
// 6Bパッド未検出
if(Pad6B == LOW)
{
// パッド情報(3B/6B共通)を格納する
if (sel)
state = (state & 0xfc0)|((cur_state & 0x1f)|(cur_state & 0x80)>>2);
else
state = (state & 0xf3f)|(((cur_state & 0x10)<<2)|(cur_state & 0x80));
}
else
{
if (sel)
// 6Bパッド情報を格納する
state = (state & 0xf0ff)|((cur_state & 0xf)<<8);
}
// Select状態(Low/High)を反転する
sel ^= 0x01;
}
////////////////////////////////
// Joystick制御
////////////////////////////////
bool change = false;
//方向キー制御
change |= set_axis(tMD,state);
// 各ボタン制御
change |= set_button(tMD,state,pA);
change |= set_button(tMD,state,pB);
change |= set_button(tMD,state,pC);
change |= set_button(tMD,state,pSTART);
// 6Bパッド検出
if(Pad6B)
{
change |= set_button(tMD,state,pMODE);
change |= set_button(tMD,state,pX);
change |= set_button(tMD,state,pY);
change |= set_button(tMD,state,pZ);
}
// Joystick情報送信
if(change)
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("MD :");
Serial.print(state,BIN);
Serial.print("\n");
Serial.print("Joystick.sendState()\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
Joystick.sendState();
}
////////////////////////////////////////////
//プレステコントローラ状態読み出し
////////////////////////////////////////////
state = 0xffff;
change = false;
//パッド状態読み出し
if (psx_cmd(PS_READ) == true)
{
//ボタン状態(デジタル)読み出し
state = (psx_data[1]<<8)|psx_data[0];
// ノーマル(デジタルモード)
if(psx_id.id==0x41)
{
// 軸を中心に戻す
memset(&psx_data[2],AXIS_CENTER,4);
}
// ネジコン
if(psx_id.id==0x23)
{
//Lボタンデジタル化(閾値より上ならL1押下に変換する)
if(psx_data[5]>0x20)
{
// L1押下(Bit11=OFF)
state ^=0x0400;
}
//Y軸は中央固定
psx_data[5]=AXIS_CENTER;
//ねじり値を退避
byte work=psx_data[2];
//IIボタンをZ回転(0~255)とする
psx_data[2]=psx_data[4];
//ねじり値をX軸、Iボタンは右Z回転(0~255)
psx_data[4]=work;
}
// ジョグコン
if(psx_id.id==0xe3)
{
// JOGダイアル位置取得(16bit)+中央補正(+0x8000)
unsigned int jog = ((psx_data[3]<<8)|psx_data[2])+(0x8000+AXIS_CENTER);
// 最小チェック
if (jog<=0x8000)
{
jog = AXIS_MIN;
}
// 最大チェック
else if (jog>=(0x8000+AXIS_MAX))
{
jog = AXIS_MAX;
}
//JOG値をX軸に割り当てる(0~255)
psx_data[4] = (byte)(jog & 0xff);
//Y軸は中央固定
psx_data[5]=AXIS_CENTER;
//Z回転、右Z回転は中央値
psx_data[2]=AXIS_CENTER;
psx_data[3]=AXIS_CENTER;
}
// アナログ/デュアルショック他
// アナログ値変化有無チェック
for (int i=0 ; i < 4 ; i++ )
{
// 前回値と変化あり(閾値4)
if(abs(cur_psx_data[i]-psx_data[i+2])>4)
{
change=true;
}
}
// 変化ありならアナログ値更新
if(change)
{
// ADC値セット
Joystick.setXAxis(psx_data[4]);
Joystick.setYAxis(psx_data[5]);
Joystick.setZAxis(psx_data[2]);
Joystick.setRzAxis(psx_data[3]);
// アナログ受信データ更新
memcpy(cur_psx_data,&psx_data[2],4);
}
//パッド未初期化?
if (psx_config_flag == false)
{
//パッド初期化
psx_config_flag = psx_config();
}
}
// 方向キー(デジタル)制御
change |= set_axis(tPSX,state);
// 各ボタン(デジタル)制御
change |= set_button(tPSX,state,pA);
change |= set_button(tPSX,state,pB);
change |= set_button(tPSX,state,pC);
change |= set_button(tPSX,state,pSTART);
change |= set_button(tPSX,state,pMODE);
change |= set_button(tPSX,state,pX);
change |= set_button(tPSX,state,pY);
change |= set_button(tPSX,state,pZ);
change |= set_button(tPSX,state,pL2);
change |= set_button(tPSX,state,pR2);
change |= set_button(tPSX,state,pL3);
change |= set_button(tPSX,state,pR3);
// Joystick情報送信
if(change)
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("PSX :");
Serial.print(state,BIN);
Serial.print("\n");
disp_psx();
Serial.print("Joystick.sendState()\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
Joystick.sendState();
}
////////////////////////////////////////////
//PCEコントローラ状態読み出し
////////////////////////////////////////////
state = 0xffff;
// 6Bパッド未検出
Pad6B = LOW;
// 6ボタン対応PAD情報取得(読み取りシーケンスを2回実施)
for( int i = 0 ; i < 2 ; i++ )
{
//6B検出フラグ初期化(シーケンス毎にクリア)
bool detect6b = LOW;
// 読出しシーケンス開始(/EN=L)
digitalWrite(PC_EN, LOW );
// SEL=Hi
digitalWrite(PC_SEL, HIGH );
delayMicroseconds(DELAY_TIME);
//パッド情報読出し(方向キーが入る)
unsigned int cur_state = PINF & 0xf0;
//6ボタンPAD検出(D0~D3全てLOW)?
if (!cur_state)
{
// 6ボタンPAD検出セット(パッド情報は保存しない)
detect6b = HIGH;
Pad6B = HIGH;
}
else
{
//方向キー状態を格納
state = (state & 0xff0)|(cur_state>>4);
}
//SEL=LOW
digitalWrite(PC_SEL, LOW );
delayMicroseconds(DELAY_TIME);
//パッド情報読出し(ボタン情報が入る)
cur_state = PINF & 0xf0;
//シーケンスHiで6Bパッド検出
if( detect6b )
{
// 6ボタン状態を格納
state = (state & 0x0ff)|(cur_state<<4);
}
else
{
// 標準ボタン状態をに格納
state = (state & 0xf0f)|cur_state;
}
// 読出しシーケンス終了(/EN=H)
digitalWrite(PC_EN, HIGH );
}
change = false;
//方向キー制御
change |= set_axis(tPCE,state);
// 各ボタン制御
change |= set_button(tPCE,state,pB);
change |= set_button(tPCE,state,pC);
change |= set_button(tPCE,state,pSTART);
// 6Bパッド未検出
if(!Pad6B)
{
// 3BパッドはSELECTをAボタン指定
change |= set_button(tPCE,state,pA);
}
else
{
// 6Bパッドボタン指定
change |= set_button(tPCE,state,pX);
change |= set_button(tPCE,state,pY);
change |= set_button(tPCE,state,pZ);
// 6BパッドはIIIをAボタンに割当
change |= set_button(tPCE,state,pIII);
// 6BパッドはSELECTボタンをMODEボタンに割当
change |= set_button(tPCE,state,pSEL);
}
// Joystick情報送信
if(change)
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("PCE :");
Serial.print(state,BIN);
Serial.print("\n");
Serial.print("Joystick.sendState()\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
Joystick.sendState();
}
////////////////////////////////////////////
//SUPER FAMICOMコントローラ状態読み出し
////////////////////////////////////////////
state = 0;
// LATCH信号HIGH出力
digitalWrite(SF_LATCH, HIGH);
delayMicroseconds(DELAY_TIME*2);
// 12μ秒後、LATCH信号LOWに戻す
digitalWrite(SF_LATCH, LOW);
delayMicroseconds(DELAY_TIME);
// ボタン状態読出し(16Bit)
for(int i = 0; i < 16; i++)
{
// CLOCK信号LOW出力
digitalWrite(SF_CLOCK, LOW);
delayMicroseconds(DELAY_TIME);
// ボタン状態を(1Bitづつ)読み出す
state |= digitalRead(SF_DATA) << i;
// CLOCK信号をHIGHに戻す
digitalWrite(SF_CLOCK, HIGH);
delayMicroseconds(DELAY_TIME);
}
change = false;
//方向キー制御
change |= set_axis(tSFC,state);
// 各ボタン制御
change |= set_button(tSFC,state,pA);
change |= set_button(tSFC,state,pB);
change |= set_button(tSFC,state,pC);
change |= set_button(tSFC,state,pSTART);
change |= set_button(tSFC,state,pMODE);
change |= set_button(tSFC,state,pX);
change |= set_button(tSFC,state,pY);
change |= set_button(tSFC,state,pZ);
// Joystick情報送信
if(change)
{
#if __Debug
///////////////////////////////////////////////////////////////////////////////
Serial.print("SFC :");
Serial.print(state,BIN);
Serial.print("\n");
Serial.print("Joystick.sendState()\n");
///////////////////////////////////////////////////////////////////////////////
#endif //__Debug
Joystick.sendState();
}
//次回ポーリング開始待ち
delay(SCAN_TIME);
}