映像出力、サウンドを試したので、次に入力デバイスをテストします。
Raspberry Pi Picoは、COMポートによる入出力以外に、GPIOによるタクトスイッチや、A/DコンバーターによるA/Dキー、アナログスティックなどの制御を簡単に行うことができると思います。
が、リアルタイムOSのコントローラUSB変換でゲームパッド(+ロータリーエンコーダ)を調べたことがあったので、その時のゲームパッドと新たに追加した以下のパッドを入力デバイスとして使用します。
回路図 |
パッドに必要なブロックは、上記「パッド端子(D-SUB9Pオス)」と「PAD ID / PAD端子電源」。
DIP SWの設定は、
XInputCom | PCエンジン | メガドライブ | スーパーファミコン | Play Station | Wii クラシックコントローラ |
---|---|---|---|---|---|
□□□□ ■■■■ |
■□□■ □■■□ |
■□■□ □■□■ |
■□■■ □■□□ |
□■□□ ■□■■ |
□■□■ ■□■□ |
未使用ピンが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 | CTS0 | SCLK1 | SDA1 | 7A |
2 | D1 | 15 | RTS0 | MOSI1 | SCL1 | 7B |
3 | D2 | 16 | TX0 | MISO0 | SDA0 | 0A |
4 | D3 | 17 | RX0 | SS0 | SCL0 | 0B |
5 | +5V | +3.3V/+5V切り替え | ||||
6 | D4 | 18 | CTS0 | SCLK0 | SDA1 | 1A |
7 | SEL | 1 | RX0 | SS0 | SCL0 | 0B |
8 | GND | GND | ||||
9 | D5 | 19 | RTS0 | MOSI0 | SCL1 | 1B |
回路図 |
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] | RTB | LTB | RT | LT | Y | X | B | A |
[2] | 下 | 上 | 右 | 左 | START | BACK | RS | LS |
[3] | ThumbLX | |||||||
[4] | ThumbLY | |||||||
[5] | ThumbRX | |||||||
[6] | ThumbRY | |||||||
[7] | LeftTrigger | |||||||
[8] | RightTrigger | |||||||
[9] | 0xf9 |
パッド状態の読み取りはスーパーファミコンと同じ。
回路図 |
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を使うらしいが未確認)。
パッド状態の読み取りシーケンスは、
/DAT1から取得するボタン状態は、以下の0から順に取得する。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | R | L | X | A | 右 | 左 | 下 | 上 | START | SELECT | Y | B |
ボタン押下時は0、ボタン解放時は1
回路図 |
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)の状態を読み込む。
SEL | D3 | D2 | D1 | D0 |
---|---|---|---|---|
1 | 左 | 上 | 右 | 上 |
0 | RUN | SELECT | II | I |
PCエンジンはマルチタップによる複数パッド読み取りに対応しており、カウンタICにより読み取るパッドの切り替えを行う。
SELを1にした後、CLRを0→1へ切り替えることでカウンタICをリセットする。
カウンタICがリセットされたことにより、カレントのパッドが0になる。
CLRを0にした後、SELを0→1へ切り替えることでカウンタICのカウンタを進める。
カウンタICを進めたことにより、読み取り対象のパッドが次のパッドへ切り替わる(カレントのパッドが0なら切り替え後はパッド1)。
たぶん、以下のシーケンスでマルチタップに繋がっている全てのパッド状態を読めます(マルチタップを持っていないため未確認)。
読み取り方を調べても良く分からなかったため、正確な読み取りシーケンスは不明。
回路を見ると、アベニューパッド6にカウンタIC(74HC163)があり、このカウンタICの奇数(QAピン)が、3/6ボタンの替えに使用されている。
とりあえず読むことに成功したが、SLOWが効かず、連射も3ボタン時に比べると速度が速くなるため調整が必要など、正しい読み取りシーケンスが不明。
6ボタン対応ソフトを使用し、実機でパッドへの通信の波形を確認したいが、6ボタン対応ソフトを持っていないため確認できないため、とりあえず問題は無視。
SELECT | D3 | D2 | D1 | D0 |
---|---|---|---|---|
0 | Ⅵ | Ⅴ | Ⅳ | Ⅲ |
1 | 0 | 0 | 0 | 0 |
SELECT | D3 | D2 | D1 | D0 |
---|---|---|---|---|
0 | RUN | SELECT | Ⅱ | Ⅰ |
1 | 左 | 下 | 右 | 上 |
QD | QC | QB | QA | 連射 | 74HC157-0選択 | 74HC157-1選択 |
---|---|---|---|---|---|---|
未接続 | 未接続 | 0 | 1 | × | × | 〇 |
未接続 | 未接続 | 0 | 1 | × | 〇 | × |
未接続 | 未接続 | 1 | 0 | 〇 | × | 〇 |
未接続 | 未接続 | 1 | 1 | 〇 | 〇 | × |
カウンタ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の繰り返し)動作をしていた。
現在は、とりあえず以下の方法で読み取り。
上記方法だとSLOWが効かないという問題が発生する。
回路図 |
D-SUB9ピン番号 | 接続先 | I/O設定 | PU/PD設定 | 補足 |
---|---|---|---|---|
1 | D0 | 入力 | プルアップ | |
2 | D1 | 入力 | プルアップ | |
3 | D2 | 入力 | プルアップ | |
4 | D3 | 入力 | プルアップ | |
5 | +5V | |||
6 | D4 | 入力 | プルアップ | セガタップ接続時はクロック出力に対する応答に使用 |
7 | SEL | 出力 | ||
8 | GND | |||
9 | D5 | 入力/出力 | プルアップ | セガタップ接続時はクロック出力に使用 |
SELの切り替え後、D0-D5を読むことでボタン状態を取得。
SEL | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|
0 | START | A | 0 | 0 | 下 | 上 |
1 | C | B | 右 | 左 | 下 | 上 |
ボタン押下時は0、ボタン解放時は1
SELの切り替えを4回行う毎に、読めるボタングループが変わる。
最初の4回までが3ボタンと同じ、その後の4回が6ボタンパッドで追加されたボタン状態を取得。
SEL | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|
0 | START | A | 0 | 0 | 下 | 上 |
1 | C | B | 右 | 左 | 下 | 上 |
0 | START | A | 0 | 0 | 下 | 上 |
1 | C | B | 右 | 左 | 下 | 上 |
0 | START | A | 0 | 0 | 0 | 0 |
1 | 1 | 1 | MODE | X | Y | Z |
0 | START | A | 1 | 1 | 1 | 1 |
1 | C | B | 右 | 左 | 下 | 上 |
ボタン押下時は0、ボタン解放時は1
SEL | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 | 1 | 1 |
セガタップから各パッドのボタン状態を取得するには、D4/D5を通常とは違う方法で使用する。
D5は、ピン設定を入力から出力へ切り替え、セガタップからデータを取得するためのクロックとして使用する。
D4は、セガタップからデータ取得の準備ができた時の応答として使用する(D5==D4となれば準備完了)。
D5 | D3 | D2 | D1 | D0 |
---|---|---|---|---|
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 |
※1 | 0から始まり、ボタン状態を取得する度、0⇔1を切り替える |
※2 | ボタン状態はパッドが接続されている時のみ存在、コントローラIDを確認してボタン状態を取得する必要あり |
コントローラID | 種類 | ボタン状態のサイズ |
---|---|---|
0 | 3ボタン | 2 |
1 | 6ボタン | 3 |
15 | 未接続 | なし |
D3 | D2 | D1 | D0 | 補足 |
---|---|---|---|---|
上 | 下 | 右 | 左 | 3/6ボタン時 |
B | C | A | START | 3/6ボタン時 |
Z | Y | X | MODE | 6ボタン時のみ |
ボタン押下時は0、ボタン解放時は1
Play Stationのコントローラに対し、設定などを行うことができるようですが、今回はDUALSHOCK2の読み取りのみ
(その他コントローラやマルチタップなどを持っていないため確認できない)。
そのため、デジタル・アナログ切り替えなどは自動で行わず、ANALOGボタン押下による切り替えのみ行う。
回路図 |
D-SUB9ピン番号 | 接続先 | I/O設定 | PU/PD設定 | 補足 |
---|---|---|---|---|
1 | 未接続 | 入力 | プルアップ | |
2 | 未接続 | 入力 | プルアップ | |
3 | MISO | 入力 | プルアップ | 外部にプルアップ抵抗が必要 |
4 | SS | 出力 | ||
5 | +3.3V | +3.3V | DIPスイッチの切り替えにより+3.3Vに対応 (抵抗分圧より安定する+配線数が減るため)。 | |
6 | SCLK | 出力 | ||
7 | ACK | 入力 | プルアップ | 外部にプルアップ抵抗が必要 |
8 | GND | |||
9 | MOSI | 出力 |
+7Vは振動機能のため接続しない
通信タイミング |
コントローラとの通信は、基本的には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データのビット反転処理が必要。
送受信バイト | 送信 | 受信 |
---|---|---|
0 | 0x01 | 0xff※1 |
1 | 0x42 | 0x41 |
2 | ※2 | 0x5a |
3 | ※2 | ボタン状態1 |
4 | ※2 | ボタン状態2 |
送受信バイト | 送信 | 受信 |
---|---|---|
0 | 0x01 | 0xff※1 |
1 | 0x42 | 0x73 |
2 | ※2 | 0x5a |
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) |
ボタン状態 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|---|
1 | 左 | 下 | 右 | 上 | START | 右アナログボタン※3 | 左アナログボタン※3 | SELECT |
2 | □ | × | 〇 | △ | R1 | L1 | R2 | L2 |
ボタン押下時は0、ボタン解放時は1
※1 | 0xffが読めるが、受信の先頭バイトのため不定値として扱ったほうが良い? |
※2 | 値は不定値で問題ないが、通常は0x00か0xff? |
※3 | アナログ時のみ有効、デジタル時は1固定 |
回路図 |
パッドとの通信にI2Cを使用しているということなので、ニンテンドークラシックミニ スーパーファミコンとコントローラの通信を、ロジアナで通信を確認しました。
スレーブアドレスは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 | SDA | Hi-Z | 外部にプルアップ抵抗が必要 | |
7 | 未接続 | |||
8 | GND | |||
9 | SCL | Hi-Z | 外部にプルアップ抵抗が必要 |
I2Cは一般的な通信方法、ただし、幾つかのコマンド後に待ち時間が必要(待たないとエラーやデータ異常になる)。
パッド状態の読み取りシーケンスは
配列 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|---|
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 | SELECT | HOME | START | R | 1 |
5 | ZL | b | y | a | x | ZR | 左 | 上 |
ボタン押下時は0、ボタン解放時は1
アナログ値は手持ちのWii クラシックコントローラで取得したもの
配列 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|---|
0 | 130固定 | |||||||
1 | 133固定 | |||||||
2 | 133固定 | |||||||
3 | 133固定 | |||||||
4 | L ボタン状態(押下時:248、解放時:0) | |||||||
5 | R ボタン状態(押下時:248、解放時:0) | |||||||
4 | 右 | 下 | L | SELECT | 1 | START | R | 1 |
5 | 1 | B | Y | A | X | 1 | 左 | 上 |
ボタン押下時は0、ボタン解放時は1