Kailh x Lofree Shadowシリーズ、Kailh Choc V2(Deep Sea Silent MINI)で使えるキーキャップを調査した

調査結果は記事の途中にあります。

Kailh Choc V2の新設計登場までを軽く振り返る

キースイッチメーカーKailhは、薄型スイッチとしてChocシリーズを販売してきました。私もChocの薄さに惹かれて、Lily58、Sparrow62キーボードでは積極的に使ってきました。一方Kailh Choc V1シリーズは、専用のキーキャップを必要とする構造をしており、今は種類が増えましたが、Kailh公式のキーキャップしかないという状態が長く続いていました。

Kailh Choc V1

その中、KailhはChocV2を2021年に発売しました。こちらはCherryMX互換ステムの形をしています。このため、種類豊富なCherryMX互換キーキャップを使用できるように思えます。しかし、CherryMX互換スイッチの特徴的な形を期待しているキーキャップが多く、中には使えないものも存在しています。

Kailh Choc V2

また、1ピンの追加と、中央の軸のサイズアップがあり、異なるフットプリントが必要になります。 Choc V1スイッチ対応自作キーボードがChoc V2スイッチには対応しないといった状況が生まれていました。

Kailh Choc V1/2のフットプリント

Kailh Choc V1データシート: https://www.kailhswitch.com/Content/upload/pdf/201915927/CPG135001D01-Red_Linear_Choc.pdf

Kailh Choc V2データシート: https://www.kailhswitch.com/Content/upload/pdf/202015927/PG135301D03.pdf

このChoc V2スイッチについて、最初、リニア(Red)、タクタイル(Brown)、クリッキー(Blue)3種類が発売されましたが、Choc V1の時のように多種の展開は行われておらず、私はてっきりDiscontinuedにするのかと思っておりました。

2つの新しいChoc V2の登場

そして、2024年、Lofree Flipにて新設計のChoc V2スイッチが利用されました。Kailh x Lofree Shadow Seriesと名付けられています。以下、Shadowスイッチと本記事では呼びます。さらに自作キーボードの愛用者にも利用できるようになりました。

www.lofree.co

Kailh x Lofree Shadow Ghost

この感触が非常に良く、以前天キーでLofree Edgeを触らせていただいたとき、その打鍵感の良さに感動しました。 素材が全て自己潤滑性を持つPOMでできており、引っかかりがなく滑らかです。 この新しいLofreeスイッチをいつか使ってみようと考えていました。

国内でも既に販売されています。

talpkeyboard.net

Lofree Ghost switch / linear (90pcs)shop.yushakobo.jp

Lofree Flow Switch (5 pieces)shop.dailycraft.jp

また、KailhからサイレントタイプのChoc V2が発売されました。それも好評だったKailh Deep Sea Silentブランドです。Kailh Deep Sea Silent MINIと名付けられているようです。

Kailh Deep Sea Silent MINI

確かにこの小ささで静音が実現されており、今までカチャカチャ感の強かったChocシリーズの新たな一歩のように思えます。

国内でも既に販売されています。

talpkeyboard.net

さらに、この後も新しいShadowシリーズ仕様、Choc V2仕様のスイッチが登場しており、非常に盛り上がってきています。

私が作っている分割自作キーボードSparrow62の、次バージョンV3を考えていたのですが、やはりChoc V2のサポートは続けたいと思いました。

さて使えるキーキャップは

さて改めて、ShadowスイッチとChoc V2スイッチに対して、どのキーキャップが使用可能か検証することにしました。

本来は、Low Profile用と銘打たれたキーキャップを使うのが正しいと思われます。 ただ、私自身が所有していないのと、Sparrow62で以前使っていたときにはSDA Profileのものを普通に使っていたため、同様に使えないかと改めて所有しているものを検証することにしました。

それで実際にSparrow62v2をLofreeスイッチで使ってみて、Sparrow62v3を考えたいと考えました。

この際に検証するのは以下の点です。

  1. まず刺さるか?
  2. 一番ボトムまで押した時に、スイッチに干渉するか?
  3. 一番ボトムまで押した時に、トッププレートに干渉するか?

2.は想像が付くと思うのですが、ステムを受ける部分が奥まっていると3.が発生する可能性があり、トッププレートがたたかれてカンという強い音が発生します。本来キースイッチで受けるべき力を、トッププレートで受ける形になってしまいます。

