キーボード沼入門ガイド - ファームウェア編
タイトルには入門ガイドと書いたが、今回はキーボードの動作原理と回路により焦点を当てていく。
押されたキーの検出
前回の記事で触れたように、キーボードは実際には複数のスイッチで構成された回路であり、マイコン(マイクロコントローラ)のGPIOピンを使ってスイッチが押されたかどうかを検出できる。具体的な方法は、スイッチの片方を電源に繋ぎ、もう片方をグラウンド(GND)に繋ぐことだ。キーを押すと回路が導通し、マイコンはその変化を感知して対応する信号を送信できる。実際にどのような信号なのかは、後の章で説明する。
しかし、この方法には欠点がある。それは各キーにピンが1つずつ必要になることで、例えば104キーのキーボードならマイコンに104本のピンが必要になる。通常、マイコンにはそれほど多くのピンはないし、一部のピンは他の用途に使う必要がある場合もある。
スキャンとキーボードマトリクス
実際には、マトリクス(行列)方式でキーボードの回路を接続する。各マトリクスがマイコンのピンに接続されるため、例えば60キーが必要な場合、6×10のマトリクスなら16本のピンで済む。

R1、R2、R3にそれぞれHighを出力し、C1、C2、C3の電位を順次読み取ることで、どのキーが押されたかを知ることができる。例えば下図のキーが押された場合、C1がHighであることを読み取ることで、どのキーが押されたかを検出できる。

ここでの順序は次の通りだ:
- R1をHighに設定
- C1を読み取る
- C2を読み取る
- C3を読み取る
- R2をHighに設定
- C1を読み取る
- C2を読み取る
- C3を読み取る
- R3をHighに設定
- C1を読み取る
- C2を読み取る
- C3を読み取る
理想を言えばマトリクスのRow(行)とCol(列)の数を同じにするのがベストだが、実際には必ずしもそうするとは限らない。一つにはマイコンのピン数が通常は十分足りていること、もう一つには無理に2つの数を同じにしようとすると配線(ルーティング)が難しくなる場合があるからだ。例えばキーボードは横の並びが多く、縦の並びが少ない。
ここで疑問に思うかもしれないのは、「R1とR2のキーを同時に押した場合、検出できないのではないか?」ということだ。なぜなら同時検出ではなく順番にスキャンしているからだ。しかし、これについては心配無用だ。一般的なマイコンは少なくとも16MHz以上で動作しており、検出速度は人間の限界よりも遥かに速い。マイコンはまず押された状態を記憶しておき、再度読み取った際に以前の状態と異なっていれば、キーが離されたという信号を送信する。
ゴースト(Ghosting)

