qmk-firmware/docs/ws2812_driver.md
Jordan Banasik 3d00620711
Add ifndef to WS2812 timing constraints (#14678)
* Add ifndef to WS2812 timing constraints

Due to the way that the PrimeKB Meridian PCB was designed, this change
is needed in order to properly adjust the LEDs.

Testing:
* Compiled primekb/meridian:default successfully
* Compiled random board (walletburner/neuron:default) successfully

* Fix linting errors

Missed some spacing

* More linting fixes

Spacing on the comments... really?

* Rename WS2812 timing parameters for clarity; add comments

* Add docs update for the WS2812 timing macros

* Fix typo on comment

* Add ifndef for WS2812_RES

* Update double backticks and table with parameters

* Move timing adjustments documentation to ws2812_drivers

* Move timings adjustment discussion to bitbang section

* Update T0H and T1H definitions in subtractions

* format

Co-authored-by: Gondolindrim <alvaro.volpato@usp.br>
Co-authored-by: zvecr <git@zvecr.com>
2021-11-25 16:55:46 +00:00

9.7 KiB

WS2812 Driver

This driver powers the RGB Lighting and RGB Matrix features.

Currently QMK supports the following addressable LEDs (however, the white LED in RGBW variants is not supported):

WS2811, WS2812, WS2812B, WS2812C, etc.
SK6812, SK6812MINI, SK6805

These LEDs are called "addressable" because instead of using a wire per color, each LED contains a small microchip that understands a special protocol sent over a single wire. The chip passes on the remaining data to the next LED, allowing them to be chained together. In this way, you can easily control the color of the individual LEDs.

Supported Driver Types

AVR ARM
bit bang ✔️ ✔️
I2C ✔️
SPI ✔️
PWM ✔️

Driver configuration

All drivers

Different versions of the addressable LEDs have differing requirements for the TRST period between frames. The default setting is 280 µs, which should work for most cases, but this can be overridden in your config.h. e.g.:

#define WS2812_TRST_US 80

Byte Order

Some variants of the WS2812 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. In this case, you can change the byte order by defining WS2812_BYTE_ORDER as one of the following values:

Byte order Known devices
WS2812_BYTE_ORDER_GRB (default) Most WS2812's, SK6812, SK6805
WS2812_BYTE_ORDER_RGB WS2812B-2020
WS2812_BYTE_ORDER_BGR TM1812

Bitbang

Default driver, the absence of configuration assumes this driver. To configure it, add this to your rules.mk:

WS2812_DRIVER = bitbang

!> This driver is not hardware accelerated and may not be performant on heavily loaded systems.

I2C

Targeting boards where WS2812 support is offloaded to a 2nd MCU. Currently the driver is limited to AVR given the known consumers are ps2avrGB/BMC. To configure it, add this to your rules.mk:

WS2812_DRIVER = i2c

Configure the hardware via your config.h:

#define WS2812_ADDRESS 0xb0 // default: 0xb0
#define WS2812_TIMEOUT 100 // default: 100
Adjusting bit timings (ChibiOS only)

The WS2812 LED communication topology depends on a serialized timed window, lasting typically 1250ns in total, where a bit is interpreted as either 0 or 1 depending on for how much time the RGB_DI pin voltage is kept high and how much time it is kept low. The WS2812 datasheet specifies quantities defined as T0H, T0L, T1H, T1L (respectively, typically 350ns, 900ns, 900ns and 350ns); a bit is interpreted as zero if the voltage on the control pin is held high for T0H and then T0L, and the bit is interpreted as one if the voltage is held high for T1H and then low for T1L. Additionally, there is also a RESET time parameter whereby an LED color is reset if the voltage control pin is kept low for more than that parameter; in WS2812 that amount is 6000 nanoseconds.

The WS2812 "bit-banged" ChibiOS driver does just that, in a simple way. It defines these values and governs the RGB_DI pin according to these times. There are, however, other LED parts that work in a communication topology similar to this but with different timing parameters; such is the case, for instance, of the SK6812. In order to better support such LEDs whilst keeping the WS2812 driver applicable, QMK allows you to tune these parameters through the definition macros:

Macro Default
`WS2812_TIMING 1250
WS2812_T0H 350
WS2812_T0L WS2812_TIMING - WS2812_T0H
WS2812_T1H 900
WS2812_T1L WS2812_TIMING - WS2812_T1L
WS2812_RES 6000

It must be noted, however, that this tuning is only available for the ARM family of microprocessors (since it's ChibiOS-dependent) and not available for PWM and SPI drivers -- so if you intend to use this be aware it will only work with the "bit-banged" driver which is knowingly slower and can possibly throttle the microcontroller if too many LEDs are used.

SPI

Targeting STM32 boards where WS2812 support is offloaded to an SPI hardware device. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. RGB_DI_PIN for this driver is the configured SPI MOSI pin. Due to the nature of repurposing SPI to drive the LEDs, the other SPI pins, MISO and SCK, must remain unused. To configure it, add this to your rules.mk:

WS2812_DRIVER = spi

Configure the hardware via your config.h:

#define WS2812_SPI SPID1 // default: SPID1
#define WS2812_SPI_MOSI_PAL_MODE 5 // MOSI pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 5
#define WS2812_SPI_SCK_PIN B3 // Required for F072, may be for others -- SCK pin, see the respective datasheet for the appropriate values for your MCU. default: unspecified
#define WS2812_SPI_SCK_PAL_MODE 5 // SCK pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 5

You must also turn on the SPI feature in your halconf.h and mcuconf.h

Circular Buffer Mode

Some boards may flicker while in the normal buffer mode. To fix this issue, circular buffer mode may be used to rectify the issue.

By default, the circular buffer mode is disabled.

To enable this alternative buffer mode, place this into your config.h file:

#define WS2812_SPI_USE_CIRCULAR_BUFFER

Setting baudrate with divisor

To adjust the baudrate at which the SPI peripheral is configured, users will need to derive the target baudrate from the clock tree provided by STM32CubeMX.

Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported by hardware.

Define Default Description
WS2812_SPI_DIVISOR 16 SPI source clock peripheral divisor

Testing Notes

While not an exhaustive list, the following table provides the scenarios that have been partially validated:

SPI1 SPI2 SPI3
f072 ? B15 ✔️ (needs SCK: B13) N/A
f103 A7 ✔️ B15 ✔️ N/A
f303 A7 ✔️ B5 ✔️ B15 ✔️ B5 ✔️

Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.

PWM

Targeting STM32 boards where WS2812 support is offloaded to an PWM timer and DMA stream. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. To configure it, add this to your rules.mk:

WS2812_DRIVER = pwm

Configure the hardware via your config.h:

#define WS2812_PWM_DRIVER PWMD2  // default: PWMD2
#define WS2812_PWM_CHANNEL 2  // default: 2
#define WS2812_PWM_PAL_MODE 2  // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 2
//#define WS2812_PWM_COMPLEMENTARY_OUTPUT // Define for a complementary timer output (TIMx_CHyN); omit for a normal timer output (TIMx_CHy).
#define WS2812_DMA_STREAM STM32_DMA1_STREAM2  // DMA Stream for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#define WS2812_DMA_CHANNEL 2  // DMA Channel for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM2_UP // DMAMUX configuration for TIMx_UP -- only required if your MCU has a DMAMUX peripheral, see the respective reference manual for the appropriate values for your MCU.

Note that using a complementary timer output (TIMx_CHyN) is possible only for advanced-control timers (TIM1, TIM8, TIM20 on STM32), and the STM32_PWM_USE_ADVANCED option in mcuconf.h must be set to TRUE. Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations.

You must also turn on the PWM feature in your halconf.h and mcuconf.h

Testing Notes

While not an exhaustive list, the following table provides the scenarios that have been partially validated:

Status
f072 ?
f103 ✔️
f303 ✔️
f401/f411 ✔️

Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.

Push Pull and Open Drain Configuration

The default configuration is a push pull on the defined pin. This can be configured for bitbang, PWM and SPI.

Note: This only applies to STM32 boards.

To configure the RGB_DI_PIN to open drain configuration add this to your config.h file:

#define WS2812_EXTERNAL_PULLUP