summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorRyan <fauxpark@gmail.com>2023-07-21 09:27:55 +1000
committerGitHub <noreply@github.com>2023-07-21 09:27:55 +1000
commit4137685f8eb3ecde7ba0158a78cd9c411ee05606 (patch)
tree52e5a157409680ae62e42318c4f6f7087bef19e2 /docs
parentb090354143612d2c0f5c8629510542de5bd4e29e (diff)
backlight: split AVR PWM and timer drivers (#21540)
Diffstat (limited to 'docs')
-rw-r--r--docs/feature_backlight.md346
1 files changed, 216 insertions, 130 deletions
diff --git a/docs/feature_backlight.md b/docs/feature_backlight.md
index 24057c608f..d032c33f18 100644
--- a/docs/feature_backlight.md
+++ b/docs/feature_backlight.md
@@ -1,12 +1,12 @@
# Backlighting :id=backlighting
-Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB underglow](feature_rgblight.md) and [RGB matrix](feature_rgb_matrix.md) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard.
+Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB Underglow](feature_rgblight.md) and [RGB Matrix](feature_rgb_matrix.md) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard.
QMK is able to control the brightness of these LEDs by switching them on and off rapidly in a certain ratio, a technique known as *Pulse Width Modulation*, or PWM. By altering the duty cycle of the PWM signal, it creates the illusion of dimming.
-The MCU can only supply so much current to its GPIO pins. Instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs.
+## Usage :id=usage
-Most keyboards have backlighting enabled by default if they support it, but if it is not working for you, check that your `rules.mk` includes the following:
+Most keyboards have backlighting enabled by default if they support it, but if it is not working for you (or you have added support), check that your `rules.mk` includes the following:
```make
BACKLIGHT_ENABLE = yes
@@ -14,57 +14,23 @@ BACKLIGHT_ENABLE = yes
## Keycodes :id=keycodes
-Once enabled, the following keycodes below can be used to change the backlight level.
+|Key |Aliases |Description |
+|-------------------------------|---------|-----------------------------------|
+|`QK_BACKLIGHT_TOGGLE` |`BL_TOGG`|Turn the backlight on or off |
+|`QK_BACKLIGHT_STEP` |`BL_STEP`|Cycle through backlight levels |
+|`QK_BACKLIGHT_ON` |`BL_ON` |Set the backlight to max brightness|
+|`QK_BACKLIGHT_OFF` |`BL_OFF` |Turn the backlight off |
+|`QK_BACKLIGHT_UP` |`BL_UP` |Increase the backlight level |
+|`QK_BACKLIGHT_DOWN` |`BL_DOWN`|Decrease the backlight level |
+|`QK_BACKLIGHT_TOGGLE_BREATHING`|`BL_BRTG`|Toggle backlight breathing |
-| Key | Aliases | Description |
-|---------------------------------|-----------|-------------------------------------|
-| `QK_BACKLIGHT_TOGGLE` | `BL_TOGG` | Turn the backlight on or off |
-| `QK_BACKLIGHT_STEP` | `BL_STEP` | Cycle through backlight levels |
-| `QK_BACKLIGHT_ON` | `BL_ON` | Set the backlight to max brightness |
-| `QK_BACKLIGHT_OFF` | `BL_OFF` | Turn the backlight off |
-| `QK_BACKLIGHT_UP` | `BL_UP` | Increase the backlight level |
-| `QK_BACKLIGHT_DOWN` | `BL_DOWN` | Decrease the backlight level |
-| `QK_BACKLIGHT_TOGGLE_BREATHING` | `BL_BRTG` | Toggle backlight breathing |
+## Basic Configuration :id=basic-configuration
-## Functions :id=functions
-
-These functions can be used to change the backlighting in custom code:
-
-|Function |Description |
-|------------------------|--------------------------------------------|
-|`backlight_toggle()` |Turn the backlight on or off |
-|`backlight_enable()` |Turn the backlight on |
-|`backlight_disable()` |Turn the backlight off |
-|`backlight_step()` |Cycle through backlight levels |
-|`backlight_increase()` |Increase the backlight level |
-|`backlight_decrease()` |Decrease the backlight level |
-|`backlight_level(x)` |Sets the backlight level to specified level |
-|`get_backlight_level()` |Return the current backlight level |
-|`is_backlight_enabled()`|Return whether the backlight is currently on|
-
-If backlight breathing is enabled (see below), the following functions are also available:
-
-|Function |Description |
-|---------------------|--------------------------------------|
-|`breathing_toggle()` |Turn the backlight breathing on or off|
-|`breathing_enable()` |Turns on backlight breathing |
-|`breathing_disable()`|Turns off backlight breathing |
-
-## Configuration :id=configuration
-
-To select which driver to use, configure your `rules.mk` with the following:
-
-```make
-BACKLIGHT_DRIVER = software
-```
-
-Valid driver values are `pwm`, `software`, `custom` or `no`. See below for help on individual drivers.
-
-To configure the backlighting, `#define` these in your `config.h`:
+Add the following to your `config.h`:
|Define |Default |Description |
|-----------------------------|------------------|-----------------------------------------------------------------------------------------------------------------|
-|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LED(s) |
+|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LEDs |
|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) |
|`BACKLIGHT_CAPS_LOCK` |*Not defined* |Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) |
|`BACKLIGHT_BREATHING` |*Not defined* |Enable backlight breathing, if supported |
@@ -76,24 +42,88 @@ To configure the backlighting, `#define` these in your `config.h`:
Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`.
-### Backlight On State :id=backlight-on-state
+### "On" State :id=on-state
Most backlight circuits are driven by an N-channel MOSFET or NPN transistor. This means that to turn the transistor *on* and light the LEDs, you must drive the backlight pin, connected to the gate or base, *high*.
Sometimes, however, a P-channel MOSFET, or a PNP transistor is used. In this case, when the transistor is on, the pin is driven *low* instead.
-This functionality is configured at the keyboard level with the `BACKLIGHT_ON_STATE` define.
+To configure the "on" state of the backlight circuit, add the following to your `config.h`:
+
+```c
+#define BACKLIGHT_ON_STATE 0
+```
+
+### Multiple Backlight Pins :id=multiple-backlight-pins
+
+Most keyboards have only one backlight pin which controls all backlight LEDs (especially if the backlight is connected to a hardware PWM pin).
+The `timer` and `software` drivers allow you to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle.
+
+This feature allows to set, for instance, the Caps Lock LED's (or any other controllable LED) brightness at the same level as the other LEDs of the backlight. This is useful if you have mapped Control in place of Caps Lock and you need the Caps Lock LED to be part of the backlight instead of being activated when Caps Lock is on, as it is usually wired to a separate pin from the backlight.
+
+To configure multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`:
+
+```c
+#define BACKLIGHT_PINS { F5, B2 }
+```
+
+## Driver Configuration :id=driver-configuration
+
+Backlight driver selection is configured in `rules.mk`. Valid drivers are `pwm` (default), `timer`, `software`, or `custom`. See below for information on individual drivers.
-### AVR Driver :id=avr-driver
+### PWM Driver :id=pwm-driver
-The `pwm` driver is configured by default, however the equivalent setting within `rules.mk` would be:
+This is the default backlight driver, which leverages the hardware PWM output capability of the microcontroller.
```make
BACKLIGHT_DRIVER = pwm
```
-#### Caveats :id=avr-caveats
+### Timer Driver :id=timer-driver
+
+This driver is similar to the PWM driver, but instead of directly configuring the pin to output a PWM signal, an interrupt handler is attached to the timer to turn the pin on and off as appropriate.
+
+```make
+BACKLIGHT_DRIVER = timer
+```
+
+### Software Driver :id=software-driver
+
+In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. However, breathing is not supported, and the backlight can flicker when the keyboard is busy.
+
+```make
+BACKLIGHT_DRIVER = software
+```
+
+### Custom Driver :id=custom-driver
+
+If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using a simple API.
+
+```make
+BACKLIGHT_DRIVER = custom
+```
+
+```c
+void backlight_init_ports(void) {
+ // Optional - runs on startup
+ // Usually you want to configure pins here
+}
+void backlight_set(uint8_t level) {
+ // Optional - runs on level change
+ // Usually you want to respond to the new value
+}
+
+void backlight_task(void) {
+ // Optional - runs periodically
+ // Note that this is called in the main keyboard loop,
+ // so long running actions here can cause performance issues
+}
+```
+
+## AVR Configuration :id=avr-configuration
+
+### PWM Driver :id=avr-pwm-driver
-On AVR boards, QMK automatically decides which driver to use according to the following table:
+The following table describes the supported pins for the PWM driver. Only cells marked with a timer number are capable of hardware PWM output; any others must use the `timer` driver.
|Backlight Pin|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P|
|-------------|-------------|----------|-------------|-------------|---------|-----------|
@@ -108,113 +138,169 @@ On AVR boards, QMK automatically decides which driver to use according to the fo
|`D4` | | | | |Timer 1 | |
|`D5` | | | | |Timer 1 | |
-All other pins will use timer-assisted software PWM:
+### Timer Driver :id=avr-timer-driver
-|Audio Pin|Audio Timer|Software PWM Timer|
-|---------|-----------|------------------|
-|`C4` |Timer 3 |Timer 1 |
-|`C5` |Timer 3 |Timer 1 |
-|`C6` |Timer 3 |Timer 1 |
-|`B5` |Timer 1 |Timer 3 |
-|`B6` |Timer 1 |Timer 3 |
-|`B7` |Timer 1 |Timer 3 |
+Any GPIO pin can be used with this driver. The following table describes the supported timers:
-When both timers are in use for Audio, the backlight PWM cannot use a hardware timer, and will instead be triggered during the matrix scan. In this case, breathing is not supported, and the backlight might flicker, because the PWM computation may not be called with enough timing precision.
+|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P|
+|-------------|----------|-------------|-------------|---------|-----------|
+|Timers 1 & 3 |Timer 1 |Timers 1 & 3 |Timer 1 |Timer 1 |Timer 1 |
-#### Hardware PWM Implementation :id=hardware-pwm-implementation
+The following `#define`s apply only to the `timer` driver:
-When using the supported pins for backlighting, QMK will use a hardware timer configured to output a PWM signal. This timer will count up to `ICRx` (by default `0xFFFF`) before resetting to 0.
-The desired brightness is calculated and stored in the `OCRxx` register. When the counter reaches this value, the backlight pin will go low, and is pulled high again when the counter resets.
-In this way `OCRxx` essentially controls the duty cycle of the LEDs, and thus the brightness, where `0x0000` is completely off and `0xFFFF` is completely on.
+|Define |Default|Description |
+|-----------------------|-------|----------------|
+|`BACKLIGHT_PWM_TIMER` |`1` |The timer to use|
-The breathing effect is achieved by registering an interrupt handler for `TIMER1_OVF_vect` that is called whenever the counter resets, roughly 244 times per second.
-In this handler, the value of an incrementing counter is mapped onto a precomputed brightness curve. To turn off breathing, the interrupt handler is simply disabled, and the brightness reset to the level stored in EEPROM.
+Note that the choice of timer may conflict with the [Audio](feature_audio.md) feature.
-#### Timer Assisted PWM Implementation :id=timer-assisted-implementation
+## ChibiOS/ARM Configuration :id=arm-configuration
-When `BACKLIGHT_PIN` is not set to a hardware backlight pin, QMK will use a hardware timer configured to trigger software interrupts. This time will count up to `ICRx` (by default `0xFFFF`) before resetting to 0.
-When resetting to 0, the CPU will fire an OVF (overflow) interrupt that will turn the LEDs on, starting the duty cycle.
-The desired brightness is calculated and stored in the `OCRxx` register. When the counter reaches this value, the CPU will fire a Compare Output match interrupt, which will turn the LEDs off.
-In this way `OCRxx` essentially controls the duty cycle of the LEDs, and thus the brightness, where `0x0000` is completely off and `0xFFFF` is completely on.
+### PWM Driver :id=arm-pwm-driver
-The breathing effect is the same as in the hardware PWM implementation.
+Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like:
-### ARM Driver :id=arm-configuration
+`halconf.h`:
+```c
+#define HAL_USE_PWM TRUE
+```
+`mcuconf.h`:
+```c
+#undef STM32_PWM_USE_TIM4
+#define STM32_PWM_USE_TIM4 TRUE
+```
-While still in its early stages, ARM backlight support aims to eventually have feature parity with AVR. The `pwm` driver is configured by default, however the equivalent setting within `rules.mk` would be:
+The following `#define`s apply only to the `pwm` driver:
-```make
-BACKLIGHT_DRIVER = pwm
+|Define |Default |Description |
+|-----------------------|--------|-----------------------------------|
+|`BACKLIGHT_PWM_DRIVER` |`PWMD4` |The PWM driver to use |
+|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use |
+|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use|
+
+Refer to the ST datasheet for your particular MCU to determine these values. For example, these defaults are set up for pin `B8` on a Proton-C (STM32F303) using `TIM4_CH3` on AF2. Unless you are designing your own keyboard, you generally should not need to change them.
+
+### Timer Driver :id=arm-timer-driver
+
+Depending on the ChibiOS board configuration, you may need to enable general-purpose timers at the keyboard level. For STM32, this would look like:
+
+`halconf.h`:
+```c
+#define HAL_USE_GPT TRUE
+```
+`mcuconf.h`:
+```c
+#undef STM32_GPT_USE_TIM15
+#define STM32_GPT_USE_TIM15 TRUE
```
-#### ChibiOS Configuration :id=arm-configuration
+The following `#define`s apply only to the `timer` driver:
-The following `#define`s apply only to ARM-based keyboards:
+|Define |Default |Description |
+|----------------------|--------|----------------|
+|`BACKLIGHT_GPT_DRIVER`|`GPTD15`|The timer to use|
-|Define |Default|Description |
-|-----------------------|-------|-----------------------------------|
-|`BACKLIGHT_PWM_DRIVER` |`PWMD4`|The PWM driver to use |
-|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use |
-|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use|
+## Example Schematic
-See the ST datasheet for your particular MCU to determine these values. Unless you are designing your own keyboard, you generally should not need to change them.
+Since the MCU can only supply so much current to its GPIO pins, instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs.
-#### Caveats :id=arm-caveats
+In this typical example, the backlight LEDs are all connected in parallel towards an N-channel MOSFET. Its gate pin is wired to one of the microcontroller's GPIO pins through a 470Ω resistor to avoid ringing.
+A pulldown resistor is also placed between the gate pin and ground to keep it at a defined state when it is not otherwise being driven by the MCU.
+The values of these resistors are not critical - see [this Electronics StackExchange question](https://electronics.stackexchange.com/q/68748) for more information.
-Currently only hardware PWM is supported, not timer assisted, and does not provide automatic configuration.
+![Backlight example circuit](https://i.imgur.com/BmAvoUC.png)
-### Software PWM Driver :id=software-pwm-driver
+## API :id=api
-In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. The tradeoff is the backlight might jitter when the keyboard is busy. To enable, add this to your `rules.mk`:
+### `void backlight_toggle(void)` :id=api-backlight-toggle
-```make
-BACKLIGHT_DRIVER = software
-```
+Toggle the backlight on or off.
-#### Multiple Backlight Pins :id=multiple-backlight-pins
+---
-Most keyboards have only one backlight pin which controls all backlight LEDs (especially if the backlight is connected to a hardware PWM pin).
-In software PWM, it is possible to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle.
+### `void backlight_enable(void)` :id=api-backlight-enable
-This feature allows to set, for instance, the Caps Lock LED's (or any other controllable LED) brightness at the same level as the other LEDs of the backlight. This is useful if you have mapped Control in place of Caps Lock and you need the Caps Lock LED to be part of the backlight instead of being activated when Caps Lock is on, as it is usually wired to a separate pin from the backlight.
+Turn the backlight on.
-To activate multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`:
+---
-```c
-#define BACKLIGHT_PINS { F5, B2 }
-```
+### `void backlight_disable(void)` :id=api-backlight-disable
-### Custom Driver :id=custom-driver
+Turn the backlight off.
-If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using this simple API provided by QMK. To enable, add this to your `rules.mk`:
+---
-```make
-BACKLIGHT_DRIVER = custom
-```
+### `void backlight_step(void)` :id=api-backlight-step
-Then implement any of these hooks:
+Cycle through backlight levels.
-```c
-void backlight_init_ports(void) {
- // Optional - runs on startup
- // Usually you want to configure pins here
-}
-void backlight_set(uint8_t level) {
- // Optional - runs on level change
- // Usually you want to respond to the new value
-}
+---
-void backlight_task(void) {
- // Optional - runs periodically
- // Note that this is called in the main keyboard loop,
- // so long running actions here can cause performance issues
-}
-```
+### `void backlight_increase(void)` :id=api-backlight-increase
-## Example Schematic
+Increase the backlight level.
-In this typical example, the backlight LEDs are all connected in parallel towards an N-channel MOSFET. Its gate pin is wired to one of the microcontroller's GPIO pins through a 470Ω resistor to avoid ringing.
-A pulldown resistor is also placed between the gate pin and ground to keep it at a defined state when it is not otherwise being driven by the MCU.
-The values of these resistors are not critical - see [this Electronics StackExchange question](https://electronics.stackexchange.com/q/68748) for more information.
+---
-![Backlight example circuit](https://i.imgur.com/BmAvoUC.png)
+### `void backlight_decrease(void)` :id=api-backlight-decrease
+
+Decrease the backlight level.
+
+---
+
+### `void backlight_level(uint8_t level)` :id=api-backlight-level
+
+Set the backlight level.
+
+#### Arguments :id=api-backlight-level-arguments
+
+ - `uint8_t level`
+ The level to set, from 0 to `BACKLIGHT_LEVELS`.
+
+---
+
+### `uint8_t get_backlight_level(void)` :id=api-get-backlight-level
+
+Get the current backlight level.
+
+#### Return Value :id=api-get-backlight-level-return
+
+The current backlight level, from 0 to `BACKLIGHT_LEVELS`.
+
+---
+
+### `bool is_backlight_enabled(void)` :id=api-is-backlight-enabled
+
+Get the current backlight state.
+
+#### Return Value :id=api-is-backlight-enabled-return
+
+`true` if the backlight is enabled.
+
+---
+
+### `void backlight_toggle_breathing(void)` :id=api-backlight-toggle-breathing
+
+Toggle backlight breathing on or off.
+
+---
+
+### `void backlight_enable_breathing(void)` :id=api-backlight-enable-breathing
+
+Turn backlight breathing on.
+
+---
+
+### `void backlight_disable_breathing(void)` :id=api-backlight-disable-breathing
+
+Turn backlight breathing off.
+
+---
+
+### `bool is_backlight_breathing(void)` :id=api-is-backlight-breathing
+
+Get the current backlight breathing state.
+
+#### Return Value :id=api-is-backlight-breathing-return
+
+`true` if backlight breathing is enabled.