先ほどの回路には問題がある。もしR3のスイッチも押された場合、電流の一部がそちらへ流れ込んでしまい、誤判定を引き起こす。その解決策としてダイオードを追加する。ダイオードには一方向にしか電流を流さない特性があるため、電流は一方向にしか流れず、ゴースト現象(Ghosting)を防ぐことができる。
HID(Human Interface Device)
次は信号をPCに送信するロジックについてだ。初期のキーボードはPS/2コネクタを使用していたが、現在最も一般的なのはUSBとBluetoothだ。
HIDは、キーボード、マウス、コントローラーなど一般的な入力デバイスのプロトコルを標準化することで、以前は必要だったドライバー開発のコストを削減し、HID規格に準拠していればどんなデバイスでも追加ドライバーなしで動作するようにした。
HIDには入力レポート(Input Report)、記述子(Descriptor)、出力レポート(Output Report)が含まれる。通常、記述子はこのデバイスが何であるか(用途、名称、ベンダー、IDなど)を表すために使われる。PCはこれらのレポートを読み取ることで、どのように処理すべきかを認識する。
USB
一般的に、マイコンにはUSB機能が組み込まれており、USBフォーマットでのデータ伝送をサポートしてくれる。
HIDも同様に、一般的にはUSBやBluetoothを介して伝送される。ただし、ここはエンジニアが気になる技術的な詳細だが、USBにはHost(ホスト)とDevice(デバイス)の2つがある。
マウスやキーボードのような入力デバイスは、CPUに比べて通常はるかに低速だ。そのため、CPUがこれらのデバイスからの入力を待ち続けるのは非常にパフォーマンスの無駄になる。実際の動作としては、PC側のUSBコントローラー(Host)がDevice側に対して継続的にポーリング(Polling)を行い、バッファにデータがあるときにCPUの割り込みを呼び出す。したがってキーボード側はデータを送り続けるだけで、残りの処理はすべてHost側が行う。
通常、HIDフォーマットをどう記述するかは、実際の開発現場ではライブラリがあらかじめ処理してくれるため、押されたキーを対応するキーコードにマッピングすることにだけ集中すればよい。
ここにRaspberry Pi Picoの例を挙げる:
static void send_hid_report(uint8_t report_id, uint32_t btn)
{
// skip if hid is not ready yet
if ( !tud_hid_ready() ) return;
switch(report_id)
{
case REPORT_ID_KEYBOARD:
{
// use to avoid send multiple consecutive zero report for keyboard
static bool has_keyboard_key = false;
if ( btn )
{
uint8_t keycode[6] = { 0 };
keycode[0] = HID_KEY_A;
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
has_keyboard_key = true;
}
...
}
この例にはボタンが1つしかなく、常に「A」キーが送信される。しかしコードを見れば、どのように書けばいいのか大体わかるだろう。コードではまず配列 keycode を宣言している。長さ6は同時に6つのキーを送信できることを意味し、ここでは keycode[0] だけを使って、tud_hid_keyboard_report を呼び出してHIDレポートを送信している。
回路基板の設計と配線
僕自身は回路基板の設計にそこまで深く精通しているわけではないが、ここでは基礎知識さえあれば誰でも基板を作れるということを伝えたい。基板設計には、非常に有名なオープンソースソフトウェアであるKiCadがあり、完全無料で使える。
このソフトを使えば、自分の好きな回路を設計でき、PCBのレイアウト配線にも対応している。設計した回路を基板上に配置して配線を引くことができるのだ。
すべて完成したら、製造ファイル(専用のファイル形式)を出力してPCB製造メーカーのWebサイトにアップロードすれば、数週間ほどで完成品が手元に届く。よく使われる安価な業者としてはJLCPCBやPCBWayがある。
QMK
自作キーボードの界隈では、LEDの光り方、キー配列のカスタマイズ、ロータリーエンコーダーの搭載、さらにはLCDスクリーンの搭載など、高度なカスタマイズに強いこだわりを持つユーザーが多い。それぞれが独自のこだわりを持っているため、ハードウェア面での技術や職人技の追求だけでなく、ファームウェアにも高度なカスタマイズ性が求められる。
(画像はZoom75公式サイトより引用)

理論上はマイコンと回路さえあればキーボードのファームウェアを書くことができる。しかし、キーマップを変更するたびにコードを書き換えたり、マイコンを変えたら一から書き直したりするのは非常に面倒だ。そのため、高度にカスタマイズ可能なファームウェアを自作するユーザーが現れた。キーボードの設定を定義するだけで、1行もコードを書くことなく対応するファームウェアを自動生成できる仕組みだ。
現在キーボード界隈で最も有名なのがQMKで、これはTMKから派生したオープンソースのファームウェアだ。キーボード界隈では極めてポピュラーで、Keychronなどのメーカー製品もQMKの設定に対応している。
先ほど挙げた機能は、QMKにはほぼすべて揃っている。キー配置の定義、マクロ、LCD、LED制御、Bluetooth、ジョイスティック、さらにはMIDIに至るまで、思いつく限りの機能がQMKにはほぼ網羅されている。しかも対応しているカスタムキーボードも非常に多く、例えば僕が現在使っているZoom65の定義ファイルもここで見つけることができる。
さらに僕がかなり驚いたのは、最近ではWebUSBにも対応している点だ。つまりWebブラウザ上で直接キーマップを変更し、変更後にそのままファームウェアの設定へ反映させることができる。
詳しい原理は僕自身まだ調べていないが、WebUSBを介してキーボードのファームウェアに直接データを送信する実装があるのだと思われる。興味のある人はVIAを参考にしてみてほしい。
おわりに
今では多くの基板やケースのオープンソースモデルや回路設計が公開されており、自分で「ゼロから」1台作り上げるハードルは昔に比べればかなり下がっている。それでも、その背景にあるプロセスを知るだけでも、キーボードを1台作ることが決して簡単ではないと実感できるはずだ。
関連記事
- 測定が目標になるとき:窓税からPull Request数まで かつて僕は小さなツールを自作し、四半期で自分がどれだけPRに貢献したか、レビューコメントをどれだけ残したか、チケットをどれだけ消化したかを集計して、上司にアウトプットを証明しようとしたことがある。上司は淡々と、評価はアウトプットだけで見るものではないと言った。数年後、僕はようやく理解した――測定が目標になるとき、それはもはや良い測定ではなくなるのだ。英国の窓税、ハノイのネズミ駆除の報奨金から、現代のPR数による開発者評価に至るまで、そのメカニズムはまったく同じだ。
- Cloudflare Images を画像ストレージ・変換ソリューションとして使う ウェブページに画像を1枚置くのはフロントエンドにとって最も簡単なことだが、リサイズや各種フォーマットの生成、さらにはトラフィックの負荷に耐えることまで完璧にやろうとすると、実際には一つの包括的なソリューションが必要になる。僕はその後、すべて Cloudflare Images に任せるようになり、オリジナル画像1枚だけを渡すようにしている。
- もう AWS Access Key を使うのはやめよう Access Key は AWS において見落とされがちなセキュリティリスクだ。OIDC と IAM Role を組み合わせることで、GitHub Actions にシークレットを一切保持させることなく、安全に AWS リソースを操作できるようにする。
- データベース主キー:AUTO_INCREMENT、UUID、そしてUUIDv7 バックエンド開発で度々直面する主キーの決定。auto incrementを使うべきか、それともUUIDか?衝突への懸念は?UUIDv7とcreated_at + インデックスの性能差はどれほどか?実際に2,000万件のデータで検証したベンチマークと設計上の意思決定を解説する。