これらを検証しました。

ただし、あくまで同Profileを名乗っていても、同様に使えることを保証するものではありません。本記事は利用した1例を紹介するものです。キーの外形よりも、内側の肉厚さが干渉を引き起こすため、同メーカのものであれば大丈夫な可能性が高い、程度に思っておくと良さそうです。

また、非対応であってもそれぞれは素敵なキーキャップであり、本記事はおとしめることを意図したものではありません。MX互換スイッチで楽しみましょう!

DSA Profile

最も安価で1u無刻印キーキャップでよく使われるDSA Profileです。全ての列で同じ高さのものです。遊舎工房さんとTalpKeyboardさんでも1個単位で扱われているため、1uを多用したキーボードを作る際に便利なものです。

今回使用したものは、遊舎工房さんで販売されている無刻印のものと、以前TalpKeyboardさんで扱われていたレーザー刻印ものです。

talpkeyboard.net

DSA 無刻印キーキャップ(1個)shop.yushakobo.jp

検証結果は、OKでした。

XDA v2 Profile

XDA Profileは、SDA同様全ての列の形が等しく、SDAよりも天面が広いのが特徴です。TalpKeyboardさんでも1個単位で無刻印を購入できます。

talpkeyboard.net

このうち、XDA v2 Profileと書かれた以下のキーキャップを利用しました。

talpkeyboard.net

検証結果は、NGでした。ボトムまで押した時に干渉し、さらに引っかかってキーが持ち上がりません。キーキャップ自体はいいものなのですが。

Cherry Profile(互換品)

今となっては低めのProfileに分類されるようです。同じ仕様の互換品がたくさんつくられています。

今回、私の所有していた、安くて良さそう思って買ったらコピー品に近かったものを使います(リンクは貼りませんが、爱菲というメーカーのもので、もの自体は良かったです)。

検証結果は、OKでした。トッププレートまでの距離も十分あります。

この結果は、数あるCherry Profileキーキャップのうちの、一つのメーカの例であることに気をつけてください。外形は同じでも、内側の肉厚さで対応していないことは考えられます。

Melgeek MDA Profile

Melgeek MDA Profileは、Melgeekの発売するキーキャップです。MDA Future Suzuriも同じものです。ダブルショットではなく、昇華印刷の仕様です。

Melgeek MDA Big Boneを使いました(正面からの写真を撮り忘れました)。以前TalpKeyboardさんから購入させていただきました。

MelGeek MDA ビッグボーン カスタム PBT キーキャップ セットwww.melgeek.com

検証結果は、OKでした。

安価なCSA Profile

Amazonやアリエクで販売されている安価なダブルショットキーキャップがあります。わりとしっかりとした作りで気に入っています。

https://aliexpress.com/item/1005005245490691.html

検証結果は、NGでした。押せますが、ボトムまで押すとプレートに接地しました。

XVX Profile

XVX Profileキーキャップは、Amazonやアリエクでも販売されている安価で背の低いキーキャップです。

写真のO、Iキーが前後が逆に刺してしまっていました…。ご容赦ください。

検証結果は、NGでした。LofreeスイッチはOKでしたが、Choc V2でボトムまで押した時にスイッチに干渉します。

XVX Low Profile(おおやけハジメさんより)

XVXのシリーズにはLow Profileのものも販売されています。

XVX Skyline R2 - Low Profile PBT Double-shot Keycap - WOBshop.yushakobo.jp

おおやけハジメさんから情報いただきました。XVX Low Profileはギリギリセーフとのことです!

Pulsar Low Profile Keycaps(おおやけハジメさんより)

[US ANSI] Pulsar Low Profile Keycaps 104 setpulsargg.jp

おおやけハジメさんから情報いただきました。pulsarの販売するLow Profile KeycapはOKとのことです!

Akko OSA Profile

Akkoが販売しているキーキャップです。遊舎工房で以前購入しました。安価な割にキーが豊富です。

en.akkogear.com

OSA Profileには、Low Profileと付いているものもありますが、検証したのは付いていないものです。

検証結果は、NGでした。スイッチに干渉します。

MelGeek MG Profile

ここからは背の高いキーキャップの検証です。

MelGeekが発売する、SAに近い背の高くレトロな感じのキーキャップです。色合いがレトロでかっこよくて使っています。なお、写真のものは販売終了しているキーボードのキーキャップのみをメルカリで落札して手に入れました。

