概要

映像出力、サウンドを試したので、次に入力デバイスをテストします。
Raspberry Pi Picoは、COMポートによる入出力以外に、GPIOによるタクトスイッチや、A/DコンバーターによるA/Dキー、アナログスティックなどの制御を簡単に行うことができると思います。
が、リアルタイムOSのコントローラUSB変換でゲームパッド(+ロータリーエンコーダ)を調べたことがあったので、その時のゲームパッドと新たに追加した以下のパッドを入力デバイスとして使用します。



準備

image
回路図

パッドに必要なブロックは、上記「パッド端子(D-SUB9Pオス)」と「PAD ID / PAD端子電源」。

DIP SWの設定は、

XInputCom PCエンジン メガドライブ スーパーファミコン Play Station Wii クラシックコントローラ
□□□□
■■■■
■□□■
□■■□
■□■□
□■□■
■□■■
□■□□
□■□□
■□■■
□■□■
■□■□


2021/08/28

未使用ピンがA/Dコンバータとして使用できたため、A/Dコンバータ+DIPスイッチによる3bitの値を取得できるようにしました(3bitにした理由は、手持ちの抵抗では重複せず、判別できる範囲のAD値が作れなかったため)。
DIPスイッチの切り替えにより値が取得できるようになったため、ソースのビルドをやり直すことなく、複数パッドに対応可能。
また、3.3Vと5Vの切り替えも追加しました。


複数デバイスに対応できるよう、Raspberry Pi PicoからD-SUB9ピンへ接続します。
ピンの並びはメガドライブに合わせてあり、以下となります。


D-SUB9ピン番号メガドライブGPIOピン番号UART SPI I2C PWM
1 D0 14 CTS0SCLK1SDA17A
2 D1 15 RTS0MOSI1SCL17B
3 D2 16 TX0 MISO0SDA00A
4 D3 17 RX0 SS0 SCL00B
5 +5V +3.3V/+5V切り替え
6 D4 18 CTS0SCLK0SDA11A
7 SEL 1 RX0 SS0 SCL00B
8 GND GND
9 D5 19 RTS0MOSI0SCL11B


XInputCom

image
回路図

ピン設定

D-SUB9ピン番号接続先 I/O設定PU/PD設定補足
1 未接続
2 未接続
3 未接続
4 RX 入力
5 +3.3V/+5V 別途電源が必要なデバイスを使用する時は接続
6 未接続
7 未接続
8 GND
9 未接続

パッド状態の読み取り

Windows PCに接続されたXInputに対応したパッドの状態を、XInputComアプリがCOMポート経由で送信する。
送信データは、以下のフォーマット。

バイト 7 6 5 4 3 2 1 0
[0]0xf8
[1]RTBLTBRTLTY X B A
[2] STARTBACKRSLS
[3]ThumbLX
[4]ThumbLY
[5]ThumbRX
[6]ThumbRY
[7]LeftTrigger
[8]RightTrigger
[9]0xf9


ファミコン

パッド状態の読み取りはスーパーファミコンと同じ。



スーパーファミコン

image
回路図

ピン設定

D-SUB9ピン番号接続先I/O設定PU/PD設定 補足
1 CLK 出力
2 P/S 出力
3 /DAT1 入力 プルダウン未接続判定のためのプルダウン
4 /DAT2 入力 プルダウン未接続判定のためのプルダウン
5 +5V +5V
6 SELECT出力
7 未接続
8 GND
9 未接続

パッド状態の読み取り

パッド状態は16bitで、1度に読めるのは/DAT1から1つのボタンのみ(マルチタップ時は/DAT2を使うらしいが未確認)。
パッド状態の読み取りシーケンスは、

  1. P/Sを0
  2. /DAT1からボタン押下状態を読み取り
  3. CLKを1→0へ切り替え(ボタン状態の読み取り位置を次のボタンへ切り替え)
  4. 上記2-3の処理を合計16回繰り返し、パッド状態16bitを取得
  5. P/Sを1

/DAT1から取得するボタン状態は、以下の0から順に取得する。


