A1FにPS/2キーボードを接続(2)
ソース と Hexファイル です。(Fuse_L=0xF7,Fuse_H=0x5F)
これを土台にすれば色々な機種にも応用が効くんじゃないかと思います。
MSXキーボードとPS/2キーボードの対応
MSX特有のキーの割り当ては、以下の様にしました。
MSX PS/2キーボード
STOP END
CLS/HOME HOME
かな カタカナひらがな
GRAPH ALT
SELECT F12
PAUSE Scroll Lock
※プログラムで割り当て変更は自由にできます。
A1FのLEDはCAPS、かな、PAUSEと3つありますがそれぞれCapsLock、NumLock、ScrollLockのLEDに対応させています。
プログラムの解説
処理の大まかな流れ
PS/2の信号のやり取りはCLKのピン変化割り込みで行います。CLKピンの変化割り込み時、CLKがLである場合DATピンの状態を読んでいき、8bitデータを完成させます。
キーボードからの8bitデータが完成すると、それをバッファに入れて入力されたキーの判定を行います。
バッファに入れる理由はデータが可変長のためで、その8bitで判断が出来なければ次の8bit受信時に判定を持ち越します。
キー判定でMSXに割り当てたキーの場合、キーマトリクス用の配列の該当ビットを変化させます。
もし、現在S1985に出力している行が変更されたらその場で出力に反映させます(この処理をしないと、キースキャン信号を変更せずに連続的にキーの状態を読むケースに対応できないため)。
PAUSEキーの場合はキーマトリクス外なので、キーマトリクス配列の12行目のLSBに割り当てて、キーチェック処理の中でその部分を見てPB3を変化させます。
メインルーチンでは、リセットのチェック、LEDのチェック、キーボードへのコマンド送信(バッファにコマンドがあれば)を繰り返しています。
リセットチェック
リセット中はS1985のキーボード信号入力ピンの機能が一部変わって、キーボード信号とは違う用途に使われます。
この時AVRからの8bitデータを伝えないようにしなければなりません。
このS1985の機能、回路を作ってから分かったので本来なら3ステートバッファを使ったほうが楽で確実だと思いますが、今回は手元に部品が無いのでソフトウェアで対応させました。なのでアチラコチラにリセット信号をチェックする箇所があってあまりスマートな処理ではありません。
クリティカルなタイミングではもしかするとバグるかも…。
LEDチェック
LEDの点灯はAVR側でCaps Lockが押されたから点灯するという処理はせず、MSX本体からのLED状態を反映するようにしています。PC4~PC6のピンの状態を確認し、状態に変化があればそれに応じてLEDの点灯・消灯を行います。
コマンドの送信
キーボードへのコマンドも可変長なのでまずバッファに入れておきます。
メインルーチンでバッファにコマンドが入っていることを確認すれば、コマンド送信ルーチンへ飛びます。
コマンド送信ルーチンはCLKを100us間Lにしたり、受信と同じくCLKピンの変化割り込みで1bitずつ送信していきますが、その間はメインルーチンに戻りません。バッファに溜まったコマンドをすべて送信したら処理はメインルーチンへ戻ります。
通信エラーの対処
パリティビットが違う、コマンドを送信したら再送要求が来たなどの状態は何も対処していません。
今の所信号が化けるようなことも無く、そもそもどの様に対処するべきかよく分からなかったため通常と同じ処理をしています。
MSXからのキースキャン信号
キースキャン信号であるPC0~PC3のピン変化割り込みがあると、速やかに該当するキーマトリクス行のデータを出力します。
これが遅れるとスペースキーを押したのに*が表示される、なんてことが発生します。
MSXのシステムは1/60秒ごとにキー入力を全部確認するので、次々行を変えてスキャンしていきます。モタモタしていると8行のスキャンのときにPD0にLを出力するはずが9行になってからLを出力していまい、表示されるのは9行のPD0=テンキーの*が表示されるわけです。
AVRは20MHz動作でほぼ1クロックで動くのでMSXに遅れを取ることはありませんが、割り込み処理中はデフォルトで割り込み禁止になっていて、その時キーボードからの8bitが完成してキー判定処理が実行されると流石に時間がかかってしまうので、PS/2のCLKピン変化割り込み中は多重割り込みができるようにしておきます。
何をおいてもMSXのキースキャンに答えるのが最優先です。
またこのキースキャン信号の割り込み時もリセットチェックを行い、リセット中はHi-Zになるようにしています(PD0~PD7)。
出力ポートについて
最初はPD0~PD7の出力をLとHで行っていました。MSXのキーボードを繋げないで動作チェックをしている間は問題なかったのですが、MSXのキーボードを接続するとMSXのキーボードの入力が一切反応しません(PS/2キーボードは反応する)。
その原因を突き止める最中に、PD0~PD7のH出力がMSXのキーを押下中uPD65013GF375のL出力へ直接流れることに気付きました。うっかりミスです。
なぜ壊れなかったのか良く分かりませんが、これはまずいだろうとPD0~PD7の出力に2SC1815を噛ませました。
すると今度はPDの出力が遅れる現象が発生。キーを押した時(L出力)は間に合うのですが行が変わって出力がHになるのが間に合わず、0キーを押すとMSXの画面には08と表示されてしまう結果に。
アナログ的な信号の遅れを比較できるような測定器を持っていないのでどうしたものかと頭をひねった結果、LとH出力ではなく、L出力とHi-Zで行けることに気付いて、現在の形になっています。
AVRのポートは出力Lにしておいて、入力出力を切り換えることで都合よくL出力、Hi-Zが実現できました。
このため、キーマトリクスの配列は負論理ではなく正論理でデータをセットし、それをDDRDに書き込むことで動作させています。
これで無事、MSXのキーボードを接続していてもH出力からL出力へ短絡することもなくPS/2キーボード、MSXキーボードどちらも同時使用できるようになりました。
キーの割り当て変更方法
キースキャンコードは押下時、1バイトのもの、2バイトのもの、4バイトのもの、8バイトのものに分けられるようです。
SetByte1(uchar n)では1バイトのキーチェックを行っています。case文の数値がスキャンコード、LineがMSXのキーマトリクスの行、BitDataがその行の押されたキーを表します。
同様にSetByte2(uchar n)で2バイトのキーチェックを行います。case文の数値は2バイト目のスキャンコードとなります。
新しくキーを割り当てたい場合は、キースキャンコードが何バイトかを調べ、SetByte1かSetByte2の中にcaseを追加することになります。
4バイト、8バイトのキーコードは、KeyCheck(uchar val),KeyE0Check(), KeyF0Check2()の中で個別に判定しているのでそこを書き換えます。
現状の問題点
今分かっている問題は、LEDが点灯しないキーボードが存在する点です。
どうもキーボードによって細かな違いがあるらしく、LED点灯コマンドを送信しても再送要求を応答するばかりで点灯してくれません。
カタカナひらがなキーのスキャンコードもキーボードによって13hのものと0edh,13hのものがありました。こちらは、両方カナカナひらがなキーとして扱うことで対処しています。
LEDが点灯しないキーボードもPCに繋げると点灯するので、何かやり方があるのだと思いますが…。