From 7f1c8ae894412111d12d89868578f12ee2c9566c Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Fri, 12 Jun 2020 12:28:58 +0200 Subject: [PATCH] Modify the ws2812 driver to support multiple led strips. --- extdrv/ws2812.c | 172 +++++++++++++++++++++++++++++----------- include/extdrv/ws2812.h | 30 ++++--- 2 files changed, 143 insertions(+), 59 deletions(-) diff --git a/extdrv/ws2812.c b/extdrv/ws2812.c index 57e0d48..a9c5b0b 100644 --- a/extdrv/ws2812.c +++ b/extdrv/ws2812.c @@ -34,35 +34,39 @@ #include "extdrv/ws2812.h" -static struct pio ws2812_gpio = LPC_GPIO_0_0; /* Configure Selected GPIO as output. */ -void ws2812_config(const struct pio* gpio) +void ws2812_config(struct ws2812_conf* ws2812, const struct pio* gpio) { config_pio(gpio, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL)); - pio_copy(&ws2812_gpio, gpio); - gpio_dir_out(ws2812_gpio); -} + pio_copy(&(ws2812->gpio), gpio); + gpio_dir_out(ws2812->gpio); -static uint8_t led_data[NB_LEDS * 3]; -static uint16_t brightness = 256; -static uint16_t max_led = 0; -static uint32_t nb_bytes = 0; + ws2812->brightness = 256; + ws2812->nb_bytes = 0; + ws2812->max_led = 0; + if (ws2812->inverted == 0) { + gpio_clear(ws2812->gpio); + } else { + gpio_set(ws2812->gpio); + } +} -static void ws2812_bit_sender(void) + +static void ws2812_bit_sender(struct ws2812_conf* ws2812) { - struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812_gpio.port); - uint32_t gpio_bit = (1 << ws2812_gpio.pin); + struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812->gpio.port); + uint32_t gpio_bit = (1 << ws2812->gpio.pin); uint32_t byte_idx = 0; uint8_t bit_idx = 7; uint8_t bit = 0; - uint8_t data = (led_data[0] * brightness) >> 8; + uint8_t data = (ws2812->led_data[0] * ws2812->brightness) >> 8; lpc_disable_irq(); /* Send data */ - while (byte_idx < nb_bytes) { + while (byte_idx < ws2812->nb_bytes) { bit = (data & (0x01 << bit_idx)); if (bit == 0) { @@ -105,7 +109,7 @@ static void ws2812_bit_sender(void) nop(); nop(); nop(); - /* "high" time : 600ns */ + /* "low" time : 600ns */ gpio_port->clear = gpio_bit; nop(); nop(); @@ -115,7 +119,81 @@ static void ws2812_bit_sender(void) if (bit_idx == 0) { bit_idx = 7; byte_idx++; - data = (led_data[byte_idx] * brightness) >> 8; + data = (ws2812->led_data[byte_idx] * ws2812->brightness) >> 8; + } else { + bit_idx--; + } + } + + lpc_enable_irq(); +} + +static void ws2812_inverted_bit_sender(struct ws2812_conf* ws2812) +{ + struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812->gpio.port); + uint32_t gpio_bit = (1 << ws2812->gpio.pin); + uint32_t byte_idx = 0; + uint8_t bit_idx = 7; + uint8_t bit = 0; + uint8_t data = (ws2812->led_data[0] * ws2812->brightness) >> 8; + + lpc_disable_irq(); + + /* Send data */ + while (byte_idx < ws2812->nb_bytes) { + bit = (data & (0x01 << bit_idx)); + + if (bit == 0) { + /* "high" time : 350ns */ + gpio_port->clear = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + /* "low" time : 800ns */ + gpio_port->set = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + } else { + /* "high" time : 700ns */ + gpio_port->clear = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + /* "low" time : 600ns */ + gpio_port->set = gpio_bit; + nop(); + nop(); + } + + /* Move to the next bit */ + if (bit_idx == 0) { + bit_idx = 7; + byte_idx++; + data = (ws2812->led_data[byte_idx] * ws2812->brightness) >> 8; } else { bit_idx--; } @@ -130,67 +208,69 @@ static void ws2812_bit_sender(void) * Call to this function will disable interrupts due to timming restrictions during * the call to ws2812_bit_sender(). */ -int ws2812_send_frame(uint16_t nb_leds) +int ws2812_send_frame(struct ws2812_conf* ws2812, uint16_t nb_leds) { - if (nb_leds > NB_LEDS) { - return -1; + if (nb_leds > ws2812->nb_leds) { + nb_leds = ws2812->nb_leds; } if (nb_leds == 0) { - nb_leds = max_led; + nb_leds = ws2812->max_led + 1; + } + ws2812->nb_bytes = nb_leds * 3; + if (ws2812->inverted == 0) { + ws2812_bit_sender(ws2812); + } else { + ws2812_inverted_bit_sender(ws2812); } - nb_bytes = (nb_leds + 1) * 3; - ws2812_bit_sender(); - /* Reset delay */ - usleep(60); return 0; } -void ws2812_set_brightness(uint8_t b) +void ws2812_set_brightness(struct ws2812_conf* ws2812, uint8_t b) { - brightness = b + 1; + ws2812->brightness = b + 1; } /* Set a pixel (led) color in the data buffer */ -int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue) +int ws2812_set_pixel(struct ws2812_conf* ws2812, uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue) { - if (pixel_num >= NB_LEDS) { + if (pixel_num >= ws2812->nb_leds) { return -1; } - led_data[ ((pixel_num * 3) + 0) ] = green; - led_data[ ((pixel_num * 3) + 1) ] = red; - led_data[ ((pixel_num * 3) + 2) ] = blue; - if (max_led < pixel_num) { - max_led = pixel_num; + ws2812->led_data[ ((pixel_num * 3) + 0) ] = green; + ws2812->led_data[ ((pixel_num * 3) + 1) ] = red; + ws2812->led_data[ ((pixel_num * 3) + 2) ] = blue; + if (ws2812->max_led < pixel_num) { + ws2812->max_led = pixel_num; } return 0; } /* Get a pixel (led) color from the data buffer */ -int ws2812_get_pixel(uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue) +int ws2812_get_pixel(struct ws2812_conf* ws2812, uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue) { - if (pixel_num >= NB_LEDS) { + if (pixel_num >= ws2812->nb_leds) { return -1; } - *green = led_data[ ((pixel_num * 3) + 0) ]; - *red = led_data[ ((pixel_num * 3) + 1) ]; - *blue = led_data[ ((pixel_num * 3) + 2) ]; + *green = ws2812->led_data[ ((pixel_num * 3) + 0) ]; + *red = ws2812->led_data[ ((pixel_num * 3) + 1) ]; + *blue = ws2812->led_data[ ((pixel_num * 3) + 2) ]; return 0; } /* Clear the internal data buffer. */ -void ws2812_clear_buffer(void) +void ws2812_clear_buffer(struct ws2812_conf* ws2812) { - memset(led_data, 0, (NB_LEDS * 3)); - max_led = 0; + memset(ws2812->led_data, 0, (ws2812->nb_leds * 3)); + ws2812->max_led = 0; } /* Clear the internal data buffer and send it to the Leds, turning them all off */ -void ws2812_clear(void) +void ws2812_clear(struct ws2812_conf* ws2812) { /* Start at first led and send all leds off */ - ws2812_clear_buffer(); - ws2812_send_frame(NB_LEDS); - max_led = 0; + ws2812_clear_buffer(ws2812); + ws2812_send_frame(ws2812, ws2812->nb_leds); + ws2812->max_led = 0; } -void ws2812_stop(void) __attribute__ ((alias ("ws2812_clear"))); +void ws2812_stop(struct ws2812_conf* ws2812) __attribute__ ((alias ("ws2812_clear"))); diff --git a/include/extdrv/ws2812.h b/include/extdrv/ws2812.h index 709bc35..f855751 100644 --- a/include/extdrv/ws2812.h +++ b/include/extdrv/ws2812.h @@ -61,14 +61,18 @@ #include "core/pio.h" -/* Size of the internal buffer. - * Change the value to the number of leds of your led strip. - */ -#define NB_LEDS 60 - +struct ws2812_conf { + struct pio gpio; + uint8_t* led_data; + uint16_t nb_leds; + uint16_t inverted; + uint16_t brightness; + uint16_t max_led; + uint32_t nb_bytes; +}; /* Configure the pin for the led data signal. */ -void ws2812_config(const struct pio* gpio); +void ws2812_config(struct ws2812_conf* ws2812, const struct pio* gpio); /* Send led data from internal buffer using the configured GPIO pin. (Set leds to the * selected color). @@ -78,12 +82,12 @@ void ws2812_config(const struct pio* gpio); * Call to this function will disable interrupts due to timming restrictions. * Return -1 on error (nb_leds above NB_LEDS), or 0 on success. */ -int ws2812_send_frame(uint16_t nb_leds); +int ws2812_send_frame(struct ws2812_conf* ws2812, uint16_t nb_leds); /* Set the global brightness of the led strip. * 'brightness' is a value between 0 (off) and 255 (full) */ -void ws2812_set_brightness(uint8_t brightness); +void ws2812_set_brightness(struct ws2812_conf* ws2812, uint8_t brightness); /* Set a pixel (led) color in the data buffer (frame) * The pixel number 'pixel_num' is the led offset in the led strip. @@ -91,16 +95,16 @@ void ws2812_set_brightness(uint8_t brightness); * while a value of 0xFF (255) is full brightness. * Return -1 on error (pixel_num above NB_LEDS), or 0 on success. */ -int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue); +int ws2812_set_pixel(struct ws2812_conf* ws2812, uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue); /* Get a pixel (led) color from the data buffer */ -int ws2812_get_pixel(uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue); +int ws2812_get_pixel(struct ws2812_conf* ws2812, uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue); /* Clear the internal data buffer. */ -void ws2812_clear_buffer(void); +void ws2812_clear_buffer(struct ws2812_conf* ws2812); /* Clear the internal data buffer and send it to the Leds, turning them all off */ -void ws2812_clear(void); +void ws2812_clear(struct ws2812_conf* ws2812); /* Alias for ws2812_clear */ -void ws2812_stop(void); +void ws2812_stop(struct ws2812_conf* ws2812); -- 2.43.0