2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

分割トラックボード自作キーボード作成の記録(ファームウェア作成・完成編)

Posted at

はじめに

前回まででキーボードと言えるものを作成することができた。
今までは仮組のファームウェアで動作確認を行っていたが今回でファームウェアを完成させていく。

キーボードの名前

その前にキーボードの名前を決めていく。
今回のキーボードのコンセプトは

  • 無線接続(Seeed XIAO BLE nRF52840を使用)
  • 狭ピッチ(17mm)
  • トラックボール
  • ガスケットマウント

実際にタイピングをしてみると打ち心地が柔らかいキーボードなので、この情報元にGeminiに相談してみる。

1. 柔らかい打ち心地(ガスケットマウント)を表現する
「柔らかさ」「しなやかさ」「浮遊感」といったイメージを持つフランス語です。

Souple (スープル): 「しなやかな、柔軟な」という意味。ガスケットマウントの柔軟性を直接的に表現できます。

Velours (ヴルール): 「ベルベット」という意味。ベルベットのような滑らかで柔らかい触感を連想させ、打ち心地の良さを表します。

Flottant (フロッタン): 「浮遊する、漂う」という意味。キーが浮いているかのような、軽やかな打鍵感をイメージさせます。

Cocon (ココン): 「繭(まゆ)」という意味。優しく包み込まれるような、快適な打ち心地を表現できます。

・・・

Cocon (ココン)いいなと思ったのでこれで決定。

ZMK環境構築

公式ドキュメントを確認しながら作成していく。

Windowsの場合下記のコマンドを実行することで必要なファイルを作成してくれる。
Keyboard Selection:は一旦適当なものを選択。
MCU Board Selection:はSeeed Studio XIAO nRF52840を選択。

powershell -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://zmk.dev/setup.ps1'))" 

コマンドを実行すると下記の様にファイルが生成される。

C:.
|   build.yaml
|
+---.github
|   \---workflows
|           build.yml
|
+---boards
|   \---shields
|           .gitkeep
|
+---config
|       corne.conf
|       corne.keymap
|       west.yml
|
\---zephyr
        module.yml

ここから必要なファイルの追加と生成されたファイルを編集していく。

build.yaml

build.yaml
include:
  - board: seeeduino_xiao_ble
    shield: Cocon_R rgbled_adapter
    snippet: studio-rpc-usb-uart
  - board: seeeduino_xiao_ble
    shield: Cocon_L rgbled_adapter
  - board: seeeduino_xiao_ble
    shield: settings_reset

west.yamlにはPMW3610のドライバとrgbled-widgetを追加する

west.yaml

west.yaml
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/zmkfirmware
    - name: badjeff
      url-base: https://github.com/badjeff
    - name: caksoylar
      url-base: https://github.com/caksoylar
  projects:
    - name: zmk
      remote: zmkfirmware
      revision: main
      import: app/west.yml
    - name: zmk-pmw3610-driver
      remote: badjeff
      revision: main
    - name: zmk-rgbled-widget
      remote: caksoylar
      revision: main
  self:
    path: config

conf

confには有効化無効化する機能を書き込んでいく。

Cocon_L.conf
CONFIG_EC11=y
CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y

CONFIG_ZMK_BATTERY_REPORTING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y

CONFIG_RGBLED_WIDGET=y
CONFIG_RGBLED_WIDGET_BATTERY_SHOW_SELF=y

右側はCentralかつトラックボールがあるので左より多め

Cocon_R.conf
CONFIG_ZMK_KEYBOARD_NAME="Cocon"

CONFIG_ZMK_MOUSE=y
CONFIG_NFCT_PINS_AS_GPIOS=y
CONFIG_ZMK_BLE=y

CONFIG_SPI=y
CONFIG_INPUT=y

CONFIG_ZMK_POINTING=y
CONFIG_PMW3610=y
CONFIG_PMW3610_INVERT_X=y

CONFIG_EC11=y
CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y

CONFIG_ZMK_BATTERY_REPORTING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y

CONFIG_ZMK_STUDIO=y
CONFIG_ZMK_STUDIO_LOCKING=n

CONFIG_RGBLED_WIDGET=y
CONFIG_RGBLED_WIDGET_BATTERY_SHOW_SELF=y
CONFIG_RGBLED_WIDGET_CONN_SHOW_USB=y

overlay

overlayではdtsiの内容と左右で異なる内容の設定を書き込んでいく。
特にxiao bleで使用するpinの設定とトラックボールの設定を行う。

回路の1行目がxiao bleのD0に接続されているので合わせていく。

image.png

Cocon_L.overlay
#include "Cocon.dtsi"

&kscan0 {
        col-gpios
            = <&xiao_d 0 GPIO_ACTIVE_HIGH>
            , <&xiao_d 1 GPIO_ACTIVE_HIGH>
            , <&xiao_d 2 GPIO_ACTIVE_HIGH>
            , <&xiao_d 3 GPIO_ACTIVE_HIGH>
            , <&xiao_d 4 GPIO_ACTIVE_HIGH>
            , <&xiao_d 5 GPIO_ACTIVE_HIGH>
            ;
};

