壁ツェーン

オンギャーの思考回路

QMK Firmware を色々改造して最強の Corne Cherry を仕立てる

f:id:kb10uy:20210217010440p:plain

QMK Firmware の改造に手を出しました。

経緯

Corne Cherry V3、標準のファームウェアが VIA 対応なんですよね。これはこれで気軽にキーマップを変更できて良いのですが、やはり物足りない点もあります。 そこで自前でファームウェアをビルドするということになるのですが、どうせなら色々機能を追加したい。

というわけで、試行錯誤しつつ良い感じになったところでメモも兼ねて共有しようと思います。

解説

リポジトリはこちらになります。

github.com

Lock LEDs

最初に実装したのがこれです。 Num/Caps/Scroll Lock の状態を表示するようにしました。これは単に表示されてないとうっかり CapsLock とかがオンになって気付きにくいかなあと思ったからですね。

ドキュメントによればこれらの LED の状態は更新されたときに bool led_update_user(led_t led_state) が呼ばれるようなので、ここで状態を保存しておいて OLED に表示する内容を追加すれば完成です。

f:id:kb10uy:20210217013105j:plain

[NCS] というフォーマットで、それぞれのアルファベットが NumLock, CapsLock, ScrollLock を表わしています。

Lighting Layers

半透明のキーキャップを手に入れたからにはやはりバックライトを透過させたいです。そしてレイヤーに応じてどのキーがどの種類か分かったら便利だし面白いしピカピカしててゲーミング感があって楽しいです。 これも公式の Lighting Layers の機能を使えば実現できそうだったので、サクっと(?)追加してみました。左右の手でレイヤー定義を個別に作る必要のでちょっとコードが煩雑になってしまいましたが……

layer_state_t layer_state_set_user(layer_state_t state) から rgblight_set_layer_state を呼んであげればレイヤー状態と同期してライティングが変化します。簡単ですね!

Shift to Double Tap

個人的にはこれが一番やりたくて自前ビルドを決心したみたいなところがあります。要は Tap Dance です。 調べた限りではこの機能を使うには本当に自分でビルドするしかないようなので、地道に頑張っていきます。

Lower レイヤーは数字行や記号列を詰め込んでいるんですが、同時に Shift を入れようと思うとけっこう指がつらい配置になってしまう問題がありました。今振り返ってみるとそこまでつらくもないな。いや、とにかくつらかったんです。 具体的に言うと一番つらかったのは { } の入力で、右手の小指を使うのがなんか苦手なのでどうしても薬指で打ってしまい、手の角度に無理が生じてしまったのです。

そこで考えたのが「Lower レイヤーを有効化するついでに Shift も同時押しになるようにしちゃおう!」というやつです。詳しいコードはここでは割愛しますが、 ACTION_TAP_DANCE_FN_ADVANCED で 2 タップ以上のときに Shift も判定が入るようにしています。

Windows/macOS Switching

前の記事でも言及したように僕は無変換と変換で IME を切り替えているんですが、これは macOS のキーボードから輸入した作法です。なんですが、 macOS では該当するキーコードが違うんですよね。クソが代!! で、これの解決方法については既に何人も対応策を公開していらっしゃるところなんですが、僕のは結果的にどれとも違うような(本当か?)実装となりました。

まず内部ステートとして Windows モードか macOS モードかを保持するフラグを定義します。そして無変換・英数と変換・かなに該当するキー、あとモード切り替え用のキーコードも定義します。最後にそれぞれのキーコードの動作について、現在のモードに対応する切り替えキーコードを送信するように実装します。完成です。

f:id:kb10uy:20210217015954j:plain

このモードも OLED に表示するようにしました。だんだんここが賑やかになってきましたね。

追記 (2021-2-23(Tue))

この設定を EEPROM に保存するようにしました。これでもう接続ごとにモードを切り替える手間とはおさらばです。

ErgoDoxEZによるCapsLockを検知してLEDを光らせる(一部未検証) - Qiita で 4byte しか保存できないのか?ということが言及されていますが全くそんなことはないです。 Pro Micro に載ってる ATmega32u4 の EEPROM 容量は 1KB 、 QMK Firmware 側で予約されている容量は 34byte (執筆時点) なので数百 byte 程度の余裕があります。ではその空き領域をどう使うのかですが、 avr-libc で定義されている関数を直接使ってしまえばいいのです。QMK Firmware 側で予約されている容量は EECONFIG_SIZE という定数で参照できるので、ここより後ろに書き込んでやる分にはコアの動作には影響を与えません。

Performance Mode

あんまり気にする人いないと思うんですが、OLED の更新ってそこそこ時間かかるんですよね。それがキー入力のレイテンシーに影響するとなると音ゲーするときちょっとやだなあと思って、 OLED の更新を完全に止めるモードを実装しました

こう書くと簡単そうですがやってみると意外と大変で、更新を止めたはずなのに勝手に再開してしまうという現象に数時間悩まされました。結果からいうとこれは oled_active 変数周りの挙動の仕様で、OLED の自動消灯がビルドオプションで有効になっている場合ループ毎に毎回 oled_on が走るという罠にも程がある挙動をしていました。いやまあわかるけどさ……。

この変数だけを使っていたのではどうにもならなさそうだぞ、ということで oled_driver.c に追記して oled_paused という変数を新たに導入、これが true の間は OLED に I2C でコマンドを送信する関数は全て即座に終了するという感じにしました。これでようやく所望の動作になりました。

Data Sync

さて、ここまで当然のように左右が同時にステートを認識するような機能の話をしてきましたが、この「左右のステートを同期させる機能」が一番(コードが)込み入っててしんどかった部分です。

まず今の Corne Cherry は分割動作のドライバとして split_common をデフォルトで使用しています。これはマスターからスレーブにはライティングの状態を、スレーブからマスターにはスレーブ側のスキャン結果を相互に送りあっています。というか、これだけしか送っていません。どのレイヤーが有効なのかなども送っていません。そういう処理は全部マスター側が受け持っていることになります。重労働だな!とまあつまり、スレーブ側から見ると、勝手に同期してくれるのはライティングの状態だけということになります。Lock LED すら同期しないわけです。

上記の機能群では LL と S2DT 以外全てにおいてステートの同期が必要になってきます。そこで、 quantum/split_common/transport.c をコピーして改造します。マスターからスレーブに送信するデータに、ユーザー定義で好きなデータを詰められるフィールドを追加します。リポジトリ内ではここが該当します。あとはまあ適当にフラグを詰めたり取り出したりします

追記 (2021-2-23(Tue))

本家にこれと同じようなことを実現しようとするプルリクが投稿されていました。ほぼ同じタイミングで。

github.com

マージされたらこの機能をベースに書き直したいなあと思ってます。はやく入らないかなあ。

感想

かなり良くなりました。大満足ですわ~~!!!

この記事は最強ファームウェアの Corne Cherry V3 で書きました。