Keycap Setwww.melgeek.com

検証結果は、NGでした。スイッチと干渉はしませんが、トッププレートに接地してしまいます。

Drop MT3 Profile

Dropキーキャップです。セールの時に購入しました。背が高いレトロなキーキャップですが、わりとそういうのも打鍵感に良い影響を与えると思っています。

drop.com

検証結果は、なんとOKでした。トッププレートと紙1枚の隙間が空きます。

SA Profile(互換品)

中華互換品のSA Profileキーキャップ(爱菲)を所有していたので試してみました。TaoBaoで買うと安いです。

https://item.taobao.com/item.htm?_u=720eid5eae6859&id=666499084888

検証結果は、NGでした。スイッチと干渉はしませんが、トッププレートに接地してしまいます。

検証結果まとめ

改めて表にすると以下となります。繰り返しになりますが、私の家にあったもので、全てのキーキャップでの結果を保証するものではありません。

name 検証結果
DSA Profile OK
XDA v2 Profile NG スイッチ干渉
Cherry Profile(互換品) OK
Melgeek MDA Profile OK
CSA Profile NG トッププレート干渉
XVX Profile NG スイッチ干渉
XVX Low Profile OK
Pulsar Low Profile Keycaps OK
Akko OSA Profile NG スイッチ干渉(LofreeスイッチはOK)
MelGeek MG Profile NG トッププレート干渉
Drop MT3 Profile OK(しかし、トッププレートと隙間は紙1枚程度)
SA Profile(互換品) NG トッププレート干渉

Cherry MX互換仕様のLow Profileキーキャップ

いくつかLow Profileと銘打たれたCherry MX互換仕様のキーキャップが販売されています。

所有していませんが、どれか買ってみようと検討しているため、紹介します。おそらくLow Profileスイッチ用に作られているものようなため、大丈夫だと思われます。

Chosfox Low Profile

ChosfoxでChoc V1用のキーキャップセットも出ていますが、CherryMX互換スイッチタイプのLow Profileも発売されています。

talpkeyboard.net

THT(Tai-Hao Hins) Low Profile

Tai-HaoからもLow Profileモデルが出ています。

talpkeyboard.net

Lofree Flow/Edge

LofreeにてFlow/Edgeの交換用キーキャップが販売されています。

Lunewww.lofree.co

Sparrow62v2にShadow Ghostスイッチを付けて検証中

Shadow Ghost(リニア)スイッチを購入し、MDA Future Suzuriを差し込んで使ってみています。

おわりに

ぜひ、本記事を参考に、Low Profileスイッチの自作キーボードに挑戦してみてください。

HHKB LifeでSparrowDialが紹介されたよ(+キーボード遍歴紹介)

HHKB LifeというHHKB公式コラムサイトにて、『ぺかそ&びあっこが厳選!2024年最新クセ強自作キーボード』というコンテンツでSparrowDialを紹介いただきました!

happyhackingkb.com

私自身、最も長く使ったキーボードはHHKB Lite2になり、キーボードに興味を持つきっかけとなったので、非常に感慨深いです。

クセ強枠ですが、他のキーボードの癖も強く、入力に特化させてゴテゴテに進化させたぜという感じの癖が強いのやら、普通そうでよく見ると配列が変わってて癖が強いのやら、自作キーボードの世界を感じられるのでとても面白い記事です。

このような記事がHHKBから出ているのは、キーボードの世界を押し広げてくれるようで、流石ですね。

SparrowDialは現在もBoothにてキットを販売中です。

74th.booth.pm

おまけ、雑なキーボードとの関わりの遍歴

  1. 1995年(小3): 東芝ワープロ ルポを誕生日に買ってもらい、キーボードに触れる日々が始まる。
  2. 1997年(小6): PC9821V166(Win95)を誕生日に買ってもらう。キーボードでもゲームをするようになる。
  3. 1998年(中1): ラノベにはまり、自分でも書いてみるようになる。
  4. 1999年(中2): ゲーム作りにはまり、フリーソフト配布サイトのホームページを作るようになる。
  5. 2000年(中3): 自作PCを組み立て、安キーボードを使う。
  6. 2002年(高2): ラノベ1本(原稿200枚くらい)を初めて書き上げる。
  7. 2004年(大1): 大学のワークステーションのキーボードがHHKBだった。本格的にプログラミングを学ぶ。文芸サークルに入部。
  8. 2007年(大4): HHKB Lite2を買う。US配列を手に入れたが、慣れずJIS配列を買い直す。プログラミングがバイトになる。長編小説1冊分を書き上げる。
  9. 2008年(M1): キーボードの配列を変更するのにはまる。CapsLockキーがESCキーになる。
  10. 2010年: 社会人になり、SEになる。客先常駐の持ち込み機器申請に、HHKB Lite2と書く。会社と自宅用に必要になり、HHKB Lite2を買い足す。
  11. 2012年: MacBookProを買い、趣味開発のメインマシンになる。
  12. 2014年: HHK Professional Type-S を買うが、打鍵感が合わず手放す。
  13. 2015年: Magic Keyboard JISを買い、これがメインキーボードになる。キーボードは薄いに限ると悟る。
  14. 2016年: 新たな派遣先が大学でキーボードがUS配列だった。US配列への改宗を決める。Magic Keyboard USを買う。
  15. 2018年: US配列のMacBookProが標準機の会社に転職する。
  16. 2018年12月: 冬コミで、Lily58に出会い、自作キーボードへの日々が始まる。Gateron赤軸+SAキーキャップが合わず、薄いChocV1で運用する。
  17. 2020年8月: ChocV2が気になりすぎて、Ajisai74を買う。しかし、実装に失敗し、リカバリ配線することで、キーボードへの理解を深める。ChocV2は良い、カラムスタッカードは良いと考えるきっかけになった。
  18. 2020年11月: オリジナルキーボードとして、ChocV2対応したLily58インスパイアSparrow62を作る。頒布するようになる。
  19. 2021年1月: HolyPandaスイッチの感触が良いと聞いた派生で、スイッチDurock T1と出会う。スイッチはDurock製に傾倒する。
  20. 2021年2月: トラックパッドまでの距離をゼロにして、ポインター操作を快適にすることを追求する。
  21. 2021年6月: 強いタクタイルでもスムーズなDurock Sunflowerが最推しすいっちになる。
  22. 2021年10月: ESP32を使ったBluetoothキーボード電卓 Sparrow24 を製作、頒布する。
  23. 2022年5月: Sparrow62v2(+1)を頒布開始する。遊舎工房にも委託する。
  24. 2022年10月: リニアスイッチを楽しむためにはケースが必要なのではと思い、Sparrow59を設計する。Poker、GH60互換ケース用キーボードを作る流れが始まる。
  25. 2023年3月: ジョイスティックモジュールStickPointVを作り、ケース付きキーボードに埋め込む。天キーVol.4にて展示する。モジュールの頒布を開始する。Durock Black Lotusに出会う。
  26. 2023年11月: M5Dialをトラックパッド化したら思いのほか良かった。天キーVol.5にて展示する。
  27. 2024年3月: M5Stackをトラックパッドとして組み込んだSparrowDial、レバーレスアケコンSparrowG21、StickPointV組み込みキーボードSparrow60Cを製作し、キーボードマーケットトーキョーで頒布する。
  28. 2024年8月: HHKB Lifeの記事で、SparrowDialが紹介される

IO Expander MCP23017で分割したキーボード(Sparrow62v2) でQMK Firmwareに対応させる

tl;dr

  • MCP23017は、I2C IOエキスパンダーで、IOが足りないない所をI2Cプロトコルで拡張できるICだよ。16bitのIOが増やせるよ。ただし、プルダウン機能はないよ。
  • Sparrow62v2では左手側にRaspberry Pi Picoを積み、右手側にMCP23017を積み、その間をI2Cで接続したキーボードだよ。今まで、KMK Firmwareしか対応してなかったよ。
  • MCP23017で利用したプロトコルは、INPUT/OUTPUT方向を決めるIODIRA, IODIRBと、値のゲットセットを行うGPIOA、GPIOBさえ使えば良いよ。
  • QMK Firmwareで、scan_custom_matrix()を実装することで、MCP23017でのマトリックススキャンを実装したよ。

Sparrow62v2

Sparrow62v2は、私の作っている左右分割型自作キーボードキットです。かつては游舎工房でも委託販売しておりましたが、現在は在庫切れとなっております。そろそろv3を作りたいと思っており、再販予定は今のところありません。

