|
|
|
@@ -0,0 +1,218 @@ |
|
|
|
static const char *TAG = "rmt_leds"; |
|
|
|
#include <esp_log.h> |
|
|
|
|
|
|
|
#include <freertos/FreeRTOS.h> |
|
|
|
#include <freertos/task.h> |
|
|
|
|
|
|
|
#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 |
|
|
|
)); |
|
|
|
} |