ゲームキューブコントローラとAVRをつなげた話

お久しぶりです。大根です。
このブログ、たまに更新するようにすると言いつつ、いざ更新するとなるとデータの整理などが億劫でなかなか記事を書かない私です。
さて、今回は題の通り、ゲームキューブコントローラ(GCコン)とAVRをつなげた話です。当然、つなげるだけじゃなくてそれでロボットを操作できるようにしたわけですが。

解説が想定外に長くなったので、先に作ったものを貼ります。このアーカイブの中のプログラムを何かに使いたい場合、好きに使って構いません。パブリックドメインというやつです。
gc_controller_avr.tar.gz 15480bytes
ロボットの制御に使う都合で、プログラムはSPI通信で他のマイコンに情報を送るようになっているものが付属してます。あとはボタン入力に合わせてLEDが光るやつと、テスト用に使っていたArduinoのプログラムです。(ちなみに、Arduinoくんには書き込み機としても働いてもらいました)

私がGCコンをロボットに最初に繋いだのは高校生の時で、このページを参考にRenesasのR8C38Cという16bitマイコンを使ってロボットに接続するための諸々を作りました。しかし処理速度の問題で、プログラムは納得の行くものになりませんでした(動くものは作れましたが)。ちなみに、処理速度の問題を克服するために専用回路の制作を考えたこともありますが、あまりにも巨大になったため断念しました。

ということで、今回作ったものはその時の知識を使ってAVRでプログラムを書き直したものです。高校生の時同様、このページを見ながら作成しました。今回使用したAVRは8bitマイコンなので演算能力はあまり高くないものの、ほとんどの命令が1クロックで実行できて、R8Cを使った時同様20MHzで動かしたのにもかかわらずだいぶ時間に余裕がありました。とは言っても、GCコンのプロトコルでは一番小さいパルスの幅が1usですから、実行時間が正確に合わせられるように命令セットを眺めながらアセンブリで書きました。(これはR8Cの時と同様ですが、R8Cでは実行が遅いせいでループが使えませんでした)

詳しい説明に入ります。

使用したマイコンはATmega328P。ArduinoUnoに乗っているのと同じものです。明らかにI/Oの数が過剰ですが、たまたま手元にあったのと、本来は学祭に使うために複数コントローラをつなぐ予定だったため、これを使用しています。同じ命令セットのマイコンならちょっとした変更で(あるいは全く変更なしに)動くようになると思います。

配布しているファイルの中の、gc_controller_avr/gc2spi/circuit1.pdfに回路図があります。無駄に英語なのは国際的な展開を予定しているからではなく、回路図エディタがマルチバイト文字に対応してくれなかったからです。
不必要に大量のコネクタがあるのは、デバッグのためと仕様変更を繰り返した結果です。とりあえず動かしたいだけなら、マイコンとICSP(SPI通信用にSS端子を追加したもの)、GCコンのコネクタ、BS170、水晶振動子とそれらの周りの細々したものだけ繋げて電源を用意すれば良いはずです。
GCコンのコネクタはハードオフで500円で買ってきたジャンクのGCを叩き壊して手に入れたものです。(非常に分解が難しく、諦めてハンマーで叩き壊しました)
ゲームキューブへの信号の送信部分に利用したBS170はスイッチングが高速なMOSFETで、真ん中の足がゲートになっているので注意してください。ちなみに、高速でスイッチングするので不用意に置き換えると動かなくなります。

gc_controller_avr/gc_cont/中のファイルがGCコンのプロトコル(通称Nintendoプロトコル、N64と共通)で通信するためのやつ(gc_cont.c, gc_cont.h)とボタン入力に合わせてPortD-2につなげたLEDを光らせるプログラム(main.c)です。MakefileはGCC用ですが、他のコンパイラを使う場合も参考になるはずです。(GCC形式の拡張インラインアセンブリに対応してないと書き換えが面倒ですが)
gc_cont.cは、PortD-0を出力,PortD-1を入力に固定してあります(可変にするのが面倒だった)。変更したい場合は、gc_cont.cの43,53,68行目の変更で入力ピンが、90,103,117,130,140行目の変更で出力ピンが変更できるはずです。それぞれの行にある命令である、sbic, sbis, cbi, sbiはそれぞれビット単位での読み書きのための命令で、第一引数が対象のI/Oアドレス(I/Oレジスタを操作する命令の一部で利用するアドレス), 第二引数が操作するビットです。変更する場合は下の表(データシートを元に作成)を参考にどうぞ。(詳細はデータシート36章のRegister Summary、14章のI/O-Portsや命令セットマニュアルの各命令の項目を参照)

レジスタの名前 アドレス I/Oアドレス
PORTB 0x25 0x05
PINB 0x23 0x03
PORTC 0x28 0x08
PINC 0x26 0x06
PORTD 0x2B 0x0B
PIND 0x29 0x09

nintendo_protocol_get_datanintendo_protocol_send_dataはそれぞれNintendoプロトコルでデータを送受信する関数ですが、この時渡すデータは各ビットを1byteで表したもの(0は0x00,1は0x01)です。この変換をするための関数も一緒に用意しているので良かったら使ってください。
インラインアセンブリの部分は、値渡しやレジスタの変化などをコンパイラに伝えるため、GCC形式の拡張インラインアセンブリを使用しています。もっとうまい書き方があるかもしれませんが、私にはこれが限界でした。
また、この関数を呼ぶときは必ず、割り込みが発生しないようにしてください。割り込みが発生するとタイミングがずれて、正しく通信できません。

gc_controller_avr/gc2spi/の中のファイルがGCコンと他のマイコンをSPIでつなげるためのプログラムです。gc_controller_avr/gc_cont/gc_cont.*に依存しているので注意してください。SPIのSlaveとして働き、GCコンとの通信をやってくれます。Masterからの3種類の命令(0x01, 0x02, 0xff)を受けて働きます。0x01はGCコンがつながっているかテストする命令, 0x02はGCコンから入力情報を取得する命令, 0xffは戻り値を読む命令です。0x01, 0x02は送った後、暫くAVRはGCコンとの通信に専念するため、何を送っても返事をしません。十分待ってから0xffを送ると、[データサイズ|1byte][データ|*byte]の列を返します。こちらもMakefileはGCC用ですが、他のコンパイラを使う場合も参考になるはずです。

gc_controller_avr/gc2spi_host.inoは、gc_controller_avr/gc2spi/のテスト用に書いたArduinoのプログラムです。シリアルポートを使ってPCに115200bpsで入力情報をガンガン送ってくれます。pin_powというのはAVRライタとして使っていた関係のピンなので、気にしなくていいです。10番ピンをSS、13, 12, 11をそれぞれSCK, MISO, MOSIに繋げて動かします。詳細はArduinoのリファレンスを参照してください。

さて、だいぶ長い説明になりましたが、これでだいたい必要なことは(誰が必要としているか謎ですが)説明が書けたと思います。私はGCコンが大好きで、もっと流行ればいいと思っているので、使いたい人はわからないことなど質問などしてくれれば喜んで答えると思います。
最後まで読んでくれてありがとうございました。これからも私の作るよくわからないガラクタをよろしくお願いします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です