STM32系のスプリットの自作キーボードの実装とファームウェアの変更点

今回も自作キーボードの話題です。元をたどれば自作キーボードというだけで、実際にはSTM32系のボードでのシリアル通信に活かせる話が半分くらい含まれます。

さて、自分でスプリット(分割)のキーボードを作ろうと思って調べてみると思いのほか資料がありません。この本読んだら全部解決できる!みたいなことが書いてあったりして肝心の中身がないためわからない場合が多かったですね。

しかも、私が設計に使っているのはSTM32系のチップ(細かくはSTM32F303CBT6とSTM32F103C8T6の2種類)で、スプリットキーボードに関しての情報が結構少ないです。唯一あったのが、以下のサイト。

ここはハードウェア的な実装について書かれているのみで、ファームウェアについては他も色々探しても見つけられませんでした。そのため、ハードウェアの実装はあっているのかも不安ですし、そもそもこの記事も開発途中での記事のようであっているか確証がありません。

なので、いざ色々リファレンスを読んで実装してみて動かないと何が悪いんだろう?となって時間を無駄にしました。

なぜ一般的なProMicro等を使わず、STM32系を使うのかと言われたら、昨今の半導体不足等による在庫を考慮してのことです。元々はMCUを基板に統合したかったのですが、在庫がないとそもそも作れませんからね。それ以外にもEEPROMが大きかったり、処理力が高かったりと性能的な面もあります。

STM32系のボードであれば、こうすれば動くというのを残しておきます。また、これは私が実装した結果上手くいったというだけで、間違いがある可能性もあるので、情報の精査は個人の判断に任せます。誤りは指摘いただければ修正します。

今回の記事は自分で作製したSTM32系チップを搭載した自作キーボード基板を使い、ファームウェアはQMKを使います。

目次

分割キーボードを設計する前に

STM32F系の分割キーボードを作るにあたっては、前提として分割しない単一のキーボードとして動く基盤が設計できる必要があります。ここでは詳しく触れませんが、ProMicroのものと同じようにキーマトリックスの実装を行えば大丈夫です。そう意味では、BluePillのようなブレークアウトボードを使って作製できるという点ではProMicro似通っていると言えます。

まずは単一のキーボードとして動くようにする

MCUを搭載した一体型基板を作製する場合に気を付けたいのは、STM32系は内部発振回路の精度が足りないため、USB機器として動作させる場合は、外部の発振回路が必要になります。細かい実装はYoutubeなんかにもいくつか転がっているぐらいなので、それをみれば大丈夫でしょう。

私の場合は、テンキーサイズのキーボードを作製して、それを使えるようにしました。GPIOもある程度引き出していますし、まずはキーマトリクスを作るところから始めてQMKの実装を確認したいという方は販売しているものを購入いただいても良いかと思います。

STM32F103C8T6搭載のボード

この基板はSTM32F系を使った実装について使いやすさもある程度意識しているので、使い道も色々あるかと思います。

とりあえずは分割しないキーボードを動くように作ることがスタート地点です。

AVR系マイコンとの実装方法の違い

上述の前提ができているとして、次はシリアル通信部分の実際の実装について確認していきます。

その前に、STM32系のチップでの分割キーボード用の通信の実装方法について紹介しておきます。STM32系ではAVR系マイコンのATmega32u4等とは通信方法が違います。リファレンスに書かれていることを紹介します。

まずはこちらのGitHubのページに通信方法について記述があります。

GitHub -QMK firmware Split Keyboard-
https://github.com/qmk/qmk_firmware/blob/master/docs/feature_split_keyboard.md

このページによると、気を付けなければいけないこととして、AVR系とARM系のマイコンを混同して通信することができないことが挙げられています。ProMicroならProMicro同士、BluePillならBluePill同士で使えと記述があります。ここらへんはチップの種類が多いので実装的に難しいのでしょう。

そして肝心な通信方法ですが、これは表にまとめられている通りこでAVR系マイコンならシリアルとI2Cで、ARM系ならシリアル通信のみがサポートされていることが書かれています。

通信方法AVRARM
シリアル
I2C×
通信方法の実装

一応、チップとしてはARM系の方でもI2Cによる通信をサポートしていますが、QMK側かChibiOS側のどちらかで実装ができていないようでQMKではARM系によるI2Cの通信はサポートはされていません。

