メモ: ZMKでPMW3610のXY Swap・Invert設定がリセットされる問題に対処する

背景
ZMKキーボードファームウェアとPMW3610光学センサーを用いたトラックボール付きキーボードを使用している。
ドライバーには以下を使用。
このドライバーには、XYをswapしたり、XYそれぞれ方向をinvertする設定がある。
キーボードの使用中にPMW3610の基板を物理的に外して再度付け直すと、これらの設定がリセットされる問題が発生する。意図的に付け外ししなくても、振動で一瞬コンスルーの接触が切れるだけでも同じ現象が起こるようで、稀にXYが逆になって困っていた。
キーボードを再起動すれば解消するが、特に無線接続時はいちいち時間がかかるのでできればやりたくない。

原因究明・仮説
ドライバーでswapやinvertがどのように実装されているか見てみると、単にソフトウェアでswap、invertしているだけだった。これはコンパイル時に確定するので、動作中に無効になることは考えにくい。
PMW3610のデータシートを見てみると、ハード側でも同じ機能が提供されており、RES_STEP
レジスタ(0x85
)に所定の値を書き込むことで設定可能となっている。
レジスタは8ビットで、上位7ビット目がSWAPXY
、6ビット目がINV_X
、5ビット目はINV_Y
、下位0~4ビットがCPIを200で割った値を入れることになっている。
SWAPXY
、INV_X
、INV_Y
の値はいずれも0
が無効、1
が有効。
ドライバーでそのレジスタをどうしているか見てみると、CPIだけ設定していて、他は常に0
(無効)になっている。
(ちなみに、このbadjeff版のドライバーの他に、inorichi版とZephyrに含まれるドライバーの計3種類が存在するが、いずれもハード側のSWAP機能は使用していない。まあ、ソフトで簡単に実装できるものをわざわざレジスタに書き込むとかしなくても、ということなのだろうか。)
この設定がハード側のデフォルト値と異なっているから、基板再接続によりリセットされると反転していしまうのではないかと推測できる。
そこで、データシートに記載のデフォルト値を見てみると、0x86
= 0b1000_0110
、つまり、SWAPXY
は有効、INV_X
は無効、INV_Y
は無効、CPIは1200のようだ。
(PMW3610のデータシートにはフルのデータシートとフルではないデータシートが存在するが、0x86
はフルではない方に書かれている値で、フルの方には0x06
と書かれている。実際の挙動からすると、フルではない方(0x86
)が正しいと思われる。)
よって、以下の仮説が立てられる。
仮説: SWAPXY
有効、INV_X
無効、INV_Y
無効となるようにRES_STEP
レジスタ(0x85
)に値を書き込めば、基板が再接続されても反転しなくなる。

検証
ソフトで制御するのではなく、レジスタに値を書き込むことでswapとinvertを行うようにドライバーを書き換えてみた。
uint8_t value = (cpi / 200);
// Set swap and invert bits in RES_STEP register
#if IS_ENABLED(CONFIG_PMW3610_SWAP_XY)
value |= (1 << 7);
#endif
#if IS_ENABLED(CONFIG_PMW3610_INVERT_X)
value |= (1 << 6);
#endif
#if IS_ENABLED(CONFIG_PMW3610_INVERT_Y)
value |= (1 << 5);
#endif
そして、ハード側のデフォルト値と一致するように、
CONFIG_PMW3610_SWAP_XY=y
CONFIG_PMW3610_INVERT_X=n
CONFIG_PMW3610_INVERT_Y=n
と設定した。
その結果実際のマウスセンサーの向きと異なっている場合は、ZMKのInput Processorを用いてソフト側で制御すればよい。
CPIは普段使用している800
のままにした。
これで再度基板を付け外ししてみたところ、XYの反転が起こらなくなった。
XとYのinvertの方を有効化(=y
)してみると、XYの反転は起こらないままinvertだけ反転した。
よって、仮説は正しかったと判断できる。
CPIもリセットされるのでは(データシートによればデフォルト値は1200で、800より大きく増えるはず)と思ったが、こちらは体感変わっていない。

これからのアクション
ドライバーを編集したのをプルリクするとか、ZMKのDiscordに投げるとかしようとも考えたのだが、ZMKがv0.4(Zephyr v4.1)のリリースが控えていることを考慮して一旦様子見することにした。
というのも、Zephyrに含まれるPMW3610ドライバーがZMK v0.4(Zephyr v4.1)では使用可能になるからだ。これまでZMKではzmk-pmw3610-driverをモジュールとして外付けしていたが、それとdevicetreeのcompatible
名が衝突するため、このまま両立はできない。Zephyrのドライバーを使うのが主流になるのか、名前の衝突を回避した上でzmk-pmw3610-driverを使い続けるのか、どちらに転ぶか見届けてからにしたい。
もしZephyrの方が主流になった場合は好都合で、Zephyrのドライバーは一度レジスタの値を読み取ってから、invert・CPIの部分だけ書き換えてレジスタに書き戻すことで、デフォルト値を上書きしないようになっている。よって、ドライバーを書き換えずとも、invertを有効にしない限り反転問題は発生しないはずだ。
または、根本的に接続が切れないようにするという解決策もある。
自分はイスのアームレストにキーボードを設置しているので、イスを立ったり座ったりすると振動が発生してしまう。普通に机の上に置いていれば問題は滅多に起こらないはずだ。
また、使用しているキーボードではPMW3610基板をコンスルーで接続しているが、別に基板を頻繁に付け外しする必要があるわけではないので、はんだ付けしてしまっても構わないだろう。