最初に書いておきますが、ArduinoでIOエキスパンダICを使うのが主な目的で、読み込むカートリッジはメガロムとかではなくて32KByteのものです。
MSXのカートリッジは16bitのアドレス線と8bitのデータ線と何本かの制御線を使って読み込むことができます。一方、ArduinoのI/Oポートはボードサイズの割には本数が多いのですが、アナログ/デジタル兼用だったりデジタル14本で16bitまとめて扱えなかったりと、直結するには少しだけ面倒です。
そこで、MCP23017というICを使って、ArduinoのI/Oポートを増やします。
MCP23017は秋月で1個120円程度で売っています。
詳しい使い方は秋月の商品ページにあるデータシートを眺めてみるのがいいと思います。英語ですが、機能の割にはArduinoのライブラリが揃っているので使い方は単純です。
あと、製品名で検索すると、実際に回路を組んでいる方のページがいくつかヒットします。こちらのページ→
素子:I/Oエキスパンダ MCP23017に機能の詳細が書かれています。
ざっくりまとめると、ArduinoにMCP23017をつなげると、2本の制御線で16本の入出力ピンを扱えるようになるということです。便利ですね。唯一の欠点は、2本←→16本という変換が入るので、入出力の速度が遅いということです。
データシートに出て来る単語で理解しにくかったのがBANKです。これについて説明します。
MCP23017は、16本のI/Oポートがありますが、内部的にはPORT-AとPORT-Bの2つの8bitに別れていて、I2Cを通したデータのやり取りは8bit単位です。データの読み書きはMCP23017のレジスタを使うのですが、レジスタは8bitサイズになっています。例えば、16Bitのデータを読み取りたい場合は、「読み取り先レジスタにPORT-Aを指定→データ読み取り→読み取り先にPORT-Bを指定→データ読み取り」という処理になりますが、I2Cでのやり取りは遅いので、出来るだけ信号のやり取りを減らしたいのです。
そこで、MCP23017にはシーケンシャルモードというものがあり、このモードにしてから、読み書きレジスタを指定してからレジスタから読み/書き込みをすると、自動的に読み書き対象のレジスタ番号が+1されるようになって、「読み取り先レジスタにPORT-Aを指定→データ読み取り(PORT-A)→データ読み取り(PORT-B)」でレジスタ指定の処理を減らせるようになります。
ここでMCP23017のレジスタ一覧をみてみると、BANK=0の時は、PORT-A(GPIOA)とPORT-B(GPIOB)のレジスタ番号は隣り合っているので、PORT-AとPORT-Bを連続して読むことが出来るわけですね。
一方、BANK=1の時はというと、レジスタ番号を並び変えて読むとわかるように、PORT-Aに関するレジスタとPORT-Bに関するレジスタが分離した並びになるので、PORT-A/PORT-Bのどちらかを一括して設定する時などに便利です。
BANKの切り替えは、IOCONレジスタという、MCP23017のモードを切り換えるレジスタを操作します。
シーケンシャル動作が不要で、常に同じPORTからデータを読みたいのであれば、バイトモードに切り換えます。電源投入時のデフォルトではシーケンシャルモードのようです。
■ ArudinoとMCP23017をつなげる
基本回路は単純です。
ArduinoのA4,A5端子をI2C用に使っています。2個のMCP23017はI2Cバスに繋げるだけでよいので配線も単純です。2個のMCP23017を識別するためにアドレスを電源とGNDにつないでアドレスを固定しています。あとは、MCP23017のI/Oピンにデバイスを繋げるだけです。
ArduinoからMCP23017を扱うには、I2C通信のプログラムをわざわざ書くまでもなく、ArduinoにはI2Cのライブラリが含まれていますし、ネットで探すと、MCP23017の制御ライブラリがあります。
https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
ページ右端にあるDownload ZIPをクリックしてZIPファイルを展開し、Adafruit_MCP23017というフォルダを作ってファイル一式を入れて、そのフォルダをArduinoがインストールしてあるフォルダにあるlibrariesへと移動します。私のPCでは、Arduino-IDE\libraries\Adafruit_MCP23017\です。
ライブラリのファイルを移動してから、ArudinoのIDEを起動すると、メニューのスケッチ→Include LibraryにAdafruit_MCP23017という項目が増えているはずです。
Adafruit_MCP23017のZIPファイルにはボタンとLEDを使ったサンプルプログラムが含まれているので、Lチカさせて動作確認するとよいかもです。
■ Adafruit_MCP23017を拡張する
Adafruit_MCP23017にはMCP23017を扱うための関数が用意されていますが、ちょっと物足りなかったので勝手に2つほど拡張して、8bit単位での出力関数を追加しました。(今みると関数名と引数がイマイチですね・・・)
Arduinoのライブラリの仕組みがわかってないのですが、これってC++なんでしょうか。
Adafruit_MCP23017.cpp
void Adafruit_MCP23017::pinMode8(uint8_t p, uint8_t d) { writeRegister(p, d); } void Adafruit_MCP23017::writeGPIO(uint8_t p, uint8_t a) { Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); if (p == 0) wiresend(MCP23017_GPIOA); else { wiresend(MCP23017_GPIOB); } wiresend(a); Wire.endTransmission(); }
ヘッダファイルのAdafruit_MCP23017.hにも2行ほど書き加えます。
void pinMode8(uint8_t p, uint8_t d); writeGPIO(uint8_t p, uint8_t a);
■ MSXのカートリッジを読み込んでみる
Lチカからいきなりすっとばしてますが、たまたま手元にあったMSXのROMカートリッジをArduino+MCP23017×2で読み取ってみました。MSXのカートリッジを読み込むには、アドレスバス16本+データバス8本+制御線数本が必要なので、MCP23017が2個必要になります。
MSXカートリッジのピン並びは適当にググってください。MSX関連ドキュメント置き場にもあります。
アドレス0のMCP23017はGPB0-7をMSXのアドレスバスA0-A7に、GPA0-7をMSXのアドレスバスA8-A15につなぎ、アドレス1のMCP23017はGPB0-7をMSXのデータバスD0-D7に、GPA0にはMSXの^CS12ピンをつなげました。
最初、MSXの端子に出ている制御線の使い方がわからず、結局、カートリッジを分解して端子を調べました。
MSXのカートリッジROM読み込み用の制御線は^CS1,^CS2、^CS12、^SLTSLがありますが、MSX版ハイドライドでは、^CS12端子だけを使っていました。また、MSX版ハイドライドは32KByteのROMなのでA15は意味を持たないように思えますが、配線を追いかけてみたところ、アドレスバスのA14が未接続でした。どうやらカートリッジ内のチップがMSXアドレス空間としてアクセスできるようにアドレス変換をしているようです。
つまり、4000HからBFFFHまでの32KByte分を読み取ればよく、0000h-3FFFhは4000h-7FFFhは同一メモリ空間になってしまう(8000h-とC000h-も)ということになるのだと思います。
全体の処理の流れはこのようになります。
- CS12を無効にする
- アドレスバスを設定する
- CS12を有効にする
- データバスからデータを読み取る
ソースコードはこちら。途中でめんどくさくなって整頓しないままなので雑ですが。シリアルポートにダンプリストの形式で送ってます。
これで4000hから読み取ってみたのですが、ちょっと不安定です。最初の256Byteくらいは確実に読み取れるのですが、後半はところどころデータが化けてしまいました。
ちゃんとした回路を起こしたのではなく、ブレッドボード上でジャンパ線を飛ばしまくったので不安定なのかも。