左右分割で左右でレイアウトが異なっているのと、トッププレート、ボトムプレート、メインボードも厚さがそれぞれ異なるPCBを利用しているため、結構生産にコストがかかります。1年半で20程度販売できたのですが、再生産コストをかけるならばv3を設計したい気持ちがあり、v2の再生産を予定していません。

一方、利用いただいてる方はいらっしゃり、QMK Firmware対応のリクエストを受けました。MCP23017を利用しており、QMK Firmwareの基本機能に習熟していないこともあり、販売当初からKMK Firmwareのみをサポートさせてもらっていました。SparrowDialを通して、QMK Firmwareについて理解を深め、I2Cデバイスを使うための機能が整備されていることを理解したため、対応をやってみることにしました。

IOエキスパンダーMCP23017を組み込む

IOエキスパンダーMCP23017は、I2Cのシリアル通信で、GPIO 16bit分を操作できるICです。秋月でも190円で購入することができます。半導体品不足の時には、在庫切れを起こしていましたが、現状在庫豊富なようです。

akizukidenshi.com

Sparrow62v2ではスペースを節約したり、MCUを2個用意させるコストに疑問を感じていたため、IOエキスパンダーを利用するようにしました。

ただ、注意点としては、MCP23017には入力に対してプルアップ(フローティング時でHighとする)する機能はあるのですが、プルダウン(フローティング時にLowとする)する機能はありません。COLからROWに電流を流すスイッチマトリックス構成の場合、入力側のROWにプルダウンが必要となります。そのため、プルダウン抵抗を回路に追加しました。

実際のSparrow62v2の右手側MCP23017周りの回路図

Sparrow62v2の構成

Sparrow62v2の内部構成はざっくり以下のような構成です。

左手メインボードではRP2040(Raspberry Pi Pico)上でQMK Firmwareが動作しており、TRRSソケットを通して右手メインボードのMCP23017のI2Cで通信を行っています。

左右のメインボードでそれぞれスイッチマトリックスが組まれており、COLからROWに電流が流れるようになっています。スイッチマトリックスがRP2040、MCP23017に接続された形になっています。

MCP23017のプロトコル

I2Cではマスター(MCU、RP2040)側から、スレイブ(MCP23017)に対して、「特定アドレスのレジスタを読み書きする」ようにプロトコルを組むことが多いです。このレジスタがアドレスごとに機能が異なっており、特定のアドレスで、GPIOの役割をセットできたり、GPIOのアウトプットを設定したり、インプットの値を取得したりします。

MCP23017には、アドレスの振り方に、16ビットモード(IOCON.BANK=0)と、8ビットモード(IOCON.BANK=1)があります。初期状態が16ビットモードになっていて、16ビットモードでもアドレスが違うだけなので、私は16ビットモードで利用しています。本稿も16ビットモードで解説します。

主に使うのは以下の4つレジスタだけです。

名称 レジスタアドレス 機能
IODIRA 0x00 GPIOAの各IO0~8のインプット・アウトプットの方向を8bit(0:アプトプット、1: インプット)で設定する
IODIRB 0x01 GPIOBの各IO0~8のインプット・アウトプットの方向を8bit(0:アプトプット、1: インプット)で設定する
GPIOA 0x12 GPIOAの各IO0~8のインプット・アウトプットの値を8bit(0: Low、1:High)で取得、設定する
GPIOB 0x13 GPIOBの各IO0~8のインプット・アウトプットの値を8bit(0: Low、1:High)で取得、設定する

IODIRAをROWのインプット、IODIRBをCOLのアウトプットとして使うため、以下を設定すれば良いとわかります。

  • GPIOAを全てインプットにするため、IODIRAに0xffをセット
  • GPIOBを全てアウトプットにするため、IODIRBに0x00をセット

そしてCOLからROWのマトリックススキャンをするため、以下を行います。

  • 読み取りたいCOLのGPIOのみをHighにするため、GPIOBに該当bitのみをHigh(例: 0b00000010)をセット
  • COL設定後に、ROWのGPIOを読み取る(対象COLにおいて、スイッチが押されたキーのROWのみがHighになる)ため、GPIOAを読み取り

キーマトリックスの制御については、Google検索すると記事が見つかるため、そちらをご覧ください。

MCP23017については詳しくは、日本語のデータシートがあるので、こちらを参照ください。

MCP23017データシート

