はじめまして、りょーつといいます。高専出身の大学院2年生です。今週は,スーパーファミコンのコントローラの値をArduinoで読む方法についてまとめてみようと思います.
目次
1.はじめに
2.コントローラの仕様
3.回路製作
4.プログラミング
5.おわりに
1. はじめに
先日ハードオフでスーパーファミコンのコントローラを入手しました.せっかくなのでArduinoを使ってコントローラの値を読んでみようと思い,いろいろやってみたところ,うまくいったので本記事を書くにいたりました.最終的なゴールとしては,Arduinoでコントローラの値を読み,ブラウザ版のしょぼんをプレイするというところまでやりたいと思っています.システム構成としては図1に示すようなものを考えています.注意しないといけないのが,PCにキーボードとして認識させることができる(HIDデバイス)のは特定のArduinoボードであることです.電子工作でよく使用されるArduino UNOは対象外となります.ATmega32u4を搭載しているArduino Leonardo か Arduino Microを使ってください.私はArduino Microを使いました.
図1 システム構成
2. コントローラの仕様
まずはスーパーファミコンのコントローラの仕様を説明します.こちらの記事を参考にしました.コントローラからは5本の線が生えており,これらを用いて本体と通信を行っています.それぞれの線の役割は表1に示すとおりです.
表1 スーパーファミコンのコントローラの各配線の役割
コントローラからのデータ取得は次の手順を踏んで行われます.まずLatchが立ち上がることで通信を開始します.次にClockが立ち上がるごとに各ボタンの状態(High/Low)がDataに反映されます.送られてくるデータは16個で,1個目のクロックはBボタン,2個目のクロックはYボタン...というふうに決まった順でデータが送信されます.各ボタンが何番目に対応しているのかについては表2を参照ください.さいごの4つはどのボタンも割り当てられておらず余っています.
図2 スーパーファミコンのデータ送受信
表2 各クロックに対応するボタン
各ボタンはコントローラ内部に配置されているICによって内部プルアップされている(らしい)ため,Dataの状態は,ボタンを押していないときにHigh,押しているときにLowとなります.スーパーファミコンの中身を空けてみたところ,謎のIC「V520B」が1つ入っているだけでした.ネット上の記事を漁ってみたところニンテンドーのオリジナルシフトレジスタICっぽいです.詳細は分かりませんが,16bitのParralel-in Serial-out(PISO)のシフトレジスタっぽいです.中身の細かい話はこちらの記事を参照ください.
結論,Arduinoでやることとしては,①Latchのデジタル出力,②Clockの生成,③Dataの取得の3つとなります.これらの詳細については4章で後述するコードを見てください.
3. 回路製作
コントローラの仕様が分かったので,Arduinoと接続するための簡単な回路を作っていきたいと思います.まずはスーパーファミコンのコントローラのコネクタをぶった切り,XA5ピンコネクタに付け替えます.ピン配置は画像参照.
図3 XAコネクタへの付け替え
また,コントローラからの信号をArduino Microに入力するためのユニバーサル基板をつくります.私はD2ピン:Data,D3ピン:Latch,D4ピン:Clockとしました.回路図や外観画像を参考に作ってみてください.Arduino Microの電源はUSB経由でPCから供給します.
図4 基板の回路図
図5 基板の外観
動作確認するだけならブレッドボードで組んでしまってもいいと思います.
4. プログラミング
第2章で述べた動作をArduinoのプログラムに落とし込みます.回路構成が私と同じ場合は以下のプログラムコードをコピペして使用してください.ボタンとキーの対応はKeyboard.press関数とKeyboard.release関数を用いて行っていますので,他のキーを使用したい場合はそこのコードをいじってください.キー入力の処理はこちらの記事を参考にしました.
とりあえずしょぼんのアクションがプレイできるようなボタン配置にしてみました.コントローラの通信速度の仕様は60Hzらしいのですが,それ以上に高速化しても問題なくデータが読めたので,限界まで処理を速くしてみました.
#include <Keyboard.h>
#define Data_BIT 1 //D2に対応するPORTDのbit番号
#define Latch_BIT 0 //D3に対応するPORTDのbit番号
#define Clock_BIT 4 //D4に対応するPORTDのbit番号
uint16_t buttons = 0b0000000000000000; //ボタン状態を格納する16bitの変数宣言
void setup() {
DDRD = (1U << Clock_BIT) | (1U << Latch_BIT); //ピンの入出力設定(pinMode()に対応)
PORTD &= ~(1U << Latch_BIT); //LatchをLOW
PORTD &= ~(1U << Clock_BIT); //ClockをLOW
Keyboard.begin();
delay(1000);
}
void loop() {
while(1){
//コントローラのデータ読み込み------------------------------
//ラッチ作動
PORTD |= (1U << Latch_BIT); //LatchをHIGH
PORTD &= ~(1U << Latch_BIT); //LatchをLOW
//最初の12bitしかデータが入っていないので,データは16bit送られてくるが,読むのは12bitだけ
for(uint8_t i = 0; i<12;i++){
PORTD |= 1U << Clock_BIT; //ClockをHIGH
buttons = (buttons & ~(1U << i)) | ((uint16_t)((PIND >> Data_BIT) & 1U) << i); //digitalRead()に相当
PORTD &= ~(1U << Clock_BIT); //ClockをLOW
}
//Clockを合計16進めるため
for(uint8_t i = 0; i<4;i++){
PORTD |= 1U << Clock_BIT; //ClockをHIGH
PORTD &= ~(1U << Clock_BIT); //ClockをLOW
}
//キーへの割り当て---------------------------------------
//※しょぼんのアクション
//Aボタンを↑に割り当て(8=9-1)
if (buttons & (1U << 8))Keyboard.release(KEY_UP_ARROW);
else Keyboard.press(KEY_UP_ARROW);
//Bボタンをスペースキーに割り当て(0=1-1)
if (buttons & (1U << 0)) Keyboard.release(' ');
else Keyboard.press(' ');
//Xボタンをoに割り当て(9=10-1)
if (buttons & (1U << 9))Keyboard.release('o');
else Keyboard.press('o');
//↓ボタンを↓に割り当て(5=6-1)
if (buttons & (1U << 5)) Keyboard.release(KEY_DOWN_ARROW);
else Keyboard.press(KEY_DOWN_ARROW);
//←ボタンを←に割り当て(6=7-1)
if (buttons & (1U << 6))Keyboard.release(KEY_LEFT_ARROW);
else Keyboard.press(KEY_LEFT_ARROW);
//→ボタンを→に割り当て(7=8-1)
if (buttons & (1U << 7))Keyboard.release(KEY_RIGHT_ARROW);
else Keyboard.press(KEY_RIGHT_ARROW);
//セレクトボタンをF1に割り当て(2=3-1)
if (buttons & (1U << 2))Keyboard.release(KEY_F1);
else Keyboard.press(KEY_F1);
//スタートボタンをEnterに割り当て(3=4-1)
if (buttons & (1U << 3))Keyboard.release(KEY_RETURN);
else Keyboard.press(KEY_RETURN);
}
}
上記のプログラムは高速化のためにdigitalWrite関数やpinMode関数を使わない構成となっています.ビットシフトやマイコンのレジスタを直接いじるものになっており,少々複雑です.プログラムの解説は次回行おうと思います.
実際にしょぼんのアクションで遊んでみた様子がこちらとなります.問題なく動作してますね.楽しいです.
5. おわりに
今週の記事では,Arduino Microを使ってスーパーファミコンのボタンの状態を取得し,キー入力に変換する方法を紹介しました.やっぱり改造したコントローラで遊ぶのはとても楽しく,ゲームの沼にハマってしまいそうです.スーパーファミコン以外のコントローラも仕様の割れてるものが多いようなので,またハードオフでいろいろ仕入れてみようと思います.来週はコード動作の詳細説明ができたらいいなと思います.今週も最後まで読んでいただき,ありがとうございました!