右側はトラックボールの設定をする。

NFCの部分は画像の右からGPIO0 10・GPIO0 9と振られているのでkscanにはGPIO0 10を割り当てる。

image.png

Cocon_R.overlay
#include "Cocon.dtsi"
#include <input/processors.dtsi>
#include <dt-bindings/zmk/input_transform.h>

&default_transform {
    col-offset = <6>;
};

&kscan0 {
        col-gpios
            = <&xiao_d 1 GPIO_ACTIVE_HIGH>
            , <&xiao_d 2 GPIO_ACTIVE_HIGH>
            , <&xiao_d 3 GPIO_ACTIVE_HIGH>
            , <&xiao_d 6 GPIO_ACTIVE_HIGH>
            , <&gpio0 10 GPIO_ACTIVE_HIGH>
            ;
};

/ {
    trackball_listener {
        compatible = "zmk,input-listener";
        device = <&trackball>;

    };
};

&pinctrl {
    spi0_default: spi0_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 5)>,
                <NRF_PSEL(SPIM_MOSI, 0, 4)>,
                <NRF_PSEL(SPIM_MISO, 0, 4)>;
        };
    };

    spi0_sleep: spi0_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 5)>,
                <NRF_PSEL(SPIM_MOSI, 0, 4)>,
                <NRF_PSEL(SPIM_MISO, 0, 4)>;
            low-power-enable;
        };
    };
};

#include <zephyr/dt-bindings/input/input-event-codes.h>

&spi0 {
    status = "okay";
    compatible = "nordic,nrf-spim";
    pinctrl-0 = <&spi0_default>;
    pinctrl-1 = <&spi0_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio0 9 GPIO_ACTIVE_LOW>;

    trackball: trackball@0 {
        status = "okay";
        compatible = "pixart,pmw3610";
        reg = <0>;
        spi-max-frequency = <2000000>;
        irq-gpios = <&gpio0 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
        cpi = <800>;
        evt-type = <INPUT_EV_REL>;
        x-input-code = <INPUT_REL_X>;
        y-input-code = <INPUT_REL_Y>;

        force-awake;
        /* keep the sensor awake while ZMK activity state is ACTIVE,
           fallback to normal downshift mode after ZMK goes into IDLE / SLEEP mode.
           thus, the sensor would be a `wakeup-source` */
    };
};

/ {
    /omit-if-no-ref/ zip_temp_layer: zip_temp_layer {
        compatible = "zmk,input-processor-temp-layer";
        #input-processor-cells = <2>;
        require-prior-idle-ms = <1000>;
        excluded-positions = <
            16 // J
            17 // K
        >;
    };

    trackball_listener {
        compatible = "zmk,input-listener";
        device = <&trackball>;
        input-processors = <&zip_temp_layer 3 500>;

        scroller {
            layers = <4>;
            input-processors = <&zip_xy_transform (INPUT_TRANSFORM_Y_INVERT)>, <&zip_xy_scaler 1 10>, <&zip_xy_to_scroll_mapper>;
            process-next;
        };
    };
};

クリックは「J・K」を使用しているので、input-processor-temp-layer押している間はLayer2にとどまるよう設定する。

またスクロールLayerをLayer4に変更、またスクロール方向の変更と下スクロールの速度を10分の1にしておく。

dtsi

dtsiではキーマトリクスの設定とkscanのrow側を設定する。
physical_layoutは下記のサイトを使用して作成。

Cocon.dtsi
#include <dt-bindings/zmk/matrix_transform.h>
#include <physical_layouts.dtsi>

