static const char *TAG = "rmt_leds"; #include #include #include #include "rmt_leds.hpp" #define WS2811_PULSE_WIDTH_NS 1250 #define RMT_TX_CHANNEL RMT_CHANNEL_0 static int bit_ticks; 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 ) { if (src == NULL || dest == NULL) { *translated_size = 0; *item_num = 0; return; } 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++; } *translated_size = size; *item_num = num; } 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; 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; } rmt_translator_init(channel, ws2811_rmt_adapter); // ns -> ticks bit_ticks = ((uint64_t)counter_clk_hz * WS2811_PULSE_WIDTH_NS) / 1e9; ESP_LOGI(TAG, "Each bit is %d RMT ticks", bit_ticks); configure(length); } RMT_LEDs::~RMT_LEDs() { ESP_ERROR_CHECK(rmt_driver_uninstall(channel)); } void RMT_LEDs::configure(int n_leds) { // We don't own the RMT buffer } void RMT_LEDs::show() { esp_err_t err; err = rmt_wait_tx_done(channel, 0); if (err != ESP_OK) { ESP_LOGE(TAG, "RMT TX incomplete: %d", err); return; } 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); } }