forked from kofal.net/zmk
docs(feat): further generalize RGB information (#2485)
* docs(feat): Provide example of PIO SPI for RGB underglow * docs(feat): further generalize RGB information * docs: use nice_nano_v2 for board-specific shield config example Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com> --------- Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
This commit is contained in:
@@ -6,16 +6,16 @@ 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.
|
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
|
:::info
|
||||||
RGB underglow can also be used for under-key lighting. If you have RGB LEDs on your keyboard, this is what you want. For PWM/single color LEDs, see [Backlight](backlight.mdx).
|
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 supports all the RGB LEDs supported by Zephyr. Here's the current list supported:
|
ZMK relies on Zephyr's `led-strip` drivers for this feature. The following LEDs/LED families have been implemented:
|
||||||
|
|
||||||
- WS2812-ish (WS2812B, WS2813, SK6812, or compatible)
|
- WS2812 (includes WS2812B, WS2813, SK6812, and others)
|
||||||
- APA102
|
- APA102
|
||||||
- LPD880x (LPD8803, LPD8806, or compatible)
|
- LPD880x (includes LPD8803, LPD8806, and others)
|
||||||
|
|
||||||
Of the compatible types, the WS2812 LED family is by far the most popular type. Currently each of these types of LEDs are expected to be run using SPI with a couple of exceptions.
|
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.
|
Here you can see the RGB underglow feature in action using WS2812 LEDs.
|
||||||
|
|
||||||
@@ -34,16 +34,15 @@ CONFIG_ZMK_RGB_UNDERGLOW=y
|
|||||||
CONFIG_WS2812_STRIP=y
|
CONFIG_WS2812_STRIP=y
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Configuration Overview](/docs/config) for more instructions on how to
|
See [Configuration Overview](/docs/config) for more instructions on how to use Kconfig.
|
||||||
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).
|
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
|
### Modifying the Number of LEDs
|
||||||
|
|
||||||
A common issue when enabling underglow is that some of the installed LEDs do not illuminate. This can happen when a board's default underglow configuration accounts only for either the downward facing LEDs or the upward facing LEDs under each key. On a split keyboard, a good sign that this may be the problem is that the unilluminated LEDs on each half are symmetrical.
|
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 number of underglow LEDs is controlled by the `chain-length` property in the `led_strip` node. You can [change the value of this property](../config/index.md#changing-devicetree-properties) in the `<keyboard>.keymap` file by adding a stanza like this one outside of any other node (i.e. above or below the `/` node):
|
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
|
```dts
|
||||||
&led_strip {
|
&led_strip {
|
||||||
@@ -51,7 +50,7 @@ The number of underglow LEDs is controlled by the `chain-length` property in the
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
where the value is the total count of LEDs (per half, for split keyboards).
|
For split keyboards, set `chain-length` to the number of LEDs installed on each half.
|
||||||
|
|
||||||
## Configuring RGB Underglow
|
## Configuring RGB Underglow
|
||||||
|
|
||||||
@@ -59,16 +58,23 @@ See [RGB underglow configuration](/docs/config/underglow).
|
|||||||
|
|
||||||
## Adding RGB Underglow to a Board
|
## Adding RGB Underglow to a Board
|
||||||
|
|
||||||
RGB underglow is always added to a board, not a shield. This is a consequence of needing to configure SPI to control the LEDs.
|
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.
|
||||||
If you have a shield with RGB underglow, you must add a `boards/` directory within your shield folder to define the RGB underglow individually for each board that supports the shield.
|
|
||||||
Inside the `boards/` folder, you define a `<board>.overlay` for each different board.
|
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.overlay` file that defines the RGB underglow for the `nice_nano` board specifically.
|
|
||||||
|
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
|
### nRF52-Based Boards
|
||||||
|
|
||||||
With nRF52 boards, you can just use `&spi3` and define the pins you want to use.
|
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.
|
||||||
|
|
||||||
Here's an example on a definition that uses P0.06:
|
:::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
|
```dts
|
||||||
#include <dt-bindings/led/led.h>
|
#include <dt-bindings/led/led.h>
|
||||||
@@ -89,38 +95,31 @@ Here's an example on a definition that uses P0.06:
|
|||||||
};
|
};
|
||||||
|
|
||||||
&spi3 {
|
&spi3 {
|
||||||
compatible = "nordic,nrf-spim";
|
compatible = "nordic,nrf-spim";
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
pinctrl-0 = <&spi3_default>;
|
pinctrl-0 = <&spi3_default>;
|
||||||
pinctrl-1 = <&spi3_sleep>;
|
pinctrl-1 = <&spi3_sleep>;
|
||||||
pinctrl-names = "default", "sleep";
|
pinctrl-names = "default", "sleep";
|
||||||
|
|
||||||
led_strip: ws2812@0 {
|
led_strip: ws2812@0 {
|
||||||
compatible = "worldsemi,ws2812-spi";
|
compatible = "worldsemi,ws2812-spi";
|
||||||
|
|
||||||
/* SPI */
|
/* SPI */
|
||||||
reg = <0>; /* ignored, but necessary for SPI bindings */
|
reg = <0>; /* ignored, but necessary for SPI bindings */
|
||||||
spi-max-frequency = <4000000>;
|
spi-max-frequency = <4000000>;
|
||||||
|
|
||||||
/* WS2812 */
|
/* WS2812 */
|
||||||
chain-length = <10>; /* number of LEDs */
|
chain-length = <10>; /* number of LEDs */
|
||||||
spi-one-frame = <0x70>;
|
spi-one-frame = <0x70>;
|
||||||
spi-zero-frame = <0x40>;
|
spi-zero-frame = <0x40>;
|
||||||
color-mapping = <LED_COLOR_ID_GREEN
|
color-mapping = <LED_COLOR_ID_GREEN
|
||||||
LED_COLOR_ID_RED
|
LED_COLOR_ID_RED
|
||||||
LED_COLOR_ID_BLUE>;
|
LED_COLOR_ID_BLUE>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
:::info
|
|
||||||
|
|
||||||
If you are configuring SPI for an nRF52 based board, double check that you are using pins that aren't restricted to low frequency I/O.
|
|
||||||
Ignoring these restrictions may result in poor wireless performance. You can find the list of low frequency I/O pins for the nRF52840 [here](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fpin.html&cp=4_0_0_6_0).
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
||||||
Standard WS2812 LEDs use a wire protocol where the bits for the colors green, red, and blue values are sent in that order.
|
Standard WS2812 LEDs use a wire protocol where the bits for the colors green, red, and blue values are sent in that order.
|
||||||
@@ -130,34 +129,60 @@ If your board/shield uses LEDs that require the data sent in a different order,
|
|||||||
|
|
||||||
### Other Boards
|
### Other Boards
|
||||||
|
|
||||||
For other boards, you must select an SPI definition that has the `MOSI` pin as your data pin going to your LED strip.
|
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.
|
||||||
|
|
||||||
Here's another example for a non-nRF52 board on `spi3`:
|
For example: the `sparkfun_pro_micro_rp2040` board can utilize SPI via PIO to run a WS2812 strip on `GP0`:
|
||||||
|
|
||||||
```dts
|
```dts
|
||||||
#include <dt-bindings/led/led.h>
|
#include <dt-bindings/led/led.h>
|
||||||
|
|
||||||
&spi3 {
|
&pinctrl {
|
||||||
|
pio0_spi0_default: pio0_spi0_default {
|
||||||
|
group1 {
|
||||||
|
pinmux = <PIO0_P0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
led_strip: ws2812@0 {
|
&pio0 {
|
||||||
compatible = "worldsemi,ws2812-spi";
|
status = "okay";
|
||||||
|
|
||||||
/* SPI */
|
pio0_spi0: pio0_spi0 {
|
||||||
reg = <0>;
|
pinctrl-0 = <&pio0_spi0_default>;
|
||||||
spi-max-frequency = <5250000>;
|
pinctrl-names = "default";
|
||||||
|
|
||||||
/* WS2812 */
|
compatible = "raspberrypi,pico-spi-pio";
|
||||||
chain-length = <10>; /* number of LEDs */
|
#address-cells = <1>;
|
||||||
spi-one-frame = <0x70>; /* make sure to configure this properly for your SOC */
|
#size-cells = <0>;
|
||||||
spi-zero-frame = <0x40>; /* make sure to configure this properly for your SOC */
|
clocks = <&system_clk>;
|
||||||
color-mapping = <LED_COLOR_ID_GREEN
|
clock-frequency = <4000000>;
|
||||||
LED_COLOR_ID_RED
|
|
||||||
LED_COLOR_ID_BLUE>;
|
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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you have your `led_strip` properly defined you need to add it to the root devicetree node `chosen` element:
|
### Final Steps
|
||||||
|
|
||||||
|
Once the `led_strip` is properly defined, add it to the `chosen` node under the root devicetree node:
|
||||||
|
|
||||||
```dts
|
```dts
|
||||||
/ {
|
/ {
|
||||||
@@ -166,11 +191,3 @@ Once you have your `led_strip` properly defined you need to add it to the root d
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally you need to enable the `CONFIG_ZMK_RGB_UNDERGLOW` and `CONFIG_*_STRIP` configuration values in the `.conf` file of your board (or set a default in the `Kconfig.defconfig`):
|
|
||||||
|
|
||||||
```ini
|
|
||||||
CONFIG_ZMK_RGB_UNDERGLOW=y
|
|
||||||
# Use the STRIP config specific to the LEDs you're using
|
|
||||||
CONFIG_WS2812_STRIP=y
|
|
||||||
```
|
|
||||||
|
|||||||
Reference in New Issue
Block a user