Skip to content

RMT: using rmt_write_sample async with all 8 channels has signficant staggered starts of last few channels #2885

Closed
@Makuna

Description

@Makuna

Hardware:

Board: ESP32 Dev Module
Core Installation version: 1.0.2 (latest public release)
IDE name: Arduino IDE
Flash Frequency: 40Mhz (default)
PSRAM enabled: no (default)
Upload Speed: 921600 (default)
Computer OS: Windows 10

Description:

When using the rmt_write_sample, with no wait (async) with all 8 channels, the last few channels have significant delay before the pulses are started.

image
Channel 5 has a 1.5ms stagger from the first, which is significant from the first four.
Channel 6 has a 14.5ms stagger from the first (as captured above as dx field), which seems way out of reasonability. This seems related to a channel buffer becoming available. The timing shows that while the last pulse of channel 0 is still presently being sent, it is just sending the extended low side of the last pulse; so it will not be calling translate again for that channel; seemingly making it available to start sending channel 6.

Sketch:

extern "C"
{
#include <driver/rmt.h>
}

const size_t dataSize = 1500;
uint8_t* data;

// selected to be in order on the board for easy connection of the 
// logic analyser 
const uint8_t ChannelPins[] = {19, 18, 5, 17, 16, 4, 2, 15};


static void IRAM_ATTR _translate(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;
    }

    size_t size = 0;
    size_t num = 0;
    uint8_t *psrc = (uint8_t *)src;
    rmt_item32_t* pdest = dest;

    for (;;) {
        uint8_t data = *psrc;

        // convert a byte into rmt item timing
        // zero bit pulse = 200ns 1000ns = 8 cycles 40 cycles  = 0x0028 8008 as rmt item val
        // one bit pulse = 1000ns 200ns = 40 cycles 8 cycles  = 0x0008 8028 as rmt item val
        for (uint8_t bit = 0; bit < 8; bit++) {
            pdest->val = (data & 0x80) ? 0x00088028 : 0x00288008;
            pdest++;
            data <<= 1;
        }
        num += 8;
        size++;

        // if this is the last byte we need to adjust the length of the last pulse
        if (size >= src_size) {
            // extend the last bits LOW value to include the full reset signal length
            pdest--;
            pdest->duration1 = 20000; // 500us reset
            // and stop updating data to send
            break; 
        }

        if (num >= wanted_num) {
            // stop updating data to send
            break;
        }

        psrc++;
    }

    *translated_size = size;
    *item_num = num;
}

void initChannel(rmt_channel_t ch, gpio_num_t pin) {
    rmt_config_t config;

    config.rmt_mode = RMT_MODE_TX;
    config.channel = ch;
    config.gpio_num = pin;
    config.mem_block_num = 1;
    config.tx_config.loop_en = false;
        
    config.tx_config.idle_output_en = true;
    config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;

    config.tx_config.carrier_en = false;
    config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;

    config.clk_div = 2; 

    rmt_config(&config);
    rmt_driver_install(ch, 0, 0);
    rmt_translator_init(ch, _translate);
}

void writeChannel(rmt_channel_t ch) {
    // wait for the last send to complete
    if (ESP_OK == rmt_wait_tx_done(ch, 10000 / portTICK_PERIOD_MS)) {
        // then start a new async send
        rmt_write_sample(ch, data, dataSize, false);
    }
}

void waitForAllDone()
{
  bool done = false;

  while (!done) {
    done = true;
    for (uint8_t ch = 0; ch < RMT_CHANNEL_MAX; ch++) {
        if (ESP_OK != rmt_wait_tx_done(static_cast<rmt_channel_t>(ch), 0)) {
            done = false;
            yield();
            break;
        }
    }
  }
}

void setup() {
  data = (uint8_t*)malloc(dataSize);
  memset(data, 0x00, dataSize);

  for (uint8_t ch = 0; ch < RMT_CHANNEL_MAX; ch++) {
    initChannel(static_cast<rmt_channel_t>(ch), static_cast<gpio_num_t>(ChannelPins[ch]));
  }
}

void loop() {
  // start all channels, then wait for them to be sent, then start all channels again
  // 
  for (uint8_t ch = 0; ch < RMT_CHANNEL_MAX; ch++) {
    writeChannel(static_cast<rmt_channel_t>(ch));
  }
  waitForAllDone();
  for (uint8_t ch = 0; ch < RMT_CHANNEL_MAX; ch++) {
    writeChannel(static_cast<rmt_channel_t>(ch));
  }

  // repeat with a long delay between so it can easily be captured in the logic analyser
  //
  delay(5000);
}

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions