Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RMT: Cannot transmit and receive on same GPIO pin when io_loop_back=true (IDFGH-10524) #11768

Closed
3 tasks done
kevinhikaruevans opened this issue Jun 28, 2023 · 4 comments
Closed
3 tasks done
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally

Comments

@kevinhikaruevans
Copy link
Contributor

kevinhikaruevans commented Jun 28, 2023

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

IDF version.

5.0.1

Operating System used.

Linux

How did you build your project?

Using PlatformIO on VSCode

If you are using Windows, please specify command line type.

None

Development Kit.

esp32-s3-devkitc-1

Power Supply used.

USB

What is the expected behavior?

When using RMT with RX/TX channels using the same GPIO with .flags.io_loop_back=true, I expect to be able to transmit and receive on both channels.

What is the actual behavior?

When using both tx+rx channels, the GPIO pin is pulled always low. Specifically, calling rmt_new_rx_channel(...) pulls the GPIO pin low and I cannot receive or transmit on that pin.

When I disable the rx channel, I can transmit just fine and the output is as-expected:
image

Steps to reproduce.

  1. Setup RMT with tx and rx channels (example code below)
  2. Observe the line is pulled low on a scope or logic analyzer

I basically followed the NEC RMT example, but tweaked a few things, so I can transmit 10-bit serial. Feel free to comment if I'm doing something wrong or missing it.

#include "freertos/FreeRTOS.h"
#include "freertos/task.h" 
#include "freertos/queue.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/rmt_encoder.h"

#include "esp_check.h"
#include "esp_log.h"
#include <stdint.h>

#define SERIAL_FREQ_HZ 1000000
#define SERIAL_GPIO_NUM 6

static const char TAG[] = "main";

const static rmt_symbol_word_t bits[2] = {
  // inverted!

  /* 0 */ {
    .level0 = 1,
    .duration0 = 5,
    .level1 = 1,
    .duration1 = 5,
  },
  /* 1 */ {
    .level0 = 0,
    .duration0 = 5,
    .level1 = 0,
    .duration1 = 5
  },
};


// #define RMT_ENCODING_RESET 0

typedef struct {
  rmt_encoder_t base;
  rmt_encoder_t *copy_encoder;
  rmt_symbol_word_t start_bit;
  int state;
  rmt_symbol_word_t end_bit;
} rmt_cur_encoder_t;


static size_t rmt_encode_cur(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *out_state) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_encode_state_t session_state = RMT_ENCODING_RESET;
  rmt_encode_state_t state = RMT_ENCODING_RESET;
  size_t encoded_symbols = 0;

  uint16_t raw = *((uint16_t *)primary_data);
  rmt_encoder_t *copy_encoder = cur_encoder->copy_encoder;

  switch(cur_encoder->state) {
    case 0: // send start bit
      encoded_symbols += copy_encoder->encode(copy_encoder, channel, &cur_encoder->start_bit, sizeof(rmt_symbol_word_t), &session_state);
      if (session_state & RMT_ENCODING_COMPLETE) {
        cur_encoder->state = 1; // go to next state
      }
      if (session_state & RMT_ENCODING_MEM_FULL) {
        state |= RMT_ENCODING_MEM_FULL;
        goto out;
      }
    
    // fall-through
    case 1: // send data symbols
      for(int i = 0; i < 10; i++) {
        // iterate through bits in data
        bool bit = (raw >> i) & 0b1;
        rmt_symbol_word_t symbol = bits[bit];
        encoded_symbols += copy_encoder->encode(copy_encoder, channel, &symbol, sizeof(rmt_symbol_word_t), &session_state);

        if (session_state & RMT_ENCODING_MEM_FULL) {
          state |= RMT_ENCODING_MEM_FULL;
          goto out;
        }
      }
      // data is encoded :)
      cur_encoder->state = 2;
      state |= RMT_ENCODING_COMPLETE;

    // fall through
    case 2: // end end bit
      encoded_symbols += copy_encoder->encode(copy_encoder, channel, &cur_encoder->end_bit, sizeof(rmt_symbol_word_t), &session_state);
      if (session_state & RMT_ENCODING_COMPLETE) {
        cur_encoder->state = RMT_ENCODING_RESET; // go to next state
        state |= RMT_ENCODING_COMPLETE;
      }
      if (session_state & RMT_ENCODING_MEM_FULL) {
        state |= RMT_ENCODING_MEM_FULL;
        goto out;
      }
      // all done!
  }

