From fee54ad7ad1d91a2d524d9e01fbf6c4810cbaf62 Mon Sep 17 00:00:00 2001 From: jrhoffa Date: Sun, 11 Dec 2022 12:42:55 -0800 Subject: [PATCH] Add RMT support focusing on the S3 variant --- main/CMakeLists.txt | 1 + main/main.cpp | 8 +- main/rmt_leds.cpp | 260 +++++++++++++------------------------------- main/rmt_leds.hpp | 10 +- 4 files changed, 84 insertions(+), 195 deletions(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 93d7397..afba0c5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -12,6 +12,7 @@ idf_component_register( "presets.cpp" "spi_leds.cpp" "screen_leds.cpp" + "rmt_leds.cpp" "display.cpp" "patterns/gradient.cpp" "patterns/random.cpp" diff --git a/main/main.cpp b/main/main.cpp index 895cfce..4dc18ae 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -17,6 +17,7 @@ static const char *TAG = "blinky"; #include "utils.hpp" #include "spi_leds.hpp" #include "screen_leds.hpp" +#include "rmt_leds.hpp" // Dummy FS @@ -93,7 +94,7 @@ extern "C" void app_main(void) { // TODO: Scrape this out log_dir("/spiffs"); - Device device("blinky-jr"); + Device device("blinky3"); cJSON *json = cJSON_Parse(read_file(config_path).c_str()); if (json) { @@ -105,8 +106,9 @@ extern "C" void app_main(void) { // TerminalLEDs() // ScreenLEDs() // SPI_LEDs(39); - LEDStrip *LEDs = new SPI_LEDs(39); - int frequency = 60; + // RMT_LEDs(17); + LEDStrip *LEDs = new RMT_LEDs(GPIO_NUM_17); + int frequency = 30; while (true) { // Trash the old preset in case we can't find the set effect diff --git a/main/rmt_leds.cpp b/main/rmt_leds.cpp index 92dd36a..9e4b75f 100644 --- a/main/rmt_leds.cpp +++ b/main/rmt_leds.cpp @@ -7,212 +7,98 @@ static const char *TAG = "rmt_leds"; #include "rmt_leds.hpp" -RMT_LEDs::RMT_LEDs(int _gpio, int length) : - LEDStrip(length), gpio(_gpio), channel(NULL), rmt_block(NULL) { - rmt_tx_channel_config_t config = { - .gpio_num = gpio, - .clk_src = RMT_CLK_SRC_DEFAULT, - .resolution_hz = 3200000, // (1.25 us / 4) => 3.2 MHz - .mem_block_symbols = 64, // memory block size, 64 * 4 = 256Bytes - .trans_queue_depth = 1, // We don't really need background transfers - .flags = { - .invert_out = false, - .with_dma = false, // TODO: Why no DMA?? - .io_loop_back = false, - }, - }; - ESP_ERROR_CHECK(rmt_new_tx_channel(&config, &channel)); - ESP_ERROR_CHECK(rmt_enable(channel)); - configure(length); -} +#define WS2811_PULSE_WIDTH_NS 1250 -void RMT_LEDs::configure(int n_leds) { - if (rmt_block) { - delete[] rmt_block; - rmt_block = NULL; - } - if (n_leds <= 0) return; - rmt_block = new rmt_symbol_word_t[n_leds * 24 + 1]; -} +#define RMT_TX_CHANNEL RMT_CHANNEL_0 + + +static int bit_ticks; -/* -static rmt_encoder_handle_t byte_enc; -static const rmt_bytes_encoder_config_t byte_enc_config = { - .bit0 = { - .duration0 = 1, - .level0 = 1, - .duration1 = 3, - .level1 = 0, - }, - .bit1 = { - .duration0 = 2, - .level0 = 1, - .duration1 = 2, - .level1 = 0, - }, - .flags = { - .msb_first = 1, - }, -}; -static esp_err_t bytes_enc_ok = rmt_new_bytes_encoder(&byte_enc_config, &byte_enc); - -static rmt_encoder_handle_t rst_enc; -static const rmt_copy_encoder_config_t rst_enc_config = {}; -static esp_err_t rst_enc_ok = rmt_new_copy_encoder(&rst_enc_config, &rst_enc); -static const rmt_symbol_word_t rst_symbol = { - .duration0 = 80, - .level0 = 0, - .duration1 = 80, - .level1 = 0, -}; - -struct strip_encoder { - rmt_encoder_t base; - int state; -}; - -static size_t encode_strip( - rmt_encoder_t *_encoder, rmt_channel_handle_t channel, - const void *data, size_t n_bytes, rmt_encode_state_t *ret_state + +static void IRAM_ATTR ws2811_rmt_adapter( + const void *src, rmt_item32_t *dest, size_t src_size, + size_t wanted_num, size_t *translated_size, size_t *item_num ) { - strip_encoder *encoder = __containerof(_encoder, strip_encoder, base); - size_t n_encoded = 0; - rmt_encode_state_t substate = 0; - rmt_encode_state_t state = 0; - - switch(encoder->state) { - case 0: - n_encoded += byte_enc.encode(&byte_enc, channel, data, n_bytes, &substate); - if (substate & RMT_ENCODING_COMPLETE) { - encoder->state = 1; - break; - } + if (src == NULL || dest == NULL) { + *translated_size = 0; + *item_num = 0; + return; + } - case 1: - n_encoded += rst_enc.encode( - &rst_enc, channel, &rst_symbol, sizeof(rst_symbol), &substate); - if (substate & RMT_ENCODING_COMPLETE) { - encoder->state = 0; - state |= RMT_ENCODING_COMPLETE; - } - if (substate & RMT_ENCODING_MEM_FULL) { - state |= RMT_ENCODING_MEM_FULL; + const uint32_t short_ticks = bit_ticks / 3; + const uint32_t long_ticks = bit_ticks - short_ticks; + const rmt_item32_t bit0 = {{{ short_ticks, 1, long_ticks, 0 }}}; // Logical 0 + const rmt_item32_t bit1 = {{{ long_ticks, 1, short_ticks, 0 }}}; // Logical 1 + size_t size = 0; + size_t num = 0; + uint8_t *psrc = (uint8_t *)src; + rmt_item32_t *pdest = dest; + while (size < src_size && num < wanted_num) { + for (int i = 0; i < 8; i++) { + // MSB first + if (*psrc & (1 << (7 - i))) { + pdest->val = bit1.val; + } else { + pdest->val = bit0.val; + } + num++; + pdest++; } + size++; + psrc++; } - *ret_state = state; - return n_encoded; + *translated_size = size; + *item_num = num; } -static esp_err_t reset_strip_encoder(rmt_encoder_t *encoder) { - strip_encoder *encoder = __containerof(encoder, strip_encoder, base); - rmt_encoder_reset(byte_enc); - rmt_encoder_reset(rst_enc); - encoder->state = 0; - return ESP_OK; -} -static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { - strip_encoder *encoder = __containerof(encoder, strip_encoder, base); - delete encoder; - return ESP_OK; -} +RMT_LEDs::RMT_LEDs(gpio_num_t _gpio, int length) : + LEDStrip(length), gpio(_gpio), channel(RMT_CHANNEL_MAX) { + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, RMT_TX_CHANNEL); + // set counter clock to 40MHz + config.clk_div = 2; -static rmt_encoder_t* make_strip_encoder() { - enc = new strip_encoder { - .base = { - .encode = encode_strip, - .reset = reset_strip_encoder, - .del = del_strip_encoder, - }, - .state = 0 - } -} -*/ - - -static rmt_encoder_handle_t encoder; -static const rmt_copy_encoder_config_t copy_encoder_config = {}; -static esp_err_t copy_encoder_ok = rmt_new_copy_encoder( - ©_encoder_config, &encoder - ); - -constexpr rmt_symbol_word_t sym_0 = { - .duration0 = 1, - .level0 = 1, - .duration1 = 3, - .level1 = 0, - }; - -constexpr rmt_symbol_word_t sym_1 = { - .duration0 = 2, - .level0 = 1, - .duration1 = 2, - .level1 = 0, - }; - -constexpr rmt_symbol_word_t sym_reset = { - .duration0 = 80, - .level0 = 0, - .duration1 = 80, - .level1 = 0, -}; - -static inline rmt_symbol_word_t* encode_byte(rmt_symbol_word_t *sym, uint8_t x) { - for (int i = 8; i > 0; --i) { - *sym = (x & 0x80) ? sym_1 : sym_0; - x <<= 1; - ++sym; + ESP_ERROR_CHECK(rmt_config(&config)); + channel = config.channel; + ESP_ERROR_CHECK(rmt_driver_install(channel, 0, 0)); + + uint32_t counter_clk_hz = 0; + int err = rmt_get_counter_clock(channel, &counter_clk_hz); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to read RMT counter: %d", err); + return; } - return sym; -} + rmt_translator_init(channel, ws2811_rmt_adapter); + + // ns -> ticks + bit_ticks = ((uint64_t)counter_clk_hz * WS2811_PULSE_WIDTH_NS) / 1e9; -/*** *** *** -static std::string termcolor(Color c) { - return "\x1b[48;2;" + - std::to_string((int)c.r) + ";" + - std::to_string((int)c.g) + ";" + - std::to_string((int)c.b) + "m"; + ESP_LOGI(TAG, "Each bit is %d RMT ticks", bit_ticks); + + configure(length); } -static void show_terminal_LEDs(const Color *pixels, int length) { - std::string line; - for (int i = 0; i < length; i++) { - line += termcolor(pixels[i]) + " "; - } - line += "\x1b[0m"; - ESP_LOGI(TAG, "%s", line.c_str()); +RMT_LEDs::~RMT_LEDs() { + ESP_ERROR_CHECK(rmt_driver_uninstall(channel)); } -*** *** ***/ -void RMT_LEDs::show() { - static const rmt_transmit_config_t config = { - .loop_count = 0, - .flags = { - .eot_level = 0, - }, - }; - - rmt_symbol_word_t *sym = rmt_block; - Color *pixel = pixels; - for (int i = length; i > 0; --i) { - sym = encode_byte(sym, pixel->r); - sym = encode_byte(sym, pixel->g); - sym = encode_byte(sym, pixel->b); - ++pixel; - } - *sym++ = sym_reset; +void RMT_LEDs::configure(int n_leds) { + // We don't own the RMT buffer +} - //show_terminal_LEDs(pixels, length); +void RMT_LEDs::show() { + esp_err_t err; - esp_err_t err = rmt_tx_wait_all_done(channel, -1); - if (err) ESP_LOGE(TAG, "Failed to wait for TX complete: %d", err); + err = rmt_wait_tx_done(channel, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "RMT TX incomplete: %d", err); + return; + } - ESP_ERROR_CHECK(rmt_transmit( - channel, encoder, - //pixels, length * sizeof(*pixels), - rmt_block, (length * 24 + 1) * sizeof(*rmt_block), - &config - )); + err = rmt_write_sample(channel, (uint8_t*)pixels, length * 3, false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write sample to RMT: %d", err); + } } diff --git a/main/rmt_leds.hpp b/main/rmt_leds.hpp index 69add17..d6924a4 100644 --- a/main/rmt_leds.hpp +++ b/main/rmt_leds.hpp @@ -1,20 +1,20 @@ #pragma once -#include +#include #include "leds.hpp" class RMT_LEDs : public LEDStrip { public: - RMT_LEDs(int gpio, int length = 0); + RMT_LEDs(gpio_num_t gpio, int length = 0); + ~RMT_LEDs(); void show(); void length_changing(int len) { configure(len); } private: - int gpio; - rmt_channel_handle_t channel; - rmt_symbol_word_t *rmt_block; + gpio_num_t gpio; + rmt_channel_t channel; void configure(int length); };