151413121110987 6 5 4 3 2 10
1 1 1 1 R L XASTARTSELECTYB

ボタン押下時は0、ボタン解放時は1



PC エンジン

image
回路図

ピン設定

D-SUB9ピン番号接続先I/O設定PU/PD設定 補足
1 D0 入力 プルダウン未接続判定のためのプルダウン
2 D1 入力 プルダウン未接続判定のためのプルダウン
3 D2 入力 プルダウン未接続判定のためのプルダウン
4 D3 入力 プルダウン未接続判定のためのプルダウン
5 +5V
6 CLR 入力
7 SEL 出力
8 GND
9 未接続


パッド状態の読み取り

パッド状態は、D0-D3のから4bitの状態を読み込むことができ、CLRを0にした後、SELピンを切り替えることにより、8bit(4bit × 2)の状態を読み込む。

SELD3 D2 D1D0
1
0 RUNSELECTIII


PCエンジンはマルチタップによる複数パッド読み取りに対応しており、カウンタICにより読み取るパッドの切り替えを行う。

カウンタICリセット

SELを1にした後、CLRを0→1へ切り替えることでカウンタICをリセットする。
カウンタICがリセットされたことにより、カレントのパッドが0になる。


次パッドへ切り替え

CLRを0にした後、SELを0→1へ切り替えることでカウンタICのカウンタを進める。
カウンタICを進めたことにより、読み取り対象のパッドが次のパッドへ切り替わる(カレントのパッドが0なら切り替え後はパッド1)。


パッド状態の読み取りシーケンス

たぶん、以下のシーケンスでマルチタップに繋がっている全てのパッド状態を読めます(マルチタップを持っていないため未確認)。

  1. SELを1、CLRを0→1へ切り替え、カウンタICリセット
  2. CLRを0、D3-D0(上|下|右|上)を読み取り
  3. SELを0、D3-D0(RUN|SELECT|II|I)を読み取り
  4. SELを0→1へ切り替え、次のパッドへ切り替え
  5. 上記2.からの処理をパッド数分繰り返す


アベニューパッド6読み取り

読み取り方を調べても良く分からなかったため、正確な読み取りシーケンスは不明。
回路を見ると、アベニューパッド6にカウンタIC(74HC163)があり、このカウンタICの奇数(QAピン)が、3/6ボタンの替えに使用されている。
とりあえず読むことに成功したが、SLOWが効かず、連射も3ボタン時に比べると速度が速くなるため調整が必要など、正しい読み取りシーケンスが不明。
6ボタン対応ソフトを使用し、実機でパッドへの通信の波形を確認したいが、6ボタン対応ソフトを持っていないため確認できないため、とりあえず問題は無視。


構成


カウンタICのQAの選択により、ボタンの読み取り先(74HC157-0/1)を切り替えているようです。
ただ、カウンタICは常に有効であり無効にできず、またリセットする手段も(たぶん)ないし、現在のカウンタ値を知る方法も(たぶん)ないため困る。
たぶん、74HC157-1のSELECT=1が方向キーになっているため、通常は全押し(D3-D0の全てが0)できないが、 カウンタICが74HC157-0に切り替わっている時は、D3-D0が全て0として読めるので、この値を判断に使用するのでは?と思います。

上記で連射が〇となっている時(QB=1)は、ボタンの入力がオフ(解放)にされるため、この仕組みにより連射機能を実現しているみたい。
3ボタン時はCLRを0→1が1度だけ発生するため、2回ごとにボタンのON⇔OFF(ON→ON→OFF→OFFの繰り返し)が発生する。
ただし、SLOWのみ4回に1度ボタンオフを挟む(ON→ON→ON→OFFの繰り返し)動作をしていた。

6ボタン読み取りシーケンス

