WSLed use new RMT driver

This commit is contained in:
2026-02-13 18:58:03 +01:00
parent 8e022f5935
commit aaa5646c8a
2 changed files with 76 additions and 40 deletions

View File

@@ -3,17 +3,10 @@
#include <ACPU.hpp>
#include <cmath>
#if defined(ARDUINO_ARCH_ESP32)
// # include "esp_task.h"
// https://github.com/JSchaenzle/ESP32-NeoPixel-WS2812-RMT/blob/master/ws2812_control.c
#include <set>
#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();
}

View File

@@ -22,9 +22,8 @@ SOFTWARE.
#include <Arduino.h>
#include <Ticker.h>
#if defined(ARDUINO_ARCH_ESP32)
#include <driver/rmt.h>
#define BITS_PER_LED_CMD 24
#define LED_BUFFER_ITEMS ((1 * BITS_PER_LED_CMD))
#include <driver/rmt_tx.h>
#include <driver/rmt_encoder.h>
#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));