View on GitHub

e3w2q.github.io

Duplex-Matrixを自作キーボードで使う方法

2019/12/23

この記事はキーボード #2 Advent Calendar 2019の23日目の記事です。

昨日の記事はblock_cube_libさんのHHKBから自作キーボードに乗り換えた話でした。次の記事は????????神????????さんのトラックボールマウスを分解してArduino Microで動かした話です。

Duplex-Matrix(倍マトリクス)とは

Duplex-Matrix(倍マトリクス)とは、キーボードの状態のスキャンの仕方を工夫することで、コントローラーで把握できるキー数を2倍にするというものです。

国内の自作キーボードコミュニティにおいては、あぷろさんが2018/11/10にSelf-Made Keyboards in Japan Discord Serverに着想を投稿[1]し、その後2019/5/1に実装例が投稿[2]され、認知されるようになりました。

最近では、天下一キーボードわいわい会Vol.3でかーくんさんが販売されたわんわんわんきーぼーどにもこの方法が使われています。

このDuplex-Matrixを自分の理解できる範囲で整理してみました。C言語のきちんとした知識を持っていないので、間違っている部分があればご指摘いただけると幸いです。

基本のキーマトリクススキャン

本題のDuplex-Matrixの説明に入る前に、基本のマトリクススキャンについて触れておきます。

多くの自作キーボードでは、限られたGPIO数でたくさんのキーの状態を取得するために、キースイッチをマトリクス状に配線して、何行目、何列目の通電状態が変わったかを判定します。

基本の回路

下図は、自作キーボード(片側2行×3列の分割キーボード)の回路図の例です。

schema3

この例では、例えばSW2のキーが押されたときに、COL2からROW1に流れる電流の状態が変化します。

ダイオードは、キーが同時押しされたときに正しく判別するために必要となります。以下の記事が詳しいです。

基本の回路の実装

自作キーボード試作用基板のSU120で実装してみた例です。

6key

6key_back

基本のファームウェア

この回路図に対応したファームウェアをQMK Firmwareで作成してみます。

QMK Firmwareのインストールについては、はじめに - QMK Firmwareを参照してください。

QMK Firmwareのインストールの後、プロジェクトのひな形を作成します。

$ ./util/new_keyboard.sh
Generating a new QMK keyboard directory

Keyboard Name: 2x3test
Keyboard Type [avr]:
Your Name [e3w2q]:

回路図に合うようにプロジェクトを修正します。config.hのキーマトリクスに関する部分は以下のようになります。

/* key matrix size */
#define MATRIX_ROWS 2*2 // 行数 右手側と左手側があるので、*2
#define MATRIX_COLS 3   // 列数
/* COL2ROW, ROW2COL*/
#define DIODE_DIRECTION COL2ROW // COL(列)のピンからROW(行)のピンに電流が流れるようにダイオードを付けた場合はCOL2ROW、逆向きの場合はROW2COL

修正点の全体はここに載せています

これをPro Microに書き込んで、回路図のとおりに配線すると、片手2x3の分割キーボードとなります。

基本のキーマトリクススキャンはどこで行われているか

通常、自作キーボードを設計する場合はここまでの知識で十分なのですが、今回は深堀りして、マトリクススキャンが行われているコードまで辿ってみます。

Makefile

    MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f build_keyboard.mk $$(MAKE_TARGET)

build_keyboard.mk

include common_features.mk

common_features.mk

# Include the standard or split matrix code if needed
ifneq ($(strip $(CUSTOM_MATRIX)), yes)
    ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
        QUANTUM_SRC += $(QUANTUM_DIR)/split_common/matrix.c
    else
        QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c
    endif
endif

分割キーボードでrules.mkにおいて「SPLIT_KEYBOARD = yes」と設定していればquantum/split_common/matrix.c

そうでない一体型キーボードであればquantum/matrix.c

このmatrix.cの、matrix_scan()という関数が、マトリクススキャンを行っている大元のコードです。

uint8_t matrix_scan(void) {
    bool changed = false;

#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
    // Set row, read cols
    for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
        changed |= read_cols_on_row(raw_matrix, current_row);
    }
#elif (DIODE_DIRECTION == ROW2COL)
    // Set col, read rows
    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
        changed |= read_rows_on_col(raw_matrix, current_col);
    }