/ {
    chosen {
        zmk,physical-layout = &Cocon_physical_layout;
    };

    Cocon_physical_layout: Cocon_physical_layout {
        compatible = "zmk,physical-layout";
        display-name = "Default";
        transform = <&default_transform>;
        kscan = <&kscan0>;

        keys  //                     w   h    x    y     rot    rx    ry
            = <&key_physical_attrs 100 100    0   38       0     0     0>
            , <&key_physical_attrs 100 100  100   12       0     0     0>
            , <&key_physical_attrs 100 100  200    0       0     0     0>
            , <&key_physical_attrs 100 100  300   12       0     0     0>
            , <&key_physical_attrs 100 100  400   25       0     0     0>
            , <&key_physical_attrs 100 100  800   25       0     0     0>
            , <&key_physical_attrs 100 100  900   12       0     0     0>
            , <&key_physical_attrs 100 100 1000    0       0     0     0>
            , <&key_physical_attrs 100 100 1100   12       0     0     0>
            , <&key_physical_attrs 100 100 1200   38       0     0     0>
            , <&key_physical_attrs 100 100    0  138       0     0     0>
            , <&key_physical_attrs 100 100  100  112       0     0     0>
            , <&key_physical_attrs 100 100  200  100       0     0     0>
            , <&key_physical_attrs 100 100  300  112       0     0     0>
            , <&key_physical_attrs 100 100  400  125       0     0     0>
            , <&key_physical_attrs 100 100  800  125       0     0     0>
            , <&key_physical_attrs 100 100  900  112       0     0     0>
            , <&key_physical_attrs 100 100 1000  100       0     0     0>
            , <&key_physical_attrs 100 100 1100  112       0     0     0>
            , <&key_physical_attrs 100 100 1200  138       0     0     0>
            , <&key_physical_attrs 100 100    0  238       0     0     0>
            , <&key_physical_attrs 100 100  100  212       0     0     0>
            , <&key_physical_attrs 100 100  200  200       0     0     0>
            , <&key_physical_attrs 100 100  300  212       0     0     0>
            , <&key_physical_attrs 100 100  400  225       0     0     0>
            , <&key_physical_attrs 100 100  500  240       0     0     0>
            , <&key_physical_attrs 100 100  800  225       0     0     0>
            , <&key_physical_attrs 100 100  900  212       0     0     0>
            , <&key_physical_attrs 100 100 1000  200       0     0     0>
            , <&key_physical_attrs 100 100 1100  212       0     0     0>
            , <&key_physical_attrs 100 100 1200  238       0     0     0>
            , <&key_physical_attrs 100 100  100  312       0     0     0>
            , <&key_physical_attrs 100 100  200  300       0     0     0>
            , <&key_physical_attrs 100 100  348  350       0     0     0>
            , <&key_physical_attrs 100 100  348  300    1500   200   800>
            , <&key_physical_attrs 100 100  348  300    3000   300   800>
            , <&key_physical_attrs 100 100  952  312 (-3000)  1080   790>
            , <&key_physical_attrs 100 100  952  330 (-1500)  1100   700>
            , <&key_physical_attrs 100 100  995  350       0     0     0>
            , <&key_physical_attrs 100 100 1100  312       0     0     0>
            , <&key_physical_attrs 100 100 1200  338       0     0     0>
            ;
    };

    default_transform: keymap_transform_0 {
        compatible = "zmk,matrix-transform";
        columns = <11>;
        rows = <4>;
        map = <
            RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4)                     RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10)
            RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4)                     RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10)
            RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5)             RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10)
            RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4)             RC(3,6) RC(3,7) RC(3,8)         RC(3,9) RC(3,10)
        >;
    };

    kscan0: kscan {
        compatible = "zmk,kscan-gpio-matrix";
        label = "KSCAN";
        diode-direction = "col2row";
            row-gpios
                = <&xiao_d 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
                , <&xiao_d 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
                , <&xiao_d 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
                , <&xiao_d 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
                ;
    };

    sensors: sensors {};
};

Cocon.yml

shields内のymlにはキーボードの名前とGitHubのURLを記載。

Cocon.yml
file_format: "1"
id: Cocon
name: Cocon
type: shield
url: https://github.com/waressyoi/Cocon-zmk-config
requires: [seeeduino_xiao_ble]
features:
  - keys

Kconfig.defconfig

残りのconfig系は下記のように設定した。

Kconfig.defconfig
if SHIELD_COCON_R

config ZMK_KEYBOARD_NAME
    default "Cocon_R"

config ZMK_SPLIT
    default y

config ZMK_SPLIT_ROLE_CENTRAL
    default y

endif

if SHIELD_COCON_L

config ZMK_KEYBOARD_NAME
    default "Cocon_L"

config ZMK_SPLIT
    default y

endif

Kconfig.shield

Kconfig.shield
config SHIELD_COCON_R
    def_bool $(shields_list_contains,Cocon_R)

config SHIELD_COCON_L
    def_bool $(shields_list_contains,Cocon_L)

ディレクトリ構成

最終的な構成はこうなった。

C:.
|   build.yaml
|
+---.github
|   \---workflows
|           build.yml
|
+---boards
|   \---shields
|       |   .gitkeep
|       |
|       \---Test
|               Cocon.dtsi
|               Cocon.yml
|               Cocon_L.conf
|               Cocon_L.overlay
|               Cocon_R.conf
|               Cocon_R.overlay
|               Kconfig.defconfig
|               Kconfig.shield
|
+---config
|       Cocon.json
|       Cocon.keymap
|       west.yml
|
\---zephyr
        module.yml

GitHub Actions

これをコミットしてリモートと同期するとGitHub Actionsでビルドが始まる。
ビルドが完了するとArtifactsからfirmwareをダウンロードできる。

image.png

完成

実際にファームウェアを導入して完成。
時間があったのでキーキャップも作成してみた。

IMG_20250727_015839.jpg

最後に

実際に作成し始めてから半年以上かかった。
回路の知識やZMKの設定等でかなり苦戦したが最後まで作り切ることができた。
ここから普段の生活で使用してみて足りない箇所や追加したい機能が出てくると思うので今後も何かあれば更新していこうと思う。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?