Browse Source

Add RMT support focusing on the S3 variant

S3
jrhoffa 3 years ago
parent
commit
fee54ad7ad
4 changed files with 84 additions and 195 deletions
  1. +1
    -0
      main/CMakeLists.txt
  2. +5
    -3
      main/main.cpp
  3. +73
    -187
      main/rmt_leds.cpp
  4. +5
    -5
      main/rmt_leds.hpp

+ 1
- 0
main/CMakeLists.txt View File

@@ -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"


+ 5
- 3
main/main.cpp View File

@@ -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


+ 73
- 187
main/rmt_leds.cpp View File

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

+ 5
- 5
main/rmt_leds.hpp View File

@@ -1,20 +1,20 @@
#pragma once

#include <driver/rmt_tx.h>
#include <driver/rmt.h>

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

Loading…
Cancel
Save