#endif

    debounce(raw_matrix, matrix, MATRIX_ROWS, changed);

    matrix_scan_quantum();
    return (uint8_t)changed;
}

COL2ROWの場合なら、一行(row)ずつ、その行の各列(col)の状態を読んでいます(read_cols_on_row)。

Duplex-Matrixによるマトリクススキャン

Duplex-Matrixの回路

では、先ほどの片手2x3の回路を、使うGPIOピン数は変えずに、Duplex-Matrixを使って二倍のキー数を判別できるようにしてみます。

schema5

既存のCOL2ROWのキーマトリクス(2行×3列=6キー)をコピーして、ダイオードの向きだけ変更したROW2COLのキーマトリクス(2行×3列=6キー)を増やしました(片手側は計6キーから計12キーに)。

回路図の修正は、これだけです。とてもシンプル。

COLからROW方向にのみスキャンしていたキーマトリクスに、ROWからCOL方向にスキャンするキーマトリクスを加えたものが、Duplex-Matrixなのです。

Duplex-Matrixの回路の実装

SU120で実装した例です。

12key

12key_back

(手持ちのソケットが尽きたので、キー入力のテストはピンセットで行いました)

Duplex-Matrixのファームウェア(1)

QMK Firmwareの標準設定では、config.hのDIODE_DIRECTIONをCOL2ROW、またはROW2COLと設定することで、matrix.cの_matrix_scan関数でCOLからROW方向、またはROWからCOL方向のスキャンが行われます。

Duplex-Matrixでは、COLからROW方向のスキャンROWからCOL方向のスキャン両方行いたいので、自分でマトリクススキャンを書く必要があります。

まずは、rules.mkで以下を記載します。

CUSTOM_MATRIX = yes

これにより、標準のmatrix.cの読み込みが止まります。

QMK Firmwareのドキュメント

CUSTOM_MATRIX

Lets you replace the default matrix scanning routine with your own code. You will need to provide your own implementations of matrix_init() and matrix_scan().

と書いてあるとおり、自前でmatrix_init()とmatrix_scan()を実装する必要があります。

rules.mkにさらに以下を追加し、自前のmatrix.cが読み込まれるようにします。

SRC += matrix.c

matrix.cの前に、config.hを修正します。今回は列を2倍(横方向に2倍)することにします。

 /* key matrix size */
 #define MATRIX_ROWS 2*2 // 行数 右手側と左手側があるので、*2
-#define MATRIX_COLS 3   // 列数
+#define MATRIX_COLS 3*2 // 列数 Duplex-Matrix法により、*2
 #define MATRIX_ROW_PINS { F6, F7 }     // 各行に割り当てるピン番号
-#define MATRIX_COL_PINS { D1, D0, D4 } // 各列に割り当てるピン番号
+#define MATRIX_COL_PINS { D1, D0, D4, D1, D0, D4 } // 各列に割り当てるピン番号

DIODE_DIRECTIONは、通常、COL2ROWまたはROW2COLを設定しますが、独自にマトリクススキャンを行う場合のためにCUSTOM_MATRIXという値の定義が用意されています。

matrix.cの書き方次第でDIODE_DIRECTIONを無視することもできるのですが、今回はDIODE_DIRECTIONをCUSTOM_MATRIXとしておきます。

 /* COL2ROW, ROW2COL*/
-#define DIODE_DIRECTION COL2ROW
+#define DIODE_DIRECTION CUSTOM_MATRIX

列数が増えたので、2x3test.h、keymaps/default/keymap.cも手直ししておきましょう。

ここまでの修正についてはここにまとめて載せています

Duplex-Matrixのファームウェア(2)

次にmatrix.cを用意します。

matrix.cはゼロから書くのではなく、分割キーボードであればquantum/split_common/matrix.cを、そうでない一体型キーボードであればquantum/matrix.cをコピーしてベースとするとよいです。

今回は分割キーボードなので、quantum/split_common/matrix.cを利用します。

書き換えの基本的な考え方は以下のとおりです。

デフォルトmatrix.cではDIRECT_PINS、COL2ROW、ROW2COLのマトリクススキャンしか書かれていないので、CUSTOM_MATRIX用の分岐を入れます。

 #ifdef DIRECT_PINS
 //...
 #elif (DIODE_DIRECTION == COL2ROW)
 //...
 #elif (DIODE_DIRECTION == ROW2COL)
 //...
