2015年8月6日
ライブラリ化を追加 2015年12月5日
ソースファイル、ライブラリファイルを更新 2016年5月3日
信号受信にDMA利用することで画面のチラつきをなくしました 2017年5月6日
以前作成したPIC32MXのSDカードブートローダーを使用して、Katsumi氏のKM-BASICを動作させることができました。BASICということでとても気軽にプログラムを書けるのですが、現在の仕様ではパソコン等でBASICのソースファイルを作成し、SDカードに保存してから、SDカードブートローダでKM-BASICを起動する、という手順を踏まなくてはなりません。プログラムを修正するたびにこれを繰り返すのはとても面倒で、せっかくのBASICの気軽さもなくなってしまいます。これを解決するには、PIC上でBASICのソースプログラムを作成・更新できるようにする必要があります。そこでまずはPIC32MXにキーボードを接続して読み込ませる実験を行ってみました。
以前使った基板を改造して作ったので、6個のボタンとSDカードスロットがありますが、
今回の実験では使っていません。また乾電池2本では動作しません。
これまでも使用してきた28ピンDIPのPIC32MX250F128Bに、ミニDIN6ピンのPS/2コネクタを取り付け、PS/2キーボードを接続しました。USBキーボードが使用できればよいのですが、PIC32MXシリーズではシステムクロックとUSBで使用するクロックが共有されており、カラーのビデオ信号をソフトで生成するために3.579545MHzの倍数をシステムクロックにしている本システムではUSB機器を使用することができません。
電源はキーボード用の5VとPIC用の3.3Vが必要です。私の場合は、5VのACアダプタの電圧をそのままキーボードにつなぎ、三端子レギュレータで単純に3.3Vを作ってPICの電源としています。
ビデオ出力部分はこれまで作成してきたものと同様です。図にはありますがオーディオ出力は今回特に使用しておりません。
これまでのゲームに接続していた6個のボタンは今回省略しました。左ボタンと右ボタンを接続していたRB8、RB9ポートにPS/2インターフェイスを割り当てました。これらのポートは5V耐性があり、また設定によりオープンドレイン接続が可能なため、後述のようにPS/2インターフェイスに最適です。元々つないでいた2個のボタンをどうするかは今後の検討課題です。
今回の実験に用いたキーボードは一般的に「109日本語キーボード」と呼ばれる、テンキー付きでWindows関連キー搭載のキーボードです。
PS/2インターフェイスやPS/2キーボードの詳細や応用については、多くのWebサイト等で解説されていますので、ここでは詳しくは解説しません。私は以下のようなサイトを参照しました。
アイオーアイオー氏 PS/2 マウス/キーボード プロトコルとインターフェース
PS/2インターフェイスの通信仕様書の日本語訳をしてくれています。
Sazanami Online PS/2 インターフェイスの研究
PS/2キーボードのコマンドとスキャンコード一覧が掲載されています。
これらからわかったことを簡単にまとめると、以下の通りです。
1.通信はCLKとDATの2本線で行う
キーボードからのデータ受信は、CLKの立ち下がりを検出しその時のDATを読み込む。
CLKの周期は60〜100マイクロ秒。
2.信号は一方通行ではなく双方向通信が可能
ホスト側からキーボードへのコマンド送信に対応しており、CLK、DATとも双方向対応が必要。
出力が衝突する可能性があるため、ポートはオープンドレイン(またはオープンコレクタ)接続とし、
5Vでプルアップをする。
3.キーボードからは「スキャンコード」が送られる
キーボードから送られるデータは「スキャンコード」と呼ばれるキー1つずつに対応したコードとなっている。
キー押下時とキーリピート発生時に通常のMakeコードが送られ、キーを離した時はBreakコードが送られる。
4.キーボードへのコマンド送信方法
CLK信号を強制的に100マイクロ秒以上Lレベルにすることで、キーボードはコマンド受信待ちとなる。
その後DAT信号のスタートビット(Lレベル)を検出すると、CLKラインにコマンド受信用のパルスを
60〜100マイクロ秒周期で発生させるので、そのタイミングでDATに信号を出力する。
キー押下により、以下のような出力が得られるシステムを作成します。
・アプリケーションから一定期間ごとに呼び出しを行い、キーが押されていた場合、1つのキーコードを返す。
その際に同時に押されていたシフト関連キーの状態も合わせて返す。
期間内に複数キーが押された場合はバッファしておき、次回呼び出しで2つ目のキーコードを返す。
・キーコードはキーボードの種類によらない仮想キーコードとする。
独自のものを考えるのは面倒なので、マイクロソフトのWindowsで使用されているコード互換とする。
・英数記号キーが押された場合は、キーコードのほかASCIIコードも合わせて返す。
シフトキーやCAPSLock、NumLockの状態によって大文字小文字等も区別する。
・全キーのリアルタイムの押下状態をキーコードの配列で保持する。
作成したシステムは大きく以下の機能に分けることができます。
パソコンの場合は専用のPS/2コントローラが内蔵されていますが、PICにはその機能はありませんのでソフトウェアで実現する必要があります。一番ハードウェア寄りの部分です。
最初にいずれかのキーが押された時、RB9ポートのCLK信号が立ち下がります。この検出には入力ポートの変化による割り込み(CN割り込み)を使用しました。必要ビット数分の割り込みで受信が完了するとスキャンコードとしてスキャンコードバッファに格納します。
また、キーボードへのコマンド送信中はキーボードからCLK信号のパルスが定期的に発生するので、それに合わせてデータを順次送信します。
受信にはPIC32のDMA(Direct Memory Access)機能を使用し、CN割り込み発生時にRBポートの内容が自動的にバッファされるように変更しました。そのため、実際には割り込みルーチンには飛びません。バッファされたシリアルデータはTimer5で定期的にチェックし、受信完了したものからスキャンコードバッファに格納するようにしました。(2017.5.6)
パソコンでいうと、キーボードドライバに相当する部分です。スキャンコードバッファに溜まったスキャンコードを一定期間ごとに読み込み、キーコードに変換してキーコードバッファに格納していきます。このタイミングはTimer5割り込みを使用して、100マイクロ秒ごととしました。
キーコードへの変換には、スキャンコードからキーコードへの変換テーブルを用意しました。このテーブルを変更することで、配列の異なる種類のキーボードにも対応することが可能となります。キーコードバッファには、その時のシフト関連キーの状態も合わせて格納します。アプリケーション側はキーコードバッファを読み込むことで、入力されたキーを順次読み出すことができます。
また、各キーコードに対応した配列にキーの押下状態を1または0で格納します。この配列をチェックすることでゲーム等で複数キーの押下状態をほぼリアルタイムで確認することが可能です。
システムリセット時のリセットコマンドや、NumLock等のインジケータランプ点灯、通信エラー発生時の再送要求等のため、キーボードに対してコマンド送信機能が必要となります。
これもキーボードドライバに相当する部分で、Timer5割り込みにより100マイクロ秒ごとに処理します。コマンドバッファにコマンドを格納しておくことで、コマンドの種類に応じてその後の必要な処理を行います。
コマンド送信のほか、信号送受信のタイムアウトエラーなどの処理もここで行います。
今回のシステムではキーボード読み込みだけでなく、画面信号出力も同時にソフトウェアで行うため、割り込みが多用されています。割り込み優先順位を間違えると正常に動作せず、暴走することがありますので、注意が必要です。以下の表に使用している割り込みをまとめました。優先度の数字が大きいほど優先順位は高くなります。
割り込みの種類 | 割り込み 優先度 | 役割 |
CLKの変化 (CN割り込み) | 6 | キーボードコントローラ キーボードからの信号受信、キーボードへの信号送信 信号受信にDMA機能を利用することで割り込み処理をなくしました。(2017.5.6) |
Timer2、OC1、OC2、OC3 | 5 | ビデオコントローラ 映像信号生成、同期信号生成 |
Timer5 | 4 | キーボードドライバ ・キーボードへのコマンド送信処理の進行管理 ・スキャンコードバッファから読み出し、変換してキーコードバッファへ格納 ・送受信エラー処理 DMAで受信したシリアルデータのスキャンコードバッファへの格納(2017.5.6) |
本当は映像信号出力関係の割り込みを最優先としたかったのですが、処理にかかる時間が長過ぎてキーボードからの信号を取りこぼしてしまうため、キーボードの信号送受信を最優先としました。そのため、キーボードを操作すると、画面が一時的にちらつくこととなります。
また、今回の実験で利用した映像信号出力システムは、以前作成したカラーテキストビデオ出力実験のシステムですが、自分より優先度の高い割り込みが発生することを想定していなかったため、一部プログラムを修正しました。
データ受信時にDMA機能を利用することで割り込み処理が不要となったため、ちらつきはなくなりました。コマンド送信(Num Lockキー押下によるインジケータランプ点灯など)は割り込み処理で行うためちらつきがあります。(2017.5.6)
アプリケーション側では、以下の関数とグローバル変数を利用してキーボードのチェックを行うことができます。
■関数
int ps2init (void)
PS/2ライブラリ初期化。
正常終了で0、キーボード未検出などの異常終了で-1を返す。
(2016.5.3) 呼び出す前に必ずグローバル変数lockkeyとkeytypeを設定するよう仕様変更
unsigned char lockkey 初期化時にLockキーの状態指定。下位3ビットが
unsigned char keytype キーボードの種類。0:日本語109キー、1:英語104キー
unsigned char ps2readkey (void)
仮想キーコードバッファから1キー分読み込み、グローバル変数vkeyに格納。
また、英数記号キー押下の場合は戻り値としてASCIIコードを返す。それ以外は0を返す。
呼び出しが行われないままキーが押し続けられ、キーコードバッファがあふれた場合、
後から押されたキーが無効となる。
unsigned char shiftkeys (void)
シフト関連キーの現在の押下状態を返す。キーコードバッファの上位8ビットと同じ。
■グローバル変数
unsigned char ps2keystatus [256]
キーコードに対応するキーの押下状態を表す配列。押下時は1、離している時0。
unsigned short vkey
ps2readkey ( )を呼び出した時にキーコードバッファの先頭から読み出した値。
下位8ビットにキーコード、上位8ビットにキーが押下された時のシフト関連キーの値を保持。
今回の実験で作成したシステムとデモ用アプリケーションのソースプログラムをダウンロードすることができます。利用する際は、MPLAB X環境でビルドしてください。CコンパイラはXC32 v1.32以下に対応しています。
コンパイル時の最適化オプションはレベル1以上としてください。(無償版でもレベル1は使用可能。)
デモ用アプリケーションでは、画面上にカーソルが点滅し、何かキーを押すとその場所に文字が表示されます。上下左右キーでカーソル移動、Enterキーで次の行に移動します。画面下まで来ると、スクロールはせずに一番上の行に移動します。また、画面最下行には、押されたキーのキーコードと、シフト関連キーの状態が表示されます。
サンプルアプリケーション動作イメージ
関連ファイル一式のダウンロード |
今回の実験は、カラービデオ画面出力をしながらPS/2キーボードの読み込みを行うところまでですが、この先は冒頭に書いたように、PIC32MX上でBASICのソースプログラムを作成・更新できるように、テキストエディタを作成し、SDカードに保存するところまで行いたいと考えています。そしてPIC32MXシステムだけで完結するKM-BASIC動作環境を実現したいと思っています。
(2015.12.5) テキストエディタが完成しました
(2015.12.5追加)
上述のPS/2キーボード利用機能をライブラリ化して、扱いやすくしました。また、カーソル点滅したり、文字列配列へのキー入力など便利機能をいくつかまとめたCのソースファイルも作成したので、合わせて添付しました。
ライブラリファイルのダウンロード |
ps2keyboard.X.a | PS/2キーボードを使用するためのライブラリ。ヘッダーファイルはps2keyboard.h |
keyinput.c | キー入力やカーソル点滅関連部品。ヘッダーファイルはkeyinput.h |
何かご不明点やご意見などありましたら、お気軽にこちらの掲示板に書き込みをお願いします。