From ea4b885d7a91a005020a99a7a770eac5874473f5 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich Date: Sun, 29 Sep 2024 19:31:31 +0200 Subject: [PATCH] docs: Streamlined the new shield guide --- .../hardware-integration/new-shield.mdx | 607 +++++++++--------- docs/docs/keymap-example-file.md | 26 - 2 files changed, 308 insertions(+), 325 deletions(-) delete mode 100644 docs/docs/keymap-example-file.md diff --git a/docs/docs/development/hardware-integration/new-shield.mdx b/docs/docs/development/hardware-integration/new-shield.mdx index 7a74f1bec839..524e1daa2622 100644 --- a/docs/docs/development/hardware-integration/new-shield.mdx +++ b/docs/docs/development/hardware-integration/new-shield.mdx @@ -4,36 +4,62 @@ title: New Keyboard Shield import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -import KeymapExampleFile from "../../keymap-example-file.md"; - import InterconnectTabs from "@site/src/components/interconnect-tabs"; import Metadata from "@site/src/data/hardware-metadata.json"; +export const SplitTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +export const SplitInvisTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + :::danger Before reading this section, it is **vital** that you read through our [clean room policy](../contributing/clean-room.md). ::: -## Overview - This guide will walk through the steps necessary to add ZMK support for a keyboard that uses an add-on MCU board (e.g. Pro Micro compatible) to provide the microprocessor. The high level steps are: -- From a template, create a new [Zephyr module](https://docs.zephyrproject.org/3.5.0/develop/modules.html) housed in a git repository containing one or more custom shields. +- Create a new [Zephyr module](https://docs.zephyrproject.org/3.5.0/develop/modules.html) to contain your shield. - Create a new shield directory. - Add the base Kconfig files. -- Add the shield overlay file to define the KSCAN driver for detecting key press/release. -- Add the matrix transform for mapping KSCAN row/column values to key positions in the keymap. -- Add a physical layout definition to select the matrix transform and KSCAN instance. +- Add the shield overlay file defining: + - The keyboard scan driver for detecting key press/release. + - The matrix transform for mapping keyboard scan row/column values to key positions in the keymap. + - The physical layout definition to select the matrix transform and keyboard scan instance. - Add a default keymap, which users can override in their own configs as needed. - Add a `.zmk.yml` metadata file to document the high level details of your shield, and the features it supports. -- Update the `build.yaml` file from the repository template to have some sample builds of the firmware to test. -After adding ZMK support for a basic shield using this guide, check the sidebar for guides on adding any additional features (such as encoders) that your keyboard has. It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/3.5.0/hardware/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing. +Many of the above files will differ depending on whether your keyboard is a unibody or is [split into multiple parts](../../features/split-keyboards.md). -:::note -ZMK support for [split keyboards](../../features/split-keyboards.md) requires a few more files than single boards to ensure proper connectivity between the central and peripheral units. Check the following guides thoroughly to ensure that all the files are in place. -::: +After adding ZMK support for a basic shield using this guide, check the sidebar for guides on adding any additional features (such as encoders) that your keyboard has. +It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/3.5.0/hardware/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing. ## New Zephyr Module Repository @@ -54,12 +80,14 @@ Follow these steps to create your new repository: - Select Public or Private, depending on your preference. - Click the green "Create repository" button -The repository is a combination of the directories and files required of a ZMK config, and those required of a shield module. To create a shield module, the following components are needed: +The repository is a combination of the directories and files required of a ZMK config, and those required of a shield module. +To create a shield module, the following components are needed: - The `boards/shields` directory, where the keyboard's files will go - The `zephyr/module.yml` file, which identifies and describes the module. See the [Zephyr documentation](https://docs.zephyrproject.org/3.5.0/develop/modules.html#module-yaml-file-description) for details on customising this file. For the purposes of creating a shield module, the default found in the template can be left untouched. -Neither of these should be moved out of their parent directory. The other files and directories such as `config` are not necessary for the purposes of a shield module, but rather intended to be used for user configuration and testing. +Neither of these should be moved out of their parent directory. +The other files and directories such as `config` are not necessary for the purposes of a shield module, but rather intended to be used for user configuration and testing. ## New Shield Directory @@ -75,74 +103,81 @@ mkdir boards/shields/ You can check out the [`shields` folder](https://github.com/zmkfirmware/zmk/tree/main/app/boards/shields) in the ZMK repo that houses [the in-tree supported shields](../../hardware.mdx) in order to copy and modify as a starting point. ::: -There are two required Kconfig files that need to be created for your new keyboard -shield to get it picked up for ZMK, `Kconfig.shield` and `Kconfig.defconfig`. +There are two required [Kconfig](https://docs.zephyrproject.org/3.5.0/build/kconfig/index.html) files that need to be created for your new keyboard shield to get it picked up for ZMK, `Kconfig.shield` and `Kconfig.defconfig`. + ### Kconfig.shield - -The `Kconfig.shield` file defines any additional Kconfig settings that may be relevant when using this keyboard. For most keyboards, there is just one additional configuration value for the shield itself. - -```kconfig -config SHIELD_MY_BOARD - def_bool $(shields_list_contains,my_board) +The `Kconfig.shield` file defines the shield name used to build your keyboard. + + +```kconfig title="Kconfig.shield" +# No whitespace after the comma or in your keyboard name! +config SHIELD_MY_KEYBOARD + def_bool $(shields_list_contains,my_keyboard) ``` - -:::warning -Kconfig uses only commas for delimiters, and keeps all whitespaces in the function call. Therefore do not add a whitespace after the comma when configuring your shield as this would be treated as  my_board (with a leading whitespace) and will cause issues. -::: - -This will make sure that a new configuration value named `SHIELD_MY_BOARD` is set to true whenever `my_board` is used as the shield name, either as the `SHIELD` variable [in a local build](../local-toolchain/build-flash.mdx) or in your `build.yaml` file [when using Github Actions](../../customization.md). Note that this configuration value will be used in `Kconfig.defconfig` to set other properties about your shield, so make sure that they match. - -**For split boards**, you will need to add configurations for the left and right sides. For example, if your split halves are named `my_board_left` and `my_board_right`, it would look like this: +This will set the `SHIELD_MY_KEYBOARD` flag to `y` whenever `my_keyboard` is used as the shield name. +The `SHIELD_MY_KEYBOARD` flag will be used in `Kconfig.defconfig` to set other properties about your shield, so make sure that they match. + + +Split keyboards have multiple shield names defined, one for each part. +For example, if your keyboard consists of two halves named `my_keyboard_left` and `my_keyboard_right`, it would look like this: ```kconfig -config SHIELD_MY_BOARD_LEFT - def_bool $(shields_list_contains,my_board_left) +# No whitespace after the comma or in your board name! +config SHIELD_MY_KEYBOARD_LEFT + def_bool $(shields_list_contains,my_keyboard_left) -config SHIELD_MY_BOARD_RIGHT - def_bool $(shields_list_contains,my_board_right) +# No whitespace after the comma or in your board name! +config SHIELD_MY_KEYBOARD_RIGHT + def_bool $(shields_list_contains,my_keyboard_right) ``` +This will set the `SHIELD_MY_KEYBOARD_LEFT` flag to `y` whenever `my_keyboard_left` is used as the shield name. +Likewise, when `my_keyboard_right` is used as the shield name the `SHIELD_MY_KEYBOARD_RIGHT` flag is set to `y`. +The `SHIELD_MY_KEYBOARD_LEFT` and `SHIELD_MY_KEYBOARD_RIGHT` flags will be used in `Kconfig.defconfig` to set other properties about your shields, so make sure that they match. + + + ### Kconfig.defconfig -The `Kconfig.defconfig` file is where overrides for various configuration settings -that make sense to have different defaults when this shield is used. One main item -that usually has a new default value set here is the `ZMK_KEYBOARD_NAME` value, -which controls the display name of the device over USB and BLE. +The `Kconfig.defconfig` file is used to set new defaults for configuration settings when this shield is used. +One main item that usually has a new default value set here is the `ZMK_KEYBOARD_NAME` value, which controls the display name of the device over USB and BLE. -The updated new default values should always be wrapped inside a conditional on the shield config name defined in the `Kconfig.shield` file. Here's the simplest example file. +The updated new default values should always be wrapped inside a conditional on the shield config name defined in the `Kconfig.shield` file. -:::danger -The keyboard name must be less than or equal to 16 characters in length, otherwise the bluetooth advertising might fail and you will not be able to find your keyboard from your device. -::: + + -```kconfig -if SHIELD_MY_BOARD +```kconfig title="Kconfig.defconfig" +if SHIELD_MY_KEYBOARD +# Name must be less than 16 characters long! config ZMK_KEYBOARD_NAME - default "My Board" + default "My Keyboard" endif ``` -For split keyboards, `Kconfig.defconfig` needs to specify a few more options. -Which side is central (usually the left) is determined via the configuration in this file. + + +For split keyboards, a central side (usually the left) is specified via the configuration in this file. For that side, the keyboard name is assigned and the central config is set. -The peripheral side is typically not assigned a name since only the central will be advertising for connections to other devices. +The peripheral side is not assigned a name. Finally, the split config needs to be set for both sides: -```kconfig -if SHIELD_MY_BOARD_LEFT +```kconfig title="Kconfig.defconfig" +if SHIELD_MY_KEYBOARD_LEFT +# Name must be less than 16 characters long! config ZMK_KEYBOARD_NAME - default "My Board" + default "My Keyboard" config ZMK_SPLIT_ROLE_CENTRAL default y endif -if SHIELD_MY_BOARD_LEFT || SHIELD_MY_BOARD_RIGHT +if SHIELD_MY_KEYBOARD_LEFT || SHIELD_MY_KEYBOARD_RIGHT config ZMK_SPLIT default y @@ -150,33 +185,83 @@ config ZMK_SPLIT endif ``` + + + +### User Configuration Files + +In addition to the `Kconfig.shield` and `Kconfig.defconfig` files, many shields will also define a user configuration file called `my_keyboard.conf`. +This file exists to provide "suggestions" of [configuration settings](../../config/index.md) for a user to select, such as enabling deep-sleep. +Note that the name should match the shield name defined in the [Kconfig.defconfig section](#Kconfig.defconfig). + +:::warning +This file can also be used to set configuration options. +However, if a flag is set in this file, **the user can no longer change it**. +Though sometimes necessary, this method of setting configuration options is discouraged. +The case for which this is necessary is due to be eliminated in the future, making this method redundant. +::: + + + + +Split keyboards can have multiple `.conf` files, one for each part. For example: +- `my_keyboard.conf` - Configuration elements affect both halves +- `my_keyboard_left.conf` - Configuration elements only affect left half +- `my_keyboard_left.conf` - Configuration elements only affect right half +In most case you'll only need to use the .conf file that affects both halves of a split board. + +:::note +The shared configuration in `my_keyboard.conf` is only applied when you are building with a [`zmk-config` folder](../local-toolchain/build-flash.mdx#building-from-zmk-config-folder) and it is present at `config/my_keyboard.conf`. +::: + + + + ## Shield Overlays + + +Shield overlay files contain a devicetree description that is merged with the primary board devicetree description during the firmware building process. +There are three main things that need to be defined in this file: + +- Your keyboard scan (kscan) driver, which determines which GPIO pins to scan for keypress events +- Your matrix transform, which acts as a "bridge" between the kscan and the keymap +- Your physical layout, which aggregates the above and (optionally) defines physical key positions so that the keyboard can be used with [ZMK Studio](../../features/studio.md). + + + +A unibody keyboard will have a single overlay file named `my_keyboard.overlay`, where `my_keyboard` is the shield name defined in the [Kconfig.defconfig section](#Kconfig.defconfig). + + +A split keyboard will have an overlay file defined for each split part. +For example, if the keyboard is split into a left and a right half: +- `my_keyboard_left.overlay` +- `my_keyboard_right.overlay` + Here `my_keyboard_left` and `my_keyboard_right` are the shield names defined in the [Kconfig.defconfig section](#Kconfig.defconfig). + +Split keyboards often share some of their devicetree description. +The standard approach is to have a core `my_keyboard.dtsi` (devicetree include) file, which is included into each of the shield overlays. + + + + +### Kscan + To use GPIO pins that are not part of the interconnects as described above, you can use the GPIO labels that are specific to each controller type. For instance, pins numbered `PX.Y` in nRF52840-based boards can be referred to via `&gpioX Y` labels. An example is `&gpio1 7` for the `P1.07` pin that the nice!nano exposes in the middle of the board. - +The [Keyboard Scan configuration documentation](../../config/kscan.md) has the full details on configuring the kscan driver. + +For a simple 3x3 macropad matrix, the kscan might look something like: -The `.overlay` is the devicetree description of the keyboard shield that is merged with the primary board devicetree description before the build. For ZMK, this file at a minimum should include the chosen node named `zmk,kscan` that references a KSCAN driver instance. For a simple 3x3 macropad matrix, -this might look something like: - -```dts +```dts title="my_keyboard.overlay" / { - chosen { - zmk,kscan = &kscan0; - }; - - kscan0: kscan_0 { + kscan0: kscan0 { compatible = "zmk,kscan-gpio-matrix"; diode-direction = "col2row"; wakeup-source; @@ -196,190 +281,152 @@ this might look something like: }; ``` -See the [Keyboard Scan configuration documentation](../../config/kscan.md) for details on configuring the KSCAN driver. - - +For split keyboards you should define your kscan in `my_keyboard.dtsi`. +If your `row-gpios` or your `col-gpios` (or both) are identical between the parts, then they should also be defined in `my_keyboard.dtsi`. +For example, for a `col2row` 2-part split keyboard (18 keys split into a 3x3 macropad on both halves) where the "row" GPIOs used are the same for both halves: -### .dtsi files and Shield Overlays (Split Shields) - -Unlike unibody keyboards, split keyboards have a core .dtsi file with shield overlays for each half of the keyboard. -It is preferred to define only the `col-gpios` or `row-gpios` in the common shield .dtsi, depending on the `diode-direction` value. -For `col2row` directed boards like the iris, the shared .dtsi file may look like this: - -```dts -#include - +```dts title="my_keyboard.dtsi" / { - chosen { - zmk,kscan = &kscan0; - zmk,matrix-transform = &default_transform; - }; - - default_transform: keymap_transform_0 { - compatible = "zmk,matrix-transform"; - columns = <16>; - rows = <4>; -// | SW6 | SW5 | SW4 | SW3 | SW2 | SW1 | | SW1 | SW2 | SW3 | SW4 | SW5 | SW6 | -// | SW12 | SW11 | SW10 | SW9 | SW8 | SW7 | | SW7 | SW8 | SW9 | SW10 | SW11 | SW12 | -// | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 | | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 | -// | SW24 | SW23 | SW22 | SW21 | SW20 | SW19 | SW25 | | SW25 | SW19 | SW20 | SW21 | SW22 | SW23 | SW24 | -// | SW29 | SW28 | SW27 | SW26 | | SW26 | SW27 | SW28 | SW29 | - map = < -RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) -RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) -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(2,11) -RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(4,2) RC(4,9) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) - RC(4,3) RC(4,4) RC(4,5) RC(4,6) RC(4,7) RC(4,8) - >; - }; - - kscan0: kscan { + kscan0: kscan0 { compatible = "zmk,kscan-gpio-matrix"; diode-direction = "col2row"; wakeup-source; row-gpios - = <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row A from the schematic file - , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row B from the schematic file - , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row C from the schematic file - , <&pro_micro 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row D from the schematic file - , <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row E from the schematic file + = <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> ; }; }; ``` -:::note -Notice that in addition to the common `row-gpios` that are declared in the kscan, the [matrix transform](#matrix-transform) is defined in the .dtsi. -::: - -The missing `col-gpios` would be defined in your `_left.overlay` and `_right.overlay` files. -Keep in mind that the mirrored position of the GPIOs means that the `col-gpios` will appear reversed when the .overlay files are compared to one another. -Furthermore, the column offset for the [matrix transform](#matrix-transform) should be added to the right half of the keyboard's overlay -because the keyboard's switch matrix is read from left to right, top to bottom. -This is exemplified with the iris .overlay files. +The missing `col-gpios` would be defined in your `my_keyboard_left.overlay` and `my_keyboard_right.overlay` files. -```dts title=iris_left.overlay -#include "iris.dtsi" // Notice that the main dtsi files are included in the overlay. +```dts title="my_keyboard_left.overlay" +#include "my_keyboard.dtsi" // The main dtsi files are included in the overlay. +// Label of the kscan node in the dtsi &kscan0 { col-gpios - = <&pro_micro 19 GPIO_ACTIVE_HIGH> // col1 in the schematic - , <&pro_micro 18 GPIO_ACTIVE_HIGH> // col2 in the schematic - , <&pro_micro 15 GPIO_ACTIVE_HIGH> // col3 in the schematic - , <&pro_micro 14 GPIO_ACTIVE_HIGH> // col4 in the schematic - , <&pro_micro 16 GPIO_ACTIVE_HIGH> // col5 in the schematic - , <&pro_micro 10 GPIO_ACTIVE_HIGH> // col6 in the schematic + = <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> ; }; ``` -```dts title=iris_right.overlay -#include "iris.dtsi" - -&default_transform { // The matrix transform for this board is 6 columns over because the left half is 6 columns wide according to the matrix. - col-offset = <6>; -}; +```dts title="my_keyboard_right.overlay" +#include "my_keyboard.dtsi" // The main dtsi files are included in the overlay. +// Label of the kscan node in the dtsi &kscan0 { col-gpios - = <&pro_micro 10 GPIO_ACTIVE_HIGH> // col6 in the schematic - , <&pro_micro 16 GPIO_ACTIVE_HIGH> // col5 in the schematic - , <&pro_micro 14 GPIO_ACTIVE_HIGH> // col4 in the schematic - , <&pro_micro 15 GPIO_ACTIVE_HIGH> // col3 in the schematic - , <&pro_micro 18 GPIO_ACTIVE_HIGH> // col2 in the schematic - , <&pro_micro 19 GPIO_ACTIVE_HIGH> // col1 in the schematic + = <&pro_micro 10 GPIO_ACTIVE_HIGH> + , <&pro_micro 11 GPIO_ACTIVE_HIGH> + , <&pro_micro 13 GPIO_ACTIVE_HIGH> ; }; - ``` -See the [Keyboard Scan configuration documentation](../../config/kscan.md) for details on configuring the KSCAN driver. - -### .conf files (Split Shields) - -While unibody boards only have one .conf file that applies configuration characteristics to the entire keyboard, -split keyboards are unique in that they contain multiple .conf files with different scopes. -For example, a split board called `my_awesome_split_board` would have the following files: - -- `my_awesome_split_board.conf` - Configuration elements affect both halves -- `my_awesome_split_board_left.conf` - Configuration elements only affect left half -- `my_awesome_split_board_right.conf` - Configuration elements only affect right half + + -In most case you'll only need to use the .conf file that affects both halves of a split board. It's used for adding features like deep-sleep or rotary encoders. +### Matrix Transform -```ini title=my_awesome_split_board.conf -CONFIG_ZMK_SLEEP=y -``` - -:::note -The shared configuration in `my_awesome_split_board.conf` is only applied when you are building with a [`zmk-config` folder](../local-toolchain/build-flash.mdx#building-from-zmk-config-folder) and when it is present at `config/my_awesome_split_board.conf`. If you are not using a `zmk-config` folder, you will need to include the shared configuration in both `my_awesome_split_board_left.conf` and `my_awesome_split_board_right.conf` files. -::: +The matrix transform is used to transform row/column events into "key position" events. - - +When a key is pressed, a kscan event is generated from it with a row value and a column value corresponding to the index (from 0) of the `row-gpios` pin and `col-gpios` pin respectively that triggered the event. +The "key position" triggered is the index of the `RC(row, column)` in the matrix transform that correponds to the kscan event. -## Matrix Transform + + +The `my_keyboard.overlay` must include a matrix transform that defines this mapping from row/column values to key positions. +Add `#include ` to the top of the file. -Internally ZMK translates all row/column events into "key position" events to maintain a consistent model that works no matter what any possible GPIO matrix may look like for a certain keyboard. This is particularly helpful when: +Here is an example of a matrix transform for the previous 3x3 macropad: -1. To reduce the used pins, an "efficient" number of rows/columns for the GPIO matrix is used, that does _not_ match the physical layout of rows/columns of the actual key switches. -1. For non rectangular keyboards with thumb clusters, non `1u` locations, etc. +```dts title="my_keyboard.overlay" +#include // Put this with the other includes at the top of your overlay -A "key position" is the numeric index (zero-based) of a given key, which identifies -the logical key location as perceived by the end user. All _keymap_ mappings actually bind behaviors to _key positions_, not to row/column values. +/ { + default_transform: keymap_transform0 { + compatible = "zmk,matrix-transform"; + columns = <3>; // Length of the "col-gpios" array + rows = <3>; // Length of the "row-gpios" array + map = < + // Key 1 | Key 2 | Key 3 + RC(0,0) RC(0,1) RC(0,2) + // Key 4 | Key 5 | Key 6 + RC(1,0) RC(1,1) RC(1,2) + // Key 7 | Key 8 | Key 9 + RC(2,0) RC(2,1) RC(2,2) + >; + }; +}; +``` -The `.overlay` must include a matrix transform that defines this mapping from row/column values to key positions. + + +Split keyboards should define their matrix transform in the shared `my_keyboard.dtsi`. Add `#include ` to the top of the file. -Here is an example for the [nice60](https://github.com/Nicell/nice60), which uses an efficient 8x8 GPIO matrix, and uses a transform: +Here is an example of a matrix transform for the previous example (18-key double macropad): -```dts -#include +```dts title="my_keyboard.dtsi" +#include // Put this with the other includes at the top of your dtsi / { - /* define kscan node with label `kscan0`... */ - - default_transform: keymap_transform_0 { + default_transform: keymap_transform0 { compatible = "zmk,matrix-transform"; - columns = <8>; - rows = <8>; -// | MX1 | MX2 | MX3 | MX4 | MX5 | MX6 | MX7 | MX8 | MX9 | MX10 | MX11 | MX12 | MX13 | MX14 | -// | MX15 | MX16 | MX17 | MX18 | MX19 | MX20 | MX21 | MX22 | MX23 | MX34 | MX25 | MX26 | MX27 | MX28 | -// | MX29 | MX30 | MX31 | MX32 | MX33 | MX34 | MX35 | MX36 | MX37 | MX38 | MX39 | MX40 | MX41 | -// | MX42 | MX43 | MX44 | MX45 | MX46 | MX47 | MX48 | MX49 | MX50 | MX51 | MX52 | MX53 | -// | MX54 | MX55 | MX56 | MX57 | MX58 | MX59 | MX60 | MX61 | + columns = <6>; + rows = <3>; map = < -RC(3,0) RC(2,0) RC(1,0) RC(0,0) RC(1,1) RC(0,1) RC(0,2) RC(1,3) RC(0,3) RC(1,4) RC(0,4) RC(0,5) RC(1,6) RC(1,7) -RC(4,0) RC(4,1) RC(3,1) RC(2,1) RC(2,2) RC(1,2) RC(2,3) RC(3,4) RC(2,4) RC(2,5) RC(1,5) RC(2,6) RC(2,7) RC(3,7) -RC(5,0) RC(5,1) RC(5,2) RC(4,2) RC(3,2) RC(4,3) RC(3,3) RC(4,4) RC(4,5) RC(3,5) RC(4,6) RC(3,6) RC(4,7) -RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(5,3) RC(6,4) RC(5,4) RC(6,5) RC(5,5) RC(6,6) RC(5,6) RC(5,7) -RC(7,0) RC(7,1) RC(7,2) RC(7,3) RC(7,5) RC(7,6) RC(6,7) RC(7,7) + // LKey 1 |LKey 2 |LKey 3 RKey 1 |RKey 2 |RKey 3 + RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) + // LKey 4 |LKey 5 |LKey 6 RKey 4 |RKey 5 |RKey 6 + RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) + // LKey 7 |LKey 8 |LKey 9 RKey 7 |RKey 8 |RKey 9 + RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) >; }; +}; +``` - /* potentially other overlay nodes... */ +The above transform has 6 columns and three rows, while each half of the keyboard only has three columns and three rows. +To allow the kscan matricies to be joined in the matrix transform, an offset is applied to the matrix transform of peripherals. + +```dts title="my_keyboard_right.overlay" +&default_transform { // Offset of 3 because the left side has 3 columns + col-offset = <3>; }; ``` -Some important things to note: +This offset means that when the right half of the keyboard has a keypress event triggered by the GPIO pins at the indicies `0,0` of its `row-gpios` and `col-gpios` arrays respectively, it will interpret it as an `RC(0,3)` event rather than an `RC(0,0)` event. +Additional peripherals would need their columns to be offset by an ever increasing number equal to the sum of the columns in the central + any peripherals that came before it. + +Note that you can also apply row offsets with `row-offset`. + + + + +See the [matrix transform section](../../config/kscan.md#matrix-transform) in the Keyboard Scan configuration documentation for further details and examples of matrix transforms. -- The `#include ` is critical. The `RC` macro is used to generate the internal storage in the matrix transform, and is actually replaced by a C preprocessor before the final devicetree is compiled into ZMK. -- `RC(row, column)` is placed sequentially to define what row and column values that position corresponds to. -- If you have a keyboard with options for `2u` keys in certain positions, ANSI vs. ISO layouts, or break away portions, define one matrix transform for each possible arrangement to be used in the physical layouts. This will allow the users to select the right layout in their keymap files. +### Physical Layout -See the [matrix transform section](../../config/kscan.md#matrix-transform) in the Keyboard Scan configuration documentation for details and more examples of matrix transforms. +The physical layout is the top level entity that aggregates all details about a certain possible layout: -## Physical Layout +- Your keyboard scan (kscan) driver +- Your matrix transform +- (Optional) Physical key positions -The physical layout is the top level entity that aggregates all details about a certain possible layout for a keyboard: the matrix transform that defines the set of key positions and what row/column they correspond to, what kscan driver is used for that layout, etc. -For keyboards that support multiple layouts, setting a `chosen` node to a defined physical layout in your keymap will allow selecting the specific layout that you've built. +The physical layout should be placed in the same file as the matrix transform, i.e. `my_keyboard.overlay` for unibodies and `my_keyboard.dtsi` for split keyboards. A physical layout is very basic, e.g.: -``` +```dts / { default_layout: default_layout { compatible = "zmk,physical-layout"; @@ -390,15 +437,11 @@ A physical layout is very basic, e.g.: }; ``` -When supporting multiple layouts, define the multiple layout nodes and then set a `chosen` for the default: +If a keyboard has multiple possible layouts (ex. you can snap off an outer column), then you should define multiple matrix transformations and multiple physical layouts. +If necessary, you can also define multiple kscan instances. -``` +```dts / { - chosen { - zmk,physical-layout = &default_layout; - ... - }; - default_layout: default_layout { compatible = "zmk,physical-layout"; display-name = "Default Layout"; @@ -415,38 +458,72 @@ When supporting multiple layouts, define the multiple layout nodes and then set }; ``` -This way, users can select a different layout by overriding the `zmk,physical-layout` chosen node in their keymap file. +See [Studio Setup](./studio-setup.md) for information on defining the physical key positions enabling [ZMK Studio](../../features/studio.md) for your keyboard. -:::note -Some keyboards use different GPIO pins for different layouts, and need different kscan nodes created for each layout. -However, if all of your physical layouts use the same `kscan` node under the hood, you can skip setting the `kscan` property on each -layout and instead assign the `zmk,kscan` chosen node to your single kscan instance. -::: +### Chosen Node -## Default Keymap +Set the `chosen` node to a defined "default" physical layout. -Each keyboard should provide a default keymap to be used when building the firmware, which can be overridden and customized by user configs. For "shield keyboards", this should be placed in the `boards/shields//.keymap` file. The keymap is configured as an additional devicetree overlay that includes the following: +```dts +/ { + chosen { + zmk,physical-layout = &default_layout; + // Other chosen items + }; +}; +``` -- A node with `compatible = "zmk,keymap"` where each child node is a layer with a `bindings` array that binds each key position to a given behavior (e.g. key press, momentary layer, etc). +If you define multiple physical layouts, users can select a different layout by overriding the `zmk,physical-layout` chosen node in their keymap file. -Here is an example simple keymap for the Kyria, with only one layer: +:::note +If all of your physical layouts use the same `kscan` node under the hood, you can skip setting the `kscan` property on each layout and instead assign the `zmk,kscan` chosen node to your single kscan instance: - +```dts +/ { + chosen { + zmk,kscan = &kscan0; + zmk,physical-layout = &default_layout; + // Other chosen items + }; +}; +``` -:::note -The two `#include` lines at the top of the keymap are required in order to bring in the default set of behaviors to make them available to bind, and to import a set of defines for the key codes, so keymaps can use parameters like `N2` or `K` instead of the raw keycode numeric values. ::: -### Keymap Behaviors +## Default Keymap + +Each keyboard should provide a default keymap to be used when building the firmware, which can be overridden and customized by user configs. +For "shield keyboards", this should be placed in the `boards/shields/my_keyboard/my_keyboard.keymap` file. +The keymap is configured as an additional devicetree overlay that includes the following: -For documentation on the available behaviors for use in keymaps, see the [overview page for behaviors](../../keymaps/behaviors/index.mdx). +Here is an example simple keymap for a 3x3 macropad, with only one layer: + +```dts title="my_keyboard.keymap" +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { // Layer 0 + // ------------------------------------- + // | Z | M | K | + // | A | B | C | + // | D | E | F | + bindings = < + &kp Z &kp M &kp K + &kp A &kp B &kp C + &kp D &kp E &kp F + >; + }; + }; + }; +``` + +See [Keymaps](../../keymaps/index.mdx) for information on defining keymaps in ZMK. ## Metadata ZMK makes use of an additional metadata YAML file for all boards and shields to provide high level information about the hardware to be incorporated into setup scripts/utilities, website hardware list, etc. -The naming convention for metadata files is `{item_id}.zmk.yml`, where the `item_id` is the board/shield identifier, including version information but excluding any optional split `_left`/`_right` suffix, e.g. `corne.zmk.yml` or `nrfmicro_11.zmk.yml`. - Here is a sample `corne.zmk.yml` file from the repository: ```yaml @@ -465,91 +542,23 @@ siblings: - corne_right ``` -You should place a properly named `foo.zmk.yml` file in the directory next to your other shield values, and fill it out completely and accurately. See [Hardware Metadata Files](hardware-metadata-files.md) for the full details. - -## Build File - -To help you test/verify your firmware, update the `build.yaml` to list your particular board/shield combinations you want built whenever changes are published to GitHub. Open `build.yaml` with your editor and add a combination, e.g.: - -```yaml -# This file generates the GitHub Actions matrix -# For simple board + shield combinations, add them -# to the top level board and shield arrays, for more -# control, add individual board + shield combinations to -# the `include` property, e.g: -# -# board: [ "nice_nano_v2" ] -# shield: [ "corne_left", "corne_right" ] -# include: -# - board: bdn9_rev2 -# - board: nice_nano_v2 -# shield: reviung41 -# ---- -include: - - board: nice_nano_v2 - shield: -``` - -For split keyboards, you will need to specify the halves/siblings separately, e.g.: - -```yaml -include: - - board: mikoto_520 - shield: _left - - board: mikoto_520 - shield: _right -``` +You should place a properly named `my_keyboard.zmk.yml` file in the directory next to your other shield values, and fill it out completely and accurately. +See [Hardware Metadata Files](hardware-metadata-files.md) for the full details. ## Testing -### GitHub Actions - -Using GitHub Actions to build your new firmware can save you from doing any local [development setup](../local-toolchain/setup/index.md), -at the expense of a longer feedback loop if there are issues. To push your changes and trigger a build: +Once you've defined everything as described above, you can build your firmware to make sure everything is working. -- Add all your pending changes with `git add .` -- Commit your changes with `git commit -m "Initial shield"` -- Push the changes to GitHub with `git push` - -Once pushed, click on the "Actions" tab of the repo you created in the first step, and you should see a new build running. If the build is successful, there will be a new `firmware.zip` artifact shown on the summary screen you can download that will contain the new `.uf2` files that can be flashed to the device. - -### Local Build - -:::note -To build locally, be sure you've followed the [development setup](../local-toolchain/setup/index.md) guide first. -::: - -Once you've fully created the new keyboard shield definition, -you should be able to test with a build command like: - -```sh -west build --pristine -b nice_nano_v2 -- -DSHIELD= -DZMK_EXTRA_MODULES=/full/path/to/your/module -# replace with e.g. _left for split keyboards, then repeat for _right -``` - -The above build command generates a `build/zephyr/zmk.uf2` file that you can flash using the steps from the following section. See the dedicated [building and flashing page](../local-toolchain/build-flash.mdx) for more details. - -### Flashing - -If your board -supports USB Flashing Format (UF2), copy that file onto the root of the USB mass -storage device for your board. The controller should flash your built firmware -and automatically restart once flashing is complete. If you need to flash an updated -UF2 file with fixes, you can re-enter the bootloader by double tapping the reset button. +### GitHub Actions -Alternatively, if your board supports flashing and you're not developing from -within a Dockerized environment, enable Device Firmware Upgrade (DFU) mode on -your board and run the following command to test your build: +To use GitHub Actions to test, push the files defining the keyboard to GitHub. +Next, [update the `build.yaml`](../../customization.md#building-additional-keyboards) of your `zmk-config` to build your keyboard. -```sh -west flash -``` +- If your shield is defined in your `zmk-config`, then the shield should start building. +- If the shield is defined in a separate module, you will need to [adjust your `west.yml` to reference the module](https://zmk.dev/docs/features/modules#building-with-modules). -Please have a look at documentation specific to -[building and flashing](../local-toolchain/build-flash.mdx) for additional information. +### Local Toolchain -:::note -Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command, -shown [here](../local-toolchain/build-flash.mdx#building-from-zmk-config-folder) -::: +You can also use a local toolchain setup to test your keyboard. +Follow [our guide for getting set up](../local-toolchain/setup/index.md), then follow the [instructions for building and flashing locally](../local-toolchain/build-flash.mdx). +You will need to specify the module of your keyboard when building. diff --git a/docs/docs/keymap-example-file.md b/docs/docs/keymap-example-file.md deleted file mode 100644 index 91213f1510bf..000000000000 --- a/docs/docs/keymap-example-file.md +++ /dev/null @@ -1,26 +0,0 @@ -```dts -#include -#include - -/ { - keymap { - compatible = "zmk,keymap"; - - default_layer { -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// | ESC | Q | W | E | R | T | | Y | U | I | O | P | \ | -// | TAB | A | S | D | F | G | | H | J | K | L | ; | ' | -// | SHIFT | Z | X | C | V | B | CTRL+A | CTRL+C | | CTRL+V | CTRL+X | N | M | , | . | / | R CTRL | -// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT | - bindings = < - &kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH - &kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT - &kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL - &kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT - >; - - sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN &inc_dec_kp PG_UP PG_DN>; - }; - }; -}; -```