開発経緯
PCで音楽や動画・YouTubeなどを再生していると、離れた場所から音量や選曲の操作をしたくなる事があります。
ワイヤレスのマウスやキーボードを使えばよいと思うかもしれませんが、マウスは画面を見なければ操作ができず、遠くからはポインタが小さすぎて意外と使いにくいのです。
キーボードはサイズが大きすぎるので、枕元などでは手軽に利用できません。
今回は手持ちの赤外線リモコンを使って、キーボードデバイスとして動作する受信ユニットを製作しました。
机に置いても邪魔にならないよう出来る限り小さく作り、可能な限り手持ちの在庫部品で金をかけずに作るため、PS/2インターフェースで接続する方式にしました。 会社の同僚に話をすると、「どうしてUSBやBluetoothにしないの?」と言われてしまいました。 USBに対応するならライターを買い直したり、MCUの使い方を勉強し直さないといけませんし、Bluetoothレシーバーも持っていないので仕方ないですよね。
机に置いても邪魔にならないよう出来る限り小さく作り、可能な限り手持ちの在庫部品で金をかけずに作るため、PS/2インターフェースで接続する方式にしました。 会社の同僚に話をすると、「どうしてUSBやBluetoothにしないの?」と言われてしまいました。 USBに対応するならライターを買い直したり、MCUの使い方を勉強し直さないといけませんし、Bluetoothレシーバーも持っていないので仕方ないですよね。
ハードウエア
開発経緯にも書いたとおり、手持ちの部品で小さく仕上げるために工夫したところを挙げていきます。
回路図
パターン図
- PS/2のDINコネクタを使わず、Micro-B USBコネクタにPS/2の信号を流している。形状変換のためにUSB-PS/2変換アダプタを使った。 このアダプタはストレートで結線してあるだけで信号変換はされていません。USBコンボマウスに付属していたおまけ品。
- 外部のメモリICは使わない。PIC内部のメモリになんとかしてデータを保存する。
- 表面実装のPICは何かあった時に交換が難しいので、手持ちのDIPパッケージを使う。
- 設定とデバッグ用のインターフェースはUSARTを使う。常時使う必要がないので、ヘッダピンを立てておくだけにする。
- 初めて使うPS/2のデバッグ用途のため、状態表示のLEDはできるだけ残しておく。
- ビアを作るのが面倒なので片面基板を使う。抵抗はできるだけ表面実装の部品を使う。
- パターンの作成にEagleを使った。ワイヤはほとんどの部分で16milを使い、細くなり過ぎないように気を付けた。
回路図
パターン図
ケース・マウント
基板の上部は半透明のキャップを使い、下部にUSBコネクタが挿せるようにスリットを切り込んでおきます。
基板はこのキャップに合うサイズで作り、うまく嵌まるように正円にカットします。
ガラスエポキシの基板は固く厚いので、カットと研磨に1時間ほどかかりました。
基板を安定して固定し適度な重量で横倒しにならないように、東急ハンズに売っているφ50×10mmのアルミ円柱にM2のタップ加工をした台座を作りました。
売られているものは単にアルミをカットしただけの物なので、バリが多く表面も研磨されていません。
ペーパーで1時間ほど磨いてヘアライン加工を施します。
ソフトウエア
ソースリスト Ver.0.46
今回はフローチャートなどをほとんど書かずに作ったので、ソースがとても汚いです。
機能をどんどん詰め込んでいったので3ワードしか残っていません。プログラムの類似部分はかなり共通化したのですが、これ以上は限界です。
今回はフローチャートなどをほとんど書かずに作ったので、ソースがとても汚いです。
機能をどんどん詰め込んでいったので3ワードしか残っていません。プログラムの類似部分はかなり共通化したのですが、これ以上は限界です。
積極的に割り込み機能を使う
受信ルーチンや時間計測はポーリングをできる限り使わず、割り込みやタイマーモジュールを使います。
割り込みに任せているのは、リモコンのパルス幅計測(CCP1IF)、リモコンのトレーラ判定(TMR1IF)、リモコンのリピート有効時間判定(TMR0IF)、
USART送受信(TXIF,RCIF)、PS/2のクロック信号監視(INT0IF)となっています。
比較的近似したタイミングで多くの割り込みが発生するので、どれか一つのモジュールで割り込みが発生した場合、処理後にそのままリターンせず他のモジュールもチェックするようにしています。
リモコンコード解析関係
過去に製作したドリームライトやTU-876CD用 赤外線リモコンモジュールでは独自の解析アルゴリズムを使っていました。
しかし、このアルゴリズムではソニーのリモコンコードを解析できないため、今回のプログラムではELMさんのサイトで公開されている赤外線リモコン制御モジュールを参考にさせていただきました。
キャプチャ(CCP)モジュールを使って信号パルスの間隔を測定し、3種類のリモコンコードの特性と比較・データの取り込みを行っています。
TU-876CD用のモジュールと同様に、リピートコードのタイムアウト特性を持たせるために、Timer0のオーバーフロー割り込みも追加で使っています。 これを応用することで、キーボードのコントロールパネルからキーリピートの待ち時間とリピート間隔を変更することができるようになっています。
TU-876CD用のモジュールと同様に、リピートコードのタイムアウト特性を持たせるために、Timer0のオーバーフロー割り込みも追加で使っています。 これを応用することで、キーボードのコントロールパネルからキーリピートの待ち時間とリピート間隔を変更することができるようになっています。
PS/2通信関係
PS/2はPOSTで認識されていなければWindowsからも使えません。
いわゆるホットスワップができないデバイスなので、何とか認識させないとデバッグすらできずにとても困ります。
また、ポートの先にキーボードかマウスのどちらがつながっているのかIDを交換しているので、マウスで初期化したポートにキーボードをつないでも動作しません。
PS/2は半二重通信でI2Cのようにオープンドレインのバスなのですが、キーボードがクロックマスター側なのにもかかわらず、 PCからデータを送る時は無理やりクロックを止めて受信を要求してくる変態仕様です。 詳しい仕様はHOLTEKのキーボードコントローラHT82K629Aの資料などを利用すると良いのですが、エラーの復帰や再送の手順があいまいでよく解りませんので、 IBMの資料(物理層からデータリンク層あたり)とMicrosoftの資料(キースキャンコード関連)を参考にするのが一番良いと思います。一番下の参考文献にリンクを張っておきます。
PS/2は半二重通信でI2Cのようにオープンドレインのバスなのですが、キーボードがクロックマスター側なのにもかかわらず、 PCからデータを送る時は無理やりクロックを止めて受信を要求してくる変態仕様です。 詳しい仕様はHOLTEKのキーボードコントローラHT82K629Aの資料などを利用すると良いのですが、エラーの復帰や再送の手順があいまいでよく解りませんので、 IBMの資料(物理層からデータリンク層あたり)とMicrosoftの資料(キースキャンコード関連)を参考にするのが一番良いと思います。一番下の参考文献にリンクを張っておきます。
キーデータの保存方法
PIC16F88はプログラムの動作中にプログラムメモリを動的に書き換え・読み出すことができます。
これを利用してリモコンコードと送出キーストロークのデータをプログラムメモリに書き込みます。
プログラムメモリに書き込むメリットとして、1ワードあたり14bit記録することができる点があります。
通常データの8bitに加え、データサイズや終端マークなどの補助データとして6bit(0~63)のデータも保存できるのでとても便利です。
今回のプログラムではリモコンコード・キーコードと合わせてそれぞれのフィールドサイズを上位バイトに記録しています。 リモコンコードが長くなるとキーコードが短くなってしまいますが、リモコンコードは通常4~6バイト程度、キーコードは1つのキーあたり1バイトなので、長いキーストロークを記録させない限りオーバーする心配はありません。 データ長フィールドに10h以上の数値が入る事はデータ構造的にありえないため、不正な値が入っている場合は空きエリアとして検出する仕組みにしています。
右の例のキーコードを送出するには、受信したリモコンコードの長さとデータ内容が一致しているかを照合した後、12h・1Chのキー押さえデータを送出し、逆方向に1Ch・12hのキー離しデータを送出します。 こうすることで、同時押さえを可能にするとともに、離しのデータを明示的に書く必要がなくなります。
最終的にプログラムメモリの半分2kワードを、16ワード×64ボタン×2マップ構成の仕様としました。
今回のプログラムではリモコンコード・キーコードと合わせてそれぞれのフィールドサイズを上位バイトに記録しています。 リモコンコードが長くなるとキーコードが短くなってしまいますが、リモコンコードは通常4~6バイト程度、キーコードは1つのキーあたり1バイトなので、長いキーストロークを記録させない限りオーバーする心配はありません。 データ長フィールドに10h以上の数値が入る事はデータ構造的にありえないため、不正な値が入っている場合は空きエリアとして検出する仕組みにしています。
右の例のキーコードを送出するには、受信したリモコンコードの長さとデータ内容が一致しているかを照合した後、12h・1Chのキー押さえデータを送出し、逆方向に1Ch・12hのキー離しデータを送出します。 こうすることで、同時押さえを可能にするとともに、離しのデータを明示的に書く必要がなくなります。
最終的にプログラムメモリの半分2kワードを、16ワード×64ボタン×2マップ構成の仕様としました。
設定画面
リモコンコードの学習やキーストロークの編集はシリアルコンソールを使います。
setコマンドに続いて希望のリモコンのボタンを押さえると、対応しているリモコンであればリモコンコードの表示に続き、キーコードを16進数で入力できます。 リモコンコードを知っているのであればsetコマンドの後に手で入力する事もできます。後述のlsコマンドで表示させたものを少し加工すれば、流し込むことも可能です。
setコマンドに続いて希望のリモコンのボタンを押さえると、対応しているリモコンであればリモコンコードの表示に続き、キーコードを16進数で入力できます。 リモコンコードを知っているのであればsetコマンドの後に手で入力する事もできます。後述のlsコマンドで表示させたものを少し加工すれば、流し込むことも可能です。
キーコードはPS/2のコードセット2に準じています。
キーの押さえはそのままコードを送信、キーの離しは頭にF0hを付けて送信、特殊キーはE0hに続いてコードを送るかE0h F0h コードのようにしてキー離しの意味にします。
E0hが付くかどうかはコードが90h以上かどうかで判断しています。90h以上ならば先行してE0hを送り、80hを引いた値を実際のスキャンコードとして送ります。
変態スキャンコードを持つPAUSEはうまい実装方法が見つからなかったので、旧コードで代用するようにしました。
lsコマンドを入力すると登録されているリモコンコードと、対応するキーコードの一覧を表示できます。
キーコード00はマップ切り替え、または、リピート送信OFFの意味です。
使ってみて分かった事
- 寝ながらPCの操作ができるのでとても便利。
- 2マップ仕様にしたので、一つのリモコンで2系統の操作ができて自由度が高い。
- しばしば学習できないと言われるダイキンのリモコンは、メーカー独自のコードが使われていた。
- 金属の取り付け台が安定していて、見た目にも高級感があり見栄えが良い(と思っている)。
- そのうち本当にPS/2が消える日が来るんだろうけど、200円ほどでPS/2からUSBに信号変換するアダプタがあるようなので、困ったらそれを買おうと思っている。
製作過程で苦労した点
PS/2に関する事
- PS/2は信号レベルとタイミング・コマンド類は規定しているが、バス衝突の挙動や再送制御に関する詳しい資料がほとんど見つからず、実装にとても苦労した。
- マザーボードやOSによって通信の挙動が異なる。特に、パリティ等のエラーを検出した場合の再送要求の有無、再送のリトライ上限回数の有無に違いがみられる。 リトライ上限が無いマザーボードやOSでは無限ループに陥る。
- 下のPC→KBの波形のように、通信中はかなりの確率でInhibitを使って次の送信を止めてくる。 自分が出しているクロックと幅が微妙に異なるのでよく見ればわかるが、初めのうちは何のパルスか分からず混乱した。 ただし、これを出さないマザーボードやOSもある。
- WindowsとMS-DOS、BIOS画面ではF0h/E0h/E1hのプレフィックスの扱いが異なる。そのためPS/2のバス衝突を起こすと、キーのデータが化ける事がある。 WindowsではF0hのプレフィックスは連続して何度でも送って問題ないが、E0hとE1hは連続して送るとWindows内部でキーエラーイベントが飛んでいる。 イメージとしては、F0hはシフトキーのように任意に併用、E0h/E1hは正しい順番で送らないといけない。MS-DOSやPOSTのタイミングではこの限りではない。 InhibitやRTSで送信を止められた時は、00hを送信するとE0h/E1hの状態がキャンセルされるようだった。 Windowsの仕様なのかマザーボードの仕様なのか、全ての環境で起きることなのかよく解らない。
- PAUSEキーのスキャンコード「E1 14 77 E1 F0 14 F0 77」の送信中に中断すると、何バイト目で中断するかによってうまく認識しない事がある。 2バイト目の「14」でWindows側が勝手にPAUSEと決め打ち認識してしまうため、初めから再送すると77が来た時点でNUMLOCKが入力されてしまう。 Windowsの内部の動作が分からず対応策が決まらないため、旧コード「14 77 (Ctrl + NumLock)」で代用することにした。
- キーボードの認識タイミングで正常に通信できていないと未接続と判断されてしまうため、パリティエラーを意図的に起こすROMと正常なROMの2つの石を取り換えながらエラー回復ルーチンをデバッグするのが大変だった。
- この記事を書く前日に、MS-DOSからdebug.exeコマンドを使ってI/Oポートにデータを送れることを知って戦慄した。 古いPCとWindows98の緊急起動ディスクを引っ張り出して実行してみると、とても簡単だった。データの受信はMS-DOSが割り込み経由で横取りするので不可能である。
- そりゃ、こんなレガシーデバイスは廃れますわ。
PS/2以外の事
- プログラムメモリの消去・書き込みを行うとCPUが約2ms停止するとデータシートに書かれてあるが、あくまでもCPUのコア部分だけが停止する。 周辺ユニットは動作しているため、メモリの消去・書き込み中にUSARTへ連続したデータを流し込むと、受信データが取り出せずオーバーラン状態となる。 RCREGからは2バイト分のデータが取り出せるため、2バイトだけしか読み出せずに停止する場合はここを疑ったほうが良い。
- 割り込みを使ったUSARTのバックグラウンド送信の仕組みが思いのほか簡単で、非常に勉強になった。
- PICは割り込み許可フラグ(xxxIE)を禁止にしていても、割り込み要因フラグ(xxxIF)が立ってしまう。 今回のようについでに他の割り込みフラグもチェックする方式にすると、本来割り込みが禁止されている機能でも誤って割り込みハンドラを起動する要因になってしまう。 これは、停止できないTimer0のオーバーフローや、INT0を無視したい場合に問題となる。
- キーデータを保存するためにプログラムメモリの半分を割り当てたため、実際のプログラムを置く余裕が無くて困った。 問題が起きたらまず空き領域の確保から始めなければならず、とても骨が折れる。
参考文献
PS/2インターフェース関連
- Keyboard 101- and 102-Key - IBM
- Keyboard and Auxiliary Device Controller - IBM
- Keyboard Scan Code Specification - Microsoft
- USB HID to PS/2 Scan Code Translation Table - Microsoft
- PS/2 マウス/キーボード プロトコルとインターフェース
- AVRでPS/2マウスを使おう
- HT82K629A Windows 2000 USB+PS/2 Keyboard Encoder - HOLTEK
- eKK8011S USB and PS/2 Keyboard Encoder - ELAN Microelectronics
- MX83093 USB+PS/2 Keyboard Encoder - Shenzhen LIZE Electronic Technology
- PS/2 Controller - Altium