Browse Source

More LED fixes (#3368)

* apa102: remove dead code

We can't store strings of hundreds of thousands of characters in RAM, so
this can't possibly have fired, historically.  Pixbufs are still RAM
objects, so that's still out.  With LFS, it would take a pathological
example to hit the required 400KB TSTRING.

* Add IRQ management functions

* ws2812: fill UART FIFOs with IRQs off

Refactor code to make the use of two fill loops less gross.
Nathaniel Wesley Filardo 3 years ago
parent
commit
109f500be7
3 changed files with 52 additions and 28 deletions
  1. 0 4
      app/modules/apa102.c
  2. 37 24
      app/modules/ws2812.c
  3. 15 0
      app/platform/cpu_esp8266_irq.h

+ 0 - 4
app/modules/apa102.c

@@ -102,10 +102,6 @@ static int apa102_write(lua_State* L) {
     return luaL_argerror(L, 3, "String or pixbuf expected");
   }
 
-  if (nbr_frames > 100000) {
-    return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark.");
-  }
-
   // Initialize the output pins
   platform_gpio_mode(data_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
   GPIO_OUTPUT_SET(alt_data_pin, PLATFORM_GPIO_HIGH); // Set pin high

+ 37 - 24
app/modules/ws2812.c

@@ -8,6 +8,7 @@
 #include "user_interface.h"
 #include "driver/uart.h"
 #include "osapi.h"
+#include "cpu_esp8266_irq.h"
 
 #include "pixbuf.h"
 
@@ -51,12 +52,18 @@ static int ws2812_init(lua_State* L) {
   return 0;
 }
 
-// Stream data using UART1 routed to GPIO2
-// ws2812.init() should be called first
-//
-// NODE_DEBUG should not be activated because it also uses UART1
-void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
+static bool
+ws2812_can_write(int uart)
+{
+  // If something to send for first buffer and enough room
+  // in FIFO buffer (we wants to write 4 bytes, so less than
+  // 124 in the buffer)
+  return (((READ_PERI_REG(UART_STATUS(uart)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124);
+}
 
+static void
+ws2812_write_byte(int uart, uint8_t value)
+{
   // Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
   // 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
   // 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
@@ -66,31 +73,37 @@ void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, c
   // But declared in ".data" section to avoid read penalty from FLASH
   static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
 
+  WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 6) & 3]);
+  WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 4) & 3]);
+  WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 2) & 3]);
+  WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 0) & 3]);
+}
+
+// Stream data using UART1 routed to GPIO2
+// ws2812.init() should be called first
+//
+// NODE_DEBUG should not be activated because it also uses UART1
+void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
   const uint8_t *end  = pixels + length;
   const uint8_t *end2 = pixels2 + length2;
 
+  /* Fill the UART fifos with IRQs disabled */
+  uint32_t irq_state = esp8266_defer_irqs();
+  while ((pixels < end) && ws2812_can_write(1)) {
+    ws2812_write_byte(1, *pixels++);
+  }
+  while ((pixels2 < end2) && ws2812_can_write(0)) {
+    ws2812_write_byte(0, *pixels2++);
+  }
+  esp8266_restore_irqs(irq_state);
+
   do {
-    // If something to send for first buffer and enough room
-    // in FIFO buffer (we wants to write 4 bytes, so less than
-    // 124 in the buffer)
-    if (pixels < end && (((READ_PERI_REG(UART_STATUS(1)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) {
-      uint8_t value = *pixels++;
-
-      // Fill the buffer
-      WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 6) & 3]);
-      WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 4) & 3]);
-      WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 2) & 3]);
-      WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
+    if (pixels < end && ws2812_can_write(1)) {
+      ws2812_write_byte(1, *pixels++);
     }
     // Same for the second buffer
-    if (pixels2 < end2 && (((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) {
-      uint8_t value = *pixels2++;
-
-      // Fill the buffer
-      WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 6) & 3]);
-      WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 4) & 3]);
-      WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 2) & 3]);
-      WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 0) & 3]);
+    if (pixels2 < end2 && ws2812_can_write(0)) {
+      ws2812_write_byte(0, *pixels2++);
     }
   } while(pixels < end || pixels2 < end2); // Until there is still something to send
 }

+ 15 - 0
app/platform/cpu_esp8266_irq.h

@@ -0,0 +1,15 @@
+#pragma once
+
+static inline uint32_t
+esp8266_defer_irqs(void)
+{
+	uint32_t state;
+	__asm__ __volatile__ ("rsil %0, 15" : "=a"(state) : : "memory");
+	return state;
+}
+
+static inline void
+esp8266_restore_irqs(uint32_t state)
+{
+	__asm__ __volatile__ ("wsr %0, ps" : : "a"(state) : "memory");
+}