Improve WS2812 leds support and add WS2811 leds support
authorNathael Pajani <nathael.pajani@ed3l.fr>
Wed, 18 Aug 2021 08:18:25 +0000 (10:18 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 10 Feb 2023 18:02:59 +0000 (19:02 +0100)
extdrv/ws2812.c
include/extdrv/ws2812.h

index 2f1f4f0..37e0c87 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)
 {
        struct pin_matrix_entry mat = LPC_GPIO;
        config_pio(gpio, &mat, LPC_IO_MODE_PULL_UP);
-       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 max_led = 0;
-static uint32_t nb_bytes = 0;
+       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 = (ws2812->led_data[0] * ws2812->brightness) >> 8;
 
        lpc_disable_irq();
 
        /* Send data */
-       while (byte_idx < nb_bytes) {
-               bit = (led_data[byte_idx] & (0x01 << bit_idx));
+       while (byte_idx < ws2812->nb_bytes) {
+               bit = (data & (0x01 << bit_idx));
 
                if (bit == 0) {
                        /* "high" time : 350ns */
-                       gpio_port->set = gpio_bit;
+                       gpio_port->toggle = gpio_bit;
                        nop();
                        nop();
                        nop();
@@ -77,7 +81,7 @@ static void ws2812_bit_sender(void)
                        nop();
                        nop();
                        /* "low" time : 800ns */
-                       gpio_port->clear = gpio_bit;
+                       gpio_port->toggle = gpio_bit;
                        nop();
                        nop();
                        nop();
@@ -88,7 +92,7 @@ static void ws2812_bit_sender(void)
                        nop();
                } else {
                        /* "high" time : 700ns */
-                       gpio_port->set = gpio_bit;
+                       gpio_port->toggle = gpio_bit;
                        nop();
                        nop();
                        nop();
@@ -106,7 +110,122 @@ static void ws2812_bit_sender(void)
                        nop();
                        nop();
                        /* "high" time : 600ns */
-                       gpio_port->clear = gpio_bit;
+                       gpio_port->toggle = 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--;
+               }
+       }
+
+       lpc_enable_irq();
+}
+
+static void ws2811_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 : 500ns */
+                       gpio_port->toggle = gpio_bit;
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       /* "low" time : 2000ns */
+                       gpio_port->toggle = gpio_bit;
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+               } else {
+                       /* "high" time : 1200ns */
+                       gpio_port->toggle = gpio_bit;
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       /* "high" time : 1300ns */
+                       gpio_port->toggle = gpio_bit;
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
+                       nop();
                        nop();
                        nop();
                }
@@ -115,6 +234,7 @@ static void ws2812_bit_sender(void)
                if (bit_idx == 0) {
                        bit_idx = 7;
                        byte_idx++;
+                       data = (ws2812->led_data[byte_idx] * ws2812->brightness) >> 8;
                } else {
                        bit_idx--;
                }
@@ -129,51 +249,67 @@ 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;
+       switch (ws2812->type) {
+               case WS_LED_WS2811:
+                       ws2811_bit_sender(ws2812);
+                       break;
+               case WS_LED_WS2812:
+               default:
+                       ws2812_bit_sender(ws2812);
+                       break;
        }
-       nb_bytes = (nb_leds + 1) * 3;
-       ws2812_bit_sender();
-       /* Reset delay */
-       usleep(60);
        return 0;
 }
 
 /* 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;
+       switch (ws2812->type) {
+               case WS_LED_WS2811:
+                       ws2812->led_data[ ((pixel_num * 3) + 0) ] = red;
+                       ws2812->led_data[ ((pixel_num * 3) + 1) ] = green;
+                       ws2812->led_data[ ((pixel_num * 3) + 2) ] = blue;
+                       break;
+               case WS_LED_WS2812:
+               default:
+                       ws2812->led_data[ ((pixel_num * 3) + 0) ] = green;
+                       ws2812->led_data[ ((pixel_num * 3) + 1) ] = red;
+                       ws2812->led_data[ ((pixel_num * 3) + 2) ] = blue;
+                       break;
+       }
+       if (ws2812->max_led < pixel_num) {
+               ws2812->max_led = pixel_num;
        }
        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 755886e..a5539d1 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
+enum ws_led_type {
+       WS_LED_WS2812,
+       WS_LED_WS2811,
+};
+
+
+struct ws2812_conf {
+       struct pio gpio;
+       uint8_t* led_data;
+       uint32_t nb_bytes;
+       uint16_t nb_leds;
+       uint16_t max_led;
+       uint8_t inverted;
+       uint8_t brightness;
+       uint8_t type;
+};
 
 
 /* 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,7 +90,7 @@ 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 a pixel (led) color in the data buffer (frame)
  * The pixel number 'pixel_num' is the led offset in the led strip.
@@ -86,13 +98,13 @@ int ws2812_send_frame(uint16_t nb_leds);
  *   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);
 
 /* 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);