out:
  *out_state = state;
  return encoded_symbols;
}

static esp_err_t rmt_del_cur_encoder(rmt_encoder_t *encoder) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_del_encoder(cur_encoder->copy_encoder);
  free(cur_encoder);
  return ESP_OK;
}

static esp_err_t rmt_cur_encoder_reset(rmt_encoder_t *encoder) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_encoder_reset(cur_encoder->copy_encoder);
  cur_encoder->state = RMT_ENCODING_RESET;
  return ESP_OK;
}

esp_err_t rmt_new_cur_encoder(rmt_encoder_handle_t *out_encoder) {
  rmt_cur_encoder_t *cur_encoder = calloc(1, sizeof(rmt_cur_encoder_t));
  // TODO: error check
  cur_encoder->state = 0;
  cur_encoder->base.encode = rmt_encode_cur;
  cur_encoder->base.del = rmt_del_cur_encoder;
  cur_encoder->base.reset = rmt_cur_encoder_reset;

  rmt_copy_encoder_config_t copy_encoder_config = {};
  rmt_new_copy_encoder(&copy_encoder_config, &cur_encoder->copy_encoder);

  // construct start, end bit
  cur_encoder->start_bit = bits[0];
  cur_encoder->end_bit = bits[1];

  *out_encoder = &cur_encoder->base;

  return ESP_OK;

}

static bool rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
    BaseType_t high_task_wakeup = pdFALSE;
    QueueHandle_t receive_queue = (QueueHandle_t)user_data;
    // send the received RMT symbols to the parser task
    xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
    return high_task_wakeup == pdTRUE;
}

static void parse_frame(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num)
{
    printf("frame start---\r\n");
    for (size_t i = 0; i < symbol_num; i++) {
        printf("{%d:%d},{%d:%d}\r\n", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0,
               rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1);
    }
    printf("---frame end: ");
}

void app_main() {
  ESP_LOGI(TAG, "create rmt tx channel");

  rmt_channel_handle_t tx_channel_handle = NULL;
  rmt_channel_handle_t rx_channel_handle = NULL;

  // init tx channel
  rmt_tx_channel_config_t tx_channel_config = {
    .clk_src=RMT_CLK_SRC_DEFAULT,
    .gpio_num = SERIAL_GPIO_NUM,
    .mem_block_symbols=48,
    .resolution_hz=SERIAL_FREQ_HZ,
    .trans_queue_depth=3,
    .flags.invert_out=true,
    .flags.with_dma=false,
    .flags.io_loop_back=true
  };
  
  ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_config, &tx_channel_handle));
  
  // init rx channel
  rmt_rx_channel_config_t rx_channel_config = {
    .clk_src=RMT_CLK_SRC_DEFAULT,
    .gpio_num=SERIAL_GPIO_NUM,
    .mem_block_symbols=48,
    .resolution_hz=SERIAL_FREQ_HZ,
    .flags.invert_in=true,
    .flags.with_dma=false,
    .flags.io_loop_back=true,
  };
  ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_channel_config, &rx_channel_handle));


  // init rx queue and callbacks
  QueueHandle_t receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));

  rmt_rx_event_callbacks_t cbs = {
    .on_recv_done=rmt_rx_done_callback
  };
  ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel_handle, &cbs, receive_queue));

  rmt_receive_config_t receive_config = {
    .signal_range_min_ns=100*1000, // 100 us
    .signal_range_max_ns=130*1000 //130 us
  };

  // create tx encoder
  rmt_encoder_handle_t encoder = NULL;
  rmt_new_cur_encoder(&encoder);
  
  ESP_ERROR_CHECK(rmt_enable(tx_channel_handle));
  ESP_ERROR_CHECK(rmt_enable(rx_channel_handle));
  
  uint16_t data = 0x2E8;
  rmt_symbol_word_t raw_symbols[64] = {0}; // more than enough
  rmt_rx_done_event_data_t rx_data;

  ESP_ERROR_CHECK(rmt_receive(rx_channel_handle, raw_symbols, sizeof(raw_symbols), &receive_config ));

  for(;;) {
    if (xQueueReceive(receive_queue, &rx_data, pdMS_TO_TICKS(1000)) == pdPASS) {
        ESP_LOGI(TAG, "received data");
        parse_frame(rx_data.received_symbols, rx_data.num_symbols);
          // start receive again
        ESP_ERROR_CHECK(rmt_receive(rx_channel_handle, raw_symbols, sizeof(raw_symbols), &receive_config));
    } else {
      ESP_LOGI(TAG, "transmitting");

      rmt_transmit_config_t tx_config = {
        .loop_count = 0,
        .flags.eot_level = 0, // stop bit: note inverted
      };
      rmt_transmit(tx_channel_handle, encoder, (void *)&data, sizeof(data), &tx_config);
      // rmt_tx_wait_all_done(serial_channel_handle, 1000);

      vTaskDelay(pdMS_TO_TICKS(1000));
    }
  }

}

