Plasma WS2812: Add RGBW and color-order support

pull/181/head
Phil Howard 2021-08-02 12:33:20 +01:00
rodzic 693e84c73d
commit 6ce80cd289
8 zmienionych plików z 99 dodań i 33 usunięć

Wyświetl plik

@ -26,9 +26,9 @@ const uint N_LEDS = 30;
//plasma::APA102 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT, plasma::PIN_CLK); //plasma::APA102 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT, plasma::PIN_CLK);
// WS28X-style LEDs with a single signal line. AKA NeoPixel // WS28X-style LEDs with a single signal line. AKA NeoPixel
// by default the WS2812 LED strip will be 400KHz, RGB with no white element
plasma::WS2812 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT); plasma::WS2812 led_strip(N_LEDS, pio0, 0, plasma::PIN_DAT);
Button button_a(plasma::BUTTON_A, Polarity::ACTIVE_LOW, 50); Button button_a(plasma::BUTTON_A, Polarity::ACTIVE_LOW, 50);
Button button_b(plasma::BUTTON_B, Polarity::ACTIVE_LOW, 50); Button button_b(plasma::BUTTON_B, Polarity::ACTIVE_LOW, 50);
RGBLED led(plasma::LED_R, plasma::LED_G, plasma::LED_B); RGBLED led(plasma::LED_R, plasma::LED_G, plasma::LED_B);

Wyświetl plik

@ -43,6 +43,7 @@ bool APA102::dma_timer_callback(struct repeating_timer *t) {
} }
void APA102::update(bool blocking) { void APA102::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
pio->txf[sm] = 0x00000000; // Output the APA102 start-of-frame bytes pio->txf[sm] = 0x00000000; // Output the APA102 start-of-frame bytes
dma_channel_set_trans_count(dma_channel, num_leds, false); dma_channel_set_trans_count(dma_channel, num_leds, false);
@ -62,7 +63,7 @@ bool APA102::stop() {
void APA102::clear() { void APA102::clear() {
for (auto i = 0u; i < num_leds; ++i) { for (auto i = 0u; i < num_leds; ++i) {
buffer[i].rgb(0, 0, 0); set_rgb(i, 0, 0, 0);
} }
} }
@ -75,12 +76,12 @@ void APA102::set_hsv(uint32_t index, float h, float s, float v) {
uint8_t t = v * (1.0f - (1.0f - f) * s); uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) { switch (int(i) % 6) {
case 0: buffer[index].rgb(v, t, p); break; case 0: set_rgb(index, v, t, p); break;
case 1: buffer[index].rgb(q, v, p); break; case 1: set_rgb(index, q, v, p); break;
case 2: buffer[index].rgb(p, v, t); break; case 2: set_rgb(index, p, v, t); break;
case 3: buffer[index].rgb(p, q, v); break; case 3: set_rgb(index, p, q, v); break;
case 4: buffer[index].rgb(t, p, v); break; case 4: set_rgb(index, t, p, v); break;
case 5: buffer[index].rgb(v, p, q); break; case 5: set_rgb(index, v, p, q); break;
} }
} }

Wyświetl plik

@ -46,9 +46,9 @@ namespace plasma {
void rgb(uint8_t r, uint8_t g, uint8_t b) { void rgb(uint8_t r, uint8_t g, uint8_t b) {
this->r = r; this->r = r;
this->g = g; this->g = g;
this->b = b; this->b = b;;
} }
RGB() : sof(0b11101111), b(0), g(0), r(0) {} RGB() : sof(0b11101111), b(0), g(0), r(0) {};
}; };
#pragma pack(pop) #pragma pack(pop)
RGB *buffer; RGB *buffer;

Wyświetl plik

