背景

先日の Steam セールで鉄拳7を購入した。ゲームパッドで遊んではいたが、やっぱりコマンドテクニックが必要になる。三島一八の風神拳ステップ ⇨☆⇩⬂⇨ を十字キーで出すのがつらい。

ふと、数年前にハードオフでアケコン Ascii Stick ZERO3 を数千円で買ったのを思い出した。 アケコンの基本はスイッチの入力を USB のプロトコルに載せるだけだからかんたんだろうと考え、 購入したものから筐体・スイッチのみを頂いて適当なマイコンに USB を喋らせ、PC用のアケコンとして再利用することにした。

Ascii Stick ZERO3

まずはスイッチ等のインターフェースを確認する。とはいっても、アーケードスティックの部分はともかく、ボタンはスイッチとしてインターフェースを出せるだろう。

分解してみると、配線は、ボタンが直接基板に配線され、アーケードスティックからはケーブルが付いていることを確認した。 ボタンは、同じように基板に直接配線できるときれいだろうが、生基板がもったいないのでケーブルを出して繋ぐことにした。

アーケードスティックは、Sanwa JLHS-8Y FLASH 1 というものらしい。いわゆる普通のアーケードスティックは、4方向にマイクロスイッチがついていて、レバーを倒すとそれぞれのスイッチが入ったり切れたりする仕組みらしいのだが、この JLHS-8Y というのは光センサー (フォトリフレクタかな?) を採用しており、接点寿命の概念がなくなり故障率が低いことがウリのようだ。もっとも、物理スイッチがないことからクリック感もなく、そのせいであまり人気はでなかったらしい。

ともあれ、この JLHS-8Y は 5V をかけると出力の4種類の端子から電圧を取れそうな見た目だったので、5V をかけてスティックをいろいろな方向に倒しながらそれぞれの端子の電圧を測った。結果、上に倒すとオレンジ、下で黄、右で青、左で緑の端子に 0.13V 程度の電圧が現れることが判明した。なるほどセンサーデータが生で出てきているので、マイコンに入力する前に電圧増幅が必要そうである。

inside Ascii Stick ZERO3
図1: Ascii Stick ZERO3 の中身

V-USB のテスト

USB 通信をするための方法について考察する。高級なマイコン (ESP32など) を使えば、はじめから SoC として USB のためのハードウェアが載っていることがあり、 それを使えばたいていかんたんに済むが、高級なマイコンは高いので、今回は手持ちで多量にある AVR ATmega88P を使うことにした。

ここで問題になるのが、レイテンシだ。 mega88P は USB のためのハードウェアを持っていないので、ソフトウェアでプロトコルを処理することになる。 用途は格ゲーなので、そのレイテンシはできるだけ小さくしたい。

レイテンシの目標は、ゲームのリフレッシュレートである 60 FPS に追いつく値にすることにした。レイテンシに直せば、16.6 ms 以下だ。 理論上は、レイテンシは短ければ短いほうが、次のフレームに入力が間に合う可能性が高くなるので好ましいのだが、人間の反応速度の限界を考えればこのあたりでいいだろう。

さて、mega88P にソフトウェアで USB 通信することにしたが、フルスクラッチで USB プロトコルを実装するのはたいへんである。今回は、V-USB と呼ばれる ready-made のライブラリを用いることにした。この記事では、V-USB の試験として、例題の vusb-20121206/examples/hid-mouse を試した。これは、円弧を描くマウス入力をエミュレートする。

この例題は 168P が対象なので、88P に対応するために少々コードを変更した。詳しくは GitHub にアップロードしてあるので、当該コミットの diff を参照してほしい。

88P のひとつのヒューズに不適な値が書き込まれてそのままにしてしまったようで (おそらく DWEN)、ISP での書き込みができなくなっていた:

Failed to enter programming mode. ispEnterProgMode: Error status received: Got 0xc0, expected 0x00 (Command has failed to execute on the tool) Unable to enter programming mode.

High Voltage Programming の必要がありそうだが、ライタを持っていないので今度適当に作る。

参考: https://www.hackster.io/sbinder/attiny85-powered-high-voltage-avr-programmer-3324e1

ソフトウェアを Docker Image https://hub.docker.com/r/dea82/avr-gcc-docker 上でビルドし、ROMに書き込み、また、Makefile に従って Atmel Studio の Device Programming ツールで FUSE を設定した。 V-USB のマニュアルに従い、ブレッドボード上に水晶発振器などを加えて配線、電源を入れたらマウスが動くのを確認できた。

ブレッドボード上の配線
図2: ブレッドボード上の配線

さて、本題のレイテンシだが、オシロスコープで観察すると、30 ms または 60 ms 間隔で信号が送られているようだ。

USB 信号の間隔。 30ms の場合。
図3: USB 信号の間隔。 30ms の場合。
USB 信号の間隔。 60ms の場合。
図4: USB 信号の間隔。 60ms の場合。

目標の 13.3 ms に届いていない。

ファームウェアのアプリケーション部分が delay を挟んでいる可能性を考えたが、ソースコードを見る限りそういうことではないので、これがこの条件での最速なのだと思われる。 USB のプロトコルに目を向けると、あるモードではマスター (ホストPC) が信号を poll するらしい。ファームウェアを読んでも、usbPoll() という呼び出しがメインルーチン内にある。したがって、どうにかして poll の周期を変更したり、他の (割り込みベースの?) モードを試すことで、レイテンシを改善できる可能性がある。

続く