From 109f500be75f4fdc4cbf398fa4af5fff2547cf5a Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 10 Jan 2021 17:19:10 +0000 Subject: [PATCH] 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. --- app/modules/apa102.c | 4 --- app/modules/ws2812.c | 61 +++++++++++++++++++++------------- app/platform/cpu_esp8266_irq.h | 15 +++++++++ 3 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 app/platform/cpu_esp8266_irq.h diff --git a/app/modules/apa102.c b/app/modules/apa102.c index 253c2648..cc832757 100644 --- a/app/modules/apa102.c +++ b/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 diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index 534d4399..536b972e 100644 --- a/app/modules/ws2812.c +++ b/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; - 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 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); - // 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]); + do { + 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 } diff --git a/app/platform/cpu_esp8266_irq.h b/app/platform/cpu_esp8266_irq.h new file mode 100644 index 00000000..44e003e7 --- /dev/null +++ b/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"); +}