@ -2,7 +2,7 @@
namespace plasma { namespace plasma {
WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, RGB* buffer) : buffer(buffer), num_leds(num_leds), pio(pio), sm(sm) { WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw, COLOR_ORDER color_order, RGB* buffer) : buffer(buffer), num_leds(num_leds), color_order(color_order), pio(pio), sm(sm) {
uint offset = pio_add_program(pio, &ws2812_program); uint offset = pio_add_program(pio, &ws2812_program);
pio_gpio_init(pio, pin); pio_gpio_init(pio, pin);
@ -11,7 +11,7 @@ WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, RGB* buffer
pio_sm_config c = ws2812_program_get_default_config(offset); pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin); sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, 24); // Discard first (APA102 global brightness) byte. TODO support RGBW WS281X LEDs sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); // Discard first (APA102 global brightness) byte. TODO support RGBW WS281X LEDs
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
@ -42,6 +42,7 @@ bool WS2812::dma_timer_callback(struct repeating_timer *t) {
} }
void WS2812::update(bool blocking) { void WS2812::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
dma_channel_set_trans_count(dma_channel, num_leds, false); dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, buffer, true); dma_channel_set_read_addr(dma_channel, buffer, true);
@ -60,7 +61,7 @@ bool WS2812::stop() {
void WS2812::clear() { void WS2812::clear() {
for (auto i = 0u; i < num_leds; ++i) { for (auto i = 0u; i < num_leds; ++i) {
buffer[i].rgb(0, 0, 0); set_rgb(i, 0, 0, 0);
} }
} }
@ -73,17 +74,36 @@ void WS2812::set_hsv(uint32_t index, float h, float s, float v) {
uint8_t t = v * (1.0f - (1.0f - f) * s); uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) { switch (int(i) % 6) {
case 0: buffer[index].rgb(v, t, p); break; case 0: set_rgb(index, v, t, p); break;
case 1: buffer[index].rgb(q, v, p); break; case 1: set_rgb(index, q, v, p); break;
case 2: buffer[index].rgb(p, v, t); break; case 2: set_rgb(index, p, v, t); break;
case 3: buffer[index].rgb(p, q, v); break; case 3: set_rgb(index, p, q, v); break;
case 4: buffer[index].rgb(t, p, v); break; case 4: set_rgb(index, t, p, v); break;
case 5: buffer[index].rgb(v, p, q); break; case 5: set_rgb(index, v, p, q); break;
} }
} }
void WS2812::set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b) { void WS2812::set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
buffer[index].rgb(r, g, b); switch(color_order) {
case COLOR_ORDER::RGB:
buffer[index].rgb(r, g, b, w);
break;
case COLOR_ORDER::RBG:
buffer[index].rgb(r, b, g, w);
break;
case COLOR_ORDER::GRB:
buffer[index].rgb(g, r, b, w);
break;
case COLOR_ORDER::GBR:
buffer[index].rgb(g, b, r, w);
break;
case COLOR_ORDER::BRG:
buffer[index].rgb(b, r, g, w);
break;
case COLOR_ORDER::BGR:
buffer[index].rgb(b, g, r, w);
break;
}
} }
void WS2812::set_brightness(uint8_t b) { void WS2812::set_brightness(uint8_t b) {

Wyświetl plik

@ -30,6 +30,14 @@ namespace plasma {
static const uint SERIAL_FREQ_400KHZ = 400000; static const uint SERIAL_FREQ_400KHZ = 400000;
static const uint SERIAL_FREQ_800KHZ = 800000; static const uint SERIAL_FREQ_800KHZ = 800000;
static const uint DEFAULT_SERIAL_FREQ = SERIAL_FREQ_400KHZ; static const uint DEFAULT_SERIAL_FREQ = SERIAL_FREQ_400KHZ;
enum class COLOR_ORDER {
RGB,
RBG,
GRB,
GBR,
BRG,
BGR
};
#pragma pack(push, 1) #pragma pack(push, 1)
union alignas(4) RGB { union alignas(4) RGB {
struct { struct {
@ -42,19 +50,20 @@ namespace plasma {
void operator=(uint32_t v) { void operator=(uint32_t v) {
srgb = v; srgb = v;
}; };
void brightness(uint8_t b) {};; void rgb(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0) {
void rgb(uint8_t r, uint8_t g, uint8_t b) {
this->r = r; this->r = r;
this->g = g; this->g = g;
this->b = b; this->b = b;
} this->w = w;
RGB() : r(0), g(0), b(0), w(0) {} };
RGB() : r(0), g(0), b(0), w(0) {};
}; };
#pragma pack(pop) #pragma pack(pop)
RGB *buffer; RGB *buffer;
uint32_t num_leds; uint32_t num_leds;
COLOR_ORDER color_order;
WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq=DEFAULT_SERIAL_FREQ, RGB* buffer=nullptr); WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq=DEFAULT_SERIAL_FREQ, bool rgbw=false, COLOR_ORDER color_order=COLOR_ORDER::RGB, RGB* buffer=nullptr);
~WS2812() { ~WS2812() {
stop(); stop();
clear(); clear();
@ -68,7 +77,7 @@ namespace plasma {
void update(bool blocking=false); void update(bool blocking=false);
void clear(); void clear();
void set_hsv(uint32_t index, float h, float s, float v); void set_hsv(uint32_t index, float h, float s, float v);
void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b); void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w=0);
void set_brightness(uint8_t b); void set_brightness(uint8_t b);
RGB get(uint32_t index) {return buffer[index];}; RGB get(uint32_t index) {return buffer[index];};

Wyświetl plik

@ -3,13 +3,14 @@
The Plasma library is intended to drive APA102 / DotStar™ or WS2812 / NeoPixel™ LEDs on the Plasma 2040 board, though it can be used with your own custom pins/wiring. The Plasma library is intended to drive APA102 / DotStar™ or WS2812 / NeoPixel™ LEDs on the Plasma 2040 board, though it can be used with your own custom pins/wiring.
- [Notes On PIO Limitations](#notes-on-pio-limitations) - [Notes On PIO Limitations](#notes-on-pio-limitations)
- [APA102](#apa102) - [WS2812](#ws2812)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [RGBW and Setting Colour Order](#rgbw-and-setting-colour-order)
- [Set An LED](#set-an-led) - [Set An LED](#set-an-led)
- [RGB](#rgb) - [RGB](#rgb)
- [HSV](#hsv) - [HSV](#hsv)
- [Set Brightness](#set-brightness) - [Set Brightness](#set-brightness)
- [WS2812](#ws2812) - [APA102](#apa102)
- [Getting Started](#getting-started-1) - [Getting Started](#getting-started-1)
- [Set An LED](#set-an-led-1) - [Set An LED](#set-an-led-1)
- [RGB](#rgb-1) - [RGB](#rgb-1)
@ -24,7 +25,7 @@ The WS2812 and APA102 drivers use the PIO hardware on the RP2040. There are only
In most cases you'll use `0` for PIO and `0` for PIO state-machine, but you should change these if you plan on running different strand types together, or if you're using something else that uses PIO. In most cases you'll use `0` for PIO and `0` for PIO state-machine, but you should change these if you plan on running different strand types together, or if you're using something else that uses PIO.
## APA102 ## WS2812
### Getting Started ### Getting Started
@ -45,6 +46,28 @@ Start the LED strip by calling `start`. This sets up a timer which tells the RP2
led_strip.start(FPS) led_strip.start(FPS)
``` ```
### RGBW and Setting Colour Order
Some WS2812-style LED strips have varying colour orders and support an additional white element. Two keyword arguments are supplied to configure this:
```
import plasma
LEDS = 30
FPS = 60
led_strip = plasma.WS2812(LEDS, 0, 0, 15, rgbw=True, color_order=plasma.COLOR_ORDER_GRB)
```
The available orders are defined as constants in `plasma`:
* `COLOR_ORDER_RGB`
* `COLOR_ORDER_RBG`
* `COLOR_ORDER_GRB`
* `COLOR_ORDER_GBR`
* `COLOR_ORDER_BRG`
* `COLOR_ORDER_BGR`
### Set An LED ### Set An LED
You can set the colour of an LED in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns. You can set the colour of an LED in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns.
@ -75,7 +98,7 @@ led_strip.set_brightness(15)
You can set brightness from `0` to `31`. This directly maps to the 5-bit brightness value sent to the APA102 LEDs. You can set brightness from `0` to `31`. This directly maps to the 5-bit brightness value sent to the APA102 LEDs.
## WS2812 ## APA102
### Getting Started ### Getting Started

Wyświetl plik

@ -62,6 +62,13 @@ STATIC const mp_map_elem_t plasma_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PIN_LED_B), MP_ROM_INT(18) }, { MP_ROM_QSTR(MP_QSTR_PIN_LED_B), MP_ROM_INT(18) },
{ MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_A), MP_ROM_INT(12) }, { MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_A), MP_ROM_INT(12) },
{ MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_B), MP_ROM_INT(13) }, { MP_ROM_QSTR(MP_QSTR_PIN_BUTTON_B), MP_ROM_INT(13) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_RGB), MP_ROM_INT(0x00) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_RBG), MP_ROM_INT(0x01) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_GRB), MP_ROM_INT(0x02) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_GBR), MP_ROM_INT(0x03) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_BRG), MP_ROM_INT(0x04) },
{ MP_ROM_QSTR(MP_QSTR_COLOR_ORDER_BGR), MP_ROM_INT(0x05) },
}; };
STATIC MP_DEFINE_CONST_DICT(mp_module_plasma_globals, plasma_globals_table); STATIC MP_DEFINE_CONST_DICT(mp_module_plasma_globals, plasma_globals_table);

Wyświetl plik

@ -60,7 +60,9 @@ mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t
ARG_sm, ARG_sm,
ARG_dat, ARG_dat,
ARG_freq, ARG_freq,
ARG_buffer ARG_buffer,
ARG_rgbw,
ARG_color_order
}; };
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_num_leds, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_num_leds, MP_ARG_REQUIRED | MP_ARG_INT },
@ -69,6 +71,8 @@ mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t
{ MP_QSTR_dat, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_dat, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = WS2812::DEFAULT_SERIAL_FREQ} }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = WS2812::DEFAULT_SERIAL_FREQ} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} }, { MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_rgbw, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_color_order, MP_ARG_INT, {.u_int = (uint8_t)WS2812::COLOR_ORDER::RGB} },
}; };
// Parse args. // Parse args.
@ -80,6 +84,8 @@ mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t
int sm = args[ARG_sm].u_int; int sm = args[ARG_sm].u_int;
int dat = args[ARG_dat].u_int; int dat = args[ARG_dat].u_int;
int freq = args[ARG_freq].u_int; int freq = args[ARG_freq].u_int;
bool rgbw = args[ARG_rgbw].u_bool;
WS2812::COLOR_ORDER color_order = (WS2812::COLOR_ORDER)args[ARG_color_order].u_int;
void *buffer = nullptr; void *buffer = nullptr;
@ -96,7 +102,7 @@ mp_obj_t PlasmaWS2812_make_new(const mp_obj_type_t *type, size_t n_args, size_t
self->base.type = &PlasmaWS2812_type; self->base.type = &PlasmaWS2812_type;
self->buf = buffer; self->buf = buffer;
self->ws2812 = new WS2812(num_leds, pio, sm, dat, freq, (WS2812::RGB *)buffer); self->ws2812 = new WS2812(num_leds, pio, sm, dat, freq, rgbw, color_order, (WS2812::RGB *)buffer);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }