aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/ws2812_driver.md30
-rw-r--r--drivers/ws2812.h32
-rw-r--r--platforms/avr/drivers/ws2812.c11
-rw-r--r--platforms/chibios/drivers/ws2812.c36
-rw-r--r--platforms/chibios/drivers/ws2812_pwm.c2
-rw-r--r--platforms/chibios/drivers/ws2812_spi.c2
6 files changed, 55 insertions, 58 deletions
diff --git a/docs/ws2812_driver.md b/docs/ws2812_driver.md
index 03dc4f2fd..289535aa9 100644
--- a/docs/ws2812_driver.md
+++ b/docs/ws2812_driver.md
@@ -49,6 +49,19 @@ WS2812_DRIVER = bitbang
49 49
50!> This driver is not hardware accelerated and may not be performant on heavily loaded systems. 50!> This driver is not hardware accelerated and may not be performant on heavily loaded systems.
51 51
52#### Adjusting bit timings
53
54The WS2812 LED communication topology depends on a serialized timed window. Different versions of the addressable LEDs have differing requirements for the timing parameters, for instance, of the SK6812.
55You can tune these parameters through the definition of the following macros:
56
57| Macro |Default | AVR | ARM |
58|---------------------|--------------------------------------------|--------------------|--------------------|
59|`WS2812_TIMING |`1250` | :heavy_check_mark: | :heavy_check_mark: |
60|`WS2812_T0H` |`350` | :heavy_check_mark: | :heavy_check_mark: |
61|`WS2812_T0L` |`WS2812_TIMING - WS2812_T0H` | | :heavy_check_mark: |
62|`WS2812_T1H` |`900` | :heavy_check_mark: | :heavy_check_mark: |
63|`WS2812_T1L` |`WS2812_TIMING - WS2812_T1L` | | :heavy_check_mark: |
64
52### I2C 65### I2C
53Targeting 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: 66Targeting 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:
54 67
@@ -62,23 +75,6 @@ Configure the hardware via your config.h:
62#define WS2812_TIMEOUT 100 // default: 100 75#define WS2812_TIMEOUT 100 // default: 100
63``` 76```
64 77
65##### Adjusting bit timings (ChibiOS only)
66
67The 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.
68
69The 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:
70
71| Macro |Default |
72|---------------------|--------------------------------------------|
73|`WS2812_TIMING |`1250` |
74|`WS2812_T0H` |`350` |
75|`WS2812_T0L` |`WS2812_TIMING - WS2812_T0H` |
76|`WS2812_T1H` |`900` |
77|`WS2812_T1L` |`WS2812_TIMING - WS2812_T1L` |
78|`WS2812_RES` |`6000` |
79
80It 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.
81
82### SPI 78### SPI
83Targeting 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: 79Targeting 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:
84 80
diff --git a/drivers/ws2812.h b/drivers/ws2812.h
index f179fcb0e..945b3d072 100644
--- a/drivers/ws2812.h
+++ b/drivers/ws2812.h
@@ -18,10 +18,40 @@
18#include "quantum/color.h" 18#include "quantum/color.h"
19 19
20/* 20/*
21 * The WS2812 datasheets define T1H 900ns, T0H 350ns, T1L 350ns, T0L 900ns. Hence, by default, these
22 * are chosen to be conservative and avoid problems rather than for maximum throughput; in the code,
23 * this is done by default using a WS2812_TIMING parameter that accounts for the whole window (1250ns)
24 * and defining T1H and T0H; T1L and T0L are obtained by subtracting their low counterparts from the window.
25 *
26 * However, there are certain "WS2812"-like LEDs, like the SK6812s, which work in a similar
27 * communication topology but use different timings for the window and the T1L, T1H, T0L and T0H.
28 * This means that, albeit the same driver being applicable, the timings must be adapted.
29 */
30
31#ifndef WS2812_TIMING
32# define WS2812_TIMING 1250
33#endif
34
35#ifndef WS2812_T1H
36# define WS2812_T1H 900 // Width of a 1 bit in ns
37#endif
38
39#ifndef WS2812_T1L
40# define WS2812_T1L (WS2812_TIMING - WS2812_T1H) // Width of a 1 bit in ns
41#endif
42
43#ifndef WS2812_T0H
44# define WS2812_T0H 350 // Width of a 0 bit in ns
45#endif
46
47#ifndef WS2812_T0L
48# define WS2812_T0L (WS2812_TIMING - WS2812_T0H) // Width of a 0 bit in ns
49#endif
50
51/*
21 * Older WS2812s can handle a reset time (TRST) of 50us, but recent 52 * Older WS2812s can handle a reset time (TRST) of 50us, but recent
22 * component revisions require a minimum of 280us. 53 * component revisions require a minimum of 280us.
23 */ 54 */
24
25#if !defined(WS2812_TRST_US) 55#if !defined(WS2812_TRST_US)
26# define WS2812_TRST_US 280 56# define WS2812_TRST_US 280
27#endif 57#endif
diff --git a/platforms/avr/drivers/ws2812.c b/platforms/avr/drivers/ws2812.c
index 77c492cd4..9150b3c52 100644
--- a/platforms/avr/drivers/ws2812.c
+++ b/platforms/avr/drivers/ws2812.c
@@ -52,20 +52,15 @@ void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
52 using the fast 800kHz clockless WS2811/2812 protocol. 52 using the fast 800kHz clockless WS2811/2812 protocol.
53*/ 53*/
54 54
55// Timing in ns
56#define w_zeropulse 350
57#define w_onepulse 900
58#define w_totalperiod 1250
59
60// Fixed cycles used by the inner loop 55// Fixed cycles used by the inner loop
61#define w_fixedlow 2 56#define w_fixedlow 2
62#define w_fixedhigh 4 57#define w_fixedhigh 4
63#define w_fixedtotal 8 58#define w_fixedtotal 8
64 59
65// Insert NOPs to match the timing, if possible 60// Insert NOPs to match the timing, if possible
66#define w_zerocycles (((F_CPU / 1000) * w_zeropulse) / 1000000) 61#define w_zerocycles (((F_CPU / 1000) * WS2812_T0H) / 1000000)
67#define w_onecycles (((F_CPU / 1000) * w_onepulse + 500000) / 1000000) 62#define w_onecycles (((F_CPU / 1000) * WS2812_T1H + 500000) / 1000000)
68#define w_totalcycles (((F_CPU / 1000) * w_totalperiod + 500000) / 1000000) 63#define w_totalcycles (((F_CPU / 1000) * WS2812_TIMING + 500000) / 1000000)
69 64
70// w1_nops - nops between rising edge and falling edge - low 65// w1_nops - nops between rising edge and falling edge - low
71#if w_zerocycles >= w_fixedlow 66#if w_zerocycles >= w_fixedlow
diff --git a/platforms/chibios/drivers/ws2812.c b/platforms/chibios/drivers/ws2812.c
index 8c882c8ee..7e870661d 100644
--- a/platforms/chibios/drivers/ws2812.c
+++ b/platforms/chibios/drivers/ws2812.c
@@ -22,6 +22,12 @@
22# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN 22# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN
23#endif 23#endif
24 24
25// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
26// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
27#ifndef WS2812_RES
28# define WS2812_RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch
29#endif
30
25#define NUMBER_NOPS 6 31#define NUMBER_NOPS 6
26#define CYCLES_PER_SEC (CPU_CLOCK / NUMBER_NOPS * NOP_FUDGE) 32#define CYCLES_PER_SEC (CPU_CLOCK / NUMBER_NOPS * NOP_FUDGE)
27#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 33#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives
@@ -40,36 +46,6 @@
40 } \ 46 } \
41 } while (0) 47 } while (0)
42 48
43/* The WS2812 datasheets define T1H 900ns, T0H 350ns, T1L 350ns, T0L 900ns. Hence, by default, these are chosen to be conservative and avoid problems rather than for maximum throughput; in the code, this is done by default using a WS2812_TIMING parameter that accounts for the whole window (1250ns) and defining T1H and T0H; T1L and T0L are obtained by subtracting their low counterparts from the window.
44However, there are certain "WS2812"-like LEDs, like the SK6812s, which work in a similar communication topology but use different timings for the window and the T1L, T1H, T0L and T0H. This means that, albeit the same driver being applicable, the timings must be adapted. The following defines are done such that the adjustment of these timings can be done in the keyboard's config.h; if nothing is said, the defines default to the WS2812 ones.
45*/
46
47#ifndef WS2812_TIMING
48# define WS2812_TIMING 1250
49#endif
50
51#ifndef WS2812_T1H
52# define WS2812_T1H 900 // Width of a 1 bit in ns
53#endif
54
55#ifndef WS2812_T1L
56# define WS2812_T1L (WS2812_TIMING - WS2812_T1H) // Width of a 1 bit in ns
57#endif
58
59#ifndef WS2812_T0H
60# define WS2812_T0H 350 // Width of a 0 bit in ns
61#endif
62
63#ifndef WS2812_T0L
64# define WS2812_T0L (WS2812_TIMING - WS2812_T0H) // Width of a 0 bit in ns
65#endif
66
67// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
68// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
69#ifndef WS2812_RES
70# define WS2812_RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch
71#endif
72
73void sendByte(uint8_t byte) { 49void sendByte(uint8_t byte) {
74 // WS2812 protocol wants most significant bits first 50 // WS2812 protocol wants most significant bits first
75 for (unsigned char bit = 0; bit < 8; bit++) { 51 for (unsigned char bit = 0; bit < 8; bit++) {
diff --git a/platforms/chibios/drivers/ws2812_pwm.c b/platforms/chibios/drivers/ws2812_pwm.c
index c17b9cd4e..19ea3cfe8 100644
--- a/platforms/chibios/drivers/ws2812_pwm.c
+++ b/platforms/chibios/drivers/ws2812_pwm.c
@@ -71,7 +71,7 @@
71 * Calculate the number of zeroes to add at the end assuming 1.25 uS/bit: 71 * Calculate the number of zeroes to add at the end assuming 1.25 uS/bit:
72 */ 72 */
73#define WS2812_COLOR_BITS (WS2812_CHANNELS * 8) 73#define WS2812_COLOR_BITS (WS2812_CHANNELS * 8)
74#define WS2812_RESET_BIT_N (1000 * WS2812_TRST_US / 1250) 74#define WS2812_RESET_BIT_N (1000 * WS2812_TRST_US / WS2812_TIMING)
75#define WS2812_COLOR_BIT_N (RGBLED_NUM * WS2812_COLOR_BITS) /**< Number of data bits */ 75#define WS2812_COLOR_BIT_N (RGBLED_NUM * WS2812_COLOR_BITS) /**< Number of data bits */
76#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N) /**< Total number of bits in a frame */ 76#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N) /**< Total number of bits in a frame */
77 77
diff --git a/platforms/chibios/drivers/ws2812_spi.c b/platforms/chibios/drivers/ws2812_spi.c
index 62722f466..ba471e0b8 100644
--- a/platforms/chibios/drivers/ws2812_spi.c
+++ b/platforms/chibios/drivers/ws2812_spi.c
@@ -77,7 +77,7 @@
77#endif 77#endif
78#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS) 78#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS)
79#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM) 79#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM)
80#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * 1250)) 80#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * WS2812_TIMING))
81#define PREAMBLE_SIZE 4 81#define PREAMBLE_SIZE 4
82 82
83static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0}; 83static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};