リファレンス通りに従うとSTM32F系はシリアル通信によって分割キーボードを作製する必要があることがわかります。

リファレンスを読み進めていくとチュートリアル的なことが掲載されていますが、これはProMicroの例なのでSTM32F系のボードでは何の役にも立ちません。では。シリアル通信で実装するには具体的にはどうすれば?となるかと思います。これについてもリファレンスが存在しています。それが以下のGitHubのページ。

GitHub -QMK firmware 'serial' Driver-
https://github.com/qmk/qmk_firmware/blob/master/docs/serial_driver.md

このページの頭に表が書かれていますが、これはシリアル通信の種類について書かれいます。また、特徴も併せて書いています。ARM系に的を絞って表をさらにまとめてみます。

方法AVRARM説明
BitbangAVR向け。単線の通信。ARMでは非推奨
Half-duplex×BitbangよりCPU使用率が低い、正確なタイミングで通信。単線の通信推奨
Full-duplex×BitbangCPU使用率が低い、正確なタイミングで通信。二線の通信推奨
シリアル通信法まとめ

このような内容が書かれています。この表の通り、BitbangはAVR系、ARM系の両方で使うことができるものの、他の通信方法に対してのメリットがないのでARM系ならHulf-duplex(半二重)かFull-duplex(全二重)のどちらかを使うことが良いでしょう。

なので、今回はこの二つに絞って実装方法を紹介していきます。

基板側の実装(半二重 Half-duplex)

実装方法もある程度書いているのですが、半二重の方がわかりくかったので微妙に沼りました。なのでここを重点的に説明します。実装の確認にはブレッドボードと最初の方に紹介したTH25TKというキーボードのプロトタイプ2枚を使用しました。ファームウェアについては後述します。

TK25TKとブレッドボード

ピン設定ということで先ほどのリファレンスにも書いていますが、どこにも繋がっていない謎のRが書かれています。本文をよく読むとプルアップ抵抗とのこと。どの電圧で吊ればいいか書いていないので手探りの状態ですが、ひとまず手持ちの部品で実装しました。

全然通信できず困っていたので、ファームウェアでデバッグを有効にして回路を色々試行錯誤していました。結果として5Vで吊ったちとき、リファレンスにある1.5k~8.2kΩで実装すれば良いことがわかりました。こんな感じの回路でいけます。

    LEFT                                 RIGHT
+-----------+                        +-----------+
|    +5V    |------------------------|    +5V    |
|           |     |            |     |           |
|           |     R            R     |           |
|           |     |            |     |           |
|TX(A9,B6等)|------------------------|TX(A9,B6等)|
|           |                        |           |
|           |        VDD(3.3V)       |           |
|    VDD    |------------------------|    VDD    |
|           |          GND           |           |
|    GND    |------------------------|    GND    |
+-----------+                        +-----------+

私は6.8kΩの抵抗を実装しました。ただ手元にあった抵抗だとこれが丁度よかっただけで特に意味はありません。

VDDの部分はMCUの駆動に用いるため、+5Vから+3.3Vへの降圧回路へ入力し駆動する場合は接続不要です。+5Vから+3.3Vへの変換回路をどちらかに含まないという変な構成でやるのであれば4本必要ですが、+5Vをプルアップで吊る分と電源の駆動を兼ねるのであれば3本のケーブルで足ります(3.3Vのみで駆動、プルアップ抵抗を実装する場合は3本でも行けますが…左右で別実装の基板の実用性は?確認もしていません)。なのでTRRSケーブルで実装するとすると、実際の構成としてはこんな感じですね。

    LEFT              TRRS               RIGHT
+-----------+  +5V   +-----+  +5V   +-----------+
|    +5V    |--------|-----|--------|    +5V    |
|           |    |   |  T  |   |    |           |
|           |    R   |     |   R    |           |
|           |    |   |     |   |    |           |
|TX(A9,B6等)|--------|-----|--------|TX(A9,B6等)|
|           | Serial |  R  | Serial |           |
|           |        |     |        |           |
|    VDD    |        |     |        |    VDD    |
|           |  GND   |  S  |  GND   |           |
|    GND    |--------|-----|--------|    GND    |
+-----------+        +-----+        +-----------+

