diff --git a/docs/LedStrip.md b/docs/LedStrip.md index 4c9808022..5b6a95482 100644 --- a/docs/LedStrip.md +++ b/docs/LedStrip.md @@ -89,6 +89,8 @@ It could be possible to be able to specify the timings required via CLI if users * Fits well under motors on mini 250 quads. * [Adafruit NeoPixel Stick](https://www.adafruit.com/products/1426) (works well) * Measured current consumption in all white mode ~ 350 mA. +* [Aliexpress SK6812 RBGWW strip](https://www.aliexpress.com/wholesale?SearchText=rgbw+sk6812) (works well) + * Alternative [Adafruit NeoPixel Stick RGBW](https://www.adafruit.com/product/2869) ### WS2811 vs WS2812 @@ -97,6 +99,8 @@ The [WS2811](https://cdn-shop.adafruit.com/datasheets/WS2811.pdf) is a LED drive The [WS2812](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) is integrated into the package of a 50:50 LED rather than as a separate device. It accepts data in the form of 8 bits each of Green-Red-Blue. +With the [SK6812](https://cdn-shop.adafruit.com/product-files/1138/SK6812+LED+datasheet+.pdf) also GRBW variants are supported, which have a fourth (white) channel and such provide a much cleaner white color. + It is thus possible, depending on the LED board/strip being used that either Red-Green-Blue or Green-Red-Blue encoding may be required. This may be controlled by setting the following. ``` @@ -107,8 +111,14 @@ or ``` set ledstrip_grb_rgb = GRB ``` +or + +``` +set ledstrip_grb_rgb = GRBW +``` Then confirm the required setting by simply setting an LED to be green. If it lights up red, you have the wrong setting. +Afterwards check if the second LED also lights up red - if not, you might have 4-color SK6812 LEDs and would have to select GRBW. ## Connections diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index e47c45f81..0b463d653 100644 --- a/src/main/cli/settings.c +++ b/src/main/cli/settings.c @@ -366,7 +366,7 @@ static const char * const lookupOverclock[] = { #ifdef USE_LED_STRIP static const char * const lookupLedStripFormatRGB[] = { - "GRB", "RGB" + "GRB", "RGB", "GRBW" }; #endif diff --git a/src/main/drivers/light_ws2811strip.c b/src/main/drivers/light_ws2811strip.c index d715e1fd5..3921165eb 100644 --- a/src/main/drivers/light_ws2811strip.c +++ b/src/main/drivers/light_ws2811strip.c @@ -31,6 +31,7 @@ #include #include "platform.h" +#include "common/maths.h" #ifdef USE_LED_STRIP @@ -139,8 +140,8 @@ void ws2811LedStripEnable(void) const hsvColor_t hsv_black = { 0, 0, 0 }; setStripColor(&hsv_black); - // RGB or GRB ordering doesn't matter for black - ws2811UpdateStrip(LED_RGB, 100); + // RGB or GRB ordering doesn't matter for black, use 4-channel LED configuraton to make sure all channels are zero + ws2811UpdateStrip(LED_GRBW, 100); ws2811Initialised = true; } @@ -153,23 +154,34 @@ bool isWS2811LedStripReady(void) STATIC_UNIT_TESTED void updateLEDDMABuffer(ledStripFormatRGB_e ledFormat, rgbColor24bpp_t *color, unsigned ledIndex) { - + uint32_t bits_per_led; uint32_t packed_colour; switch (ledFormat) { case LED_RGB: // WS2811 drivers use RGB format packed_colour = (color->rgb.r << 16) | (color->rgb.g << 8) | (color->rgb.b); + bits_per_led = 24; break; + case LED_GRBW: // SK6812 drivers use this + { + /* reconstruct white channel from RGB, making the intensity a bit nonlinear, but thats fine for this use case */ + uint8_t white = MIN(MIN(color->rgb.r, color->rgb.g), color->rgb.b); + packed_colour = (color->rgb.g << 24) | (color->rgb.r << 16) | (color->rgb.b << 8) | (white); + bits_per_led = 32; + break; + } + case LED_GRB: // WS2812 drivers use GRB format default: packed_colour = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b); + bits_per_led = 24; break; } unsigned dmaBufferOffset = 0; - for (int index = 23; index >= 0; index--) { - ledStripDMABuffer[ledIndex * WS2811_BITS_PER_LED + dmaBufferOffset++] = (packed_colour & (1 << index)) ? BIT_COMPARE_1 : BIT_COMPARE_0; + for (int index = bits_per_led-1; index >= 0; index--) { + ledStripDMABuffer[ledIndex * bits_per_led + dmaBufferOffset++] = (packed_colour & (1 << index)) ? BIT_COMPARE_1 : BIT_COMPARE_0; } } diff --git a/src/main/drivers/light_ws2811strip.h b/src/main/drivers/light_ws2811strip.h index 9eb20c3f3..9e0b6bd23 100644 --- a/src/main/drivers/light_ws2811strip.h +++ b/src/main/drivers/light_ws2811strip.h @@ -26,11 +26,11 @@ #define WS2811_LED_STRIP_LENGTH 32 -#define WS2811_BITS_PER_LED 24 +#define WS2811_BITS_PER_LED_MAX 32 #if defined(USE_WS2811_SINGLE_COLOUR) #define WS2811_DATA_BUFFER_SIZE 1 -#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE * WS2811_BITS_PER_LED) +#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE * WS2811_BITS_PER_LED_MAX) // Do 2 extra iterations of the DMA transfer with the output set to low to generate the > 50us delay. #define WS2811_DELAY_ITERATIONS 2 #else @@ -38,7 +38,7 @@ // for 50us delay #define WS2811_DELAY_BUFFER_LENGTH 42 // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes) -#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE * WS2811_BITS_PER_LED + WS2811_DELAY_BUFFER_LENGTH) +#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE * WS2811_BITS_PER_LED_MAX + WS2811_DELAY_BUFFER_LENGTH) #endif #ifdef USE_LEDSTRIP_CACHE_MGMT @@ -64,7 +64,8 @@ extern uint32_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE]; // Enumeration to match the string options defined in lookupLedStripFormatRGB in settings.c typedef enum { LED_GRB, - LED_RGB + LED_RGB, + LED_GRBW } ledStripFormatRGB_e; void ws2811LedStripInit(ioTag_t ioTag);