https://ww1.microchip.com/downloads/jp/DeviceDoc/20001952C_JP.pdf

QMK Firmwareでのカスタムキーマトリックスをスキャンのインターフェイス

QMK Firmwareにおいては、カスタムのキーマトリックスを実装するための機能が用意されています。このうち、CUSTOM_MATRIX=liteの機能があり、こちらを利用することにしました。

docs.qmk.fm

liteでは以下の関数のみ実装します。

  • void matrix_init_custom(void)
    • キーマトリックの初期化を行う関数
    • MCP23017や、RP2040のGPIOの設定を行う。
  • bool matrix_scan_custom(matrix_row_t current_matrix)
    • キーマトリックスのスキャンを行う関数
    • 引数current_matrix に行ごとの16bitのビット列が渡される。この各行値を、各列のbitを1(押されいる)/0(押されいない)に設定することで、どのキーが押されているかを渡す。
    • 戻り値に、current_matrix[]に変更がある場合、trueを返す。

一点注意点としては、通常はkeyboard.jsonに"matrix_pins"を設定すると思いますが、カスタムマトリックスliteを有効にすると、この値は無視されます。そのため、MCP23017を使わないRP2040側で行うマトリックススキャンも実装する必要があります。

カスタムマトリックスliteと、I2C通信の有効化

カスタムマトリックスlite

カスタムマトリックスを有効にするには、keyboards/{キーボード名}/rules.mkに以下を記述します。

CUSTOM_MATRIX = lite
SRC += matrix.c

keyboard.jsonの"matrix_pins"で通常は生成される、MATRIX_ROWS、MATRIX_COLSの値をkeyboards/{キーボード名}/config.hに設定します。

#pragma once

#define MATRIX_ROWS 5
#define MATRIX_COLS 14

最後に、スキャンの実装を行うkeyboars/{キーボード名}/matrix.cを追加します。

void matrix_init_custom(void) {
}

bool matrix_scan_custom(matrix_row_t current_matrix[]) {
}

I2C通信の有効化

QMK FirmwareにはI2C通信のための機能があり、こちらを有効化します。

くわしくはQMK Firmwareのドキュメントを参照ください。

docs.qmk.fm

keyboard/{キーボード名}/config.hに以下を追加します。RP2040では、I2C0とI2C1の2つがあります。I2C_DRIVERでどちらを利用するのか指定します(下記はI2C0の例)。さらに、I2Cで利用するピンを設定します。I2C0であったとしてもI2C1_SDA/SCL_PINに設定します。F_SCLはI2Cの通信速度ですが、電子工作レベルでは100kHzくらいが使われます。うまく通信ができない場合、この値を落としてみたり、スキャン時の速度が遅い場合は上げてみるのも手です。ただ、MCUによって使える速度の種類に制限があります。

#define I2C1_SCL_PIN GP13
#define I2C1_SDA_PIN GP12
#define I2C_DRIVER I2CD0
#define F_SCL 100000

keyboard/{キーボード名}/halconf.hを作成し、以下を追加します。

#define HAL_USE_I2C TRUE

keyboard/{キーボード名}/mcuconf.hを作成し、以下を追加します。

#pragma once

#include_next <mcuconf.h>

#undef RP_I2C_USE_I2C0
#define RP_I2C_USE_I2C0 TRUE

#undef RP_I2C_USE_I2C1
#define RP_I2C_USE_I2C1 FALSE

デバッグコンソールを有効にする

QMK Firmwareのロジック実装時は、プリント文によるデバッグをできるようにしておきます。

まず、ログを出力するコンソールを有効化するには、keyboard.jsonのfeaturesにconsoleをtrueに設定します。

{
    // 略
    "features": {
        "bootmagic": true,
        "command": false,
        "console": true, // これ!
        "extrakey": true,
        "mousekey": true,
        "nkro": false
    },
    // 略
}

これで、各コードで#include <print.h>をして、printf()を呼ぶとコンソールに出力されるようになります。

デバッグ用のdprintf()も用意されており、デバッグ時はこちらを利用しておけば、デバッグ終了後もprintf()を削除する必要がなくなります。

dprintf()では、keymap.cにて、以下のフックを実装し、デバッグを有効化しておきます。

void keyboard_post_init_user(void) {
  // Customise these values to desired behaviour
  debug_enable = true;
  debug_matrix = false;
  //debug_keyboard=true;
  //debug_mouse=true;
}

