NEC PC-8001/88/98シリーズ配列テンキーの作成(その3)
Pro Micro(Arduino Leonardo)対応編2(2018/7/17)

戻る

1.欲しい機能と、回路設計

既にPC98配列モドキを実装した試作1号を運用しており、その上で改良したい点をまとめてみました。

○改良したい点
(1)電卓用にバックスペースや、ESCを追加する。
(2)Excel操作向けにカッコも入力できるようにしたい。
(3)JIS/US配列を切り替えたい。また、ユーザー定義のキー配列も実装したい。
(4)薄く、コンパクトかつ、十分な強度を持たせたい。

ひとまず、こんなところでしょうか。
(1)、(2)に関しては、操作用のキースイッチを24個に増やす事で良さそうです。
(3)については、試作1号でも実装していたモード切替用のスイッチを追加する必要がありそうです。
状態表示についてはLEDの色指定を変えれば、NumLock ON/OFFと、モード表示を兼用できそうです。
(4)は筐体設計なので、後回しです。

○回路検討
・キースキャン回路は、6行×4桁(モード切替を考慮すると7行×4桁)とする。
・シリアルLED1個で表示する。

と言うことで、以下のような回路になりました。


2.ブレッドボードへの実装

定番の"Lチカ"とか"Hello World"等のステップもやろうかなと思ったのですが、既にIDEからマイコンが認識されていたのと
部品は購入済みだったので、そちらは飛ばして、いきなり 回路図をもとに実装しました。

こんな感じです。
何かあったとき用にリセットボタン(RSTとGND間)も実装しています。

LEDはチップ型のWS2812Bを使っており、 セラミックコンデンサ(0.1μF)をVDD/GND間に挿入しています。
(チップ型はPin配置が回路図(D型用)とピン配置が違うので注意)
タクトスイッチ+基板4x2は、回路的に8x1行だったので、基板パターンを一部カットして4x2行として利用しています。
本来ダイオードはキー毎に必要ですが、タクトスイッチ基板の都合でスキャン桁ごとに1本(計8本)としています。
モードキーは、ポート2にジャンパ線だけを実装して、押下時にポート10,16,14のダイオードの足に触れることで代用します。
スケッチの機能確認レベルなら(複数キーの同時押しなどをしなければ)問題ないでしょう。

次は各機能を検討していきましょう。


2.Num Lock状態の取得とシリアルLED表示

ホストPC側のNum Lock LED状態を表示します。つまり"Lチカ"です。
こんなスケッチを組んでみました。

(うまくソース表示されない人はこちらからどうぞ)

先頭でシリアルLEDと、HIDライブラリをインクルードします。
LEDの個数と利用するI/O(Port21)を定義しておき(8,9行目)、LED表示用構造体の初期化(14行目)を行います。
スケッチにも書いていますが、D型チップ型で色の並び順が違うため、指定方法が変わります。
・D型(D2812) = NEO_RGB
・チップ型(WS2812B) = NEO_GRB

○ホストPC側のNum Lock LED状態制御について
setup()でHIDライブラリを初期化(BootKeyboard.begin())しておき、
loop()で周期的にBootKeyboard.getLeds()でホストPC側の状態を取得します。
あとは、状態に従ってLED表示に反映するだけとなっています。
この実装は、ほぼHIDライブラリ付属のサンプルコード(KeyboardLed.ino)のままです。

○スケッチの書き込み/動作確認
Arduino IDEで「ファイル」→「新規ファイル」で新規スケッチを開いて、上記のスケッチを全て貼り付けます。

マイコンが認識していることを確認したら、「マイコンボードに書き込む」をクリックしてコンパイル・書き込みを実行します。


ホストPC側のNum Lockキーを押下して、Num Lock状態を変化させてみてください。
ボード側のLEDが連動してON/OFFすると思います。また、周期的に色が変化(緑→青→赤)します。

次は、ボード側のキー操作に従って、キーコードをホストPC側に送信する機能を実装していきます。


3.キー押下状態の取得(キースキャン)とNum Lock制御

ホストPC側のNum Lock LED状態を表示できたので、これにキースキャン制御を追加します。
まずは簡易的に、どのキーを押してもNum Lockキーとして動作するようにしてみました。
先ほどとは逆に、マイコン側のキー押下により、ホスト側のNum Lock状態とマイコン側のLED表示が同期して変化します。
こんなスケッチになりました。

(うまくソース表示されない人はこちらからどうぞ)

まず、キーコードとしてNum Lock(0x53)を定義しています。このキーコードは、
HID仕様書の53ページから記載の一覧表より引用しています。