現在は、とりあえず以下の方法で読み取り。


  1. リセット(SEL=1時に、CLE=0→1)
  2. CLR=0にし、パッド状態0(|左|下|右|上|、または|0|0|0|0|)を読み取り
  3. SEL=0にし、パッド状態1(|RUN|SELECT|Ⅱ|Ⅰ|、または|Ⅵ|Ⅴ|Ⅳ|Ⅲ|)を読み取り
  4. 状態0が15でないなら3ボタンと同じ
  5. CLR=1、SEL=1、CLR=0の順に書き込み、パッド状態3(|RUN|SELECT|Ⅱ|Ⅰ|)を読み取り
  6. この状態だと連射が速くなるため、カウンタICをカウントアップ(CLR=1→0→1→0)し連射位置を合わせる

上記方法だとSLOWが効かないという問題が発生する。



メガドライブ

image
回路図

ピン設定

D-SUB9ピン番号接続先I/O設定 PU/PD設定 補足
1 D0 入力 プルアップ
2 D1 入力 プルアップ
3 D2 入力 プルアップ
4 D3 入力 プルアップ
5 +5V
6 D4 入力 プルアップセガタップ接続時はクロック出力に対する応答に使用
7 SEL 出力
8 GND
9 D5 入力/出力プルアップセガタップ接続時はクロック出力に使用

3ボタンパッド

SELの切り替え後、D0-D5を読むことでボタン状態を取得。

SELD5 D4D3D2D1D0
0STARTA 0 0
1C B

ボタン押下時は0、ボタン解放時は1


6ボタンパッド

SELの切り替えを4回行う毎に、読めるボタングループが変わる。
最初の4回までが3ボタンと同じ、その後の4回が6ボタンパッドで追加されたボタン状態を取得。

SELD5 D4D3 D2D1D0
0 STARTA 0 0
1 C B
0 STARTA 0 0
1 C B
0 STARTA 0 0 0 0
1 1 1 MODEX Y Z
0 STARTA 1 1 1 1
1 C B

ボタン押下時は0、ボタン解放時は1


セガタップ

SELD5D4D3D2D1D0
0 0 0 1 1 1 1
1 0 0 0 0 1 1

セガタップから各パッドのボタン状態を取得するには、D4/D5を通常とは違う方法で使用する。
D5は、ピン設定を入力から出力へ切り替え、セガタップからデータを取得するためのクロックとして使用する。
D4は、セガタップからデータ取得の準備ができた時の応答として使用する(D5==D4となれば準備完了)。


データ取得方法

  1. SELを0
  2. D5へクロック出力(0から始まり、データの受信を行う度、0⇔1を切り替える)
  3. D4がクロック出力の値と同じ値になるまで待つ
  4. D3-D0から受信データ4bitを取得
  5. データ数回、2-4を繰り返す(最小6回、最大18回)
  6. SELを1

データフォーマット

D5 D3D2D1D0
0 1 1 1 1
1 1 1 1 1
0 ポートA コントローラID
1 ポートB コントローラID
0 ポートC コントローラID
1 ポートD コントローラID
n※1ポートA ボタン状態※2
n※1ポートB ボタン状態※2
n※1ポートC ボタン状態※2
n※1ポートD ボタン状態※2
※10から始まり、ボタン状態を取得する度、0⇔1を切り替える
※2ボタン状態はパッドが接続されている時のみ存在、コントローラIDを確認してボタン状態を取得する必要あり

コントローラID

コントローラID種類 ボタン状態のサイズ
0 3ボタン2
1 6ボタン3
15 未接続 なし

ボタン状態

D3D2D1D0 補足
3/6ボタン時
B C A START3/6ボタン時
Z Y X MODE 6ボタン時のみ

ボタン押下時は0、ボタン解放時は1


パッド状態取得

  1. ポートDのコントローラIDまで、4bit×6回のデータを取得
  2. ポートAのコントローラIDが、3ボタンなら4bit×2回、6ボタンなら4bit×3回のデータを取得
  3. ポートBのコントローラIDが、3ボタンなら4bit×2回、6ボタンなら4bit×3回のデータを取得
  4. ポートCのコントローラIDが、3ボタンなら4bit×2回、6ボタンなら4bit×3回のデータを取得
  5. ポートDのコントローラIDが、3ボタンなら4bit×2回、6ボタンなら4bit×3回のデータを取得

