From aaa5646c8a46eaddad4a86b2d7ac84e7f52aed3b Mon Sep 17 00:00:00 2001 From: Pablo2048 Date: Fri, 13 Feb 2026 18:58:03 +0100 Subject: [PATCH] WSLed use new RMT driver --- src/WSLed.cpp | 101 +++++++++++++++++++++++++++++++++----------------- src/WSLed.hpp | 15 ++++---- 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/src/WSLed.cpp b/src/WSLed.cpp index 922227a..1859059 100644 --- a/src/WSLed.cpp +++ b/src/WSLed.cpp @@ -3,17 +3,10 @@ #include #include #if defined(ARDUINO_ARCH_ESP32) - // # include "esp_task.h" - // https://github.com/JSchaenzle/ESP32-NeoPixel-WS2812-RMT/blob/master/ws2812_control.c - #include - #ifndef LED_RMT_TX_CHANNEL - #define LED_RMT_TX_CHANNEL RMT_CHANNEL_0 - #endif - // These values are determined by measuring pulse timing with logic analyzer and adjusting to match datasheet. - #define T0H 16 // 0 bit high time - #define T1H 32 // 1 bit high time - #define T0L 34 // 0 bit low time - #define T1L 18 // 1 bit low time + // WS2812 timing specifications (in microseconds) + // Bit 0: T0H=0.3us, T0L=0.9us + // Bit 1: T1H=0.9us, T1L=0.3us + // Reset: 50us low #endif #if defined(ARDUINO_ARCH_ESP8266) // due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are @@ -37,6 +30,10 @@ inline bool wsLED::canShow() const { return (micros() - mEndTime) >= 70L; } +/** + * Send the current color to the WS2812 LED using RMT transmit. + * The bytes encoder handles the timing conversion automatically. + */ void wsLED::show() { uint8_t color[3]; @@ -53,16 +50,17 @@ void wsLED::show() { __asm__ volatile("nop"); } #if defined(ARDUINO_ARCH_ESP32) - uint32_t bits_to_send = ((uint32_t) color[0] << 16) | ((uint32_t) color[1] << 8) | (uint32_t) color[2]; - uint32_t mask = 1 << (BITS_PER_LED_CMD - 1); - for (auto & bit : led_data_buffer) { - uint32_t bit_is_set = bits_to_send & mask; + // Transmit the 3-byte RGB data using the bytes encoder + // The encoder automatically converts each bit to WS2812 timing + rmt_transmit_config_t tx_config = {}; + tx_config.loop_count = 0; // no looping - bit = bit_is_set ? (rmt_item32_t){{{T1H, 1, T1L, 0}}} : (rmt_item32_t){{{T0H, 1, T0L, 0}}}; - mask >>= 1; - } - ESP_ERROR_CHECK(rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false)); - mEndTime = micros() + 100; // TODO: unfortunately this time is not valid for ESP32 using RMT peripheral + ESP_ERROR_CHECK(rmt_transmit(mRmtChannel, mRmtEncoder, color, sizeof(color), &tx_config)); + + // Wait for transmission to complete (WS2812 needs reset time) + ESP_ERROR_CHECK(rmt_tx_wait_all_done(mRmtChannel, 100)); + + mEndTime = micros(); #else ACPU::Interrupts::disable(); #if defined(WSLED_PL9823) @@ -83,6 +81,24 @@ wsLED::wsLED() : mPin(NOT_A_PIN), mOrder(RGB) { } +/** + * Destructor - cleanup RMT resources. + * Properly delete encoder and channel handles to prevent memory leaks. + */ +wsLED::~wsLED() { +#if defined(ARDUINO_ARCH_ESP32) + if (mRmtEncoder != nullptr) { + rmt_del_encoder(mRmtEncoder); + mRmtEncoder = nullptr; + } + if (mRmtChannel != nullptr) { + rmt_disable(mRmtChannel); + rmt_del_channel(mRmtChannel); + mRmtChannel = nullptr; + } +#endif +} + void wsLED::begin(int pin) { @@ -90,6 +106,10 @@ void wsLED::begin(int pin) begin(); } +/** + * Initialize the WS2812 LED using the new RMT driver. + * Creates RMT TX channel and bytes encoder for WS2812 timing. + */ void wsLED::begin() { if (NOT_A_PIN != mPin) { @@ -97,21 +117,36 @@ void wsLED::begin() { pinMode(mPin, OUTPUT); #endif #if defined(ARDUINO_ARCH_ESP32) - // TODO: mozna nejak zkusit najit volny RMT kanal? - rmt_config_t config; + // Configure RMT TX channel + rmt_tx_channel_config_t tx_chan_config = {}; + tx_chan_config.clk_src = RMT_CLK_SRC_DEFAULT; + tx_chan_config.gpio_num = (gpio_num_t) mPin; + tx_chan_config.mem_block_symbols = 64; + tx_chan_config.resolution_hz = mResolutionHz; + tx_chan_config.trans_queue_depth = 4; + tx_chan_config.flags.invert_out = false; + tx_chan_config.flags.with_dma = false; - config.rmt_mode = RMT_MODE_TX; - config.channel = LED_RMT_TX_CHANNEL; - config.gpio_num = (gpio_num_t) mPin; - config.mem_block_num = 3; - config.tx_config.loop_en = false; - config.tx_config.carrier_en = false; - config.tx_config.idle_output_en = true; - config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - config.clk_div = 2; + ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &mRmtChannel)); - ESP_ERROR_CHECK(rmt_config(&config)); - ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); + // Configure bytes encoder with WS2812 timing + // Calculate durations based on resolution (10MHz = 0.1us per tick) + // T0H=0.3us, T0L=0.9us, T1H=0.9us, T1L=0.3us + rmt_bytes_encoder_config_t bytes_encoder_config = {}; + bytes_encoder_config.bit0.level0 = 1; + bytes_encoder_config.bit0.duration0 = 0.3 * mResolutionHz / 1000000; // 3 ticks for 0.3us + bytes_encoder_config.bit0.level1 = 0; + bytes_encoder_config.bit0.duration1 = 0.9 * mResolutionHz / 1000000; // 9 ticks for 0.9us + bytes_encoder_config.bit1.level0 = 1; + bytes_encoder_config.bit1.duration0 = 0.9 * mResolutionHz / 1000000; // 9 ticks for 0.9us + bytes_encoder_config.bit1.level1 = 0; + bytes_encoder_config.bit1.duration1 = 0.3 * mResolutionHz / 1000000; // 3 ticks for 0.3us + bytes_encoder_config.flags.msb_first = 1; // WS2812 expects MSB first + + ESP_ERROR_CHECK(rmt_new_bytes_encoder(&bytes_encoder_config, &mRmtEncoder)); + + // Enable the channel + ESP_ERROR_CHECK(rmt_enable(mRmtChannel)); #endif show(); } diff --git a/src/WSLed.hpp b/src/WSLed.hpp index 4d6abb8..406cfce 100644 --- a/src/WSLed.hpp +++ b/src/WSLed.hpp @@ -22,9 +22,8 @@ SOFTWARE. #include #include #if defined(ARDUINO_ARCH_ESP32) - #include - #define BITS_PER_LED_CMD 24 - #define LED_BUFFER_ITEMS ((1 * BITS_PER_LED_CMD)) + #include + #include #endif struct LEDRGB { @@ -64,13 +63,13 @@ struct LEDRGB { } // allow construction from R, G, B - inline LEDRGB(uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline)) + inline LEDRGB(const uint8_t ir, const uint8_t ig, const uint8_t ib) __attribute__((always_inline)) : r(ir), g(ig), b(ib) { } // allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code - inline LEDRGB(uint32_t colorcode) __attribute__((always_inline)) + inline explicit LEDRGB(uint32_t colorcode) __attribute__((always_inline)) : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) { } @@ -141,8 +140,9 @@ class wsLED { uint32_t mBlinkSpeed = 0; LEDRGB mPulseColor; #if defined(ARDUINO_ARCH_ESP32) - int _rmtChannel = 0; - rmt_item32_t led_data_buffer[LED_BUFFER_ITEMS] = {0}; + rmt_channel_handle_t mRmtChannel = nullptr; + rmt_encoder_handle_t mRmtEncoder = nullptr; + uint32_t mResolutionHz = 10000000; // 10MHz resolution for precise timing #endif uint32_t mEndTime = 0; uint8_t mPrevColor[3] = {1, 2, 3}; // some not used colors @@ -161,6 +161,7 @@ class wsLED { public: explicit wsLED(int pin, int order = RGB); wsLED(); + ~wsLED(); void begin(); void begin(int pin); void setColors(const LEDRGB & color1, const LEDRGB & color2 = LEDRGB(0, 0, 0));