docs: Move more sections under hardware integration (#2704)

docs: Move battery sensing hardware integration to new page

docs: Refactor lighting hardware integrations to new category

docs: Link to pin control page from lighting pages

docs: Consolidate lighting feature pages

docs: Remove incorrect redirect

docs: Consolidate lighting config pages
This commit is contained in:
Cem Aksoylar
2025-01-13 21:14:25 -08:00
committed by GitHub
parent 8dddb1d9d7
commit 700e9b264f
17 changed files with 244 additions and 171 deletions

View File

@@ -1,255 +0,0 @@
---
title: Backlight
sidebar_label: Backlight
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
Backlight is a feature used to control an array of LEDs, usually placed through or under switches.
:::info
Unlike [RGB Underglow](underglow.md), backlight can only control single color LEDs. Additionally, because backlight LEDs all receive the same power, it's not possible to dim individual LEDs.
:::
## Enabling Backlight
To enable backlight on your board or shield, add the following line to your `.conf` file of your user config directory as such:
```ini
CONFIG_ZMK_BACKLIGHT=y
```
If your board or shield does not have backlight configured, refer to [Adding Backlight to a board or a shield](#adding-backlight-to-a-board-or-a-shield).
## Configuring Backlight
There are various Kconfig options used to configure the backlight feature. These can all be set in the `.conf` file.
| Option | Description | Default |
| ------------------------------------ | ----------------------------------------------------- | ------- |
| `CONFIG_ZMK_BACKLIGHT_BRT_STEP` | Brightness step in percent | 20 |
| `CONFIG_ZMK_BACKLIGHT_BRT_START` | Default brightness in percent | 40 |
| `CONFIG_ZMK_BACKLIGHT_ON_START` | Default backlight state | y |
| `CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE` | Turn off backlight when keyboard goes into idle state | n |
| `CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB` | Turn off backlight when USB is disconnected | n |
## Adding Backlight to a Board or a Shield
<Tabs
defaultValue="shieldpin"
values={[
{label: 'Adding to a board', value: 'boardpin'},{label: 'Adding to a shield', value: 'shieldpin'},
]}>
<TabItem value="boardpin">
First, you must enable PWM by adding the following lines to your `Kconfig.defconfig` file:
```kconfig
if ZMK_BACKLIGHT
config PWM
default y
config LED_PWM
default y
endif # ZMK_BACKLIGHT
```
Create a `<board>-pinctrl.dtsi` file if it does not already exist, and include it at the beginning of the `<board>.dts` file. `CONFIG_PINCTRL=y` must be added to `<board>_defconfig` if it isn't already enabled.
The pinctrl file has a `&pinctrl` node that encompasses all pinctrl settings, including I2C or SPI peripherals (e.g. WS2812 LEDs, Battery fuel gauges):
```dts
&pinctrl {
// Other pinctrl definitions for other hardware
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 13)>;
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 13)>;
low-power-enable;
};
};
};
```
Pin numbers are handled differently depending on the MCU. On nRF MCUs pins are configured using `(PWM_OUTX, Y, Z)`, where `X` is the PWM channel used (usually 0), `Y` is the first part of the hardware port (_PY.01_) and `Z` is the second part of the hardware port (_P1.Z_).
For example, _P1.13_ would give you `(PWM_OUT0, 1, 13)` and _P0.15_ would give you `(PWM_OUT0, 0, 15)`.
Add the PWM device to the `board.dts` file and assign the pinctrl definitions to it:
```dts
&pwm0 {
status = "okay";
pinctrl-0 = <&pwm0_default>;
pinctrl-1 = <&pwm0_sleep>;
pinctrl-names = "default", "sleep";
};
```
Then add the following lines to the same `<board>.dts` file, but inside the root devicetree node:
```dts
/ {
backlight: pwmleds {
compatible = "pwm-leds";
pwm_led_0 {
pwms = <&pwm0 0 PWM_MSEC(10) PWM_POLARITY_NORMAL>;
};
};
};
```
The value inside `pwm_led_0` after `&pwm0` must be the channel number. Since `PWM_OUT0` is defined in the pinctrl node, the channel in this example is 0.
In this example, `PWM_MSEC(10)` is the period of the PWM waveform. This period can be altered if your drive circuitry requires different values or the frequency is audible.
If your board uses a P-channel MOSFET to control backlight instead of a N-channel MOSFET, you may want to change `PWM_POLARITY_NORMAL` for `PWM_POLARITY_INVERTED`.
Finally you need to add backlight to the `chosen` element of the root devicetree node:
```dts
/ {
chosen {
zmk,backlight = &backlight;
};
};
```
</TabItem>
<TabItem value="shieldpin">
You must first add a `boards/` directory within your shield folder. For each board that supports the shield you must create a `<board>.defconfig` file and a `<board>.overlay` file inside the `boards/` folder.
Inside your `<board>.defconfig` file, add the following lines:
```kconfig
if ZMK_BACKLIGHT
config PWM
default y
config LED_PWM
default y
endif # ZMK_BACKLIGHT
```
Then add the following lines to your `.overlay` file:
```dts
&pinctrl {
// Other pinctrl definitions for other hardware
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 13)>;
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 13)>;
low-power-enable;
};
};
};
```
Pin numbers are handled differently depending on the MCU. On nRF MCUs pins are configured using `(PWM_OUTX, Y, Z)`, where `X` is the PWM channel used (usually 0), `Y` is the first part of the hardware port (_PY.01_) and `Z` is the second part of the hardware port (_P1.Z_).
For example, _P1.13_ would give you `(PWM_OUT0, 1, 13)` and _P0.15_ would give you `(PWM_OUT0, 0, 15)`.
Add the PWM device to the `<board>.overlay` file and assign the pinctrl definitions to it:
```dts
&pwm0 {
status = "okay";
pinctrl-0 = <&pwm0_default>;
pinctrl-1 = <&pwm0_sleep>;
pinctrl-names = "default", "sleep";
};
```
Then add the following lines to the same `<board>.overlay` file, but inside the root devicetree node:
```
/ {
backlight: pwmleds {
compatible = "pwm-leds";
pwm_led_0 {
pwms = <&pwm0 0 PWM_MSEC(10) PWM_POLARITY_NORMAL>;
};
};
};
```
In this example, `PWM_MSEC(10)` is the period of the PWM waveform. This period can be altered if your drive circuitry requires different values or the frequency is audible.
If your board uses a P-channel MOSFET to control backlight instead of a N-channel MOSFET, you may want to change `PWM_POLARITY_NORMAL` for `PWM_POLARITY_INVERTED`.
The value inside `pwm_led_0` after `&pwm0` must be the channel number. Since `PWM_OUT0` is defined in the pinctrl node, the channel in this example is 0.
Finally you need to add backlight to the `chosen` element of the root devicetree node:
```dts
/ {
chosen {
zmk,backlight = &backlight;
};
};
```
</TabItem>
</Tabs>
### Multiple Backlight LEDs
It is possible to control multiple backlight LEDs at the same time. This is useful if, for example, you have a Caps Lock LED connected to a different pin and you want it to be part of the backlight.
In order to do that, first configure PWM for each pin in the pinctrl node:
```dts
&pinctrl {
// Other Pinctrl definitions go here
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 20)>, // LED 0
<NRF_PSEL(PWM_OUT1, 0, 22)>, // LED 1
<NRF_PSEL(PWM_OUT2, 0, 24)>; // LED 2
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 20)>, // LED 0
<NRF_PSEL(PWM_OUT1, 0, 22)>, // LED 1
<NRF_PSEL(PWM_OUT2, 0, 24)>; // LED 2
low-power-enable;
};
};
};
```
This part will vary based on your MCU as different MCUs have a different number of modules, channels and configuration options.
Add each of your LEDs to the backlight node in the same manner as for one LED, using the channel number definitions in the pinctrl node:
```dts
backlight: pwmleds {
compatible = "pwm-leds";
pwm_led_0: pwm_led_0 {
pwms = <&pwm0 0 PWM_MSEC(10) PWM_POLARITY_NORMAL>;
};
pwm_led_1: pwm_led_1 {
pwms = <&pwm0 1 PWM_MSEC(10) PWM_POLARITY_NORMAL>;
};
pwm_led_2: pwm_led_2 {
pwms = <&pwm0 2 PWM_MSEC(10) PWM_POLARITY_NORMAL>;
};
};
```

View File

@@ -15,25 +15,5 @@ Windows may not properly ask the keyboard to notify it of changes in battery lev
## Adding a Battery Sensor to a Board
To enable a battery sensor on a new board, add the driver for the sensor to your board's `.dts` file. ZMK provides two drivers for estimating the battery level using its voltage:
- `zmk,battery-voltage-divider`: Reads the voltage on an analog input pin.
- `zmk,battery-nrf-vddh`: Reads the power supply voltage on a Nordic nRF52's VDDH pin.
See the [battery level configuration page](../config/battery.md) for the configuration supported by each driver provided by ZMK.
Zephyr also provides some drivers for fuel gauge ICs such as the TI bq274xx series and Maxim MAX17xxx series. If you use a battery sensor that does not have an existing driver, you will need to write a new driver that supports the `SENSOR_CHAN_GAUGE_STATE_OF_CHARGE` sensor channel and contribute it to Zephyr or ZMK.
Once you have the sensor driver defined, add a `zmk,battery` property to the `chosen` node and set it to reference the sensor node. For example:
```dts
/ {
chosen {
zmk,battery = &vbatt;
};
vbatt: vbatt {
compatible = "zmk,battery-nrf-vddh";
};
}
```
If your keyboard is using one of the [boards supported in ZMK](../hardware.mdx) it will already be configured to sense and report battery levels.
If you are using a custom board, see [battery sensing hardware integration page](../development/hardware-integration/battery.md) to add support.

View File

@@ -0,0 +1,105 @@
---
title: Lighting
sidebar_label: Lighting
---
ZMK supports two distinct systems in order to control lighting hardware integrated into keyboards.
Your keyboard likely uses only one type, depending on the type of LED hardware it supports:
- [RGB underglow](#rgb-underglow) system controls LED strips composed of addressable RGB LEDs.
Most keyboards that have multi-color lighting utilizes these.
- [Backlight](#backlight) system controls parallel-connected, non-addressable, single color LEDs.
These are found on keyboards that have a single color backlight that only allows for brightness control.
:::warning
Although the naming of the systems might imply it, which system you use typically does _not_ depend on the physical location of the LEDs.
Instead, you should use the one that supports the LED hardware type that your keyboard has, as described above.
:::
## RGB Underglow
RGB underglow is a feature used to control "strips" of RGB LEDs. Most of the time this is called underglow and creates a glow underneath the board using a ring of LEDs around the edge, hence the name. However, this can be extended to be used to control anything from a single LED to a long string of LEDs anywhere on the keyboard.
:::info
RGB underglow can also be used for per-key lighting. If you have RGB LEDs on your keyboard, this is what you want. For PWM/single color LEDs, see [Backlight section below](#backlight).
:::
ZMK relies on Zephyr's `led-strip` drivers for this feature. The following LEDs/LED families have been implemented:
- WS2812 (includes WS2812B, WS2813, SK6812, and others)
- APA102
- LPD880x (includes LPD8803, LPD8806, and others)
The WS2812 LED family is by far the most popular of these types, so this page will primarily focus on working with it.
Here you can see the RGB underglow feature in action using WS2812 LEDs.
<figure class="video-container">
<iframe src="//www.youtube.com/embed/2KJkq8ssDU0" frameborder="0" allowfullscreen width="100%"></iframe>
</figure>
### Enabling RGB Underglow
To enable RGB underglow on your board or shield, simply enable the `CONFIG_ZMK_RGB_UNDERGLOW` and `CONFIG_*_STRIP` configuration values in the `.conf` file for your board or shield.
For example:
```ini
CONFIG_ZMK_RGB_UNDERGLOW=y
# Use the STRIP config specific to the LEDs you're using
CONFIG_WS2812_STRIP=y
```
See [Configuration Overview](../config/index.md) for more instructions on how to use Kconfig.
If your board or shield does not have RGB underglow configured, refer to the [Adding RGB Underglow Support to a Keyboard](#adding-rgb-underglow-support-to-a-keyboard) section.
#### Modifying the Number of LEDs
The number of LEDs specified in the default configuration for your board or shield may not match the number you have installed. For example, the `corne` shield specifies only 10 LEDs per side while supporting up to 27. On a split keyboard, a good sign of this mismatch is if the lit LEDs on each half are symmetrical.
The `chain-length` property of the `led_strip` node controls the number of underglow LEDs. If it is incorrect for your build, [you can change this property](../config/index.md#changing-devicetree-properties) in your `<keyboard>.keymap` file by adding a stanza like this one outside of any other node (i.e. above or below the `/` node):
```dts
&led_strip {
chain-length = <21>;
};
```
For split keyboards, set `chain-length` to the number of LEDs installed on each half.
### Configuring RGB Underglow
See [RGB underglow configuration](../config/lighting.md#rgb-underglow).
### Adding RGB Underglow Support to a Keyboard
See [RGB underglow hardware integration page](../development/hardware-integration/lighting/underglow.md) on adding underglow support to a ZMK keyboard.
## Backlight
Backlight is a feature used to control an array of LEDs, usually placed through or under switches.
:::info
Unlike [RGB underglow](#rgb-underglow), backlight can only control single color LEDs. Additionally, because backlight LEDs all receive the same power, it's not possible to dim individual LEDs.
:::
### Enabling Backlight
To enable backlight on your board or shield, add the following line to your `.conf` file of your user config directory as such:
```ini
CONFIG_ZMK_BACKLIGHT=y
```
If your board or shield does not have backlight configured, refer to [Adding Backlight to a board or a shield](#adding-backlight-to-a-board-or-a-shield).
### Configuring Backlight
There are various Kconfig options used to configure the backlight feature.
See [backlight configuration](../config/lighting.md#backlight) for details.
### Adding Backlight to a Board or a Shield
See [backlight hardware integration page](../development/hardware-integration/lighting/backlight.mdx) for information on adding backlight support to a ZMK keyboard.

View File

@@ -1,193 +0,0 @@
---
title: RGB Underglow
sidebar_label: RGB Underglow
---
RGB underglow is a feature used to control "strips" of RGB LEDs. Most of the time this is called underglow and creates a glow underneath the board using a ring of LEDs around the edge, hence the name. However, this can be extended to be used to control anything from a single LED to a long string of LEDs anywhere on the keyboard.
:::info
RGB underglow can also be used for per-key lighting. If you have RGB LEDs on your keyboard, this is what you want. For PWM/single color LEDs, see [Backlight](backlight.mdx).
:::
ZMK relies on Zephyr's `led-strip` drivers for this feature. The following LEDs/LED families have been implemented:
- WS2812 (includes WS2812B, WS2813, SK6812, and others)
- APA102
- LPD880x (includes LPD8803, LPD8806, and others)
The WS2812 LED family is by far the most popular of these types, so this page will primarily focus on working with it.
Here you can see the RGB underglow feature in action using WS2812 LEDs.
<figure class="video-container">
<iframe src="//www.youtube.com/embed/2KJkq8ssDU0" frameborder="0" allowfullscreen width="100%"></iframe>
</figure>
## Enabling RGB Underglow
To enable RGB underglow on your board or shield, simply enable the `CONFIG_ZMK_RGB_UNDERGLOW` and `CONFIG_*_STRIP` configuration values in the `.conf` file for your board or shield.
For example:
```ini
CONFIG_ZMK_RGB_UNDERGLOW=y
# Use the STRIP config specific to the LEDs you're using
CONFIG_WS2812_STRIP=y
```
See [Configuration Overview](/docs/config) for more instructions on how to use Kconfig.
If your board or shield does not have RGB underglow configured, refer to [Adding RGB Underglow to a Board](#adding-rgb-underglow-to-a-board).
### Modifying the Number of LEDs
The number of LEDs specified in the default configuration for your board or shield may not match the number you have installed. For example, the `corne` shield specifies only 10 LEDs per side while supporting up to 27. On a split keyboard, a good sign of this mismatch is if the lit LEDs on each half are symmetrical.
The `chain-length` property of the `led_strip` node controls the number of underglow LEDs. If it is incorrect for your build, [you can change this property](../config/index.md#changing-devicetree-properties) in your `<keyboard>.keymap` file by adding a stanza like this one outside of any other node (i.e. above or below the `/` node):
```dts
&led_strip {
chain-length = <21>;
};
```
For split keyboards, set `chain-length` to the number of LEDs installed on each half.
## Configuring RGB Underglow
See [RGB underglow configuration](/docs/config/underglow).
## Adding RGB Underglow to a Board
Support for RGB underglow is always added to a board, not a shield. This is because the LED strip drivers rely on hardware-specific interfaces (e.g. SPI, I2S) and configurations, which shields do not control.
Shields written for boards which support RGB underglow should add a `boards/` folder underneath the shield folder. Inside this `boards/` folder, create a `<board>.overlay` for any of the boards the shield can be used with. Place all hardware-specific configurations in these `.overlay` files.
For example: the `kyria` shield has a [`boards/nice_nano_v2.overlay`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/kyria/boards/nice_nano_v2.overlay) and a [`boards/nrfmicro_13.overlay`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/kyria/boards/nrfmicro_13.overlay), which configure a WS2812 LED strip for the `nice_nano_v2` and `nrfmicro_13` boards respectively.
### nRF52-Based Boards
Using an SPI-based LED strip driver on the `&spi3` interface is the simplest option for nRF52-based boards. If possible, avoid using pins which are limited to low-frequency I/O for this purpose. The resulting interference may result in poor wireless performance.
:::info
The list of low frequency I/O pins for the nRF52840 can be found [here](https://docs.nordicsemi.com/bundle/ps_nrf52840/page/pin.html).
:::
The following example uses `P0.06` as the "Data In" pin of a WS2812-compatible LED strip:
```dts
#include <dt-bindings/led/led.h>
&pinctrl {
spi3_default: spi3_default {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 6)>;
};
};
spi3_sleep: spi3_sleep {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 6)>;
low-power-enable;
};
};
};
&spi3 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi3_default>;
pinctrl-1 = <&spi3_sleep>;
pinctrl-names = "default", "sleep";
led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <4000000>;
/* WS2812 */
chain-length = <10>; /* number of LEDs */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
color-mapping = <LED_COLOR_ID_GREEN
LED_COLOR_ID_RED
LED_COLOR_ID_BLUE>;
};
};
```
:::note
Standard WS2812 LEDs use a wire protocol where the bits for the colors green, red, and blue values are sent in that order.
If your board/shield uses LEDs that require the data sent in a different order, the `color-mapping` property ordering should be changed to match.
:::
### Other Boards
Be sure to check the Zephyr documentation for the LED strip and necessary hardware bindings. Not every board has an `spi3` node, or configures `pinctrl` the same way. Reconcile this with any hardware restrictions found in the manufacturer's datasheet. Additional hardware interfaces may need to be enabled via Kconfig.
For example: the `sparkfun_pro_micro_rp2040` board can utilize SPI via PIO to run a WS2812 strip on `GP0`:
```dts
#include <dt-bindings/led/led.h>
&pinctrl {
pio0_spi0_default: pio0_spi0_default {
group1 {
pinmux = <PIO0_P0>;
};
};
};
&pio0 {
status = "okay";
pio0_spi0: pio0_spi0 {
pinctrl-0 = <&pio0_spi0_default>;
pinctrl-names = "default";
compatible = "raspberrypi,pico-spi-pio";
#address-cells = <1>;
#size-cells = <0>;
clocks = <&system_clk>;
clock-frequency = <4000000>;
clk-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; /* Must be defined. Select a pin that is not used elsewhere. */
mosi-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; /* Data In pin. */
miso-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; /* Must be defined. Re-using the DI pin is OK for WS2812. */
led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <4000000>;
/* WS2812 */
chain-length = <10>; /* number of LEDs */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
color-mapping = <LED_COLOR_ID_GREEN
LED_COLOR_ID_RED
LED_COLOR_ID_BLUE>;
};
};
};
```
### Final Steps
Once the `led_strip` is properly defined, add it to the `chosen` node under the root devicetree node:
```dts
/ {
chosen {
zmk,underglow = &led_strip;
};
};
```