このように実装することでTRRSケーブルを使って通信を行うことが可能となります。また、この場合は3本しか使っていないため、コネクタ側の実装を工夫することで、TRRSジャック・ケーブルを使いながらTRSケーブルでも通信を行うことが可能です。汎用性の高さを重視するのであれば、ケーブルも気にしないで良いこの方法で実装するのがおすすめです。ピンについては、チップのデータシートを読み、USARTx_TXが割り振られている、または割り振ることができるピンを指定する必要があります。

基板側の実装(全二重 Full-duplex)

もう一つの実装方法の全二重 Full-duplexについても説明しますが、個人的にはこちらは回路の実装が簡単な分、汎用性に欠けるのでおすすめしません。どの点が汎用性に欠けるかというのは説明を終えてからします。

これは回路をリファレンス通り迷わず組めば大丈夫です。

  LEFT                      RIGHT
+-------+                 +-------+
|       |      SERIAL     |       |
|    TX |-----------------| RX    |
|       |      SERIAL     |       |
|    RX |-----------------| TX    |
|       |       VDD       |       |
|       |-----------------|       |
|       |       GND       |       |
|       |-----------------|       |
+-------+                 +-------+

ポイントは左右でTXとRXを入れ替えて配線しなければならない点ですね。

この配線を見ての通り、仮にTRRSのようなケーブルで接続する場合、左右で実装を変更する必要があります。部品の実装を変えたりファームウェアを変更しないといけないため、量産などには向かないでしょう。また、最低でも4本の線を必要とするため、TRRSコネクタで実装するとTRRSのケーブルが必要になるという点がお勧めできない理由になっています。

半二重の場合は同じ基板を2枚用意しTRS・TRRSのどちらのケーブルでも利用できるのに対し、全二重の場合はTRRS専用になり、左右で異なった実装をしないといけないという差があり、半二重の方が汎用性という意味で優れています。そのため全二重はおすすめしないと書いたわけです。

QMK firmwareの変更点

単一のキーボードとして動くQMK firmwareのコードがあるという前提で話を勧めます。基本的には今まで紹介したリファレンスの記述通りで、説明があちらこちらに飛んでいるのでまとめます。STM32F103系だけ実装が微妙に違うようなので気を付けてください。一応F303とF103の二つで検証しているので問題はないはずです。

通常のキーボードのファイル群にいくつかの記述を加えていきます。始めにrules.mkに以下を追記します。

SERIAL_DRIVER = usart
SPLIT_KEYBOARD = yes

halconf.hに以下を追記します。

#define HAL_USE_SERIAL TRUE

config.hに以下の記述を追加と変更します。ピンの入れ替えに関する表などは先ほどのリファレンスぺージの下の方にあります。

#define MATRIX_ROWS 5
     ↓ROWSの値2倍に変更する
#define MATRIX_ROWS 10

//追記する
#define SERIAL_USART_TX_PIN A9 //TXピンの指定
#define SERIAL_USART_DRIVER SD1 //A9のときはこれ。ピンによって変わる

//全二重 Full-duplexのときは以下の設定も行う
#define SERIAL_USART_FULL_DUPLEX
#define SERIAL_USART_RX_PIN A10 //RXピンの指定

//ピン機能の入れ替えを行う場合は必要なものを追記する。
#define SERIAL_USART_PIN_SWAP //TXとRXピンの入れ替え(一部MCUのみ)
#define USART1_REMAP //F103のときはこれでAlternate functionのTX等を有効化
#define SERIAL_USART_TX_PAL_MODE 7 //F103以外はリファレンスを参照して記述

追記する分に以下の文がありますが、このSD1の1は使うピンによって記述を変更する必要があります。

#define SERIAL_USART_DRIVER SD1

使うチップのデータシートを見ると機能として利用するピンにUSART2_TXのような表記があると思います。今回使うA9というピンはUSART1_TXと書かれているため、SD1としています。USART2_TXであればSD2、USART3_TXならSD3のようにする必要あります。

mcuconf.hに追記を行いますが、これは上述の書き方を参照して使うピンに合わせて追記をします。

#undef STM32_SERIAL_USE_USART1
#define STM32_SERIAL_USE_USART1 TRUE

USART1の数字の部分を使うピンに応じて変更します。

ここまででチップに関する設定は終わりで、次にキーマップなどを編集していきます。

最初にkeyboard.hを編集します。名前はフォルダの名前に依存するやつですね。まずは完成形を見てみます。

