みなさん、ファームウェア書いてますか???
というわけで、本記事は前回の記事である「【自作キーボード】両手トラックボール付き分割キーボードを自作した話【設計編】」の続きになります。

まだ閲覧していない方は、是非覗いてみてくださいね!
前回の記事ではWトラボ付き有線分割キーボードの回路設計およびPCB設計を行いました。
記事内ではサッと作成したかのように淡々と工程を書きましたが、実は思ったより回り道をしながら完成まで至っているんですよね。
…よし、ここまで来たら後はファーム書いてケース設計するだけだ…!
長かった...完成までもう少し…ついに自設計キーボードが動くぞ…!

否、実際はここまでで進捗五割程度である。
もちろん五割と言うのは「完全初心者が一から分割Wトラボ付きキーボードを設計した場合」ということになりますが、まだまだ完成とは程遠いのが事実なんですね。
初心者なのに、「分割」「Wトラボ」「ロータリエンコーダ」の三つを盛り込んだ為に、このファームウェア設計フェーズではえらい苦労しました…

が、そんな苦労は一旦おいて置き、本記事ではトラボ周りのファームウェアを中心に完成までほぼほぼ一直線でお届けしようと思います!
では行ってみましょう!!!
※本記事内容を参照して起きたトラブルについて、筆者は一切の責任は持ちませんので予めご了承ください。
※細かい説明は省略しているので、ある程度知識がある方向けの記事となっています。
Contents

回路、PCB設計後の流れ
まずはここからですね。
回路とPCB設計をしたと言っても、それだけじゃキーボードは動いてくれません。
何をすればいいのか、
それは大きく分けて二つあります。
① キーボードに命を吹き込む(ファームウェア設計)
② キーボードをかっこよくする(ケース設計)
です。
②に関しては通常のキー入力のみを想定したキーボードであれば必須ではありませんが、トラックボールを用いる場合は必須になります。
なにせトラックボールをセンサーからある程度離して支える機構が必要になりますからね。
そんなこんなでまだまだやることはたくさんあるのですが、
本記事ではファームウェアについて取り上げます。

が、事細かく書いていると記事の量がとんでもないことになるので、要所要所(トラボ周り)を拾って綴るつもりですのであしからず。
筆者はQMKファームウェアへの理解がまだまだ浅いです。
現時点で不具合なく自設計キーボードが動いているという事実の元、記事を執筆していますので、誤った情報が記載されている場合があります。
あくまでも参考程度に捉えていただけると助かります。

ファームウェア設計
ではさっそくファームウェア設計について話していきます。
ファームウェアとは、キーボードの設計図のことですね。
昨今の自作キーボード界隈で使用されるファームウェアは主に以下の二種類が存在します。
・ QMKファームウェア(有線前提、VIALやREMAPに対応可能)
・ ZMKファームウェア(バッテリーを用いた無線接続が可能)
今回僕は有線かつVIAL、REMAP対応のキーボードを作りたかったので、「QMKファームウェア」での開発を実施しました。
有線にこだわる理由は単純で、「安定しているから」です。
元々業務利用を想定しており、一日八時間以上ぶっ通しで使用することになるので、安定していることは絶対条件なんですね。

バッテリー等を気にしながら業務なんてやってられないですしね。
キーマップも気軽に変更できる方がいいですし、普段使い慣れているVIALやREMAPに対応させることもほぼほぼ確定していました。
…が、ここで困った問題が発生します。
両手にトラックボールを搭載した分割キーボード(QMKファームウェアで作成)の作例がかなり少ないのです。
よく探すと片手にトラックボールを搭載している作例は数多あるのですが、両手となると一気に少なくなります。
日本で開発されている有名なキーボードでいうと、ゆびなが氏が開発されている「LotusOrb」ぐらいしか見つけられませんでした。

作例が少ない。これは非常に由々しき問題です。
なぜならこれまで丁寧に惹かれたレールをただ走っているだけだった初心者が、いきなり大海原に投げ出されたも同然だからです。

