From bdf880cf8434f8b22a6d4d4a84a5a0d198d42ec5 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:22:52 +0100 Subject: [PATCH] Improved auto-selection of LED hardware support (RMT, SPI) (#22618) --- CHANGELOG.md | 1 + lib/lib_basic/TasmotaLED/src/TasmotaLED.h | 8 +- .../TasmotaLED/src/TasmotaLEDPusher.cpp | 57 ++++++------ .../TasmotaLED/src/TasmotaLEDPusher.h | 13 ++- .../TasmotaLED/src/TasmotaLEDPusherRMT.cpp | 27 +++--- .../TasmotaLED/src/TasmotaLEDPusherSPI.cpp | 89 ++++++++++--------- .../xlgt_01_ws2812_esp32.ino | 16 +--- 7 files changed, 105 insertions(+), 106 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c46f65b..49c001ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file. - Command `SetOption162 1` to disable adding export energy to energy today (#22578) - ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 (#22600) - Support for Sonoff POWCT Energy Export Active (#22596) +- Improved auto-selection of LED hardware support (RMT, SPI) ### Breaking Changed - ESP32 ArtNet switches from GRB to RGB encoding (#22556) diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h index ac1b78c1e..dd2fb8dc9 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h @@ -44,10 +44,10 @@ enum TasmotaLEDTypesEncoding : uint16_t { enum TasmotaLEDHardware : uint32_t { // low-order bits are reserved for channels numbers and hardware flags - currenlty not useds // bits 16..23 - TasmotaLed_HW_Default = 0x0 << 16, - TasmotaLed_RMT = 1 << 16, - TasmotaLed_SPI = 2 << 16, - TasmotaLed_I2S = 3 << 16, + TasmotaLed_HW_Default = 0x000000, + TasmotaLed_RMT = (1 << 0) << 16, + TasmotaLed_SPI = (1 << 1) << 16, + TasmotaLed_I2S = (1 << 2) << 16, TasmotaLed_HW_None = 0xFF << 16, // indicates that the specified HW is not supported }; diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp index d765703e5..19280a7dd 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp @@ -30,40 +30,33 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D // convert to the appropriate hardware acceleration based on capacities of the SOC -uint32_t TasmotaLEDPusher::ResolveHardware(uint32_t hw) { -uint32_t hw_orig = hw; +uint32_t TasmotaLEDPusher::ResolveHardware(uint32_t hw_input) { // Step 1. discard any unsupported hardware, and replace with TasmotaLed_HW_Default - uint32_t hw_type = hw & 0xFF0000; // discard bits 0..15 + uint32_t hw = hw_input & 0xFF0000; // discard bits 0..15 #if !TASMOTALED_HARDWARE_RMT - if (hw_type == TasmotaLed_RMT) { - hw = TasmotaLed_HW_None; - } + hw &= ~TasmotaLed_RMT; // remove RMT flag if not supported by hardware #endif // TASMOTALED_HARDWARE_RMT #if !TASMOTALED_HARDWARE_SPI - if (hw_type == TasmotaLed_SPI) { - hw = TasmotaLed_HW_None; - } + hw &= ~TasmotaLed_SPI; // remove SPI flag if not supported by hardware #endif // TASMOTALED_HARDWARE_SPI #if !TASMOTALED_HARDWARE_I2S - if (hw_type == TasmotaLed_I2S) { - hw = TasmotaLed_HW_None; - } + hw &= ~TasmotaLed_I2S; // remove I2S flag if not supported by hardware #endif // TASMOTALED_HARDWARE_I2S // Step 2. If TasmotaLed_HW_Default, find a suitable scheme, RMT preferred #if TASMOTALED_HARDWARE_RMT - if ((hw & 0xFF0000) == TasmotaLed_HW_Default) { - hw = TasmotaLed_RMT; + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_RMT; } #endif // TASMOTALED_HARDWARE_RMT #if TASMOTALED_HARDWARE_I2S - if ((hw & 0xFF0000) == TasmotaLed_HW_Default) { - hw = TasmotaLed_I2S; + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_I2S; } #endif // TASMOTALED_HARDWARE_I2S #if TASMOTALED_HARDWARE_SPI - if ((hw & 0xFF0000) == TasmotaLed_HW_Default) { - hw = TasmotaLed_SPI; + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_SPI; } #endif // TASMOTALED_HARDWARE_SPI return hw; @@ -75,22 +68,30 @@ TasmotaLEDPusher * TasmotaLEDPusher::Create(uint32_t hw, int8_t gpio) { hw = TasmotaLEDPusher::ResolveHardware(hw); - switch (hw & 0XFF0000) { #if TASMOTALED_HARDWARE_RMT - case TasmotaLed_RMT: - pusher = new TasmotaLEDPusherRMT(gpio); + if (pusher == nullptr && (hw & TasmotaLed_RMT)) { + pusher = new TasmotaLEDPusherRMT(gpio); + if (pusher->Initialized()) { AddLog(LOG_LEVEL_DEBUG, "LED: RMT gpio %i", gpio); - break; + } else { + AddLog(LOG_LEVEL_INFO, "LED: Error create %s bus failed %i err=%i", "RMT", gpio, pusher->Error()); + delete pusher; + pusher = nullptr; + } + } #endif // TASMOTALED_HARDWARE_RMT #if TASMOTALED_HARDWARE_SPI - case TasmotaLed_SPI: - pusher = new TasmotaLEDPusherSPI(gpio); + if (pusher == nullptr && (hw & TasmotaLed_SPI)) { + pusher = new TasmotaLEDPusherSPI(gpio); + if (pusher->Initialized()) { AddLog(LOG_LEVEL_DEBUG, "LED: SPI gpio %i", gpio); - break; -#endif // TASMOTALED_HARDWARE_SPI - default: - break; + } else { + AddLog(LOG_LEVEL_INFO, "LED: Error create %s bus failed %i err=%i", "SPI", gpio, pusher->Error()); + delete pusher; + pusher = nullptr; + } } +#endif // TASMOTALED_HARDWARE_SPI return pusher; } diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h index 2b8eb4896..89b402997 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h @@ -80,9 +80,11 @@ typedef struct TasmotaLED_Timing { \*******************************************************************************************/ class TasmotaLEDPusher { public: - TasmotaLEDPusher() : _pixel_count(0), _pixel_size(0), _led_timing(nullptr) {}; + TasmotaLEDPusher() : _initialized(false), _err(ESP_OK), _pixel_count(0), _pixel_size(0), _led_timing(nullptr) {}; virtual ~TasmotaLEDPusher() {}; + bool Initialized(void) const { return _initialized; } + esp_err_t Error(void) const { return _err; } virtual bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { _pixel_count = pixel_count; _pixel_size = pixel_size; @@ -96,6 +98,8 @@ public: static TasmotaLEDPusher * Create(uint32_t hw, int8_t gpio); // create instance for the provided type, or nullptr if failed protected: + bool _initialized; // did the hardware got correctly initialized + esp_err_t _err; uint16_t _pixel_count; uint16_t _pixel_size; const TasmotaLED_Timing * _led_timing; @@ -110,7 +114,7 @@ protected: #include "driver/rmt_tx.h" class TasmotaLEDPusherRMT : public TasmotaLEDPusher { public: - TasmotaLEDPusherRMT(int8_t pin) : _pin(pin) {}; + TasmotaLEDPusherRMT(int8_t pin); ~TasmotaLEDPusherRMT(); bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override; @@ -144,7 +148,7 @@ typedef struct led_strip_spi_obj_t { class TasmotaLEDPusherSPI : public TasmotaLEDPusher { public: - TasmotaLEDPusherSPI(int8_t pin) : _pin(pin), _spi_strip({}) {}; + TasmotaLEDPusherSPI(int8_t pin); ~TasmotaLEDPusherSPI(); bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override; @@ -154,7 +158,8 @@ public: protected: int8_t _pin; - struct led_strip_spi_obj_t _spi_strip; + struct led_strip_spi_obj_t _spi_strip = {};; + const bool _with_dma = true; }; #endif // TASMOTALED_HARDWARE_SPI diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp index 747985008..d6ca12197 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp @@ -155,9 +155,7 @@ TasmotaLEDPusherRMT::~TasmotaLEDPusherRMT() { } } -bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { - TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); - +TasmotaLEDPusherRMT::TasmotaLEDPusherRMT(int8_t pin) : _pin(pin) { esp_err_t ret = ESP_OK; rmt_tx_channel_config_t config = {}; config.clk_src = RMT_CLK_SRC_DEFAULT; @@ -168,11 +166,15 @@ bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const config.flags.invert_out = false; // do not invert output signal config.flags.with_dma = false; // do not need DMA backend - ret = rmt_new_tx_channel(&config, &_channel); - if (ret != ESP_OK) { - AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize Gpio %i err=%i", _pin, ret); - return false; + _err = rmt_new_tx_channel(&config, &_channel); + if (_err == ESP_OK) { + _initialized = true; } +} + +bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + if (!_initialized) { return false; } + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); led_strip_encoder_config_t encoder_config = { .resolution = RMT_LED_STRIP_RESOLUTION_HZ, }; @@ -198,13 +200,13 @@ bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const .level1 = 1, }; // AddLog(LOG_LEVEL_INFO, "RMT: RmtBit0 0x%08X RmtBit1 0x%08X RmtReset 0x%08X", RmtBit0.val, RmtBit1.val, RmtReset.val); - ret = rmt_new_led_strip_encoder(&encoder_config, &_led_encoder, RmtBit0, RmtBit1, RmtReset); - if (ret != ESP_OK) { + _err = rmt_new_led_strip_encoder(&encoder_config, &_led_encoder, RmtBit0, RmtBit1, RmtReset); + if (_err != ESP_OK) { // AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize led strip encoder err=%i", ret); return false; } - ret = rmt_enable(_channel); - if (ret != ESP_OK) { + _err = rmt_enable(_channel); + if (_err != ESP_OK) { // AddLog(LOG_LEVEL_INFO, "RMT: cannot enable channel err=%i", ret); return false; } @@ -212,7 +214,7 @@ bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const } bool TasmotaLEDPusherRMT::CanShow(void) { - if (_channel) { + if (_channel && _initialized) { return (ESP_OK == rmt_tx_wait_all_done(_channel, 0)); } else { return false; @@ -220,6 +222,7 @@ bool TasmotaLEDPusherRMT::CanShow(void) { } bool TasmotaLEDPusherRMT::Push(uint8_t *buf) { + if (!_initialized) { return false; } // wait for not actively sending data // this will time out at 1 second, an arbitrarily long period of time diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp index 72ea342ef..74366ebb8 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp @@ -95,31 +95,9 @@ TasmotaLEDPusherSPI::~TasmotaLEDPusherSPI() { } } -bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { - TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); - _spi_strip.bytes_per_pixel = _pixel_size; - _spi_strip.strip_len = _pixel_count; - - esp_err_t err = ESP_OK; - uint32_t mem_caps = MALLOC_CAP_DEFAULT; - // spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT; - spi_bus_config_t spi_bus_cfg; - spi_device_interface_config_t spi_dev_cfg; +TasmotaLEDPusherSPI::TasmotaLEDPusherSPI(int8_t pin) : _pin(pin) { spi_host_device_t spi_host = SPI2_HOST; - bool with_dma = true; /// TODO: pass value or compute based on pixelcount - int clock_resolution_khz = 0; - - if (with_dma) { // TODO - // DMA buffer must be placed in internal SRAM - mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; - } - _spi_strip.pixel_buf = (uint8_t *)heap_caps_calloc(1, _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, mem_caps); - if (_spi_strip.pixel_buf == nullptr) { - AddLog(LOG_LEVEL_INFO, PSTR("LED: Error no mem for spi strip")); - goto err; - } - - spi_bus_cfg = { + spi_bus_config_t spi_bus_cfg = { .mosi_io_num = _pin, //Only use MOSI to generate the signal, set -1 when other pins are not used. .miso_io_num = -1, @@ -128,32 +106,55 @@ bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const .quadhd_io_num = -1, .max_transfer_sz = _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, }; - err = spi_bus_initialize(spi_host, &spi_bus_cfg, with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED); - if (err != ESP_OK) { - AddLog(LOG_LEVEL_INFO, PSTR("LED: Error create SPI bus failed")); + _err = spi_bus_initialize(spi_host, &spi_bus_cfg, _with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED); + if (_err == ESP_OK) { + _spi_strip.spi_host = spi_host; // confirmed working, so keep it's value to free it later + _initialized = true; + } +} + +bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + if (!_initialized) { + return false; + } + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); + _spi_strip.bytes_per_pixel = _pixel_size; + _spi_strip.strip_len = _pixel_count; + + uint32_t mem_caps = MALLOC_CAP_DEFAULT; + // spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT; + spi_device_interface_config_t spi_dev_cfg; + int clock_resolution_khz = 0; + + if (_with_dma) { // TODO + // DMA buffer must be placed in internal SRAM + mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; + } + _spi_strip.pixel_buf = (uint8_t *)heap_caps_calloc(1, _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, mem_caps); + if (_spi_strip.pixel_buf == nullptr) { + AddLog(LOG_LEVEL_INFO, PSTR("LED: Error no mem for spi strip")); goto err; } - _spi_strip.spi_host = spi_host; // confirmed working, so keep it's value to free it later spi_dev_cfg = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = 0, - //set -1 when CS is not used - .clock_source = SPI_CLK_SRC_DEFAULT, // clk_src, - .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION, - .spics_io_num = -1, - .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE, + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, + //set -1 when CS is not used + .clock_source = SPI_CLK_SRC_DEFAULT, // clk_src, + .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION, + .spics_io_num = -1, + .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE, }; - err = spi_bus_add_device(_spi_strip.spi_host, &spi_dev_cfg, &_spi_strip.spi_device); - if (err != ESP_OK) { + _err = spi_bus_add_device(_spi_strip.spi_host, &spi_dev_cfg, &_spi_strip.spi_device); + if (_err != ESP_OK) { // AddLog(LOG_LEVEL_INFO, "LED: Error failed to add spi device"); goto err; } - spi_device_get_actual_freq(_spi_strip.spi_device, &clock_resolution_khz); - if (err != ESP_OK) { + _err = spi_device_get_actual_freq(_spi_strip.spi_device, &clock_resolution_khz); + if (_err != ESP_OK) { // AddLog(LOG_LEVEL_INFO, "LED: Error failed to get spi frequency"); goto err; } @@ -163,7 +164,6 @@ bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const // AddLog(LOG_LEVEL_INFO, "LED: Error unsupported clock resolution: %dKHz", clock_resolution_khz); goto err; } - return true; err: if (_spi_strip.spi_device) { @@ -172,15 +172,16 @@ err: if (_spi_strip.spi_host) { spi_bus_free(_spi_strip.spi_host); } + _initialized = false; return false; } bool TasmotaLEDPusherSPI::CanShow(void) { - return true; // TODO + return _initialized; // TODO } bool TasmotaLEDPusherSPI::Push(uint8_t *buf) { - + if (!_initialized) { return false; } if (CanShow()) { led_strip_transmit_buffer(&_spi_strip, buf); } diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino index dc5b47120..e3ad5aa54 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino @@ -95,20 +95,8 @@ const uint16_t kTasLed_PixelWhite = TasmotaLed_xxxW; const uint16_t kTasLed_Type = kTasLed_PixelSize | kTasLed_PixelOrder | kTasLed_PixelWhite | kTasLed_Timing; -// select hardware acceleration - bitbanging is not supported on ESP32 due to interference of interrupts -#if CONFIG_IDF_TARGET_ESP32C2 - const uint32_t kTasLed_Hardware = TasmotaLed_SPI; // no I2S for the C2 -#else // all other ESP32 variants - #if defined(USE_WS2812_DMA) - const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT - #elif defined(USE_WS2812_RMT) - const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT - #elif defined(USE_WS2812_I2S) - const uint32_t kTasLed_Hardware = TasmotaLed_I2S; // I2S - #else - const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT - #endif -#endif +// select hardware acceleration - bitbanging is not supported on ESP32 due to interference of interrupts - use default +const uint32_t kTasLed_Hardware = TasmotaLed_HW_Default; // use whatever is available #if (USE_WS2812_HARDWARE == NEO_HW_P9813) #error "P9813 is not supported by this library"