Using multiple RMT memory blocks in tx mode corrupts waveform

Matthis02
Posts: 3
Joined: Thu Jan 28, 2021 12:26 pm

Using multiple RMT memory blocks in tx mode corrupts waveform

Postby Matthis02 » Thu Jan 28, 2021 1:00 pm

Hello,

i could use a helping hand with RMT.
I want to generate a waveform with 194 steps using 4 blocks of the RMT internal memory.
Everything works fine with 64 steps or less, but adding additional steps produces invalid waveforms.
The SDK does not return any errors though.

I created a minimum "not working example" with a clock output that reproduces the problem.
Results and source below. What am i doing wrong here?

Waveform with <= 64 steps (OK)
SDS00011.png
SDS00011.png (20.22 KiB) Viewed 2171 times
Waveform with > 64 steps (nOK)
SDS00012.png
SDS00012.png (15.29 KiB) Viewed 2171 times
Waveform with > 64 steps, zoomed out (wtf?!)
SDS00013.png
SDS00013.png (15.58 KiB) Viewed 2171 times
Sourcecode
  1. #include "freertos/FreeRTOS.h"
  2. #include "freertos/task.h"
  3. #include "driver/rmt.h"
  4. #include "esp_log.h"
  5.  
  6. #define TAG     "TEST"
  7.  
  8. static rmt_config_t const g_rmt_out_config =
  9. {
  10.     .rmt_mode = RMT_MODE_TX,
  11.     .channel = RMT_CHANNEL_0,
  12.     .gpio_num = GPIO_NUM_14,
  13.     .clk_div = 1,
  14.     .mem_block_num = 4,
  15.     .tx_config =
  16.     {
  17.         .carrier_freq_hz = 1000,
  18.         .carrier_level = RMT_CARRIER_LEVEL_LOW,
  19.         .idle_level = RMT_IDLE_LEVEL_LOW,
  20.         .carrier_duty_percent = 50,
  21.         .carrier_en = false,
  22.         .loop_en = false,
  23.         .idle_output_en = true,
  24.     }
  25. };
  26.  
  27. static rmt_item32_t const gp_rtm_out_item[] =
  28. {
  29.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  30.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  31.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  32.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  33.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  34.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  35.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  36.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  37.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  38.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  39.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  40.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  41.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  42.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  43.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  44.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  45.  
  46.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  47.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  48.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  49.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  50.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  51.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  52.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  53.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  54.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  55.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  56.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  57.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  58.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  59.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  60.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  61.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  62.  
  63.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  64.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  65.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  66.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  67.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  68.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  69.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  70.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  71.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  72.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  73.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  74.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  75.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  76.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  77.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  78.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  79.  
  80.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  81.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  82.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  83.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  84.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  85.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  86.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  87.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  88.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  89.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  90.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  91.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  92.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  93.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  94.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  95.     { .duration0 = 20, .level0 = 0, .duration1 = 20, .level1 = 1 },
  96.  
  97.     { .duration0 = 0, .level0 = 0, .duration1 = 0, .level1 = 1 },   //stop
  98. };
  99.  
  100. static uint16_t const g_rtm_out_item_size = sizeof(gp_rtm_out_item) / sizeof(rmt_item32_t);
  101.  
  102. void app_main(void)
  103. {
  104.     ESP_LOGE(TAG, "start");
  105.  
  106.     if(ESP_OK != rmt_config(&g_rmt_out_config))
  107.     {
  108.         ESP_LOGE(TAG, "rmt_config failed");
  109.     }
  110.  
  111.     if(ESP_OK != rmt_driver_install(g_rmt_out_config.channel, 0, 0))
  112.     {
  113.         ESP_LOGE(TAG, "rmt_driver_install failed");
  114.     }
  115.  
  116.     if(ESP_OK != rmt_fill_tx_items(g_rmt_out_config.channel, gp_rtm_out_item, g_rtm_out_item_size, 0))
  117.     {
  118.         ESP_LOGE(TAG, "rmt_fill_tx_items failed");
  119.     }
  120.  
  121.     while (true)
  122.     {
  123.         rmt_tx_start(RMT_CHANNEL_0, true);
  124.  
  125.         vTaskDelay(1000 / portTICK_PERIOD_MS);
  126.     }
  127. }