パッド判別?

  1. SELが0時にD5-D0が111111なら未接続(プルアップしているため)
  2. SELが0時にD5-D0が001111、SELが1時にD5-D0が1000011ならセガタップ
  3. SELを4回切り替えた後、D3-D0が0000なら6ボタン
  4. 上記以外なら3ボタン


Play Station

Play Stationのコントローラに対し、設定などを行うことができるようですが、今回はDUALSHOCK2の読み取りのみ
(その他コントローラやマルチタップなどを持っていないため確認できない)。
そのため、デジタル・アナログ切り替えなどは自動で行わず、ANALOGボタン押下による切り替えのみ行う。


image
回路図

ピン設定

D-SUB9ピン番号接続先I/O設定 PU/PD設定補足
1 未接続入力プルアップ
2 未接続入力プルアップ
3 MISO 入力プルアップ 外部にプルアップ抵抗が必要
4 SS 出力
5 +3.3V +3.3V +5Vでも良いらしいので+5Vを繋いでいるが、安全に電圧下げた方が良いか?
DIPスイッチの切り替えにより+3.3Vに対応
(抵抗分圧より安定する+配線数が減るため)。
6 SCLK 出力
7 ACK 入力プルアップ 外部にプルアップ抵抗が必要
8 GND
9 MOSI 出力

+7Vは振動機能のため接続しない


パッド状態の読み取り

image
通信タイミング

コントローラとの通信は、基本的にはSPI(モード3)を250kHzのクロックで動作させるのと同じ。
ただし、8ビットデータを転送するごとにACKを返すため(I2Cの書き込み時のような感じ)、ACKが返るまで待つ処理が必要となる。
単純にSPIをDMA設定するだけで終わりとはならないため、高速+低負荷で動作させるにはACKピンの割り込みを考慮する必要がある(今回は片方のCPUの空き時間を使用するため、処理負荷を気にせず、簡単にポーリングで処理)。
また、ACKは次の8bitデータの準備ができたことを表しているようで、例えば5バイトデータの通信を行う場合、先頭から4バイトデータの転送後にACKは発生するが、最後の1バイトデータの転送後にACKは発生しない。
8bitデータの送受信はLSBファーストとなっており、Raspberry Pi PicoのSPIはLSBファーストに未対応、CPUもビット反転の命令が存在しないため、通信とは別に8bitデータのビット反転処理が必要。


DUALSHOCK2

デジタル(ALALOG LED 消灯時)

送受信バイト送信 受信
0 0x01 0xff※1
1 0x42 0x41
2 ※20x5a
3 ※2ボタン状態1
4 ※2ボタン状態2

アナログ(ALALOG LED 赤時)

送受信バイト送信 受信
0 0x01 0xff※1
1 0x42 0x73
2 ※20x5a
3 ※2ボタン状態1
4 ※2ボタン状態2
5 ※2右アナログ水平方向(左:0、中心:128、右:255)
6 ※2右アナログ垂直方向(上:0、中心:128、下:255)
7 ※2左アナログ水平方向(左:0、中心:128、右:255)
8 ※2左アナログ垂直方向(上:0、中心:128、下:255)

ボタン状態

ボタン状態bit7bit6bit5bit4bit3 bit2 bit1 bit0
1 START右アナログボタン※3左アナログボタン※3SELECT
2 × R1 L1 R2 L2

ボタン押下時は0、ボタン解放時は1

※10xffが読めるが、受信の先頭バイトのため不定値として扱ったほうが良い?
※2値は不定値で問題ないが、通常は0x00か0xff?
※3アナログ時のみ有効、デジタル時は1固定


Wii クラシックコントローラ / ニンテンドークラシックミニ スーパーファミコンコントローラ

image
回路図

パッドとの通信にI2Cを使用しているということなので、ニンテンドークラシックミニ スーパーファミコンとコントローラの通信を、ロジアナで通信を確認しました。

image

スレーブアドレスは0x52、通信速度は200kHz、各コマンドの送信間隔が約6.6msで行っています。

