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);
// 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);
Button button_a(plasma::BUTTON_A, 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);

Wyświetl plik

@ -43,6 +43,7 @@ bool APA102::dma_timer_callback(struct repeating_timer *t) {
}
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
pio->txf[sm] = 0x00000000; // Output the APA102 start-of-frame bytes
dma_channel_set_trans_count(dma_channel, num_leds, false);
@ -62,7 +63,7 @@ bool APA102::stop() {
void APA102::clear() {
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);
switch (int(i) % 6) {
case 0: buffer[index].rgb(v, t, p); break;
case 1: buffer[index].rgb(q, v, p); break;
case 2: buffer[index].rgb(p, v, t); break;
case 3: buffer[index].rgb(p, q, v); break;
case 4: buffer[index].rgb(t, p, v); break;
case 5: buffer[index].rgb(v, p, q); break;
case 0: set_rgb(index, v, t, p); break;
case 1: set_rgb(index, q, v, p); break;
case 2: set_rgb(index, p, v, t); break;
case 3: set_rgb(index, p, q, v); break;
case 4: set_rgb(index, t, p, v); 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) {
this->r = r;
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)
RGB *buffer;

Wyświetl plik

@ -2,7 +2,7 @@
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);
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);
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);
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) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
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_read_addr(dma_channel, buffer, true);
@ -60,7 +61,7 @@ bool WS2812::stop() {
void WS2812::clear() {
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);
switch (int(i) % 6) {
case 0: buffer[index].rgb(v, t, p); break;
case 1: buffer[index].rgb(q, v, p); break;
case 2: buffer[index].rgb(p, v, t); break;
case 3: buffer[index].rgb(p, q, v); break;
case 4: buffer[index].rgb(t, p, v); break;
case 5: buffer[index].rgb(v, p, q); break;
case 0: set_rgb(index, v, t, p); break;
case 1: set_rgb(index, q, v, p); break;
case 2: set_rgb(index, p, v, t); break;
case 3: set_rgb(index, p, q, v); break;
case 4: set_rgb(index, t, p, v); 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) {
buffer[index].rgb(r, g, b);
void WS2812::set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
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) {

Wyświetl plik

@ -30,6 +30,14 @@ namespace plasma {
static const uint SERIAL_FREQ_400KHZ = 400000;
static const uint SERIAL_FREQ_800KHZ = 800000;
static const uint DEFAULT_SERIAL_FREQ = SERIAL_FREQ_400KHZ;
enum class COLOR_ORDER {
RGB,
RBG,
GRB,
GBR,
BRG,
BGR
};
#pragma pack(push, 1)
union alignas(4) RGB {
struct {
@ -42,19 +50,20 @@ namespace plasma {
void operator=(uint32_t v) {
srgb = v;
};
void brightness(uint8_t b) {};;
void rgb(uint8_t r, uint8_t g, uint8_t b) {
void rgb(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0) {
this->r = r;
this->g = g;
this->b = b;
}
RGB() : r(0), g(0), b(0), w(0) {}
this->w = w;
};
RGB() : r(0), g(0), b(0), w(0) {};
};
#pragma pack(pop)
RGB *buffer;
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() {
stop();
clear();
@ -68,7 +77,7 @@ namespace plasma {
void update(bool blocking=false);
void clear();
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);
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.
- [Notes On PIO Limitations](#notes-on-pio-limitations)
- [APA102](#apa102)
- [WS2812](#ws2812)
- [Getting Started](#getting-started)
- [RGBW and Setting Colour Order](#rgbw-and-setting-colour-order)
- [Set An LED](#set-an-led)
- [RGB](#rgb)
- [HSV](#hsv)
- [Set Brightness](#set-brightness)
- [WS2812](#ws2812)
- [APA102](#apa102)
- [Getting Started](#getting-started-1)
- [Set An LED](#set-an-led-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.
## APA102
## WS2812
### 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)
```
### 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
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.
## WS2812
## APA102
### 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_BUTTON_A), MP_ROM_INT(12) },
{ 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);

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_dat,
ARG_freq,
ARG_buffer
ARG_buffer,
ARG_rgbw,
ARG_color_order
};
static const mp_arg_t allowed_args[] = {
{ 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_freq, MP_ARG_INT, {.u_int = WS2812::DEFAULT_SERIAL_FREQ} },
{ 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.
@ -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 dat = args[ARG_dat].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;
@ -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->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);
}