ESP32 / RMT loosing ticks /

dirkxus
Posts: 3
Joined: Fri Sep 21, 2018 12:13 pm

ESP32 / RMT loosing ticks /

Postby dirkxus » Fri Sep 21, 2018 12:24 pm

I am having an odd issue with the latest version of IDF (which may well be a regression introduced with 1.0.0).

Every 5 to 100 seconds the RMT output seems to miss a tick/beat or show some other glitch:

Image
Image

Below is a simplified example which still exhibits the issue. The code outputs simply 13 1's and then 13 0's within 400 RMT items; these are managed in typical 'double buffer' style.

Does this ring a bell with anyone ? I was assuming the RTM to be totally hardware based and essentially un-fased by anything happening elsewhere (and in the example below - loop() is empty; there is no WiFi -- and I still see the glitches).

Note that running it contineously without any refresh works fine; and that the digitalWrite() in the IRQ shows it getting called regularly at a steady clip.

Suggestions how to get to the bottom of this appreciated.

Dw.

Code: Select all

/* Simple 'RMT' example which uses a 'double buffering' method
 *  to populate the other buffer once it starts on the second one.
 * 
 *  (c) Copyright 2018 Dirk-Willem van Gulik - All rights reserved.
 *      Under the Apache Software License version 2.0.
 */

#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define RMT_TX_CHANNEL (RMT_CHANNEL_0)
#define RMT_TX_GPIO (GPIO_NUM_5)

// Lenght of 1's or 0's to emit until toggling to the other.
//
#define RUNLENGTH (13)

// Number of bits in our buffer -
#define ITEM_CNT_HALF (200)

#if ITEM_CNT_HALF*2 > 512
#error Only 512 rmt_item32 items for both our buffers.
#endif

// Our run spans multiple bottom/top halfs; so we keep a global counter./
static unsigned roll = 0;

// Keep track of wether we do the top of bottom half.
//
static unsigned int at = 0;
 
void IRAM_ATTR rmt_isr_handler(void *arg) {
  RMT.int_clr.ch0_tx_thr_event = 1;

  fillTopOrBottomHalf();
}


void setup() {
  rmt_config_t config;
  pinMode(RMT_TX_GPIO, OUTPUT);

  config.rmt_mode = RMT_MODE_TX;
  config.channel = RMT_TX_CHANNEL;
  config.gpio_num = RMT_TX_GPIO;

  config.mem_block_num = ((ITEM_CNT_HALF*2) / 64 + 1);
  config.clk_div = 1;

  config.tx_config.loop_en = 1;

  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;


  ESP_ERROR_CHECK(rmt_config(&config));
  ESP_ERROR_CHECK(rmt_set_source_clk(RMT_TX_CHANNEL, RMT_BASECLK_APB)); // 80 Mhz.
  ESP_ERROR_CHECK(rmt_isr_register(rmt_isr_handler, NULL, ESP_INTR_FLAG_LEVEL1, 0));

  // Fill the entire block with 'end of block' markers.
  for (int i = 0; i < config.mem_block_num; i++)
    for (int j = 0 ; j < 64; j++)
      RMTMEM.chan[RMT_TX_CHANNEL + i ].data32[j].val = 0;

  fillTopOrBottomHalf();
  fillTopOrBottomHalf();

  /* Trigger the interupt every ITEM_CNT_HALF entries; i.e. at the start of the second block
   * or the start of the first block - while the other is getting sent out.
   */
  ESP_ERROR_CHECK(rmt_set_tx_thr_intr_en(RMT_TX_CHANNEL, true, ITEM_CNT_HALF));
  ESP_ERROR_CHECK(rmt_tx_start(RMT_TX_CHANNEL, true));
}

void fillTopOrBottomHalf()
 { 
  // specify which half we do; top or bottom.
  //
  if (at == ITEM_CNT_HALF) at = 0; else at = ITEM_CNT_HALF;

  for (int i = 0; i < ITEM_CNT_HALF; i++) {
    unsigned char level1 = 0 , level2 = 0;

    if (roll < RUNLENGTH) level1  = 1;
 
    roll++;
    if (roll >= RUNLENGTH*2) roll = 0;

    if (roll < RUNLENGTH) level2  = 1;

    roll++;   
    if (roll >= RUNLENGTH*2) roll = 0;

    rmt_item32_t w =   {{{ 400, level1, 400, level2 }}};
    RMTMEM.chan[RMT_TX_CHANNEL].data32[at + i].val = w.val;
  }
}

void loop() {
}

dirkxus
Posts: 3
Joined: Fri Sep 21, 2018 12:13 pm

Re: ESP32 / RMT loosing ticks /

Postby dirkxus » Fri Sep 21, 2018 6:27 pm

Updated version - which should make it easier to understand. Still shows glitches though.

Code: Select all

/* Simple 'RMT' example which uses a 'double buffering' method
    to populate the other buffer once it starts on the second one.

    (c) Copyright 2018 Dirk-Willem van Gulik - All rights reserved.
        Under the Apache Software License version 2.0.
*/

#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define RMT_TX_CHANNEL (RMT_CHANNEL_0)
#define RMT_TX_GPIO (GPIO_NUM_5)

// Lenght of 1's or 0's to emit until toggling to the other.
//
#define RUNLENGTH (7)

// Number of bits in our buffer -
#define ITEM_CNT_HALF (200)

#if ITEM_CNT_HALF*2 > 512
#error Only 512 rmt_item32 items for both our buffers.
#endif

// Our run spans multiple bottom/top halfs; so we keep a global counter./
static unsigned roll = 0;

// Keep track of wether we do the top of bottom half.
//
static unsigned int at = 0;

void IRAM_ATTR rmt_isr_handler(void *arg) {
  RMT.int_clr.ch0_tx_thr_event = 1;
  RMT.apb_conf.fifo_mask = RMT_DATA_MODE_MEM;
  digitalWrite(GPIO_NUM_13, !digitalRead(GPIO_NUM_13));
  fillTopOrBottomHalf();
  digitalWrite(GPIO_NUM_15, at != 0);
}


void setup() {
  rmt_config_t config;
  pinMode(RMT_TX_GPIO, OUTPUT);

  pinMode(GPIO_NUM_13, OUTPUT);
  pinMode(GPIO_NUM_15, OUTPUT);

  config.rmt_mode = RMT_MODE_TX;
  config.channel = RMT_TX_CHANNEL;
  config.gpio_num = RMT_TX_GPIO;

  config.mem_block_num = ((ITEM_CNT_HALF * 2) / 64 + 1);
  config.clk_div = 1;

  config.tx_config.loop_en = 1;

  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;


  ESP_ERROR_CHECK(rmt_config(&config));
  ESP_ERROR_CHECK(rmt_set_source_clk(RMT_TX_CHANNEL, RMT_BASECLK_APB)); // 80 Mhz.
  ESP_ERROR_CHECK(rmt_isr_register(rmt_isr_handler, NULL, ESP_INTR_FLAG_LEVEL1, 0));

  // Fill the entire block with 'end of block' markers.
  rmt_item32_t endSentinel =   {{{ 0, 0, 0, 0 }}};
  for (int i = 0; i < config.mem_block_num; i++)
    for (int j = 0 ; j < 64; j++)
      RMTMEM.chan[RMT_TX_CHANNEL + i ].data32[j].val = endSentinel.val;

  fillTopOrBottomHalf(); // fill bottom half
  fillTopOrBottomHalf(); // fill top half
  assert(at != 0);

  ESP_ERROR_CHECK(rmt_set_tx_thr_intr_en(RMT_TX_CHANNEL, true, ITEM_CNT_HALF));
  ESP_ERROR_CHECK(rmt_tx_start(RMT_TX_CHANNEL, true));

}


void fillTopOrBottomHalf()
{
  for (int i = 0; i < ITEM_CNT_HALF; i++) {
    unsigned char level1 = 0 , level2 = 0;

    if (roll < RUNLENGTH) level1  = 1;

    roll++;
    if (roll >= RUNLENGTH * 2) roll = 0;

    if (roll < RUNLENGTH) level2  = 1;

    roll++;
    if (roll >= RUNLENGTH * 2) roll = 0;

    unsigned int d = 400, e = 400;

    rmt_item32_t w =   {{{ d, level1, e, level2 }}};
    RMTMEM.chan[RMT_TX_CHANNEL].data32[at + i].val = w.val;
  }
 
  // specify which half we do next time; top or bottom.
  //
  if (at) at = 0; else at = ITEM_CNT_HALF;
}

void loop() {
}

dirkxus
Posts: 3
Joined: Fri Sep 21, 2018 12:13 pm

Re: ESP32 / RMT loosing ticks /

Postby dirkxus » Sun Sep 23, 2018 5:05 pm

Am strongly suspecting that, when using the RMT system in loop rather than in one-shot mode; it either ignores, or gets confused by the sentinels; and mis-counts during the final ticks in the final block.

If one ensures that all blocks are fully filled with no '0' length blocks (i.e. not use the 0,1,0,0 or 0,0,0,0 sentinels) -- the glitches go away.

This does mean that any pulse train needs to massaged into 64x2 element sequences.

https://github.com/dirkx/SMPTE-EBU-Time ... ator-ESP32 - and specifically the file uses this approach: https://github.com/dirkx/SMPTE-EBU-Time ... or/RMT.ino -- and this is stable against the March verson of IDF.

Who is online

Users browsing this forum: Bing [Bot], jcsbanks and 8 guests