A similar looking problem occures when i remove the stop line. I took that one from the examples. Why do i need that?

  1.     { .duration0 = 0, .level0 = 0, .duration1 = 0, .level1 = 1 },   //stop

Any help or ideas are welcome, because im stuck :oops:

My Setup:
Hardware: ESP32-D0WD-V3 custom board with ISSI flash memory
SDK: ESP-IDF v4.1

Matthis02
Posts: 3
Joined: Thu Jan 28, 2021 12:26 pm

Re: Using multiple RMT memory blocks in tx mode corrupts waveform

Postby Matthis02 » Mon Feb 01, 2021 8:36 am

I solved it myself. The problem is in the esp-idf driver. When you call

Code: Select all

esp_err_t rmt_fill_tx_items(rmt_channel_t channel, const rmt_item32_t *item, uint16_t item_num, uint16_t mem_offset)
{
    RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, (0));
    RMT_CHECK((item != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
    RMT_CHECK((item_num > 0), RMT_DRIVER_LENGTH_ERROR_STR, ESP_ERR_INVALID_ARG);

    /*Each block has 64 x 32 bits of data*/
    uint8_t mem_cnt = rmt_ll_get_mem_blocks(p_rmt_obj[channel]->hal.regs, channel);
    RMT_CHECK((mem_cnt * RMT_MEM_ITEM_NUM >= item_num), RMT_WR_MEM_OVF_ERROR_STR, ESP_ERR_INVALID_ARG);
    rmt_fill_memory(channel, item, item_num, mem_offset);
    return ESP_OK;
}
the function calls

Code: Select all

static void IRAM_ATTR rmt_fill_memory(rmt_channel_t channel, const rmt_item32_t *item,
                                      uint16_t item_num, uint16_t mem_offset)
{
    RMT_ENTER_CRITICAL();
    rmt_ll_set_mem_owner(p_rmt_obj[channel]->hal.regs, channel, RMT_MEM_OWNER_SW);
    rmt_ll_write_memory(p_rmt_obj[channel]->hal.mem, channel, item, item_num, mem_offset);
    rmt_ll_set_mem_owner(p_rmt_obj[channel]->hal.regs, channel, RMT_MEM_OWNER_HW);
    RMT_EXIT_CRITICAL();
}
which finally calls the memory writing function

Code: Select all

static inline void rmt_ll_write_memory(rmt_mem_t *mem, uint32_t channel, const rmt_item32_t *data, uint32_t length, uint32_t off)
{
    length = (off + length) > RMT_CHANNEL_MEM_WORDS ? (RMT_CHANNEL_MEM_WORDS - off) : length;
    for (uint32_t i = 0; i < length; i++) {
        mem->chan[channel].data32[i + off].val = data[i].val;
    }
}
The check in the 3rd line caps the items to 64. Writing more than 64 items is no option here. Since my source has no stop line in the first 64 items, the transmitter dont stop at item 64 and sends uninitialized data instead.

I solved the issue by rewriting the check to this:

Code: Select all

static inline void rmt_ll_write_memory(rmt_mem_t *mem, uint32_t channel, const rmt_item32_t *data, uint32_t length, uint32_t off)
{
    //length = (off + length) > RMT_CHANNEL_MEM_WORDS ? (RMT_CHANNEL_MEM_WORDS - off) : length;
    length = (off + length) > (RMT_CHANNEL_MEM_WORDS * (8 - channel)) ? ((RMT_CHANNEL_MEM_WORDS * (8 - channel)) - off) : length;
    for (uint32_t i = 0; i < length; i++) {
        mem->chan[channel].data32[i + off].val = data[i].val;
    }
}
I dont know if i also should set the mem owner bit for the aditional memory blocks (like it is done in rmt_fill_memory).
It seems to work fine without.

If you do not want to touch the drivers, you might get away with calling rmt_config, rmt_driver_install and rmt_fill_tx_items for every block of memory you use, and then call rmt_driver_uninstall on the blocks that are just memory extensions. However, I have not tested this.

Who is online

Users browsing this forum: Sang_Huynh and 177 guests