zmk/docs/docs/features/backlight.md
ReFil 99417b0e5c
Apply suggestions from code review
Co-authored-by: Less/Rikki <86894501+lesshonor@users.noreply.github.com>
2023-12-03 20:46:59 +00:00

8.1 KiB

title sidebar_label
Backlight 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, 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:

CONFIG_ZMK_BACKLIGHT=y

If your board or shield does not have backlight configured, refer to 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'}, ]}>

First, you must enable PWM by adding the following lines to your Kconfig.defconfig file:

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 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):

&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:

&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:

/ {
    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:

/ {
    chosen {
        zmk,backlight = &backlight;
    };
};

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:

if ZMK_BACKLIGHT

config PWM
    default y

config LED_PWM
    default y

endif # ZMK_BACKLIGHT

Then add the following lines to your .overlay file:

&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:

&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:

/ {
    chosen {
        zmk,backlight = &backlight;
    };
};

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:

&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:

backlight: pwmleds {
    compatible = "pwm-leds";
    label = "Backlight 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>;
    };
};