わからないから書籍を買ったり調べたりして時短開発をしてたのに!調べても中々出てこなくて書籍も無いんじゃ詰みじゃないか!!!
というわけで、この段階で明らかに開発効率が落ちました。
が、振り返ってみると
この機能どうやって実装すればいいんだわからん助けて!!!!!
→(数日後) なんかできた!!!
の繰り返しでした。
ひたすらコードの調整を繰り返してコンパイル、エラーとにらめっこしながらまた修正、動くようになる。
開発者とはかくあるものなのかもしれません。(悟り)

よくわからない自分語りはここまでにして、実際のファームウェアについて見ていきましょう!
公式ドキュメントはこちらです。
誰の助けを借りずとも、このページを読み込めばあなたもQMKファームウェアマスターとなれるでしょう。
ちなみに私は要所要所でしか確認していません。
https://docs.qmk.fm/newbs

前提条件
ファームウェアを開発するためには、開発用環境の作成が必要です。
こちらは調べれば記事がたくさん出てくるので、本記事では割愛します。
予めご了承ください。
また、実際のファームウェアについてはへびたわのGitHubリポジトリにて公開中です。
手っ取り早く確認したい方はそちらをご覧ください。
使用したQMK CLIのバージョンは「1.1.8」です。
今回紹介するファームウェアは左右共通ファームウェアです。
左右それぞれで別のファームウェアを適用して制御する方法もありますが、本記事では取り上げませんので合わせてご了承ください。

ちなみにファームを1つで完結させているのは地味なこだわりポイントです。
1つだと管理も楽ですし、ビルドの際の手間も省けるのでとても良い…!
既存の作例では大半が左右別ファームだったので作成が地味に大変でした…
左右共通ファームとするためには回路設計において工夫が必要です。
具体的に言うと、左右で使用するピンが同一であるということが条件となります。
ファームウェア階層
ファームウェア階層についてです。
QMKファームウェアを使用して自作キーボードを作成する際のフォルダ構成についてはあらかた決まっています。
「/qmk_firmware/keyboards/★自キーのフォルダ名★/」配下の階層にファイルを置いていくイメージですね。
僕の場合はvialに対応させたかった事、トラボやLEDの処理は他のキーボードにも流用出来るようにフォルダを分けておきたかった事から、以下の四階層にしました。

四階層にしましたとあたかも最初からそう決めて設計を始めたみたいな書き方をしていますが、実際は色々こねくり回したあげくたどり着いた姿です。
「resquareフォルダー」と「vialフォルダー」に格納されているファイルは分割Wトラボキーボードを動かすための基本的なファイルたちで、「libraryフォルダー」に格納されているファイルは、トラックボールを動かした際の細かい挙動の制御を行っているファイルです。
本記事では前者について取り上げようと思います。
(後者はカスタムしまくっているため汎用的で無く、解説もやや複雑になりそうな為)

これからQMKファームウェアでWトラボキーボードを作成される方の参考になるように、トラボ周りのパラメータについてなるべく中身を解説しながら話を進めていければと思っていますので、よろしくお願いします!
「resquareフォルダー」
このフォルダを便宜上一番上の階層とします。
全ての基本となるフォルダですね。
簡潔に述べると、「キーボードのハード構成の情報を置いているフォルダ」です。
五つファイルがあると思いますが、それぞれの役割は以下です。
・ keyboard.json →ハード情報やレイアウト定義
・ config.h →分割やトラボセンサー、エンコーダやVIAL用設定定義
・ rules.mk →各種機能のON,OFF定義
・ mcuconf.h →MCU設定定義
・ halconf.h →SPI有効化等定義
全てのファイルについて、Wトラボを実現する為に必要なコードのみに絞ってお届けします。
keyboard.json ファイル
長々とハードの情報やレイアウトが記載されていますが、トラボを動かすにあたり必要な項目は以下の赤字部分のみです。

