diff --git a/main/rmt_leds.cpp b/main/rmt_leds.cpp new file mode 100644 index 0000000..92dd36a --- /dev/null +++ b/main/rmt_leds.cpp @@ -0,0 +1,218 @@ +static const char *TAG = "rmt_leds"; +#include + +#include +#include + +#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); +} + +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]; +} + +/* +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 + ) { + 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; + } + + 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; + } + } + + *ret_state = state; + return n_encoded; +} + +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; +} + +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; + } + return sym; +} + + +/*** *** *** +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"; +} + +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()); +} +*** *** ***/ + +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; + + //show_terminal_LEDs(pixels, length); + + esp_err_t err = rmt_tx_wait_all_done(channel, -1); + if (err) ESP_LOGE(TAG, "Failed to wait for TX complete: %d", err); + + ESP_ERROR_CHECK(rmt_transmit( + channel, encoder, + //pixels, length * sizeof(*pixels), + rmt_block, (length * 24 + 1) * sizeof(*rmt_block), + &config + )); +} diff --git a/main/rmt_leds.hpp b/main/rmt_leds.hpp new file mode 100644 index 0000000..69add17 --- /dev/null +++ b/main/rmt_leds.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "leds.hpp" + + +class RMT_LEDs : public LEDStrip { +public: + RMT_LEDs(int gpio, int length = 0); + void show(); + void length_changing(int len) { configure(len); } + +private: + int gpio; + rmt_channel_handle_t channel; + rmt_symbol_word_t *rmt_block; + + void configure(int length); +};