パッド状態の読み取りテストを行った時、初めはI2Cの通信速度を400kHzで試していましたが、なかなか通信に成功しないためSCL/SDAの波形をオシロで確認した所、だいぶ波形がなまっています(I2Cの波形がなまるのは良く見かけますが、ここまでなまっていると良く通信できるなと)。
抵抗値を色々と変更して試してみましたが通信エラーの発生は改善されず、試しに実機と同じ200kHzへ落とした所通信に成功。
ただ、200kHzでは遅すぎるため、350kHz(350kHzは設定値、実測320kHz程度)で動作するように抵抗を選びました。

次に、Wii クラシックコントローラを試してみたところ、こちらは400kHzで動作します。

コントローラによる違いが気になったため、試しにSCL/SDAの電圧を下げて通信してみると、ニンテンドークラシックミニ スーパーファミコンコントローラは2.8V辺りでエラーが発生、Wii クラシックコントローラは2.5V以下でもエラーが発生せず、閾値に結構違いがあるようです。


ピン設定

D-SUB9ピン番号接続先I/O設定PU/PD設定 補足
1 DETET 入力 プルダウン未接続判定のためのプルダウン
2 未接続
3 未接続
4 未接続
5 +3.3V +3.3V
6 SDAHi-Z 外部にプルアップ抵抗が必要
7 未接続
8 GND
9 SCLHi-Z 外部にプルアップ抵抗が必要

I2Cは一般的な通信方法、ただし、幾つかのコマンド後に待ち時間が必要(待たないとエラーやデータ異常になる)。

パッド状態の読み取りシーケンスは


  1. スレーブアドレス0x52へ、「0xf0, 0x55」を書き込む。
  2. スレーブアドレス0x52へ、「0xfb, 0x00」を書き込む。
  3. 10usec程度待つ。
  4. スレーブアドレス0x52へ、「0xfe, 0x03」を書き込む。
  5. スレーブアドレス0x52へ、「0xfa」を書き込む。
  6. 70usec程度待つ。
  7. スレーブアドレス0x52から、6バイト読み込む。
    (読み込みデータの4バイト目が0x03、5バイト目が0x01でなければエラー)
  8. 読み込みデータの0バイト目にパッド種類(たぶん)
    • 0x00 = Wii クラシックコントローラ
    • 0x01 = ニンテンドークラシックミニ スーパーファミコンとコントローラ
    • 上記以外は不明
  9. スレーブアドレス0x52へ、「0x00」を書き込む。
  10. 30usec程度待つ。
  11. スレーブアドレス0x52から、21バイト読み込む。
    (読み込みデータの8から20バイトが0x00でなければエラー)
  12. 読み込みデータの0から7バイトにパッド状態
  13. 上記シーケンスが正常に行われたなら、上記10-12の処理を繰り返しでパッド状態を取得

Wii クラシックコントローラ

配列bit7bit6bit5bit4 bit3bit2 bit1bit0
0 LX アナログ値(左:30近辺、中心:130近辺、右:228近辺)
1 RX アナログ値(左:30近辺、中心:130近辺、右:228近辺)
2 LY アナログ値(下:30近辺、中心:124近辺、上:228近辺)
3 RY アナログ値(下:30近辺、中心:128近辺、上:228近辺)
4 L ボタンアナログ値(押下時:34近辺、解放時:252近辺)
5 R ボタンアナログ値(押下時:26近辺、解放時:230近辺)
4 L SELECTHOMESTARTR 1
5 ZL b y a x ZR

ボタン押下時は0、ボタン解放時は1
アナログ値は手持ちのWii クラシックコントローラで取得したもの


ニンテンドークラシックミニ スーパーファミコン

配列bit7bit6bit5bit4 bit3bit2 bit1bit0
0 130固定
1 133固定
2 133固定
3 133固定
4 L ボタン状態(押下時:248、解放時:0)
5 R ボタン状態(押下時:248、解放時:0)
4 L SELECT1 STARTR 1
5 1 B Y A X 1

ボタン押下時は0、ボタン解放時は1