+#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
+//...
 #endif

COL2ROWのスキャンを行ってからROW2COLのスキャンを行うようにします。COL2ROWのスキャンは、元の列の範囲(今回なら1~3列目)で行い、ROW2COLのスキャンは、増やした列の範囲(今回なら4~6列目)で行います。

+    // Set row, read cols
+    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
+        changed |= read_cols_on_row(raw_matrix, current_row);
+    }
+    // Set col, read rows
+    for (uint8_t current_col = MATRIX_COLS/2; current_col < MATRIX_COLS; current_col++) {
+        changed |= read_rows_on_col(raw_matrix, current_col);
+    }

正直、matrix.cをDuplex-Matrix用に修正する作業がなかなかうまくいかず一番大変だったのですが、コードをたくさん貼っても面白くないので、デフォルトのmatrix.cからの変更点についてはここにまとめて載せました

おわりに

これで、通常のキーマトリクスに必要なピン数の1/2のピン数でキーマトリクススキャンを行えるようになりました!

例えば拙作のSU120では2つのPro Microを使うと120キーまでのキーボードを作れるというものですが、Duplex-Matrixを使えばなんと倍の240キーのキーボードが作れてしまいます。

240key

まあ、ここまでのキー数は不要としても、1つのPro Microでフルキーボードのキー数を使えるのは魅力的ですよね。

また、ロータリーエンコーダーを使うとピン数が足りなくなりがちになるので、そういう際にも便利です。

サンプルコードの全体は以下に載せています。

https://github.com/e3w2q/qmk_firmware/tree/2x3test/keyboards/2x3test

皆さんの自作キーボード設計の一助となれば幸いです。

この記事は、あぷろさんの着想、かーくんさんのわんわんわんきーぼーどのQMK Firmwareソースコードを読んで得た知識をもとに書きました。あぷろさん、かーくんさん、ありがとうございました。

補足:その他の特殊なマトリクスについて

記事を書くにあたって、Self-Made Keyboards in Japan Discord ServerでDuplex-Matrixの初出を尋ねたところ、ここで説明したDuplex-Matrixのほかのマトリクス回路のお話も聞くことができました。

チャーリープレックス

1995年にMaxim Integrated社のCharlie Allen氏が考案したLEDマトリクスのコントロール方法。各ピンの間にLEDを2つ付けるもの(それぞれ逆向き)。Nピンで、(N-1)*Nのマトリクスを制御できます。

https://en.wikipedia.org/wiki/Charlieplexing

2乗マトリクス

チャーリープレックスをキーマトリクスに適用したもの。各ピンの間にスイッチ+ダイオードを2つ付けるもの(ダイオードはそれぞれ逆向き)。Nピンで、(N-1)*Nのマトリクスを取得できるので、Duplex-Matrixよりさらに多くのキースイッチを付けられます。IKeJIさんのキーボードのマトリクス方式の分類2乗マトリクスの項に詳しく載っています。

国内自作キーボードコミュニティでは、IKeJIさんが2017年10月3日にTwitterで触れ2018年に電卓で実装されたのが初出のようです。

海外キーボードコミュニティでの”Duplex Matrix”

マトリクスを組み替えることで使用するピン数を削減するもの。

https://wiki.ai03.me/books/pcb-design/page/matrices-and-duplex-matrix

例えばUS60%キーボード(61キー)を素直にマトリクスで組もうとすると、5行×14列=最大70キーとなり、ピンは5+14=19必要となります。

us60percentkeyboard

マトリクスの組み方を工夫し、列に使うピン数を1/2に、行に使うピン数を2倍にしても同じキー数を実現できます。こうすると10x7=最大70キーはそのままで、必要なピン数は10+7=17となり、使用するピン数が2つ減りました。

もともとai03さんが2017年にWebに掲載した記事の言葉がキーボードコミュニティで定着した(してしまった)とのことです[3]


[1] https://discordapp.com/channels/376937950409392130/377073908496465920/510497794843213824

[2] https://discordapp.com/channels/376937950409392130/381074986208591884/572998059793842187
https://discordapp.com/channels/376937950409392130/418326189644709889/573354228701986828

[3] https://discordapp.com/channels/376937950409392130/635679787905712148/658272879430860833

一覧へ