WSLed use new RMT driver
This commit is contained in:
101
src/WSLed.cpp
101
src/WSLed.cpp
@@ -3,17 +3,10 @@
|
|||||||
#include <ACPU.hpp>
|
#include <ACPU.hpp>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
// # include "esp_task.h"
|
// WS2812 timing specifications (in microseconds)
|
||||||
// https://github.com/JSchaenzle/ESP32-NeoPixel-WS2812-RMT/blob/master/ws2812_control.c
|
// Bit 0: T0H=0.3us, T0L=0.9us
|
||||||
#include <set>
|
// Bit 1: T1H=0.9us, T1L=0.3us
|
||||||
#ifndef LED_RMT_TX_CHANNEL
|
// Reset: 50us low
|
||||||
#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
|
|
||||||
#endif
|
#endif
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
// due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are
|
// 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;
|
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() {
|
void wsLED::show() {
|
||||||
uint8_t color[3];
|
uint8_t color[3];
|
||||||
|
|
||||||
@@ -53,16 +50,17 @@ void wsLED::show() {
|
|||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
}
|
}
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
uint32_t bits_to_send = ((uint32_t) color[0] << 16) | ((uint32_t) color[1] << 8) | (uint32_t) color[2];
|
// Transmit the 3-byte RGB data using the bytes encoder
|
||||||
uint32_t mask = 1 << (BITS_PER_LED_CMD - 1);
|
// The encoder automatically converts each bit to WS2812 timing
|
||||||
for (auto & bit : led_data_buffer) {
|
rmt_transmit_config_t tx_config = {};
|
||||||
uint32_t bit_is_set = bits_to_send & mask;
|
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}}};
|
ESP_ERROR_CHECK(rmt_transmit(mRmtChannel, mRmtEncoder, color, sizeof(color), &tx_config));
|
||||||
mask >>= 1;
|
|
||||||
}
|
// Wait for transmission to complete (WS2812 needs reset time)
|
||||||
ESP_ERROR_CHECK(rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false));
|
ESP_ERROR_CHECK(rmt_tx_wait_all_done(mRmtChannel, 100));
|
||||||
mEndTime = micros() + 100; // TODO: unfortunately this time is not valid for ESP32 using RMT peripheral
|
|
||||||
|
mEndTime = micros();
|
||||||
#else
|
#else
|
||||||
ACPU::Interrupts::disable();
|
ACPU::Interrupts::disable();
|
||||||
#if defined(WSLED_PL9823)
|
#if defined(WSLED_PL9823)
|
||||||
@@ -83,6 +81,24 @@ wsLED::wsLED()
|
|||||||
: mPin(NOT_A_PIN), mOrder(RGB) {
|
: 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)
|
void wsLED::begin(int pin)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -90,6 +106,10 @@ void wsLED::begin(int pin)
|
|||||||
begin();
|
begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the WS2812 LED using the new RMT driver.
|
||||||
|
* Creates RMT TX channel and bytes encoder for WS2812 timing.
|
||||||
|
*/
|
||||||
void wsLED::begin() {
|
void wsLED::begin() {
|
||||||
|
|
||||||
if (NOT_A_PIN != mPin) {
|
if (NOT_A_PIN != mPin) {
|
||||||
@@ -97,21 +117,36 @@ void wsLED::begin() {
|
|||||||
pinMode(mPin, OUTPUT);
|
pinMode(mPin, OUTPUT);
|
||||||
#endif
|
#endif
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
// TODO: mozna nejak zkusit najit volny RMT kanal?
|
// Configure RMT TX channel
|
||||||
rmt_config_t config;
|
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;
|
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &mRmtChannel));
|
||||||
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_config(&config));
|
// Configure bytes encoder with WS2812 timing
|
||||||
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
|
// 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
|
#endif
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ SOFTWARE.
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Ticker.h>
|
#include <Ticker.h>
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
#include <driver/rmt.h>
|
#include <driver/rmt_tx.h>
|
||||||
#define BITS_PER_LED_CMD 24
|
#include <driver/rmt_encoder.h>
|
||||||
#define LED_BUFFER_ITEMS ((1 * BITS_PER_LED_CMD))
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct LEDRGB {
|
struct LEDRGB {
|
||||||
@@ -64,13 +63,13 @@ struct LEDRGB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow construction from R, G, B
|
// 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)
|
: r(ir), g(ig), b(ib)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code
|
// 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)
|
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -141,8 +140,9 @@ class wsLED {
|
|||||||
uint32_t mBlinkSpeed = 0;
|
uint32_t mBlinkSpeed = 0;
|
||||||
LEDRGB mPulseColor;
|
LEDRGB mPulseColor;
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
int _rmtChannel = 0;
|
rmt_channel_handle_t mRmtChannel = nullptr;
|
||||||
rmt_item32_t led_data_buffer[LED_BUFFER_ITEMS] = {0};
|
rmt_encoder_handle_t mRmtEncoder = nullptr;
|
||||||
|
uint32_t mResolutionHz = 10000000; // 10MHz resolution for precise timing
|
||||||
#endif
|
#endif
|
||||||
uint32_t mEndTime = 0;
|
uint32_t mEndTime = 0;
|
||||||
uint8_t mPrevColor[3] = {1, 2, 3}; // some not used colors
|
uint8_t mPrevColor[3] = {1, 2, 3}; // some not used colors
|
||||||
@@ -161,6 +161,7 @@ class wsLED {
|
|||||||
public:
|
public:
|
||||||
explicit wsLED(int pin, int order = RGB);
|
explicit wsLED(int pin, int order = RGB);
|
||||||
wsLED();
|
wsLED();
|
||||||
|
~wsLED();
|
||||||
void begin();
|
void begin();
|
||||||
void begin(int pin);
|
void begin(int pin);
|
||||||
void setColors(const LEDRGB & color1, const LEDRGB & color2 = LEDRGB(0, 0, 0));
|
void setColors(const LEDRGB & color1, const LEDRGB & color2 = LEDRGB(0, 0, 0));
|
||||||
|
|||||||
Reference in New Issue
Block a user