ポインティングデバイスを使うよーと明示的に指定してあげるだけですね。
{
"manufacturer": "hebitawa",
"keyboard_name": "resquare",
"maintainer": "hebitawa",
"bootloader": "rp2040",
"diode_direction": "COL2ROW",
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"nkro": true,
"pointing_device": true
},
- 以下略 -
config.h ファイル
トラボを動かす肝ファイルです。
ここの記述はかなり思考錯誤しました…
トラボが片方しか動かなかったり、不安定になったり、公式ドキュメントを見ながらトライ&エラーで書いていった記憶があります。
■ ポインティングデバイスに関する公式ドキュメント
特に重要なのは以下の赤字部分です。
#pragma once
#define EE_HANDS
#define DYNAMIC_KEYMAP_LAYER_COUNT 4
// Split parameter
#define SPLIT_HAND_PIN GP3
#define SERIAL_USART_FULL_DUPLEX
#define SERIAL_USART_TX_PIN GP12
#define SERIAL_USART_RX_PIN GP13
#define SPLIT_USB_TIMEOUT 2000
#define SPLIT_USB_TIMEOUT_POLL 20
// SPI Setting
#define SPI_DRIVER SPID0
#define POINTING_DEVICE_CS_PIN GP5
#define SPI_SCK_PIN GP6
#define SPI_MISO_PIN GP4
#define SPI_MOSI_PIN GP7
// PMW3360 Setting
#define POINTING_DEVICE_INVERT_X_RIGHT
#define SPLIT_POINTING_ENABLE
#define POINTING_DEVICE_COMBINED
#define POINTING_DEVICE_SCROLL_ENABLE
- 以下略 -
■ SPI Setting について
そもそもSPIとはなんぞやという話なのですが、簡単に言うと「マイコンと周辺デバイスの通信方式」の一つです。
そしてSPIは基本的に以下の四つの信号で動作します。
・ SCK →クロック(タイミング合わせ)
・ MOSI →マイコンからデバイスへ
・ MISO →デバイスからマイコンへ
・ CS →どのデバイスと通信するか選ぶ
そしてこれらの信号を扱うことが出来るピンについては、マイコンによって定められています。
今回使用するRP2040-Zeroであれば以下のピン(赤枠内)が対象です。
ピンク背景に白文字で「SPI0 or SPI1」となっているピンが対象のピンということですね。

SPIの0を使うのか、1を使うのかをまず決める必要があります。
また、「SCK」を割り当てられるピンは「SCK」ピンのみで、「CS」を割り当てられるピンは「CSn」ピンのみです。「MOSI」と「MISO」に関しては同SPIの中であれば任意のピンで構いません。

ここを確認せず適当にピンを割り当てると全く動作せず悲しい結末を迎えます。
今回の僕の設計だと、GP4~7のSPI0を使用する方針としたので
#define SPI_DRIVER SPID0 →SPI0使うよ
#define POINTING_DEVICE_CS_PIN GP5 →CSはGP5を使うよ
#define SPI_SCK_PIN GP6 →SCKはGP6を使うよ
#define SPI_MISO_PIN GP4 →MISOはGP4を使うよ
#define SPI_MOSI_PIN GP7 →MOSIはGP7を使うよ
ということになっているわけですね。
■ PMW3360 Setting について
こちらはその名の通りセンサー周りの制御設定です。

一つずつ見ていきましょう!
> #define POINTING_DEVICE_INVERT_X_RIGHT
右側のポインティングデバイスのX軸を反転する設定です。
これ無しだと右のポインター移動が反転していたため、さらに反転させて元に戻しています。
> #define SPLIT_POINTING_ENABLE
分割キーボードでポインティングデバイスの使用を有効化する設定です。
これ無しじゃ始まりません。
> #define POINTING_DEVICE_COMBINED
左右のポインティング入力を合成する設定です。
Wトラボを実現する肝設定です。
Wトラボって内部でどんな処理になっているかというと、
右手側 → ボールの移動をX軸Y軸の移動とする
左手側 → ボールの移動を水平垂直方向の移動(X軸移動を垂直移動に、Y軸移動を水平移動に変換)とする
右手と左手のレポートを合成してX軸Y軸の移動(カーソル移動)、水平垂直方向の移動(スクロール移動)をPCに送っている。
こんなことをしています。
両手のトラボの移動の合成は必須なので、「COMBINED」を使っているのですね。
「RIGHT」や「LEFT」を使用してしまうと片側しか動かなくなるので注意です。

