ws2812.md (14472B)
1 # WS2812 Driver {#ws2812-driver} 2 3 This driver provides support for WorldSemi addressable RGB(W) LEDs, and compatible equivalents: 4 5 * WS2811, WS2812, WS2812B, WS2812C, etc. 6 * SK6812, SK6812MINI, SK6805 7 8 These LEDs are often called "addressable" because instead of using a wire per color (and per LED), each LED contains a small microchip that understands a special protocol sent over a single wire. 9 The LEDs can be chained together, and the remaining data is passed on to the next. In this way, you can easily control the color of many LEDs using a single GPIO. 10 11 ## Usage {#usage} 12 13 In most cases, the WS2812 driver code is automatically included if you are using either the [RGBLight](../features/rgblight) or [RGB Matrix](../features/rgb_matrix) feature with the `ws2812` driver set, and you would use those APIs instead. 14 15 However, if you need to use the driver standalone, add the following to your `rules.mk`: 16 17 ```make 18 WS2812_DRIVER_REQUIRED = yes 19 ``` 20 21 You can then call the WS2812 API by including `ws2812.h` in your code. 22 23 ## Basic Configuration {#basic-configuration} 24 25 Add the following to your `config.h`: 26 27 |Define |Default |Description | 28 |-------------------|-----------------------|------------------------------------------------------------------------------------------------| 29 |`WS2812_DI_PIN` |*Not defined* |The GPIO pin connected to the DI pin of the first LED in the chain | 30 |`WS2812_LED_COUNT` |*Not defined* |Number of LEDs in the WS2812 chain - automatically set when RGBLight or RGB Matrix is configured| 31 |`WS2812_TIMING` |`1250` |The total length of a bit (TH+TL) in nanoseconds | 32 |`WS2812_T1H` |`900` |The length of a "1" bit's high phase in nanoseconds | 33 |`WS2812_T0H` |`350` |The length of a "0" bit's high phase in nanoseconds | 34 |`WS2812_TRST_US` |`280` |The length of the reset phase in microseconds | 35 |`WS2812_BYTE_ORDER`|`WS2812_BYTE_ORDER_GRB`|The byte order of the RGB data | 36 |`WS2812_RGBW` |*Not defined* |Enables RGBW support (except `i2c` driver) | 37 38 ### Timing Adjustment {#timing-adjustment} 39 40 The WS2812 LED communication protocol works by encoding a "1" bit with a long high pulse (T<sub>1</sub>H), and a "0" bit with a shorter pulse (T<sub>0</sub>H). The total cycle length of a bit is the same. 41 The "reset" pulse (T<sub>RST</sub>) latches the sent RGB data to all of the LEDs and denotes a completed "frame". 42 43 Some WS2812 variants have slightly different timing parameter requirements, which can be accounted for if necessary using the above `#define`s in your `config.h`. 44 45 ### Byte Order {#byte-order} 46 47 Some WS2812 variants may have their color components in a different physical or logical order. For example, the WS2812B-2020 has physically swapped red and green LEDs, which causes the wrong color to be displayed, because the default order of the bytes sent over the wire is defined as GRB. 48 If you find your LED colors are consistently swapped, you may need to change the byte order by adding the following to your `config.h`: 49 50 ```c 51 #define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB 52 ``` 53 54 Where the byte order may be one of: 55 56 |Byte Order|Known Devices | 57 |----------|----------------------------| 58 |`GRB` |Most WS2812s, SK6812, SK6805| 59 |`RGB` |WS2812B-2020 | 60 |`BGR` |TM1812 | 61 62 ### RGBW Support {#rgbw-support} 63 64 Rendering the color white with RGB LEDs is typically inconsistent due to inherent variations between each individual LED die. However, some WS2812 variants (such as SK6812RGBW) also possess a white LED along with the red, green, and blue channels, which allows for a more accurate white to be displayed. 65 66 QMK can automatically convert the RGB data to be sent to the LEDs to mix in the white channel: 67 68 ``` 69 w = min(r, g, b) 70 r -= w 71 g -= w 72 b -= w 73 ``` 74 75 Thus, an RGB triplet of `255,255,255` will simply turn on the white LED fully (`0,0,0,255`). 76 77 To enable RGBW conversion, add the following to your `config.h`: 78 79 ```c 80 #define WS2812_RGBW 81 ``` 82 83 ## Driver Configuration {#driver-configuration} 84 85 Driver selection can be configured in `rules.mk` as `WS2812_DRIVER`, or in `info.json` as `ws2812.driver`. Valid values are `bitbang` (default), `i2c`, `spi`, `pwm`, `vendor`, or `custom`. See below for information on individual drivers. 86 87 ### Bitbang Driver {#bitbang-driver} 88 89 This is the default WS2812 driver. It operates by "bit-banging" ie. directly toggling the GPIO. 90 91 Please note that on AVR devices, due to the tight timing requirements longer chains and/or heavy CPU loads may cause visible lag. Unfortunately this driver is usually the only option for AVR. 92 93 ```make 94 WS2812_DRIVER = bitbang 95 ``` 96 97 ### I2C Driver {#i2c-driver} 98 99 A specialized driver mainly used for PS2AVRGB (Bootmapper Client) boards, which possess an ATtiny85 that handles the WS2812 LEDs. 100 101 ```make 102 WS2812_DRIVER = i2c 103 ``` 104 105 The following `#define`s apply only to the `i2c` driver: 106 107 |Define |Default|Description | 108 |--------------------|-------|---------------------------------| 109 |`WS2812_I2C_ADDRESS`|`0xB0` |The I2C address of the ATtiny85. | 110 |`WS2812_I2C_TIMEOUT`|`100` |The I2C timeout, in milliseconds.| 111 112 ### PIO Driver {#pio-driver} 113 114 This driver is RP2040-only, and leverages the onboard PIO (programmable I/O) system and DMA to offload processing from the CPU. 115 116 The WS2812 PIO program uses one state machine, six instructions and one DMA interrupt handler callback. Due to the implementation the time resolution for this driver is 50 ns - any value not specified in this interval will be rounded to the next matching interval. 117 118 ```make 119 WS2812_DRIVER = vendor 120 ``` 121 122 ### PWM Driver {#pwm-driver} 123 124 This driver is ARM-only, and leverages the onboard PWM peripheral and DMA to offload processing from the CPU. 125 126 ```make 127 WS2812_DRIVER = pwm 128 ``` 129 130 ### SPI Driver {#spi-driver} 131 132 This driver is ARM-only, and leverages the onboard SPI peripheral and DMA to offload processing from the CPU. The DI pin **must** be connected to the MOSI pin on the MCU, and all other SPI pins **must** be left unused. This is also very dependent on your MCU's SPI peripheral clock speed, and may or may not be possible depending on the MCU selected. 133 134 ```make 135 WS2812_DRIVER = spi 136 ``` 137 138 ## ChibiOS/ARM Configuration {#arm-configuration} 139 140 The following defines apply only to ARM devices: 141 142 |Define |Default |Description | 143 |------------|------------------------------|---------------------------------------------------------------------------------| 144 |`WS2812_T1L`|`(WS2812_TIMING - WS2812_T1H)`|The length of a "1" bit's low phase in nanoseconds (bitbang and PIO drivers only)| 145 |`WS2812_T0L`|`(WS2812_TIMING - WS2812_T0H)`|The length of a "0" bit's low phase in nanoseconds (bitbang and PIO drivers only)| 146 147 ### Logic Levels {#logic-levels} 148 149 WS2812 LEDs usually operate at 5V, but some microcontrollers, particularly ARM-based ones, run on 3.3V. This can pose an issue when driving the LED chain as the logic level voltage is lower than the power supply voltage, leading to unreliable data transmission. There are two main workarounds: 150 151 #### 1. Open Drain Circuit {#open-drain-circuit} 152 153 By default, `WS2812_DI_PIN` is configured as a *push-pull* output, meaning the pin is effectively always driven either to VCC or to ground; however, it can be configured in *open drain* mode instead. 154 155 In this mode, the MCU will only pull the GPIO *low*, and leaves it floating otherwise. A pullup resistor (typically around 10kΩ) between DI and 5V is then responsible for pulling the line high when the MCU is not driving the GPIO. 156 157 To use the DI pin in open drain configuration, add the following to your `config.h`: 158 159 ```c 160 #define WS2812_EXTERNAL_PULLUP 161 ``` 162 163 ::: warning 164 Because the GPIO is being pulled to 5V in this situation rather than VCC (3.3V), **it must be a 5V tolerant pin**. Consult your MCU's datasheet first – if there are no eligible pins, you must use a level shifter instead. 165 ::: 166 167 #### 2. Level Shifter {#level-shifter} 168 169 A level shifter IC, such as the SN74LV1T34, can be placed between the GPIO and the first LED's DI pin to convert the 3.3V logic to 5V. This requires no additional configuration in the firmware, nor a 5V tolerant GPIO, but may be more expensive and is generally less handwire-friendly. 170 171 ### SPI Driver {#arm-spi-driver} 172 173 Depending on the ChibiOS board configuration, you may need to enable SPI at the keyboard level. For STM32, this would look like: 174 175 ::: code-group 176 ```c [halconf.h] 177 #pragma once 178 179 #define HAL_USE_SPI TRUE // [!code focus] 180 181 #include_next <halconf.h> 182 ``` 183 ```c [mcuconf.h] 184 #pragma once 185 186 #include_next <mcuconf.h> 187 188 #undef STM32_SPI_USE_SPI1 // [!code focus] 189 #define STM32_SPI_USE_SPI1 TRUE // [!code focus] 190 ``` 191 ::: 192 193 The following `define`s apply only to the `spi` driver: 194 195 |Define |Default |Description | 196 |--------------------------------|-------------|-------------------------------------------------------------------------------| 197 |`WS2812_SPI_DRIVER` |`SPID1` |The SPI driver to use | 198 |`WS2812_SPI_MOSI_PAL_MODE` |`5` |The MOSI pin alternative function to use | 199 |`WS2812_SPI_SCK_PIN` |*Not defined*|The SCK pin - required for F072 and possibly others | 200 |`WS2812_SPI_SCK_PAL_MODE` |`5` |The SCK pin alternative function to use - required for F072 and possibly others| 201 |`WS2812_SPI_DIVISOR` |`16` |The divisor used to adjust the baudrate | 202 |`WS2812_SPI_USE_CIRCULAR_BUFFER`|*Not defined*|Enable a circular buffer for improved rendering | 203 204 #### Setting the Baudrate {#arm-spi-baudrate} 205 206 To adjust the SPI baudrate, you will need to derive the target baudrate from the clock tree provided by STM32CubeMX, and add the following to your `config.h`: 207 208 ```c 209 #define WS2812_SPI_DIVISOR 16 210 ``` 211 212 Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported on STM32 devices. Other MCUs may have similar constraints -- check the reference manual for your respective MCU for specifics. 213 214 #### Circular Buffer {#arm-spi-circular-buffer} 215 216 A circular buffer can be enabled if you experience flickering. 217 218 To enable the circular buffer, add the following to your `config.h`: 219 220 ```c 221 #define WS2812_SPI_USE_CIRCULAR_BUFFER 222 ``` 223 224 ### PIO Driver {#arm-pio-driver} 225 226 The following `#define`s apply only to the PIO driver: 227 228 |Define |Default |Description | 229 |---------------------|-------------|---------------------------------------| 230 |`WS2812_PIO_USE_PIO1`|*Not defined*|Use the PIO1 peripheral instead of PIO0| 231 232 ### PWM Driver {#arm-pwm-driver} 233 234 Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like: 235 236 ::: code-group 237 ```c [halconf.h] 238 #pragma once 239 240 #define HAL_USE_PWM TRUE // [!code focus] 241 242 #include_next <halconf.h> 243 ``` 244 ```c [mcuconf.h] 245 #pragma once 246 247 #include_next <mcuconf.h> 248 249 #undef STM32_PWM_USE_TIM2 // [!code focus] 250 #define STM32_PWM_USE_TIM2 TRUE // [!code focus] 251 ``` 252 ::: 253 254 The following `#define`s apply only to the `pwm` driver: 255 256 |Define |Default |Description | 257 |---------------------------------|--------------------|------------------------------------------------------------------------------------------| 258 |`WS2812_PWM_DRIVER` |`PWMD2` |The PWM driver to use | 259 |`WS2812_PWM_CHANNEL` |`2` |The PWM channel to use | 260 |`WS2812_PWM_PAL_MODE` |`2` |The pin alternative function to use | 261 |`WS2812_PWM_DMA_STREAM` |`STM32_DMA1_STREAM2`|The DMA Stream for `TIMx_UP` | 262 |`WS2812_PWM_DMA_CHANNEL` |`2` |The DMA Channel for `TIMx_UP` | 263 |`WS2812_PWM_DMAMUX_ID` |*Not defined* |The DMAMUX configuration for `TIMx_UP` - only required if your MCU has a DMAMUX peripheral| 264 |`WS2812_PWM_COMPLEMENTARY_OUTPUT`|*Not defined* |Whether the PWM output is complementary (`TIMx_CHyN`) | 265 266 ::: tip 267 Using a complementary timer output (`TIMx_CHyN`) is possible only for advanced-control timers (1, 8 and 20 on STM32). Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations. 268 ::: 269 270 ## API {#api} 271 272 ### `void ws2812_init(void)` {#api-ws2812-init} 273 274 Initialize the LED driver. This function should be called first. 275 276 --- 277 278 ### `void ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue)` {#api-ws2812-set-color} 279 280 Set the color of a single LED. This function does not immediately update the LEDs; call `ws2812_flush()` after you are finished. 281 282 #### Arguments {#api-ws2812-set-color-arguments} 283 284 - `int index` 285 The LED index in the WS2812 chain. 286 - `uint8_t red` 287 The red value to set. 288 - `uint8_t green` 289 The green value to set. 290 - `uint8_t blue` 291 The blue value to set. 292 293 --- 294 295 ### `void ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue)` {#api-ws2812-set-color-all} 296 297 Set the color of all LEDs. 298 299 #### Arguments {#api-ws2812-set-color-all-arguments} 300 301 - `uint8_t red` 302 The red value to set. 303 - `uint8_t green` 304 The green value to set. 305 - `uint8_t blue` 306 The blue value to set. 307 308 --- 309 310 ### `void ws2812_flush(void)` {#api-ws2812-flush} 311 312 Flush the PWM values to the LED chain.