#define LAYOUT_all( \
    K000, K001, K002, K003, K004, R000, R001, R002, R003, R004,\
    K100, K101, K102, K103, K104, R100, R101, R102, R103, R104,\
    K200, K201, K202, K203, K204, R200, R201, R202, R203, R204,\
    K300, K301, K302, K303, K304, R300, R301, R302, R303, R304,\
    K400, K401, K402, K403, K404, R400, R401, R402, R403, R404 \
) \
{ \
    { K000, K001, K002, K003, K004}, \
    { K100, K101, K102, K103, K104}, \
    { K200, K201, K202, K203, K204}, \
    { K300, K301, K302, K303, K304}, \
    { K400, K401, K402, K403, K404}, \
    { R000, R001, R002, R003, R004}, \
    { R100, R101, R102, R103, R104}, \
    { R200, R201, R202, R203, R204}, \
    { R300, R301, R302, R303, R304}, \
    { R400, R401, R402, R403, R404}, \
}

このように記述します。Kから始まる番号が元からあったもので、Rから始まるものが追記した部分になります。このように単一のキーボードの右にもう一つ分の配列を上の方で追加して、下の方では、これでどこまでが一固まりになっているかを明示します。

あとはkeymap.cを先ほどの上の方形をいじる感じで作製します。

[_L1] = LAYOUT_all( /* Base */
    KC_1,  KC_2,  KC_3,  KC_4,   KC_5,   KC_6,  KC_7,  KC_8,  KC_9,   KC_0,
    KC_Q,   KC_W,    KC_E,    KC_R,    KC_T,  KC_Y,   KC_U,    KC_I,    KC_O,    KC_P,
    KC_P4,   KC_P5,    KC_P6,    KC_PPLS,    MO(_L3),KC_P4,   KC_P5,    KC_P6,    KC_PPLS,    MO(_L3),
    KC_P1,   KC_P2,    KC_P3,    KC_SPACE,   MO(_L4),KC_P1,   KC_P2,    KC_P3,    KC_SPACE,   MO(_L4),
    KC_COMM, KC_P0,    KC_PDOT,  KC_ENT,     KC_F5,KC_P1,   KC_P2,    KC_P3,    KC_SPACE,   MO(_L4)
    )

見にくいですが、しっかり5×10の配列でキーマップを作製しています。これでQMKの変更点は以上となります。細かいオプションは各自で調べて追記してください。

自作キーボード作製にあたってはDebugが必要になるタイミングもあるかと思います。その際にQMK ToolBoxでデバッグメッセージを表示する場合は2か所変更を加える必要があります。一つはrules.mkの以下の文です。以下の記述に合わせてください。

CONSOLE_ENABLE = yes

keymap.cの下の方に以下の関数を追加します。

void keyboard_post_init_user(void) {
  debug_enable=true;
  debug_matrix=true;
}

これでデバッグができます。通信ができない状態をコンソールで見てみると、原因がわかります。ハンドシェイクのエラーのことが大半だと思いますが、メッセージがあるのとないのでは結構違いますよ。

VIAでの変更点

VIAは基本的にQMKでの実装に倣うので、同じように変更してコンパイルすればVIA用ファームが作成できます。

ですが、肝心のjsonファイルの書き方がよくわからなかったので色々やってみたところkeyboard.hの配列を参考に配列を書くことで正しく動作するようです。

通常なら5列までしかありませんが、今回はファームウェア上でも10列あることになっているのでそのような解釈で書くと良いみたいですね。

一応私がTH25TK用に書いたファームウェアはレポジトリとして用意しているので違いを見ていただくのもありだと思います。

GitHub -TK25TK firmware-
https://github.com/T-H-Un/th25tk

まとめ

STM32系における分割キーボードの実装方法について紹介しました、多少はProMicroと違う部分もありProMicroに慣れ親しんだ人からすると面倒な感じもあるかもしれません。

半導体不足や価格の上昇を受けてSTM32系のチップという別の選択を持つということは大切だと考えています。しかし、先人がいないことには誰も始めないと思うので、STM32系のチップでの実装を頑張っています。私が調べた中だと日本の方でSTM32系のチップの実装を丁寧に紹介している方がいなかったので、誰かの役に立つことを願って書きました。

半導体不足で四苦八苦している部分は多いと思いますが、別の選択肢を持てるような状況に一役買えればうれしい限りです。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です