Debug Logs.

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x1648
load:0x403c9700,len:0xbe0
load:0x403cc700,len:0x2ef8
entry 0x403c9900
␛[0;32mI (25) boot: ESP-IDF 5.0.1 2nd stage bootloader␛[0m
␛[0;32mI (25) boot: compile time 09:00:25␛[0m
␛[0;32mI (25) boot: chip revision: v0.1␛[0m
␛[0;32mI (26) boot.esp32s3: Boot SPI Speed : 80MHz␛[0m
␛[0;32mI (31) boot.esp32s3: SPI Mode       : DIO␛[0m
␛[0;32mI (36) boot.esp32s3: SPI Flash Size : 8MB␛[0m
␛[0;32mI (41) boot: Enabling RNG early entropy source...␛[0m
␛[0;32mI (46) boot: Partition Table:␛[0m
␛[0;32mI (50) boot: ## Label            Usage          Type ST Offset   Length␛[0m
␛[0;32mI (57) boot:  0 nvs              WiFi data        01 02 00009000 00006000␛[0m
␛[0;32mI (64) boot:  1 phy_init         RF data          01 01 0000f000 00001000␛[0m
␛[0;32mI (72) boot:  2 factory          factory app      00 00 00010000 00100000␛[0m
␛[0;32mI (79) boot: End of partition table␛[0m
␛[0;32mI (84) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0abb0h ( 43952) map␛[0m
␛[0;32mI (100) esp_image: segment 1: paddr=0001abd8 vaddr=3fc92000 size=02410h (  9232) load␛[0m
␛[0;32mI (102) esp_image: segment 2: paddr=0001cff0 vaddr=40374000 size=03028h ( 12328) load␛[0m
␛[0;32mI (112) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=1dde8h (122344) map␛[0m
␛[0;32mI (139) esp_image: segment 4: paddr=0003de10 vaddr=40377028 size=0afb8h ( 44984) load␛[0m
␛[0;32mI (155) boot: Loaded app from partition at offset 0x10000␛[0m
␛[0;32mI (155) boot: Disabling RNG early entropy source...␛[0m
␛[0;32mI (167) cpu_start: Pro cpu up.␛[0m
␛[0;32mI (167) cpu_start: Starting app cpu, entry point is 0x403763dc␛[0m
␛[0;32mI (0) cpu_start: App cpu up.␛[0m
␛[0;32mI (181) cpu_start: Pro cpu start user code␛[0m
␛[0;32mI (181) cpu_start: cpu freq: 160000000 Hz␛[0m
␛[0;32mI (182) cpu_start: Application information:␛[0m
␛[0;32mI (184) cpu_start: Project name:     curtisrmt␛[0m
␛[0;32mI (190) cpu_start: App version:      1␛[0m
␛[0;32mI (194) cpu_start: Compile time:     Jun 28 2023 11:26:51␛[0m
␛[0;32mI (200) cpu_start: ELF file SHA256:  f9aa97c14fbafdc2...␛[0m
␛[0;32mI (206) cpu_start: ESP-IDF:          5.0.1␛[0m
␛[0;32mI (211) cpu_start: Min chip rev:     v0.0␛[0m
␛[0;32mI (216) cpu_start: Max chip rev:     v0.99 ␛[0m
␛[0;32mI (220) cpu_start: Chip rev:         v0.1␛[0m
␛[0;32mI (225) heap_init: Initializing. RAM available for dynamic allocation:␛[0m
␛[0;32mI (232) heap_init: At 3FC94E40 len 000548D0 (338 KiB): D/IRAM␛[0m
␛[0;32mI (239) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM␛[0m
␛[0;32mI (245) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM␛[0m
␛[0;32mI (252) heap_init: At 600FE010 len 00001FF0 (7 KiB): RTCRAM␛[0m
␛[0;32mI (259) spi_flash: detected chip: generic␛[0m
␛[0;32mI (263) spi_flash: flash io: dio␛[0m
␛[0;32mI (267) cpu_start: Starting scheduler on PRO CPU.␛[0m
␛[0;32mI (0) cpu_start: Starting scheduler on APP CPU.␛[0m
␛[0;32mI (287) main: create rmt tx channel␛[0m
␛[0;32mI (287) gpio: GPIO[6]| InputEn: 1| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 ␛[0m
␛[0;32mI (297) gpio: GPIO[6]| InputEn: 1| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 ␛[0m
␛[0;32mI (1307) main: transmitting␛[0m
␛[0;32mI (3307) main: transmitting␛[0m
␛[0;32mI (5307) main: transmitting␛[0m
␛[0;32mI (7307) main: transmitting␛[0m
␛[0;32mI (9307) main: transmitting␛[0m
␛[0;32mI (11307) main: transmitting␛[0m


### More Information.

_No response_
@kevinhikaruevans kevinhikaruevans added the Type: Bug bugs in IDF label Jun 28, 2023
@espressif-bot espressif-bot added the Status: Opened Issue is new label Jun 28, 2023
@github-actions github-actions bot changed the title RMT: Cannot transmit and receive on same GPIO pin when io_loop_back=true RMT: Cannot transmit and receive on same GPIO pin when io_loop_back=true (IDFGH-10524) Jun 28, 2023
@kevinhikaruevans
Copy link
Contributor Author

kevinhikaruevans commented Jun 28, 2023

Interestingly, I can set the rx pin to a new pin, then physically jump the rx/tx together and it works as-expected.

Here's the code I'm using with jumped separate pins (6 and 7):

#include "freertos/FreeRTOS.h"
#include "freertos/task.h" 
#include "freertos/queue.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/rmt_encoder.h"

#include "esp_check.h"
#include "esp_log.h"
#include <stdint.h>

#define SERIAL_FREQ_HZ 1000000
#define SERIAL_TX_GPIO_NUM 6
#define SERIAL_RX_GPIO_NUM 7

static const char TAG[] = "main";

const static rmt_symbol_word_t bits[2] = {
  // inverted!

  /* 0 */ {
    .level0 = 1,
    .duration0 = 5,
    .level1 = 1,
    .duration1 = 5,
  },
  /* 1 */ {
    .level0 = 0,
    .duration0 = 5,
    .level1 = 0,
    .duration1 = 5
  },
};


// #define RMT_ENCODING_RESET 0

typedef struct {
  rmt_encoder_t base;
  rmt_encoder_t *copy_encoder;
  rmt_symbol_word_t start_bit;
  int state;
  rmt_symbol_word_t end_bit;
} rmt_cur_encoder_t;


static size_t rmt_encode_cur(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *out_state) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_encode_state_t session_state = RMT_ENCODING_RESET;
  rmt_encode_state_t state = RMT_ENCODING_RESET;
  size_t encoded_symbols = 0;

  uint16_t raw = *((uint16_t *)primary_data);
  rmt_encoder_t *copy_encoder = cur_encoder->copy_encoder;

  switch(cur_encoder->state) {
    case 0: // send start bit
      encoded_symbols += copy_encoder->encode(copy_encoder, channel, &cur_encoder->start_bit, sizeof(rmt_symbol_word_t), &session_state);
      if (session_state & RMT_ENCODING_COMPLETE) {
        cur_encoder->state = 1; // go to next state
      }
      if (session_state & RMT_ENCODING_MEM_FULL) {
        state |= RMT_ENCODING_MEM_FULL;
        goto out;
      }
    
    // fall-through
    case 1: // send data symbols
      for(int i = 0; i < 10; i++) {
        // iterate through bits in data
        bool bit = (raw >> i) & 0b1;
        rmt_symbol_word_t symbol = bits[bit];
        encoded_symbols += copy_encoder->encode(copy_encoder, channel, &symbol, sizeof(rmt_symbol_word_t), &session_state);

        if (session_state & RMT_ENCODING_MEM_FULL) {
          state |= RMT_ENCODING_MEM_FULL;
          goto out;
        }
      }
      // data is encoded :)
      cur_encoder->state = 2;
      state |= RMT_ENCODING_COMPLETE;

    // fall through
    case 2: // end end bit
      encoded_symbols += copy_encoder->encode(copy_encoder, channel, &cur_encoder->end_bit, sizeof(rmt_symbol_word_t), &session_state);
      if (session_state & RMT_ENCODING_COMPLETE) {
        cur_encoder->state = RMT_ENCODING_RESET; // go to next state
        state |= RMT_ENCODING_COMPLETE;
      }
      if (session_state & RMT_ENCODING_MEM_FULL) {
        state |= RMT_ENCODING_MEM_FULL;
        goto out;
      }
      // all done!
  }

out:
  *out_state = state;
  return encoded_symbols;
}

static esp_err_t rmt_del_cur_encoder(rmt_encoder_t *encoder) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_del_encoder(cur_encoder->copy_encoder);
  free(cur_encoder);
  return ESP_OK;
}

static esp_err_t rmt_cur_encoder_reset(rmt_encoder_t *encoder) {
  rmt_cur_encoder_t *cur_encoder = __containerof(encoder, rmt_cur_encoder_t, base);
  rmt_encoder_reset(cur_encoder->copy_encoder);
  cur_encoder->state = RMT_ENCODING_RESET;
  return ESP_OK;
}

esp_err_t rmt_new_cur_encoder(rmt_encoder_handle_t *out_encoder) {
  rmt_cur_encoder_t *cur_encoder = calloc(1, sizeof(rmt_cur_encoder_t));
  // TODO: error check
  cur_encoder->state = 0;
  cur_encoder->base.encode = rmt_encode_cur;
  cur_encoder->base.del = rmt_del_cur_encoder;
  cur_encoder->base.reset = rmt_cur_encoder_reset;

  rmt_copy_encoder_config_t copy_encoder_config = {};
  rmt_new_copy_encoder(&copy_encoder_config, &cur_encoder->copy_encoder);

  // construct start, end bit
  cur_encoder->start_bit = bits[0];
  cur_encoder->end_bit = bits[1];

  *out_encoder = &cur_encoder->base;

  return ESP_OK;

}

static bool rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
    BaseType_t high_task_wakeup = pdFALSE;
    QueueHandle_t receive_queue = (QueueHandle_t)user_data;
    // send the received RMT symbols to the parser task
    xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
    return high_task_wakeup == pdTRUE;
}


static void parse_frame(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num)
{
    printf("frame start---\r\n");
    uint16_t data = 0;
    int data_idx = 0;

    for (size_t i = 0; i < symbol_num; i++) {
      // should maybe use rounding here, in case we have like <10 us 
      // this also does not work correctly, I'm messing something up rn
      rmt_symbol_word_t symbol = rmt_nec_symbols[i];
      // first symbol:
      if (symbol.level0 == 1) {
        int duration = symbol.duration1 / 10;
        if (data_idx == 0) {
          duration--;
        }
        data_idx += duration;
      }
      if (symbol.level1 == 0) {
        int duration = symbol.duration0 / 10;
        if (data_idx == 0) {
          // start bit, ignore
          duration--;
        }
        for(int j = 0; j < duration; j++) {
          data |= 1 << data_idx;
          data_idx++;
        }
      }
      printf("{%d:%d},{%d:%d}\r\n", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0,
              rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1);
    }

    ESP_LOGI(TAG, ">> data = %x", data);
    printf("---frame end: ");
}

void app_main() {
  ESP_LOGI(TAG, "create rmt tx channel");

  rmt_channel_handle_t tx_channel_handle = NULL;
  rmt_channel_handle_t rx_channel_handle = NULL;

  // init tx channel
  rmt_tx_channel_config_t tx_channel_config = {
    .clk_src=RMT_CLK_SRC_DEFAULT,
    .gpio_num = SERIAL_TX_GPIO_NUM,
    .mem_block_symbols=48,
    .resolution_hz=SERIAL_FREQ_HZ,
    .trans_queue_depth=3,
    .flags.invert_out=true,
    .flags.with_dma=false,
    .flags.io_loop_back=true
  };
  
  ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_config, &tx_channel_handle));
  
  // init rx channel
  rmt_rx_channel_config_t rx_channel_config = {
    .clk_src=RMT_CLK_SRC_DEFAULT,
    .gpio_num=SERIAL_RX_GPIO_NUM,
    .mem_block_symbols=48,
    .resolution_hz=SERIAL_FREQ_HZ,
    .flags.invert_in=true,
    .flags.with_dma=false,
    .flags.io_loop_back=true,
  };
  ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_channel_config, &rx_channel_handle));


  // init rx queue and callbacks
  QueueHandle_t receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));

  rmt_rx_event_callbacks_t cbs = {
    .on_recv_done=rmt_rx_done_callback
  };
  ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel_handle, &cbs, receive_queue));

  rmt_receive_config_t receive_config = {
    .signal_range_min_ns=100*1000,
    .signal_range_max_ns=130*1000 
  };

  // create tx encoder
  rmt_encoder_handle_t encoder = NULL;
  rmt_new_cur_encoder(&encoder);
  
  ESP_ERROR_CHECK(rmt_enable(tx_channel_handle));
  ESP_ERROR_CHECK(rmt_enable(rx_channel_handle));
  
  uint16_t data = 0x2E8;
  rmt_symbol_word_t raw_symbols[64] = {0}; // more than enough
  rmt_rx_done_event_data_t rx_data;

  ESP_ERROR_CHECK(rmt_receive(rx_channel_handle, raw_symbols, sizeof(raw_symbols), &receive_config ));

  for(;;) {
    if (xQueueReceive(receive_queue, &rx_data, pdMS_TO_TICKS(1000)) == pdPASS) {
        ESP_LOGI(TAG, "received data");
        parse_frame(rx_data.received_symbols, rx_data.num_symbols);
          // start receive again
        ESP_ERROR_CHECK(rmt_receive(rx_channel_handle, raw_symbols, sizeof(raw_symbols), &receive_config));
    } else {
      ESP_LOGI(TAG, "transmitting");

      rmt_transmit_config_t tx_config = {
        .loop_count = 0,
        .flags.eot_level = 0, // stop bit: note inverted
      };
      rmt_transmit(tx_channel_handle, encoder, (void *)&data, sizeof(data), &tx_config);
      // rmt_tx_wait_all_done(serial_channel_handle, 1000);

      vTaskDelay(pdMS_TO_TICKS(1000));
    }
  }

}

@suda-morris
Copy link
Collaborator

TLDR, you may need to install the RX channel first, then the TX channel. Please check the test code we used: https://github.com/espressif/esp-idf/blob/master/components/driver/test_apps/rmt/main/test_rmt_rx.c#L43

@suda-morris suda-morris removed the Type: Bug bugs in IDF label Jun 29, 2023
@kevinhikaruevans
Copy link
Contributor Author

kevinhikaruevans commented Jun 29, 2023

TLDR, you may need to install the RX channel first, then the TX channel. Please check the test code we used: https://github.com/espressif/esp-idf/blob/master/components/driver/test_apps/rmt/main/test_rmt_rx.c#L43

That worked. I called rmt_new_rx_channel before rmt_new_tx_channel to get it working correctly.

It would be nice to have a note on the RMT page under rmt_tx_channel_config_t::io_loop_back stating the rx channel must be initialized before tx.

Question: Why does the rx channel need to be installed before the tx channel?

@suda-morris
Copy link
Collaborator

suda-morris commented Jun 30, 2023

Yes, we will update the doc. Thanks.

Because this code will change the gpio matrix signal connection. If the TX was set first, then during the RX set up, the previous RMT channel signal will be overridden by the GPIO control signal.

@espressif-bot espressif-bot added Status: In Progress Work is in progress Status: Done Issue is done internally Resolution: NA Issue resolution is unavailable and removed Status: Opened Issue is new Status: In Progress Work is in progress labels Jun 30, 2023
espressif-bot pushed a commit that referenced this issue Jul 7, 2023
Added a note about RX Channel and TX Channel initialization order when
bound RX and TX to the same gpio.

Closes #11768
espressif-bot pushed a commit that referenced this issue Jul 15, 2023
Added a note about RX Channel and TX Channel initialization order when
bound RX and TX to the same gpio.

Closes #11768
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

3 participants