このコンソールに接続して見るには、qmkコマンドを使います。

qmk console

MCP23017の初期化を実装する

matrix.cの実装に入っていきます。

ポイントとして以下の部分です。

  • i2cの初期化として、i2c_init();を呼ぶ。
  • i2c_write_register()で、IODIRA、IODIRBで、各GPIOのインプット・アウトぷっとの方向を設定する
#include "timer.h"
#include "matrix.h"
#include "debug.h"
#include "wait.h"
#include <print.h>
#include "platforms/chibios/gpio.h"
#include "i2c_master.h"

#define MCP23017_I2C_ADDRESS 0x20
#define MCP23017_IODIR_A 0x00
#define MCP23017_IODIR_B 0x01

#ifndef MCP23017_I2C_TIMEOUT
#    define MCP23017_I2C_TIMEOUT 100
#endif

void matrix_init_custom(void) {
    i2c_init();
    wait_ms(10);

    // MCU側のキーマトリックスの方向設定
    // 略

    // GPIOAをInputに設定
    uint8_t      iodir_a  = 0xff;
    status = i2c_write_register(MCP23017_I2C_ADDRESS << 1, MCP23017_IODIR_A, &iodir_a, 1, MCP23017_I2C_TIMEOUT);
    dprintf("set I2C IODIR_A i2c_status_t:%d\n", status);

    // GPIOBをOutputに設定
    uint8_t      iodir_b  = 0x00;
    status = i2c_write_register(MCP23017_I2C_ADDRESS << 1, MCP23017_IODIR_B, &iodir_b, 1, MCP23017_I2C_TIMEOUT);
    dprintf("set I2C IODIR_B i2c_status_t:%d\n", status);
}

i2c_write_registerが、レジスタに書き込む関数になります。

MCP23017のI2Cアドレス(同じI2Cバスに繋がった複数のI2Cスレーブデバイスを、マスター側から指示しわけるためのアドレス)が0x20となっています。ただし、i2c_write_registerにおいては1bitシフトして設定する必要があります。

MCP23017からキーマトリックスのスキャンを実装する

COL2ROWのキーマトリックスのスキャンでは以下を行う必要があります。

  1. COL0をHighにし、それ以外のCOLをLowにする
  2. ROWを読み取る
  3. 1〜2を各COLで繰り返す

各COL/ROWを設定するためのbit値を示す配列として、RIGHT_COL_BITS、RIGHT_ROW_BITSを指定しました。COL0のときはRIGHT_COL_BITS[0]を使う形になります。

uint8_t RIGHT_COL_BITS[] = {1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7};
uint8_t RIGHT_ROW_BITS[] = {1, 1 << 1, 1 << 2, 1 << 3, 1 << 4};

なお、私のキーボードではRP2040側のキーマトリックスのGPIOの定義もしています。

uint8_t LEFT_COLS[]      = {GP5, GP6, GP7, GP8, GP9, GP10, GP11};
uint8_t LEFT_ROWS[]      = {GP0, GP1, GP2, GP3, GP4};

わかりにくいのですが、QMK Firmwareにとっては以下となるように私のキーボードでは実装することになります。なので、キーボードにとってのCOLと、MCP23017として処理するCOLがずれることに注意してください。

スキャンは以下のように実装しました。RP2040側のキーマトリックスの処理についてはブログ中では省略します。コードは公開していますので、そちらを参照ください。

#define MCP23017_I2C_ADDRESS 0x20
#define MCP23017_GPIO_A 0x12
#define MCP23017_GPIO_B 0x13

