|
|
|
@@ -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); |
|
|
|
} |
|
|
|
} |