弊社では業務PCとしてノートPCが支給されますが、外付けキーボードやマウスを利用したい方は追加支給してもらうこともできます。ですが自分好みのキーボード・マウスを利用したい方は、各人の責任で持ち込んで利用することも認められています。
その流れで私は自作したキーボードを業務PCに接続して使っていますが、そのキーボードを作成した際の知見を少しまとめました。
自作キーボードで使うソフトウェアについて
私は自作キーボードでqmk_firmware(Quantum Mechanical Keyboard)というオープンソースのファームウェアとその派生物を使っています。
qmkのキー入力処理を大まかに俯瞰すると、以下のような流れになります
- まず大元になるメインループがあります。
- そこから呼び出した関数でキースイッチのマトリクスを確認します。
- 前回確認した状態からマトリクスに変化があれば、それぞれの変更点についてactionを発行してキューに積みます。
- 最後にUSBデータを送信します。
マトリクスを確認してactionを発行する際に自作キーボード特有のTapDance(1つのキーに複数の機能を割り当てる機能)やCombo(複数のキーの組み合わせで特定の機能を呼び出す機能)などの便利機能に展開するようです。
キーの入力マトリクス
qmkはC言語で記述され、上記マトリクスの状態はグローバル配列変数になっていました。
ということは、同じプログラムの中なら別のソースファイルからも参照することができます。
キーマップを参照するには、keymap.cファイルなどで以下の宣言をします。
1 |
extern matrix_row_t raw_matrix[MATRIX_ROWS]; |
これでマトリクスを参照できるようになるので、データをそのままOLED(有機ELディスプレイ)の表示関数 oled_write_raw_P() に渡すことで、まばらな点の集まりですがキーの入力状態を確認できるようになりました。
1 |
oled_write_raw_P((char*)raw_matrix, sizeof(raw_matrix)); |

処理中のデータを直接リアルタイムで確認できるOLEDは、デバッグやトラブル調査で非常に便利です。(画像では斜めに点が並んでいますが、この点は総当たりマトリクスのcolとrowを繋ぐダイオードの影響です。)
キースイッチ以外のキー入力手段
最初にお話ししたキースキャンの流れから、適切なドライバが無いデバイスや、既存のドライバと挙動を変えたいときには、以下の方法があります
- ユーザの操作でon/offを意図的に操作したりキーリピートを発生させる場合には、action_exec() でonとoffを都度発行します。
- 単発のキー入力ができればよい場合には、on/offをセットで発行してくれる tap_code_delay() を使います。これにより、瞬間的なピークを抑えつつ入力に必要な時間間隔でonとoffを発行してくれます。
ダイナミックキーマップ変更
自作キーボードを調べて回ると、ファームウェアをビルドする際にキーと入力されるキーコードの対応を決定するだけでなく via, remap, vialなどのツールを使ってキーマップを動的に変更できるようにしておくことが求められているようです。
これらのツールは基本的に以下の方法で動作します
- キーマトリクスの範囲で確保された配列を用意します。
- マトリクスのrow(行)とcol(列)の交点を結びつけた表を作成します。
- PCからRAW HIDの手順で書き換え指示を受け取る
- 上記の表を書き換えることでキーマップを変更します。
ツールを使った場合でもマトリクスの交点位置を動的に変更することは無いため、キーボードごとに一貫した固有のものを決めておき、どのように利用するかを工夫するところがファーム作成の醍醐味と感じています。
未実装スイッチとマトリクススキャン
マトリクスのなかでスイッチを実装していないだけであれば悪影響の可能性は低いですが、rowとcolを共用してダイオードで意味を読み替える形の総当たりマトリクス(改良二乗マトリクスなど他の呼び方もあります)を使う際、row=colの位置は読み飛ばす必要があるため、「#define MATRIX_MASKED」を設定します。
MATRIX_MASKEDについてはじめは誤解していたのですが、スキャンを抑止するのではなくスキャンの結果入力されていても無視する挙動となっております。これは時間的制約が厳しいGPIO操作周辺のコードをシンプルに保ちつつ、効率を高めるためではないかと考えます。
この挙動はquantum/matrix.cのmatrix_scan()のスキャン部分と、quantum/matrix_common.cのmatrix_get_row()でスキャン結果をマスクして返答することから確認できます。
入力されたキーを無効として扱う方法は以下のような方法がありますが、計算量を考慮するとMATRIX_MASKEDが軽量で最適です
- MATRIX_MASKEDを使用する
- process_record_user()で発生したキーコードを否定する
- process_record_user()でmatrix[]配列を直接操作する
また、マスクされた位置を読み出しで無効化するのみで他の箇所で判定しない現在の実装は、未実装スイッチを他のスイッチデバイスの入力に転用することがやりやすくなっていますが、その話はまた別の機会にお話しさせていただきたいと思います。
おわりに
自作キーボードのファームウェアはソフトウェアと電子回路、機械的な事柄まで幅広い知識が身に着けられるのでソフトウェアエンジニアとして幅を広げるにはいい課題ではないでしょうか。
キーボードと言う毎日使うデバイスですので、活動の結果がQOLに直結する点も素晴らしいと思います。
この記事で興味を持って一人でも多くの仲間が現れることを期待しています。