個人的にここで結構嵌まりました…
左右共通ファームって中々実装例が見つけれず、左右別々のファームじゃないと無理なのかと思い、色々試したのですがうまくいかず…
こうやって一つずつ確認すると単純なんですが、やってる時は中々切り分けできませんでしたね。
ちなみに「右手と左手のレポートを合成する」と書きましたが、正確に言うと「スレーブ側のレポートをマスターに送って合成している」です。
センサーのマスター、スレーブの判定はキーボードの左右の判定とリンクしています。
僕の設計だと左手がマスターで、右手がスレーブなので、センサーもそのまま左がマスター、右がスレーブとなっています。
> #define POINTING_DEVICE_SCROLL_ENABLE
ポインティングデバイスでのスクロールを有効化する設定です。
左手側はスクロール動作にしたいので必須ですね。
■ Split parameter について
一見トラボとは関係なさそうな分割用の設定値ですが、
左右を認識させる際の通信のポーリング間隔を調整しないとセンサーの初期化に失敗してトラボが動かないことがありました。
よって関連のパラメータを調整しています。
この辺はまだまだ微調整可能だと思うので、適宜変更して最適値を探っていくのがいいと思います。
ちなみに該当箇所はこちら。
#define SPLIT_USB_TIMEOUT 2000
#define SPLIT_USB_TIMEOUT_POLL 20
rules.mk ファイル
各種機能を有効化する為のファイルです。
センサーで何を使うか等もここに記載します。
今回は「PMW3360」を使用する設計としています。
- 前略 -
# POINTING DEVICE
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = pmw3360
MOUSEKEY_ENABLE = yes
SPLIT_POINTING_ENABLE = yes
SPI_DRIVER_REQUIRED = yes
- 後略 -

こちらも一つずつ見ていきましょう!
> POINTING_DEVICE_ENABLE = yes
ポインティングデバイスの使用を有効化する設定です。
> POINTING_DEVICE_DRIVER = pmw3360
センサーとして「PMW3360」を使うよという定義です。
> MOUSEKEY_ENABLE = yes
マウスキーを有効化する設定です。
ポインタ移動やスクロールだけでなく、マウスのクリック等も使用したいので必須ですね。
> SPLIT_POINTING_ENABLE = yes
分割キーボードでポインティングデバイスの使用を有効化する設定です。
左右それぞれの入力を扱えるようにする設定みたいですね。
> SPI_DRIVER_REQUIRED = yes
SPIドライバを強制的に有効化する設定です。
PMW3360ではSPI通信を使用するので必須ですね。
mcuconf.h ファイル
SPI0 と SPI1 のどちらを使うかを決めているファイルです。
conf.hファイルの段落でも説明した通り、今回僕はSPI0を使用します。
なのでSPI0を有効に、SPI1を無効にする設定を明示的に書いているんですね。
ちなみに有効化は「#define~」の行ですが、その前の行に無効化する為の「#undef~」を記述しています。
無効化した後に有効化するような記述にしているわけですね。
何故有効化だけじゃダメなのかは正直わかりませんが、国内外の作例を見るとみなさんこのように記述されていたので、慣習なのかもしれません。

僕も例に漏れず同様の記述をしています。このような記述にすると綺麗に上書きが出来るのかも…?
#pragma once
#undef RP2040_SPI_USE_SPI0
#define RP2040_SPI_USE_SPI0 TRUE
#undef RP2040_SPI_USE_SPI1
#define RP2040_SPI_USE_SPI1 FALSE
#include_next <mcuconf.h>
halconf.h ファイル
SPIを使うよということを明示的に記述しているファイルです。
mcuconf.hファイルと同じく、一回無効化して有効化するスタイル。
こちらもみなさん同じように作成されていました。
#pragma once
#undef HAL_USE_SPI
#define HAL_USE_SPI TRUE
#include_next <halconf.h>

ぶっちゃけこの「halconf.h」ファイルはコピペで取り敢えず置いておけばいいと思ってます。

おわりに
いかがだったでしょうか?
解説記事と言うよりは、備忘録記事に近くなってしまいましたが、これからQMKファームウェアで分割Wトラボキーボードを作成しようとしている誰かの参考になれば幸いです。
また、記事内で誤った知識を記載しているのを発見した場合は是非筆者まで連絡いただけますと非常に助かります。
(お問い合わせフォームまたはXのリプやDMでいただきたいです)
トラボの細かい挙動をカスタムしているファイルの方の解説は筆者に余裕が出来たらしようかと思います。

ではみなさん、次の記事でお会いしましょう!
良きキーボードライフを!
※この記事は「Re:Square」を使用して執筆いたしました。

