docs: Physical layout docs improvements (#2533)

* docs: Added layout configuration reference page
* docs: Refactored and revamped physical layout creation information
* docs: Added note in studio features page
* docs: added studio_unlock note in features section
---------

Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
Co-authored-by: Joel Spadin <joelspadin@gmail.com>
Co-authored-by: Peter Johanson <peter@peterjohanson.com>
This commit is contained in:
Nicolas Munnich
2024-10-12 21:25:45 +02:00
committed by GitHub
parent d3f2895744
commit a72327bdff
9 changed files with 882 additions and 342 deletions

View File

@@ -335,170 +335,3 @@ Definition file: [zmk/app/dts/bindings/zmk,kscan-mock.yaml](https://github.com/z
| `exit-after` | bool | Exit the program after running all events | false |
The `events` array should be defined using the macros from [app/module/include/dt-bindings/zmk/kscan_mock.h](https://github.com/zmkfirmware/zmk/blob/main/app/module/include/dt-bindings/zmk/kscan_mock.h).
## Matrix Transform
Defines a mapping from keymap logical positions to physical matrix positions.
Transforms should be used any time the physical layout of a keyboard's keys does not match the layout of its electrical matrix and/or when not all positions in the matrix are used. This applies to most non-ortholinear boards.
Transforms can also be used for keyboards with multiple layouts. You can define multiple matrix transform nodes, one for each layout, and users can select which one they want from the `/chosen` node in their keymaps.
See the [new shield guide](../development/hardware-integration/new-shield.mdx#matrix-transform) for more documentation on how to define a matrix transform.
### Devicetree
Applies to: `compatible = "zmk,matrix-transform"`
Definition file: [zmk/app/dts/bindings/zmk,matrix-transform.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Cmatrix-transform.yaml)
| Property | Type | Description | Default |
| ------------ | ----- | --------------------------------------------------------------------- | ------- |
| `rows` | int | Number of rows in the transformed matrix | |
| `columns` | int | Number of columns in the transformed matrix | |
| `row-offset` | int | Adds an offset to all rows before looking them up in the transform | 0 |
| `col-offset` | int | Adds an offset to all columns before looking them up in the transform | 0 |
| `map` | array | A list of position transforms | |
The `map` array should be defined using the `RC()` macro from [dt-bindings/zmk/matrix_transform.h](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/matrix_transform.h). It should have one item per logical position in the keymap. Each item should list the physical row and column that should trigger the key in that position.
### Example: Skipping Unused Positions
Any keyboard which is not a grid of 1 unit keys will likely have some unused positions in the matrix. A matrix transform can be used to skip the unused positions so users don't have to set them to `&none` in keymaps.
```dts
// numpad.overlay
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
// define row-gpios with 5 elements and col-gpios with 4...
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <5>;
columns = <4>;
// ┌───┬───┬───┬───┐
// │NUM│ / │ * │ - │
// ├───┼───┼───┼───┤
// │ 7 │ 8 │ 9 │ + │
// ├───┼───┼───┤ │
// │ 4 │ 5 │ 6 │ │
// ├───┼───┼───┼───┤
// │ 1 │ 2 │ 3 │RET│
// ├───┴───┼───┤ │
// │ 0 │ . │ │
// └───────┴───┴───┘
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3)
RC(1,0) RC(1,1) RC(1,2) RC(1,3)
RC(2,0) RC(2,1) RC(2,2)
RC(3,0) RC(3,1) RC(3,2) RC(3,3)
RC(4,0) RC(4,1)
>;
};
};
```
```dts
// numpad.keymap
/ {
keymap {
compatible = "zmk,keymap";
default {
bindings = <
&kp KP_NUM &kp KP_DIV &kp KP_MULT &kp KP_MINUS
&kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_PLUS
&kp KP_N4 &kp KP_N5 &kp KP_N6
&kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_ENTER
&kp KP_N0 &kp KP_DOT
>;
};
}
};
```
### Example: Non-standard Matrix
Consider a keyboard with a [duplex matrix](https://wiki.ai03.com/books/pcb-design/page/matrices-and-duplex-matrix), where the matrix has twice as many rows and half as many columns as the keyboard has keys. A matrix transform can be used to correct for this so that keymaps can match the layout of the keys, not the layout of the matrix.
```dts
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
// define row-gpios with 12 elements and col-gpios with 8...
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <6>;
columns = <16>;
// ESC F1 F2 F3 ...
// ` 1 2 3 ...
// Tab Q W E ...
// Caps A S D ...
// Shift Z X C ...
// Ctrl Alt ...
map = <
RC(0,0) RC(1,0) RC(0,1) RC(1,1) // ...
RC(2,0) RC(3,0) RC(2,1) RC(3,1) // ...
RC(4,0) RC(5,0) RC(4,1) RC(5,1) // ...
RC(6,0) RC(7,0) RC(6,1) RC(7,1) // ...
RC(8,0) RC(9,0) RC(8,1) RC(9,1) // ...
RC(10,0) RC(11,0) // ...
>;
};
};
```
### Example: Charlieplex
Since a charlieplex driver will never align with a keyboard directly due to the un-addressable positions, a matrix transform should be used to map the pairs to the layout of the keys.
Note that the entire addressable space does not need to be mapped.
```devicetree
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-charlieplex";
wakeup-source;
interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) >;
gpios
= <&pro_micro 16 GPIO_ACTIVE_HIGH>
, <&pro_micro 17 GPIO_ACTIVE_HIGH>
, <&pro_micro 18 GPIO_ACTIVE_HIGH>
, <&pro_micro 19 GPIO_ACTIVE_HIGH>
, <&pro_micro 20 GPIO_ACTIVE_HIGH>
; // addressable space is 5x5, (minus paired values)
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <3>;
columns = <5>;
// Q W E R
// A S D F
// Z X C V
map = <
RC(0,1) RC(0,2) RC(0,3) RC(0,4)
RC(1,0) RC(1,2) RC(1,3) RC(1,4)
RC(2,0) RC(2,1) RC(2,3) RC(2,4)
>;
};
};
```

225
docs/docs/config/layout.md Normal file
View File

@@ -0,0 +1,225 @@
---
title: Layout Configuration
sidebar_label: Layout
---
See [Configuration Overview](index.md) for instructions on how to change these settings.
## Matrix Transform
Defines a mapping from keymap logical positions to physical [kscan](./kscan.md) positions.
You can define multiple matrix transform nodes, one for each layout, and users can select which one they want from the `/chosen` node in their keymaps.
See the [new shield guide](../development/hardware-integration/new-shield.mdx#matrix-transform) for more documentation on how to define a matrix transform.
### Devicetree
Applies to: `compatible = "zmk,matrix-transform"`
Definition file: [zmk/app/dts/bindings/zmk,matrix-transform.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Cmatrix-transform.yaml)
| Property | Type | Description | Default |
| ------------ | ----- | --------------------------------------------------------------------- | ------- |
| `rows` | int | Number of rows in the transformed matrix | |
| `columns` | int | Number of columns in the transformed matrix | |
| `row-offset` | int | Adds an offset to all rows before looking them up in the transform | 0 |
| `col-offset` | int | Adds an offset to all columns before looking them up in the transform | 0 |
| `map` | array | A list of position transforms | |
The `map` array should be defined using the `RC()` macro from [dt-bindings/zmk/matrix_transform.h](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/matrix_transform.h). It should have one item per logical position in the keymap. Each item should list the physical row and column that should trigger the key in that position.
### Example: Skipping Unused Positions
Any keyboard which is not a grid of 1 unit keys will likely have some unused positions in the matrix. A matrix transform can be used to skip the unused positions so users don't have to set them to `&none` in keymaps.
```dts
// numpad.overlay
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
// define row-gpios with 5 elements and col-gpios with 4...
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <5>;
columns = <4>;
// ┌───┬───┬───┬───┐
// │NUM│ / │ * │ - │
// ├───┼───┼───┼───┤
// │ 7 │ 8 │ 9 │ + │
// ├───┼───┼───┤ │
// │ 4 │ 5 │ 6 │ │
// ├───┼───┼───┼───┤
// │ 1 │ 2 │ 3 │RET│
// ├───┴───┼───┤ │
// │ 0 │ . │ │
// └───────┴───┴───┘
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3)
RC(1,0) RC(1,1) RC(1,2) RC(1,3)
RC(2,0) RC(2,1) RC(2,2)
RC(3,0) RC(3,1) RC(3,2) RC(3,3)
RC(4,0) RC(4,1)
>;
};
};
```
```dts
// numpad.keymap
/ {
keymap {
compatible = "zmk,keymap";
default {
bindings = <
&kp KP_NUM &kp KP_DIV &kp KP_MULT &kp KP_MINUS
&kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_PLUS
&kp KP_N4 &kp KP_N5 &kp KP_N6
&kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_ENTER
&kp KP_N0 &kp KP_DOT
>;
};
}
};
```
### Example: Non-standard Matrix
Consider a keyboard with a [duplex matrix](https://wiki.ai03.com/books/pcb-design/page/matrices-and-duplex-matrix), where the matrix has twice as many rows and half as many columns as the keyboard has keys. A matrix transform can be used to correct for this so that keymaps can match the layout of the keys, not the layout of the matrix.
```dts
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
// define row-gpios with 12 elements and col-gpios with 8...
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <6>;
columns = <16>;
// ESC F1 F2 F3 ...
// ` 1 2 3 ...
// Tab Q W E ...
// Caps A S D ...
// Shift Z X C ...
// Ctrl Alt ...
map = <
RC(0,0) RC(1,0) RC(0,1) RC(1,1) // ...
RC(2,0) RC(3,0) RC(2,1) RC(3,1) // ...
RC(4,0) RC(5,0) RC(4,1) RC(5,1) // ...
RC(6,0) RC(7,0) RC(6,1) RC(7,1) // ...
RC(8,0) RC(9,0) RC(8,1) RC(9,1) // ...
RC(10,0) RC(11,0) // ...
>;
};
};
```
### Example: Charlieplex
Since a charlieplex driver will never align with a keyboard directly due to the un-addressable positions, a matrix transform should be used to map the pairs to the layout of the keys.
Note that the entire addressable space does not need to be mapped.
```devicetree
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix-transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-charlieplex";
wakeup-source;
interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) >;
gpios
= <&pro_micro 16 GPIO_ACTIVE_HIGH>
, <&pro_micro 17 GPIO_ACTIVE_HIGH>
, <&pro_micro 18 GPIO_ACTIVE_HIGH>
, <&pro_micro 19 GPIO_ACTIVE_HIGH>
, <&pro_micro 20 GPIO_ACTIVE_HIGH>
; // addressable space is 5x5, (minus paired values)
};
default_transform: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <3>;
columns = <5>;
// Q W E R
// A S D F
// Z X C V
map = <
RC(0,1) RC(0,2) RC(0,3) RC(0,4)
RC(1,0) RC(1,2) RC(1,3) RC(1,4)
RC(2,0) RC(2,1) RC(2,3) RC(2,4)
>;
};
};
```
## Physical Layout
Defines a keyboard layout by joining together a [matrix transform](#matrix-transform), a [keyboard scan](./kscan.md), and a list of physical key properties.
Multiple physical layouts can be defined for keyboards with multiple physical key layouts.
Read through the [page on physical layouts](../development/hardware-integration/physical-layouts.md) for more information.
### Devicetree
Applies to: `compatible = zmk,physical-layout`
Definition file: [zmk/app/dts/bindings/zmk,physical-layout.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Cphysical-layout.yaml)
| Property | Type | Description | Default |
| -------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------- | ------- |
| `display-name` | string | The name of this layout, for display purposes | |
| `transform` | phandle | The matrix transform to use along with this layout | |
| `kscan` | phandle | The kscan to use along with this layout. The `zmk,kscan` chosen will be used as a fallback if this property is omitted | |
| `keys` | phandle-array | Array of key physical attributes. | |
Each element of the `keys` array has the shape `<&key_physical_attrs w h x y r rx ry>`, with the following properties:
| Property | Type | Description | Unit |
| ---------- | -------- | ------------------------------------ | ------------------------------------------------------- |
| Width | int (>0) | Key(cap) width | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
| Height | int (>0) | Key(cap) height | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
| X | uint | Key X position (top-left point) | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
| Y | uint | Key Y position (top-left point) | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
| Rotation | int | Key rotation (positive => clockwise) | [centi-](https://en.wikipedia.org/wiki/Centi-)degree |
| Rotation X | int | Rotation origin X position | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
| Rotation Y | int | Rotation origin Y position | [centi-](https://en.wikipedia.org/wiki/Centi-)"keyunit" |
The `key_physical_attrs` node is defined in [`dts/physical_layouts.dtsi`](https://github.com/zmkfirmware/zmk/blob/main/app/dts/physical_layouts.dtsi) and is mandatory.
## Physical Layout Position Map
Defines a mapping between [physical layouts](#physical-layout), allowing key mappings to be preserved in the same locations as previously when using [ZMK Studio](../features/studio.md). Read through the [page on physical layouts](../development/hardware-integration/physical-layouts.md) for more information.
### Devicetree
Applies to: `compatible = zmk,physical-layout-position-map`
Definition file: [zmk/app/dts/bindings/zmk,physical-layout-position-map.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Cphysical-layout-position-map.yaml)
| Property | Type | Description | Default |
| ---------- | ------- | ------------------------------------------------------------------------------------------------ | ------- |
| `complete` | boolean | If the mapping complete describes the key mapping, and no position based mapping should be used. | |
The `zmk,physical-layout-position-map` node should have one child node per physical layout. Each child node should have the following properties:
| Property | Type | Description | Default |
| ----------------- | ------- | --------------------------------------------------------------------------------- | ------- |
| `physical-layout` | phandle | The physical layout that corresponds to this mapping entry | |
| `positions` | array | Array of key positions that match the same array entry in the other sibling nodes | |