Modify the ws2812 driver to support multiple led strips.
authorNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 12 Jun 2020 10:28:58 +0000 (12:28 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
extdrv/ws2812.c
include/extdrv/ws2812.h

index 57e0d48..a9c5b0b 100644 (file)
 #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")));
 
index 709bc35..f855751 100644 (file)
 #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);