Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add SK6812 RGBW 4-channel LEDs #10762

Merged
merged 1 commit into from Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/LedStrip.md
Expand Up @@ -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
Expand All @@ -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.

```
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/main/cli/settings.c
Expand Up @@ -366,7 +366,7 @@ static const char * const lookupOverclock[] = {

#ifdef USE_LED_STRIP
static const char * const lookupLedStripFormatRGB[] = {
"GRB", "RGB"
"GRB", "RGB", "GRBW"
};
#endif

Expand Down
22 changes: 17 additions & 5 deletions src/main/drivers/light_ws2811strip.c
Expand Up @@ -31,6 +31,7 @@
#include <string.h>

#include "platform.h"
#include "common/maths.h"

#ifdef USE_LED_STRIP

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/main/drivers/light_ws2811strip.h
Expand Up @@ -26,19 +26,19 @@

#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
#define WS2811_DATA_BUFFER_SIZE WS2811_LED_STRIP_LENGTH
// 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
Expand All @@ -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);
Expand Down