次に11-14行目でキースキャン用のI/Oポートの定義を行っています。
回路図の通り、7行×4桁として、それぞれのI/Oポート番号を割り付けてます。
あとは、キー状態の変化をとらえるため、以下の配列を定義しています。
 bool cur_key_sts[ROW_NUM][COL_NUM]; //最新のKEY状態
 bool old_key_sts[ROW_NUM][COL_NUM]; //前回のKEY状態

○setup()について
追加したキー制御I/Oと、キー状態の初期化を行っています。
注目すべきは、7行×4桁の初期化時に、行に割り付けたポート(rowPins[])はOUTPUT指定として、
列に割り付けたポート(colPins[])はINPUT_PULLUP(LOW Active)としている辺りでしょうか。
このLOW Activeによるキースキャン回路及びその制御は、
ゆかりメモ:オリジナルキーボードを作ってみる まとめで詳細が解説されています。
興味のある方は、ぜひ一読してみるとよいかと思います。

○loop()について
for文を利用してキーに割り当てたI/Oポートの状態を行(row)/列(col)の順に読み出しています。(82-115行)
85行目でOUTPUT指定した行(row)側のI/Oを一時的にLOW指定して、90行目で各列(col)を読み出しているのがポイントです。
キースイッチがON(導通)状態だと、各列(col)のI/O状態が、行(row)側のI/O出力に従ってLOWになります。
その信号をcur_key_sts[]に保存しておき、前回の入力状態(old_key_sts[])と比較して変化があった場合に、
「キー押下状態変化あり(chg=1)」としています。(90-96行目)
この前回の入力状態との比較を行うことで、同じキーコードが連続で送付されてしまうといった問題を回避しています。

○キー押下判定とHIDライブラリ制御について
キー判定に従って、以下のHIDライブラリ関数を使って制御しています。
(1)キーコード追加(状態=LOW=押下)
 BootKeyboard.add(KeyboardKeycode(KEYCODE));
(2)キーコード削除(状態=HIGH=離している)
 BootKeyboard.remove(KeyboardKeycode(KEYCODE));
(3)キーコードをホストに送信
 BootKeyboard.send();

この使い方は、かとうさんのBlog記事 Arduino Micro を使って、USB 小型 キーボードを自作を参考にさせていただきました。
ポイントは、(1)、(2)でキーコードのON/OFFをライブラリに指示しておいて、(3)で一括で送付することにより、
ホストとの通信回数を最小限に抑えている点でしょうか。なかなかスマートな制御をされていると思います。

さて、これでキーボードの基本的な制御である、キースキャン、キー状態判定、ホスト状態表示が動作したことになります。


4.Shift制御とメディアキーの実装

こまで来たら、あとはキーマップを定義して、Shift制御と、メディアキー(電卓アプリ起動)処理を追加すれば、ほぼ機能としては完成です。
一気に実装してしまいます。

(うまくソース表示されない人はこちらからどうぞ)

○キーマップ
7x4のマップを定義しています。
同じくHID仕様書の53ページ記載の一覧表よりキーコードを引用しています。
並びは回路図通りに実装していますが、1行目(Port2番)のマップはダミー実装です。
モード制御周りは、実機を組み立てて各OS環境に接続しながら、煮詰めて行きたいと思います。

○Shift制御について
実は、HIDライブラリレベルでShift制御は実装されていますが、Shift押下だけでなく、
解除もライブラリで実施してしまったりと微妙に使い勝手が良くなさそうだった私の理解不足のため、
自前でShiftコードを送付する制御にしています。
キーコードの上位側を(勝手に)拡張して、SHIFT押下対象コードを判別できるようにしておいて、
キー押下時とリリース時にそれぞれShiftコードの付加と削除を行っています。
かなり適当な実装ですが、カッコ()やイコール=も、特に文字化けもなく動作しているようです。

○メディアキー機能とHIDライブラリ制御について
メディアキー機能は、以下のライブラリ関数を利用しています。
(1)ライブラリ初期化
 Consumer.begin();
(2)電卓アプリ起動
 Consumer.write(ConsumerKeycode(CONSUMER_CALCULATOR));

この実装は、HIDライブラリ付属のサンプルコード(Consumer.ino)のままです。
キー押下時のみ処理している点(2重起動防止)がポイントでしょうか。

動作状態です。
画像に記載した文字列(2行目)は、ブレッドボード実装のマイコンから入力しました。

たまにチャタリングが発生したりと、少々微妙な動作だったりしますが、ひとまず、
ファームの実装がひと段落しましたので、次回はいよいよ実機の制作に入ります。


戻る