bool matrix_scan_custom(matrix_row_t current_matrix[]) {
    // スキャン後のマトリックスの状態を入れておく
    matrix_row_t scaned_matrix[MATRIX_ROWS];
    memset(scaned_matrix, 0, sizeof(scaned_matrix));

    for (int col = 0; col < MATRIX_COLS; col++) {
        if (col < sizeof(LEFT_COLS)) {
            // MCU側のキーマトリックススキャン
            // 略
        } else {
            // MCP23017側のMatrix

            // COL側、特定のCOLだけHIGHにして、他はLOWにする
            uint8_t      write_buf = RIGHT_COL_BITS[col - sizeof(LEFT_COLS)];
            i2c_status_t status    = i2c_write_register(MCP23017_I2C_ADDRESS << 1, MCP23017_GPIO_B, &write_buf, 1, MCP23017_I2C_TIMEOUT);
            dprintf("write I2C GPIOB i2c_status_t:%d value:0x%02X col:%d\n", status, write_buf, col);

            if (status != I2C_STATUS_SUCCESS) {
                return 0;
            }

            // ROW側、読み取り
            uint8_t read_buf;
            status = i2c_read_register(MCP23017_I2C_ADDRESS << 1, MCP23017_GPIO_A, &read_buf, 1, MCP23017_I2C_TIMEOUT);

            dprintf("read I2C GPIOA i2c_status_t:%d value:0x%02X col:%d\n", status, read_buf, col);


            if (status != I2C_STATUS_SUCCESS) {
                return 0;
            }

            for (int row = 0; row < sizeof(RIGHT_ROW_BITS); row++) {
                // ROWごとに1(ボタンが押された行)があるか確認
                if (read_buf & RIGHT_ROW_BITS[row]) {
                    // このループのCOLのbitを立てる。
                    scaned_matrix[row] |= 1 << col;
                }
            }
        }
    }

    // 更新があったかチェックして、QMK Firmwareにスキャン結果を戻す配列current_matrixを更新する
    bool updated = false;
    for (int row = 0; row < MATRIX_ROWS; row++) {
        if (current_matrix[row] != scaned_matrix[row]) {
            current_matrix[row] = scaned_matrix[row];
            updated             = true;
        }
    }

    return updated;
}

ポイントは以下です。

1 COLごとにループする。 2 COLごとのループで、対象のCOLのみHigh、それ以外はLowとなるように、レジスタGPIOBに書き込む。 3. 2.のあとにROWの各値を読み込むように、レジスタGPIOAを読み込む。読み込んでHighだった場合、スキャン結果の変数scaned_matrix[row]に書き込む。 4. 最後にscanned_matrixと引数current_matrixを比較して、引数current_matrixを更新する。また、更新された場合、戻り値にtrueを返す。

キーマトリクスの処理自体は非常に簡単なため、理屈がわかればそれほど実装量も多くありません。

このコードは以下で公開しています。

github.com

デアッグのポイント

上記コードにもdpritfがたくさん含まれるとおり、プリントデバッグを繰り返しました。

キーマトリックススキャンの度にログを出していると、毎秒100回近く動いているのもあり、結構ログの流量が多くなり、目で追えません。

そのため、私はデバッグログの出力を500msに1度しか行わないように工夫しました。

# ログの出力インターバル
#ifndef CUSTOM_MATRIX_DEBUG_INTERVAL
#    define CUSTOM_MATRIX_DEBUG_INTERVAL 500
#endif

static uint16_t d_timer = 0;

bool matrix_scan_custom(matrix_row_t current_matrix[]) {
    bool debug = false;
    if (timer_elapsed(d_timer) > CUSTOM_MATRIX_DEBUG_INTERVAL) {
        debug   = true;
        d_timer = timer_read();
    }

    if (debug) {
        dprintf("-- matrix_scan_custom start --\n");
    }

    // 略
}

ちゃんと動くか?

デバッグを繰り返し、ちゃんと動くようになりました!! 最近はSparrowDialなど、QMK Firmwareで作り込みをしていたため、その資産がそのまま使えるようになり、久しぶりに分割キーボードを楽しんでいます。

ただ、ファームウェア更新のためにRP2040側にリセットをかけたとき、I2C通信がそれ以降成功しない(ログ上もi2c_status=-1が返っている)ことが3回に1回くらい発生しました。MCP23017側が悪いのか、RP2040側が悪いのかまでは判別できていません。ただ、USBを再接続すると直ります。

RP2040から、I2CのVBUSを瞬断してMCP23017を再起動できるように、MOSFETを追加しても良かったなと思っています。

終わりに

ここのところはSparrow60C、SparrowDialと割れてないキーボードを使っていたのですが、やはり分割キーボードの良さとはまた別のものです。久しぶりに分割キーボードをQMK Firmwareで楽しめています。

Sparrow62v2ではMCP23017を利用していましたが、最近はSparrowTVでもIOエキスパンダとして、CH32V003F4P6にそのような実装をして利用しています。今後は、Sparrow62v3を作る際にはCH32V003F4P6を左右分割機能の要として活用したいと思っています。