Răsfoiți Sursa

Merge branch 'feature/rmt_clock_support_ref_tick' into 'master'

rmt: support ref tick && refactor unit test && re-enable unit test on ESP32-S2

Closes IDFGH-1715

See merge request espressif/esp-idf!7614
Angus Gratton 5 ani în urmă
părinte
comite
a9854f7085

+ 5 - 0
components/driver/include/driver/rmt.h

@@ -28,6 +28,8 @@ extern "C" {
 #include "soc/rmt_struct.h"
 #include "hal/rmt_types.h"
 
+#define RMT_CHANNEL_FLAGS_ALWAYS_ON (1 << 0)    /*!< Channel can work when APB frequency is changing (RMT channel adopts REF_TICK as clock source) */
+
 /**
  * @brief Define memory space of each RMT channel (in words = 4 bytes)
  *
@@ -65,6 +67,7 @@ typedef struct {
     gpio_num_t gpio_num;   /*!< RMT GPIO number */
     uint8_t clk_div;       /*!< RMT channel counter divider */
     uint8_t mem_block_num; /*!< RMT memory block number */
+    uint32_t flags;        /*!< RMT channel extra configurations, OR'd with RMT_CHANNEL_FLAGS_[*] */
     union {
         rmt_tx_config_t tx_config; /*!< RMT TX parameter */
         rmt_rx_config_t rx_config; /*!< RMT RX parameter */
@@ -82,6 +85,7 @@ typedef struct {
         .gpio_num = gpio,                            \
         .clk_div = 80,                               \
         .mem_block_num = 1,                          \
+        .flags = 0,                                  \
         .tx_config = {                               \
             .carrier_freq_hz = 38000,                \
             .carrier_level = RMT_CARRIER_LEVEL_HIGH, \
@@ -104,6 +108,7 @@ typedef struct {
         .gpio_num = gpio,                       \
         .clk_div = 80,                          \
         .mem_block_num = 1,                     \
+        .flags = 0,                             \
         .rx_config = {                          \
             .idle_threshold = 12000,            \
             .filter_ticks_thresh = 100,         \

+ 17 - 11
components/driver/rmt.c

@@ -27,11 +27,6 @@
 #include "hal/rmt_hal.h"
 #include "hal/rmt_ll.h"
 
-#define RMT_SOUCCE_CLK_APB (APB_CLK_FREQ) /*!< RMT source clock is APB_CLK */
-#define RMT_SOURCE_CLK_REF (1 * 1000000)  /*!< not used yet */
-
-#define RMT_SOURCE_CLK(select) ((select == RMT_BASECLK_REF) ? (RMT_SOURCE_CLK_REF) : (RMT_SOUCCE_CLK_APB))
-
 #define RMT_CHANNEL_ERROR_STR "RMT CHANNEL ERR"
 #define RMT_ADDR_ERROR_STR "RMT ADDRESS ERR"
 #define RMT_MEM_CNT_ERROR_STR "RMT MEM BLOCK NUM ERR"
@@ -89,7 +84,8 @@ typedef struct {
     const uint8_t *sample_cur;
 } rmt_obj_t;
 
-rmt_obj_t *p_rmt_obj[RMT_CHANNEL_MAX] = {0};
+static rmt_obj_t *p_rmt_obj[RMT_CHANNEL_MAX] = {0};
+static uint32_t s_rmt_src_clock_hz[RMT_CHANNEL_MAX] = {0};
 
 // Event called when transmission is ended
 static rmt_tx_end_callback_t rmt_tx_end_callback;
@@ -160,7 +156,7 @@ esp_err_t rmt_set_tx_carrier(rmt_channel_t channel, bool carrier_en, uint16_t hi
     RMT_ENTER_CRITICAL();
     rmt_ll_set_carrier_high_low_ticks(p_rmt_obj[channel]->hal.regs, channel, high_level, low_level);
     rmt_ll_set_carrier_to_level(p_rmt_obj[channel]->hal.regs, channel, carrier_level);
-    rmt_ll_enable_tx_carrier(p_rmt_obj[channel]->hal.regs, channel, carrier_en);
+    rmt_ll_enable_carrier(p_rmt_obj[channel]->hal.regs, channel, carrier_en);
     RMT_EXIT_CRITICAL();
     return ESP_OK;
 }
@@ -425,6 +421,7 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
     uint8_t clk_div = rmt_param->clk_div;
     uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz;
     bool carrier_en = rmt_param->tx_config.carrier_en;
+    uint32_t rmt_source_clk_hz = 0;
 
     RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
     RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG);
@@ -439,12 +436,21 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
     rmt_ll_enable_mem_access(dev, true);
     rmt_ll_reset_tx_pointer(dev, channel);
     rmt_ll_reset_rx_pointer(dev, channel);
-    rmt_ll_set_counter_clock_src(dev, channel, RMT_BASECLK_APB); // only support APB clock for now
+    if (rmt_param->flags & RMT_CHANNEL_FLAGS_ALWAYS_ON) {
+        // clock src: REF_CLK
+        rmt_source_clk_hz = REF_CLK_FREQ;
+        rmt_ll_set_counter_clock_src(dev, channel, RMT_BASECLK_REF);
+    } else {
+        // clock src: APB_CLK
+        rmt_source_clk_hz = APB_CLK_FREQ;
+        rmt_ll_set_counter_clock_src(dev, channel, RMT_BASECLK_APB);
+    }
     rmt_ll_set_mem_blocks(dev, channel, mem_cnt);
     rmt_ll_set_mem_owner(dev, channel, RMT_MEM_OWNER_HW);
+    rmt_ll_enable_carrier(dev, channel, false); // disable carrier feature by default
     RMT_EXIT_CRITICAL();
 
-    uint32_t rmt_source_clk_hz = RMT_SOURCE_CLK(RMT_BASECLK_APB);
+    s_rmt_src_clock_hz[channel] = rmt_source_clk_hz;
 
     if (mode == RMT_MODE_TX) {
         uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent;
@@ -458,7 +464,7 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
         rmt_ll_enable_tx_idle(dev, channel, rmt_param->tx_config.idle_output_en);
         rmt_ll_set_tx_idle_level(dev, channel, idle_level);
         /*Set carrier*/
-        rmt_ll_enable_tx_carrier(dev, channel, carrier_en);
+        rmt_ll_enable_carrier(dev, channel, carrier_en);
         if (carrier_en) {
             uint32_t duty_div, duty_h, duty_l;
             duty_div = rmt_source_clk_hz / carrier_freq_hz;
@@ -990,7 +996,7 @@ esp_err_t rmt_get_counter_clock(rmt_channel_t channel, uint32_t *clock_hz)
     RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
     RMT_CHECK(clock_hz, "parameter clock_hz can't be null", ESP_ERR_INVALID_ARG);
     RMT_ENTER_CRITICAL();
-    *clock_hz = rmt_hal_get_counter_clock(&p_rmt_obj[channel]->hal, channel, RMT_SOURCE_CLK(RMT_BASECLK_APB));
+    *clock_hz = rmt_hal_get_counter_clock(&p_rmt_obj[channel]->hal, channel, s_rmt_src_clock_hz[channel]);
     RMT_EXIT_CRITICAL();
     return ESP_OK;
 }

+ 1 - 2
components/driver/test/CMakeLists.txt

@@ -1,4 +1,3 @@
 idf_component_register(SRC_DIRS . param_test
                        INCLUDE_DIRS include param_test/include
-                       REQUIRES unity test_utils driver nvs_flash esp_serial_slave_link
-                      )
+                       REQUIRES unity test_utils driver nvs_flash esp_serial_slave_link infrared_tools)

+ 236 - 704
components/driver/test/test_rmt.c

@@ -1,782 +1,314 @@
-/**
- * @brief To run this unit test, MAKE SURE GPIO18(TX) is connected to GPIO19(RX)!
- *
- */
+// RMT driver unit test is based on extended NEC protocol
+// Please don't use channel number: RMT_CHANNELS_NUM - 1
 #include <stdio.h>
 #include <string.h>
 #include "sdkconfig.h"
-#include "unity.h"
-#include "test_utils.h"
-#include "driver/rmt.h"
-#include "driver/periph_ctrl.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "freertos/queue.h"
-#include "freertos/semphr.h"
-#include "esp_err.h"
 #include "esp_log.h"
-#include "soc/soc.h"
-#include "soc/rmt_periph.h"
-
-#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2)
-//No runners
-static const char *TAG = "RMT.test";
-
-#define RMT_RX_ACTIVE_LEVEL 1 /*!< Data bit is active high for self test mode */
-#define RMT_TX_CARRIER_EN 0   /*!< Disable carrier for self test mode  */
-
-#define RMT_TX_CHANNEL 1                                     /*!< RMT channel for transmitter */
-#define RMT_TX_GPIO_NUM 18                                   /*!< GPIO number for transmitter signal */
-#define RMT_RX_CHANNEL 0                                     /*!< RMT channel for receiver */
-#define RMT_RX_GPIO_NUM 19                                   /*!< GPIO number for receiver */
-#define RMT_CLK_DIV 100                                      /*!< RMT counter clock divider */
-#define RMT_TICK_10_US (APB_CLK_FREQ / RMT_CLK_DIV / 100000) /*!< RMT counter value for 10 us */
-
-// NEC protocol related parameters
-#define HEADER_HIGH_US 9000                       /*!< NEC protocol header: positive 9ms */
-#define HEADER_LOW_US 4500                        /*!< NEC protocol header: negative 4.5ms*/
-#define BIT_ONE_HIGH_US 560                       /*!< NEC protocol data bit 1: positive 0.56ms */
-#define BIT_ONE_LOW_US (2250 - BIT_ONE_HIGH_US)   /*!< NEC protocol data bit 1: negative 1.69ms */
-#define BIT_ZERO_HIGH_US 560                      /*!< NEC protocol data bit 0: positive 0.56ms */
-#define BIT_ZERO_LOW_US (1120 - BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */
-#define BIT_END 560                               /*!< NEC protocol end: positive 0.56ms */
-#define BIT_MARGIN 160                            /*!< NEC parse margin time */
-
-#define ITEM_DURATION(d) ((d & 0x7fff) * 10 / RMT_TICK_10_US) /*!< Parse duration time from memory register value */
-#define DATA_ITEM_NUM 34                                      /*!< NEC code item number: header + 32bit data + end */
-#define RMT_TX_DATA_NUM 50                                    /*!< NEC tx test data number */
-#define RMT_ITEM32_TIMEOUT_US 9500                            /*!< RMT receiver timeout value(us) */
-
-/**
- * @brief Build register value of waveform for NEC one data bit
- */
-static inline void fill_item_level(rmt_item32_t *item, int high_us, int low_us)
-{
-    item->level0 = 1;
-    item->duration0 = (high_us) / 10 * RMT_TICK_10_US;
-    item->level1 = 0;
-    item->duration1 = (low_us) / 10 * RMT_TICK_10_US;
-}
-
-/**
- * @brief Generate NEC header value: active 9ms + negative 4.5ms
- */
-static void fill_item_header(rmt_item32_t *item)
-{
-    fill_item_level(item, HEADER_HIGH_US, HEADER_LOW_US);
-}
-
-/*
- * @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms
- */
-static void fill_item_bit_one(rmt_item32_t *item)
-{
-    fill_item_level(item, BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
-}
+#include "driver/rmt.h"
+#include "ir_tools.h"
+#include "unity.h"
+#include "test_utils.h"
 
-/**
- * @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms
- */
-static void fill_item_bit_zero(rmt_item32_t *item)
-{
-    fill_item_level(item, BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
-}
+// CI ONLY: Don't connect any other signals to this GPIO
+#define RMT_DATA_IO (12) // bind signal RMT_SIG_OUT0_IDX and RMT_SIG_IN0_IDX on the same GPIO
 
-/**
- * @brief Generate NEC end signal: positive 0.56ms
- */
-static void fill_item_end(rmt_item32_t *item)
-{
-    fill_item_level(item, BIT_END, 0x7fff);
-}
+static const char *TAG = "RMT.test";
+static ir_builder_t *s_ir_builder = NULL;
+static ir_parser_t *s_ir_parser = NULL;
 
-/**
- * @brief Check whether duration is around target_us
- */
-static inline bool check_in_range(int duration_ticks, int target_us, int margin_us)
+static void rmt_setup_testbench(int tx_channel, int rx_channel, uint32_t flags)
 {
-    if ((ITEM_DURATION(duration_ticks) < (target_us + margin_us)) &&
-            (ITEM_DURATION(duration_ticks) > (target_us - margin_us))) {
-        return true;
-    } else {
-        return false;
+    // RMT channel configuration
+    if (tx_channel >= 0) {
+        rmt_config_t tx_config = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, tx_channel);
+        tx_config.flags = flags;
+        TEST_ESP_OK(rmt_config(&tx_config));
     }
-}
 
-/**
- * @brief Check whether this value represents an NEC header
- */
-static bool header_if(rmt_item32_t *item)
-{
-    if ((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) &&
-            check_in_range(item->duration0, HEADER_HIGH_US, BIT_MARGIN) &&
-            check_in_range(item->duration1, HEADER_LOW_US, BIT_MARGIN)) {
-        return true;
+    if (rx_channel >= 0) {
+        rmt_config_t rx_config = RMT_DEFAULT_CONFIG_RX(RMT_DATA_IO, rx_channel);
+        rx_config.flags = flags;
+        TEST_ESP_OK(rmt_config(&rx_config));
     }
-    return false;
-}
 
-/**
- * @brief Check whether this value represents an NEC data bit 1
- */
-static bool bit_one_if(rmt_item32_t *item)
-{
-    if ((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) &&
-            check_in_range(item->duration0, BIT_ONE_HIGH_US, BIT_MARGIN) &&
-            check_in_range(item->duration1, BIT_ONE_LOW_US, BIT_MARGIN)) {
-        return true;
-    }
-    return false;
-}
+    // Routing internal signals by IO Matrix (bind rmt tx and rx signal on the same GPIO)
+    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[RMT_DATA_IO], PIN_FUNC_GPIO);
+    TEST_ESP_OK(gpio_set_direction(RMT_DATA_IO, GPIO_MODE_INPUT_OUTPUT));
+    gpio_matrix_out(RMT_DATA_IO, RMT_SIG_OUT0_IDX + tx_channel, 0, 0);
+    gpio_matrix_in(RMT_DATA_IO, RMT_SIG_IN0_IDX + rx_channel, 0);
 
-/**
- * @brief Check whether this value represents an NEC data bit 0
- */
-static bool bit_zero_if(rmt_item32_t *item)
-{
-    if ((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) &&
-            check_in_range(item->duration0, BIT_ZERO_HIGH_US, BIT_MARGIN) &&
-            check_in_range(item->duration1, BIT_ZERO_LOW_US, BIT_MARGIN)) {
-        return true;
-    }
-    return false;
-}
+    // install driver
+    if (tx_channel >= 0) {
+        TEST_ESP_OK(rmt_driver_install(tx_channel, 0, 0));
 
-/**
- * @brief Parse NEC 32 bit waveform to address and command.
- */
-static int parse_items(rmt_item32_t *item, int item_num, uint16_t *addr, uint16_t *data)
-{
-    int w_len = item_num;
-    if (w_len < DATA_ITEM_NUM) {
-        return -1;
+        ir_builder_config_t ir_builder_config = IR_BUILDER_DEFAULT_CONFIG((ir_dev_t)tx_channel);
+        ir_builder_config.flags = IR_TOOLS_FLAGS_PROTO_EXT;
+        s_ir_builder = ir_builder_rmt_new_nec(&ir_builder_config);
+        TEST_ASSERT_NOT_NULL(s_ir_builder);
     }
-    int i = 0, j = 0;
-    if (!header_if(item++)) {
-        return -1;
-    }
-    uint16_t addr_t = 0;
-    for (j = 0; j < 16; j++) {
-        if (bit_one_if(item)) {
-            addr_t |= (1 << j);
-        } else if (bit_zero_if(item)) {
-            addr_t |= (0 << j);
-        } else {
-            return -1;
-        }
-        item++;
-        i++;
-    }
-    uint16_t data_t = 0;
-    for (j = 0; j < 16; j++) {
-        if (bit_one_if(item)) {
-            data_t |= (1 << j);
-        } else if (bit_zero_if(item)) {
-            data_t |= (0 << j);
-        } else {
-            return -1;
-        }
-        item++;
-        i++;
-    }
-    *addr = addr_t;
-    *data = data_t;
-    return i;
-}
 
-/**
- * @brief Build NEC 32bit waveform.
- */
-static int build_items(int channel, rmt_item32_t *item, int item_num, uint16_t addr, uint16_t cmd_data)
-{
-    int i = 0, j = 0;
-    if (item_num < DATA_ITEM_NUM) {
-        return -1;
-    }
-    fill_item_header(item++);
-    i++;
-    for (j = 0; j < 16; j++) {
-        if (addr & 0x1) {
-            fill_item_bit_one(item);
-        } else {
-            fill_item_bit_zero(item);
-        }
-        item++;
-        i++;
-        addr >>= 1;
-    }
-    for (j = 0; j < 16; j++) {
-        if (cmd_data & 0x1) {
-            fill_item_bit_one(item);
-        } else {
-            fill_item_bit_zero(item);
-        }
-        item++;
-        i++;
-        cmd_data >>= 1;
-    }
-    fill_item_end(item);
-    i++;
-    return i;
-}
+    if (rx_channel >= 0) {
+        TEST_ESP_OK(rmt_driver_install(rx_channel, 3000, 0));
 
-static void set_tx_data(int tx_channel, uint16_t cmd, uint16_t addr, int item_num, rmt_item32_t *item, int offset)
-{
-    while (1) {
-        int i = build_items(tx_channel, item + offset, item_num - offset, ((~addr) << 8) | addr, cmd);
-        if (i < 0) {
-            break;
-        }
-        cmd++;
-        addr++;
-        offset += i;
+        ir_parser_config_t ir_parser_config = IR_PARSER_DEFAULT_CONFIG((ir_dev_t)rx_channel);
+        ir_parser_config.flags = IR_TOOLS_FLAGS_PROTO_EXT | IR_TOOLS_FLAGS_INVERSE;
+        s_ir_parser = ir_parser_rmt_new_nec(&ir_parser_config);
+        TEST_ASSERT_NOT_NULL(s_ir_parser);
     }
 }
 
-static int get_rx_data(RingbufHandle_t rb)
+static void rmt_clean_testbench(int tx_channel, int rx_channel)
 {
-    uint16_t tmp = 0;
-    while (rb) {
-        size_t rx_size = 0;
-        rmt_item32_t *rx_item = (rmt_item32_t *)xRingbufferReceive(rb, &rx_size, 1000);
-        if (rx_item) {
-            uint16_t rmt_addr;
-            uint16_t rmt_cmd;
-            int rx_offset = 0;
-            while (1) {
-                int res = parse_items(rx_item + rx_offset, rx_size / 4 - rx_offset, &rmt_addr, &rmt_cmd);
-                if (res > 0) {
-                    rx_offset += res + 1;
-                    ESP_LOGI(TAG, "receive cmd %d from addr %d", rmt_cmd, rmt_addr & 0xFF);
-                    TEST_ASSERT(rmt_cmd == tmp);
-                    tmp++;
-                } else {
-                    break;
-                }
-            }
-            vRingbufferReturnItem(rb, (void *)rx_item);
-        } else {
-            break;
-        }
+    if (tx_channel >= 0) {
+        TEST_ESP_OK(rmt_driver_uninstall(tx_channel));
+        TEST_ESP_OK(s_ir_builder->del(s_ir_builder));
+        s_ir_builder = NULL;
     }
-    return tmp;
-}
 
-/**
- * @brief RMT transmitter initialization
- */
-static void tx_init(void)
-{
-    // the sender once it send something, its frq is 38kHz, and the duty cycle is 50%
-    rmt_tx_config_t tx_cfg = {
-        .loop_en = false,
-        .carrier_duty_percent = 50,
-        .carrier_freq_hz = 38000,
-        .carrier_level = 1,
-        .carrier_en = RMT_TX_CARRIER_EN,
-        .idle_level = 0,
-        .idle_output_en = true,
-    };
-    rmt_config_t rmt_tx = {
-        .channel = RMT_TX_CHANNEL,
-        .gpio_num = RMT_TX_GPIO_NUM,
-        .mem_block_num = 1,
-        .clk_div = RMT_CLK_DIV,
-        .tx_config = tx_cfg,
-        .rmt_mode = 0,
-    };
-    rmt_config(&rmt_tx);
-    rmt_driver_install(rmt_tx.channel, 0, 0);
+    if (rx_channel >= 0) {
+        TEST_ESP_OK(rmt_driver_uninstall(rx_channel));
+        TEST_ESP_OK(s_ir_parser->del(s_ir_parser));
+        s_ir_parser = NULL;
+    }
 }
 
-/**
- * @brief RMT receiver initialization
- */
-static void rx_init(void)
+TEST_CASE("RMT wrong configuration", "[rmt][error]")
 {
-    rmt_rx_config_t rx_cfg = {
-        .filter_en = true,
-        .filter_ticks_thresh = 100,
-        .idle_threshold = RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US),
-    };
-    rmt_config_t rmt_rx = {
-        .channel = RMT_RX_CHANNEL,
-        .gpio_num = RMT_RX_GPIO_NUM,
-        .clk_div = RMT_CLK_DIV,
-        .mem_block_num = 1,
-        .rmt_mode = RMT_MODE_RX,
-        .rx_config = rx_cfg,
-    };
-    rmt_config(&rmt_rx);
-    rmt_driver_install(rmt_rx.channel, (sizeof(rmt_item32_t) * DATA_ITEM_NUM * (RMT_TX_DATA_NUM + 6)), 0);
-}
+    rmt_config_t correct_config = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 0);
+    rmt_config_t wrong_config = correct_config;
 
-//A sample case to test if sending 63 data will cause crash in error interrupt.
-TEST_CASE("RMT tx test", "[rmt][test_env=UT_T1_RMT]")
-{
-    tx_init();
-    rmt_item32_t *items = (rmt_item32_t*)malloc(sizeof(rmt_item32_t) * 63);
-    for(int i = 0; i < 63; i++) {
-        items[i] = (rmt_item32_t){{{200, 1, 200, 0}}};
-    }
-    TEST_ESP_OK(rmt_write_items(RMT_TX_CHANNEL, items,
-                                    63, /* Number of items */
-                                    1 /* wait till done */));
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-    free(items);
-}
+    wrong_config.clk_div = 0;
+    TEST_ASSERT(rmt_config(&wrong_config) == ESP_ERR_INVALID_ARG);
 
-TEST_CASE("RMT init config", "[rmt][test_env=UT_T1_RMT]")
-{
-    // tx settings
-    rmt_tx_config_t tx_cfg = {
-        .loop_en = false,
-        .carrier_duty_percent = 50,
-        .carrier_freq_hz = 38000,
-        .carrier_level = 1,
-        .carrier_en = RMT_TX_CARRIER_EN,
-        .idle_level = 0,
-        .idle_output_en = true,
-    };
-    rmt_config_t rmt_tx = {
-        .channel = RMT_TX_CHANNEL,
-        .gpio_num = RMT_TX_GPIO_NUM,
-        .mem_block_num = 1,
-        .clk_div = RMT_CLK_DIV,
-        .tx_config = tx_cfg,
-    };
-    TEST_ESP_OK(rmt_config(&rmt_tx));
-    TEST_ESP_OK(rmt_driver_install(rmt_tx.channel, 0, 0));
-    TEST_ESP_OK(rmt_driver_uninstall(rmt_tx.channel));
-
-    //rx settings
-    rmt_rx_config_t rx_cfg = {
-        .filter_en = true,
-        .filter_ticks_thresh = 100,
-        .idle_threshold = RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US),
-    };
-    rmt_config_t rmt_rx = {
-        .channel = RMT_RX_CHANNEL,
-        .gpio_num = RMT_RX_GPIO_NUM,
-        .clk_div = RMT_CLK_DIV,
-        .mem_block_num = 1,
-        .rmt_mode = RMT_MODE_RX,
-        .rx_config = rx_cfg,
-    };
-    TEST_ESP_OK(rmt_config(&rmt_rx));
-    TEST_ESP_OK(rmt_driver_install(rmt_rx.channel, 1000, 0));
-    TEST_ESP_OK(rmt_driver_uninstall(rmt_rx.channel));
-
-    //error param setting
-    rmt_config_t temp_rmt_rx1 = {
-        .channel = 2,
-        .gpio_num = 15,
-        .clk_div = RMT_CLK_DIV,
-        .mem_block_num = 1,
-        .rmt_mode = RMT_MODE_RX,
-        .rx_config = rx_cfg,
-    };
-    rmt_config_t temp_rmt_rx2 = temp_rmt_rx1;
-
-    temp_rmt_rx2.clk_div = 0; // only invalid parameter to test
-    TEST_ASSERT(rmt_config(&temp_rmt_rx2) == ESP_ERR_INVALID_ARG);
-
-    temp_rmt_rx2 = temp_rmt_rx1;
-    temp_rmt_rx2.channel = RMT_CHANNEL_MAX;
-    TEST_ASSERT(rmt_config(&temp_rmt_rx2) == ESP_ERR_INVALID_ARG);
-
-    temp_rmt_rx2 = temp_rmt_rx1;
-    temp_rmt_rx2.channel = 2;
-    temp_rmt_rx2.mem_block_num = 8;
-    TEST_ASSERT(rmt_config(&temp_rmt_rx2) == ESP_ERR_INVALID_ARG);
-}
+    wrong_config = correct_config;
+    wrong_config.channel = RMT_CHANNELS_NUM;
+    TEST_ASSERT(rmt_config(&wrong_config) == ESP_ERR_INVALID_ARG);
 
-TEST_CASE("RMT init set function", "[rmt][test_env=UT_T1_RMT]")
-{
-    rmt_channel_t channel = 7;
-    TEST_ESP_OK(rmt_driver_install(channel, 0, 0));
-    TEST_ESP_OK(rmt_set_pin(channel, RMT_MODE_RX, RMT_RX_GPIO_NUM));
-    TEST_ESP_OK(rmt_set_clk_div(channel, RMT_CLK_DIV * 2));
-    TEST_ESP_OK(rmt_set_mem_block_num(channel, 1));
-    TEST_ESP_OK(rmt_set_rx_filter(channel, 1, 100));
-    TEST_ESP_OK(rmt_set_rx_idle_thresh(channel, RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US) * 2));
-    TEST_ESP_OK(rmt_driver_uninstall(channel));
+    wrong_config = correct_config;
+    wrong_config.channel = 2;
+    wrong_config.mem_block_num = 8;
+    TEST_ASSERT(rmt_config(&wrong_config) == ESP_ERR_INVALID_ARG);
+    TEST_ASSERT(rmt_set_mem_block_num(wrong_config.channel, -1) == ESP_ERR_INVALID_ARG);
 }
 
-// need to make sure its phenomenon by logic analyzer, can't run in CI
-TEST_CASE("RMT clock devider, clock source set(logic analyzer)", "[rmt][ignore]")
+TEST_CASE("RMT miscellaneous functions", "[rmt]")
 {
+    rmt_channel_t channel = RMT_CHANNELS_NUM - 2;
     uint8_t div_cnt;
     rmt_source_clk_t src_clk;
-    rmt_config_t rmt_tx;
-    rmt_tx.channel = RMT_TX_CHANNEL;
-    rmt_tx.mem_block_num = 1;
-    rmt_tx.gpio_num = RMT_TX_GPIO_NUM;
-    rmt_tx.clk_div = RMT_CLK_DIV;
-    rmt_tx.tx_config.loop_en = true;
-    rmt_tx.tx_config.carrier_duty_percent = 50;
-    rmt_tx.tx_config.carrier_freq_hz = 38000;
-    rmt_tx.tx_config.carrier_level = 1;
-    rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
-    rmt_tx.tx_config.idle_level = 0;
-    rmt_tx.tx_config.idle_output_en = true;
-    rmt_tx.rmt_mode = RMT_MODE_TX;
-
-    TEST_ESP_OK(rmt_config(&rmt_tx));
-    TEST_ESP_OK(rmt_driver_install(rmt_tx.channel, 0, 0));
-    TEST_ESP_OK(rmt_get_clk_div(RMT_TX_CHANNEL, &div_cnt));
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV);
-    vTaskDelay(1000 / portTICK_PERIOD_MS);
-
-    // reset it and check it
-    TEST_ESP_OK(rmt_set_clk_div(RMT_TX_CHANNEL, 160));
-    TEST_ESP_OK(rmt_get_clk_div(RMT_TX_CHANNEL, &div_cnt));
-    vTaskDelay(1000 / portTICK_PERIOD_MS);
-
-    TEST_ESP_OK(rmt_set_source_clk(RMT_TX_CHANNEL, RMT_BASECLK_APB));
-    TEST_ESP_OK(rmt_get_source_clk(RMT_TX_CHANNEL, &src_clk));
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, 160);
-    TEST_ASSERT_EQUAL_INT(src_clk, RMT_BASECLK_APB);
-    TEST_ESP_OK(rmt_driver_uninstall(rmt_tx.channel));
-}
-
-TEST_CASE("RMT rx set and get properties", "[rmt][test_env=UT_T1_RMT]")
-{
-    rmt_channel_t channel = RMT_RX_CHANNEL;
     uint8_t memNum;
-    uint8_t div_cnt;
-    uint16_t idleThreshold;
+    uint16_t idle_thres;
     rmt_mem_owner_t owner;
 
-    rx_init();
+    // TX related functions
+    rmt_setup_testbench(channel, -1, 0);
 
-    TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt));
+    TEST_ESP_OK(rmt_set_mem_block_num(channel, 2));
     TEST_ESP_OK(rmt_get_mem_block_num(channel, &memNum));
-    TEST_ESP_OK(rmt_get_rx_idle_thresh(channel, &idleThreshold));
+    TEST_ASSERT_EQUAL_UINT8(2, memNum);
 
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV);
-    TEST_ASSERT_EQUAL_UINT8(memNum, 1);
-    TEST_ASSERT_EQUAL_UINT16(idleThreshold, RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US));
+    TEST_ESP_OK(rmt_set_clk_div(channel, 160));
+    TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt));
+    TEST_ASSERT_EQUAL_UINT8(160, div_cnt);
 
-    TEST_ESP_OK(rmt_set_pin(channel, RMT_MODE_RX, 22));
-    TEST_ESP_OK(rmt_set_clk_div(channel, RMT_CLK_DIV * 2));
-    TEST_ESP_OK(rmt_set_mem_block_num(channel, 2));
-    TEST_ESP_OK(rmt_set_rx_filter(channel, 1, 100));
-    TEST_ESP_OK(rmt_set_rx_idle_thresh(channel, RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US) * 2));
-    TEST_ESP_OK(rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX));
+    TEST_ESP_OK(rmt_set_source_clk(channel, RMT_BASECLK_REF));
+    TEST_ESP_OK(rmt_get_source_clk(channel, &src_clk));
+    TEST_ASSERT_EQUAL_INT(RMT_BASECLK_REF, src_clk);
 
-    TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt));
-    TEST_ESP_OK(rmt_get_mem_block_num(channel, &memNum));
-    TEST_ESP_OK(rmt_get_rx_idle_thresh(channel, &idleThreshold));
+    TEST_ESP_OK(rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX));
     TEST_ESP_OK(rmt_get_memory_owner(channel, &owner));
+    TEST_ASSERT_EQUAL_INT(RMT_MEM_OWNER_RX, owner);
 
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV * 2);
-    TEST_ASSERT_EQUAL_UINT8(memNum, 2);
-    TEST_ASSERT_EQUAL_UINT16(idleThreshold, RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US) * 2);
-    TEST_ASSERT_EQUAL_INT(owner, RMT_MEM_OWNER_RX);
+    TEST_ESP_OK(rmt_set_tx_carrier(channel, 0, 1, 0, 1));
+    TEST_ESP_OK(rmt_set_idle_level(channel, 1, 0));
 
-    TEST_ESP_OK(rmt_driver_uninstall(channel));
-}
+    rmt_clean_testbench(channel, -1);
 
-TEST_CASE("RMT tx set and get properties", "[rmt][test_env=UT_T1_RMT]")
-{
-    rmt_channel_t channel = RMT_TX_CHANNEL;
-    uint8_t memNum;
-    uint8_t div_cnt;
-    bool loop_en;
-    rmt_mem_owner_t owner;
+    // RX related functions
+    rmt_setup_testbench(-1, channel, 0);
 
-    tx_init();
-    TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt));
-    TEST_ESP_OK(rmt_get_mem_block_num(channel, &memNum));
-    TEST_ESP_OK(rmt_get_tx_loop_mode(channel, &loop_en));
+    TEST_ESP_OK(rmt_set_rx_idle_thresh(channel, 200));
+    TEST_ESP_OK(rmt_get_rx_idle_thresh(channel, &idle_thres));
+    TEST_ASSERT_EQUAL_UINT16(200, idle_thres);
 
-    TEST_ASSERT_EQUAL_INT8(loop_en, 0);
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV);
-    TEST_ASSERT_EQUAL_UINT8(memNum, 1);
+    TEST_ESP_OK(rmt_set_rx_filter(channel, 1, 100));
 
-    //reset by "set"
-    TEST_ESP_OK(rmt_set_pin(channel, RMT_MODE_TX, RMT_TX_GPIO_NUM));
-    TEST_ESP_OK(rmt_set_clk_div(channel, RMT_CLK_DIV * 2));
-    TEST_ESP_OK(rmt_set_mem_block_num(channel, 2));
-    TEST_ESP_OK(rmt_set_tx_loop_mode(channel, 1));
-    TEST_ESP_OK(rmt_set_tx_carrier(channel, 0, 1, 0, 1));
-    TEST_ESP_OK(rmt_set_idle_level(channel, 1, 0));
-    TEST_ESP_OK(rmt_set_memory_owner(channel, RMT_MEM_OWNER_TX));
+    rmt_clean_testbench(-1, channel);
+}
 
-    TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt));
-    TEST_ESP_OK(rmt_get_mem_block_num(channel, &memNum));
-    TEST_ESP_OK(rmt_get_tx_loop_mode(channel, &loop_en));
-    TEST_ESP_OK(rmt_get_memory_owner(channel, &owner));
+TEST_CASE("RMT multiple channels", "[rmt]")
+{
+    rmt_config_t tx_cfg1 = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 0);
 
-    TEST_ASSERT_EQUAL_INT8(loop_en, 1);
-    TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV * 2);
-    TEST_ASSERT_EQUAL_UINT8(memNum, 2);
-    TEST_ASSERT_EQUAL_INT(owner, RMT_MEM_OWNER_TX);
+    TEST_ESP_OK(rmt_config(&tx_cfg1));
+    TEST_ESP_OK(rmt_driver_install(tx_cfg1.channel, 0, 0));
 
-    rmt_item32_t item;
-    item.duration0 = 300 / 10 * RMT_TICK_10_US; //300us
-    item.level0 = 1;
-    item.duration1 = 0;
-    item.level1 = 0;
-    for (int i = 0; i < 100; i++) {
-        TEST_ESP_OK(rmt_write_items(RMT_TX_CHANNEL, &item,
-                                    1, /* Number of items */
-                                    1 /* wait till done */));
-        vTaskDelay(10 / portTICK_PERIOD_MS); //every 10ms to write the item
-    }
-    TEST_ESP_OK(rmt_driver_uninstall(channel));
-}
+    rmt_config_t tx_cfg2 = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 1);
+    TEST_ESP_OK(rmt_config(&tx_cfg2));
+    TEST_ESP_OK(rmt_driver_install(tx_cfg2.channel, 0, 0));
+
+    rmt_config_t tx_cfg3 = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 2);
+    TEST_ESP_OK(rmt_config(&tx_cfg3));
+    TEST_ESP_OK(rmt_driver_install(tx_cfg3.channel, 0, 0));
 
-TEST_CASE("RMT use multi channel", "[rmt][test_env=UT_T1_RMT]")
-{
-    rmt_tx_config_t tx_cfg = {
-        .loop_en = true, // set it as true
-        .carrier_duty_percent = 50,
-        .carrier_freq_hz = 38000,
-        .carrier_level = 1,
-        .carrier_en = RMT_TX_CARRIER_EN,
-        .idle_level = 0,
-        .idle_output_en = true,
-    };
-    rmt_config_t rmt_tx1 = {
-        .channel = RMT_TX_CHANNEL,
-        .gpio_num = RMT_TX_GPIO_NUM,
-        .mem_block_num = 4,
-        .clk_div = RMT_CLK_DIV,
-        .tx_config = tx_cfg,
-        .rmt_mode = 0,
-    };
-    rmt_config(&rmt_tx1);
-    rmt_driver_install(rmt_tx1.channel, 0, 0);
-
-    rmt_config_t rmt_tx2 = rmt_tx1;
-    rmt_tx2.channel = 2;
-    rmt_config(&rmt_tx2);
-    rmt_driver_install(rmt_tx2.channel, 0, 0);
-
-    rmt_config_t rmt_tx3 = rmt_tx1;
-    rmt_tx3.channel = 7;
-    rmt_tx3.mem_block_num = 1;
-    rmt_config(&rmt_tx3);
-    rmt_driver_install(rmt_tx3.channel, 0, 0);
-
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
     TEST_ESP_OK(rmt_driver_uninstall(2));
-    TEST_ESP_OK(rmt_driver_uninstall(7));
+    TEST_ESP_OK(rmt_driver_uninstall(1));
+    TEST_ESP_OK(rmt_driver_uninstall(0));
 }
 
-TEST_CASE("RMT memory test", "[rmt][test_env=UT_T1_RMT]")
+TEST_CASE("RMT install/uninstall test", "[rmt][pressure]")
 {
-    rmt_config_t rmt_rx;
-    rmt_rx.channel = RMT_RX_CHANNEL;
-    rmt_rx.gpio_num = RMT_RX_GPIO_NUM;
-    rmt_rx.clk_div = RMT_CLK_DIV;
-    rmt_rx.mem_block_num = 1;
-    rmt_rx.rmt_mode = RMT_MODE_RX;
-    rmt_rx.rx_config.filter_en = true;
-    rmt_rx.rx_config.filter_ticks_thresh = 100;
-    rmt_rx.rx_config.idle_threshold = RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US);
-    TEST_ESP_OK(rmt_config(&rmt_rx));
-
+    rmt_config_t rx_cfg = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, RMT_CHANNELS_NUM - 2);
+    TEST_ESP_OK(rmt_config(&rx_cfg));
     for (int i = 0; i < 100; i++) {
-        TEST_ESP_OK(rmt_driver_install(rmt_rx.channel, 1000, 0));
-        TEST_ESP_OK(rmt_driver_uninstall(rmt_rx.channel));
+        TEST_ESP_OK(rmt_driver_install(rx_cfg.channel, 1000, 0));
+        TEST_ESP_OK(rmt_driver_uninstall(rx_cfg.channel));
     }
 }
 
-// RMT channel num and memory block relationship
-TEST_CASE("RMT memory block test", "[rmt][test_env=UT_T1_RMT]")
+TEST_CASE("RMT NEC TX and RX", "[rmt][timeout=240]")
 {
-    rmt_channel_t channel = 0;
-    rmt_config_t rmt_rx;
-    rmt_rx.channel = channel;
-    rmt_rx.gpio_num = RMT_RX_GPIO_NUM;
-    rmt_rx.clk_div = RMT_CLK_DIV;
-    rmt_rx.mem_block_num = 1;
-    rmt_rx.rmt_mode = RMT_MODE_RX;
-    rmt_rx.rx_config.filter_en = true;
-    rmt_rx.rx_config.filter_ticks_thresh = 100;
-    rmt_rx.rx_config.idle_threshold = RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US);
-    TEST_ESP_OK(rmt_config(&rmt_rx));
-    TEST_ESP_OK(rmt_driver_install(rmt_rx.channel, 1000, 0));
-
-    TEST_ESP_OK(rmt_set_mem_block_num(channel, 8));
-    TEST_ASSERT(rmt_set_mem_block_num(channel, 9) == ESP_ERR_INVALID_ARG);
-    TEST_ASSERT(rmt_set_mem_block_num(channel, -1) == ESP_ERR_INVALID_ARG);
-    TEST_ESP_OK(rmt_driver_uninstall(rmt_rx.channel));
-
-    rmt_rx.channel = 7;
-    TEST_ESP_OK(rmt_config(&rmt_rx));
-    TEST_ESP_OK(rmt_driver_install(rmt_rx.channel, 1000, 0));
-    TEST_ASSERT(rmt_set_mem_block_num(rmt_rx.channel, 2) == ESP_ERR_INVALID_ARG);
-    TEST_ASSERT(rmt_set_mem_block_num(rmt_rx.channel, -1) == ESP_ERR_INVALID_ARG);
-    TEST_ESP_OK(rmt_driver_uninstall(rmt_rx.channel));
-}
+    RingbufHandle_t rb = NULL;
+    rmt_item32_t *items = NULL;
+    uint32_t length = 0;
+    uint32_t addr = 0x10;
+    uint32_t cmd = 0x20;
+    bool repeat = false;
+    int tx_channel = 0;
+    int rx_channel = 1;
+
+    uint32_t test_flags[] = {0, RMT_CHANNEL_FLAGS_ALWAYS_ON}; // test REF_TICK clock source
+
+    // test on different flags combinations
+    for (int run = 0; run < sizeof(test_flags) / sizeof(test_flags[0]); run++) {
+        rmt_setup_testbench(tx_channel, rx_channel, test_flags[run]);
+
+        // get ready to receive
+        TEST_ESP_OK(rmt_get_ringbuf_handle(rx_channel, &rb));
+        TEST_ASSERT_NOT_NULL(rb);
+        TEST_ESP_OK(rmt_rx_start(rx_channel, true));
+
+        vTaskDelay(pdMS_TO_TICKS(1000));
+
+        // build NEC codes
+        cmd = 0x20;
+        while (cmd <= 0x30) {
+            ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr);
+            // Send new key code
+            TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
+            TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &items, &length));
+            if (cmd & 0x01) {
+                TEST_ESP_OK(rmt_write_items(tx_channel, items, length, false)); // no wait
+                TEST_ESP_OK(rmt_wait_tx_done(tx_channel, portMAX_DELAY));
+            } else {
+                TEST_ESP_OK(rmt_write_items(tx_channel, items, length, true)); // wait until done
+            }
+            cmd++;
+        }
 
-TEST_CASE("RMT send waveform(logic analyzer)", "[rmt][test_env=UT_T1_RMT][ignore]")
-{
-    tx_init();
-    rmt_item32_t items[1];
-    items[0].duration0 = 300 / 10 * RMT_TICK_10_US; //300us
-    items[0].level0 = 1;
-    for (int i = 0; i < 500; i++) {
-        TEST_ESP_OK(rmt_write_items(RMT_TX_CHANNEL, items,
-                                    1, /* Number of items */
-                                    1 /* wait till done */));
-        vTaskDelay(10 / portTICK_PERIOD_MS); //every 10ms to write the item
-    }
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-}
+        // parse NEC codes
+        while (rb) {
+            items = (rmt_item32_t *) xRingbufferReceive(rb, &length, 1000);
+            if (items) {
+                length /= 4; // one RMT = 4 Bytes
+                if (s_ir_parser->input(s_ir_parser, items, length) == ESP_OK) {
+                    if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
+                        ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
+                    }
+                }
+                vRingbufferReturnItem(rb, (void *) items);
+            } else {
+                ESP_LOGI(TAG, "done");
+                break;
+            }
+        }
 
-TEST_CASE("RMT basic TX and RX", "[rmt][test_env=UT_T1_RMT]")
-{
-    rx_init();
-    RingbufHandle_t rb = NULL;
-    rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
-    rmt_rx_start(RMT_RX_CHANNEL, 1);
-    ESP_LOGI(TAG, "Star receiving RMT data...");
-
-    tx_init();
-    uint16_t cmd = 0x0;
-    uint16_t addr = 0x11;
-    int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM;
-    rmt_item32_t *items = calloc(num_items + 1, sizeof(rmt_item32_t));
-
-    vTaskDelay(pdMS_TO_TICKS(2000));
-
-    ESP_LOGI(TAG, "Sending RMT data...");
-    // send data
-    set_tx_data(RMT_TX_CHANNEL, cmd, addr, num_items, items, 0);
-    // wait until tx done
-    rmt_write_items(RMT_TX_CHANNEL, items, num_items, 1);
-    free(items);
-    // receive data
-    uint16_t tmp = get_rx_data(rb);
-    TEST_ASSERT(tmp == RMT_TX_DATA_NUM);
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL));
+        TEST_ASSERT_EQUAL(0x30, cmd);
+        rmt_clean_testbench(tx_channel, rx_channel);
+    }
 }
 
-TEST_CASE("RMT TX write item wait some ticks", "[rmt][test_env=UT_T1_RMT]")
+TEST_CASE("RMT TX (RMT_CHANNEL_MEM_WORDS-1) symbols", "[rmt][boundary]")
 {
-    rx_init();
-    RingbufHandle_t rb = NULL;
-    rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
-    rmt_rx_start(RMT_RX_CHANNEL, 1);
-    ESP_LOGI(TAG, "Star receiving RMT data...");
-
-    tx_init();
-    uint16_t cmd = 0x0;
-    uint16_t addr = 0x11;
-    int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM;
-    rmt_item32_t *items = calloc(num_items + 1, sizeof(rmt_item32_t));
-
-    vTaskDelay(pdMS_TO_TICKS(2000));
-
-    ESP_LOGI(TAG, "Sending RMT data...");
-
-    // send data
-    set_tx_data(RMT_TX_CHANNEL, cmd, addr, num_items, items, 0);
-    rmt_write_items(RMT_TX_CHANNEL, items, num_items, 0);
-    rmt_wait_tx_done(RMT_TX_CHANNEL, portMAX_DELAY);
+    int tx_channel = 0;
+    rmt_setup_testbench(tx_channel, -1, 0);
+    rmt_item32_t *items = malloc(sizeof(rmt_item32_t) * (RMT_CHANNEL_MEM_WORDS - 1));
+    for (int i = 0; i < RMT_CHANNEL_MEM_WORDS - 1; i++) {
+        items[i] = (rmt_item32_t) {
+            {{
+                    200, 1, 200, 0
+                }
+            }
+        };
+    }
+    TEST_ESP_OK(rmt_write_items(tx_channel, items, RMT_CHANNEL_MEM_WORDS - 1, 1));
     free(items);
-
-    // receive data
-    uint16_t tmp = get_rx_data(rb);
-    TEST_ASSERT(tmp == RMT_TX_DATA_NUM);
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL));
+    rmt_clean_testbench(tx_channel, -1);
 }
 
-TEST_CASE("RMT TX stop test", "[rmt][test_env=UT_T1_RMT]")
+TEST_CASE("RMT TX stop", "[rmt]")
 {
-    rx_init();
     RingbufHandle_t rb = NULL;
-    rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
-    rmt_rx_start(RMT_RX_CHANNEL, 1);
-    ESP_LOGI(TAG, "Star receiving RMT data...");
-
-    tx_init();
-    uint16_t cmd = 0x0;
-    uint16_t addr = 0x11;
-    int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM;
-    rmt_item32_t *items = calloc(num_items + 1, sizeof(rmt_item32_t));
-
-    vTaskDelay(pdMS_TO_TICKS(2000));
-
-    ESP_LOGI(TAG, "Sending RMT data...");
-
-    // send data
-    set_tx_data(RMT_TX_CHANNEL, cmd, addr, num_items, items, 0);
-    rmt_write_items(RMT_TX_CHANNEL, items, num_items, 0);
-    vTaskDelay(1000 / portTICK_PERIOD_MS);
-    rmt_tx_stop(RMT_TX_CHANNEL);
-    free(items);
+    rmt_item32_t *frames = NULL;
+    uint32_t length = 0;
+    uint32_t count = 10;
+    uint32_t addr = 0x10;
+    uint32_t cmd = 0x20;
+    bool repeat = false;
+    int tx_channel = 0;
+    int rx_channel = 1;
+
+    rmt_setup_testbench(tx_channel, rx_channel, 0);
+
+    // re-install ir_builder, to enlarge internal buffer size
+    TEST_ESP_OK(s_ir_builder->del(s_ir_builder));
+    ir_builder_config_t ir_builder_config = IR_BUILDER_DEFAULT_CONFIG((ir_dev_t)tx_channel);
+    ir_builder_config.buffer_size *= count;
+    ir_builder_config.flags = IR_TOOLS_FLAGS_PROTO_EXT;
+    s_ir_builder = ir_builder_rmt_new_nec(&ir_builder_config);
+    TEST_ASSERT_NOT_NULL(s_ir_builder);
+
+    // get ready to receive
+    TEST_ESP_OK(rmt_get_ringbuf_handle(rx_channel, &rb));
+    TEST_ASSERT_NOT_NULL(rb);
+    TEST_ESP_OK(rmt_rx_start(rx_channel, true));
+
+    vTaskDelay(pdMS_TO_TICKS(1000));
+
+    // build NEC codes
+    ESP_LOGI(TAG, "Plan to send command 0x%x~0x%x to address 0x%x", cmd, cmd + count, addr);
+    for (int i = 0; i <= count; i++) {
+        TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
+        cmd++;
+    }
+    TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &frames, &length));
 
-    // receive data
-    uint16_t tmp = get_rx_data(rb);
-    TEST_ASSERT(tmp < RMT_TX_DATA_NUM);
+    // send for 1 second and then stop
+    TEST_ESP_OK(rmt_write_items(tx_channel, frames, length, true));
+    vTaskDelay(pdMS_TO_TICKS(100));
+    TEST_ESP_OK(rmt_tx_stop(tx_channel));
 
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL));
-}
+    // parse NEC codes
+    uint32_t num = 0;
+    while (rb) {
+        frames = (rmt_item32_t *) xRingbufferReceive(rb, &length, 1000);
+        if (frames) {
+            length /= 4; // one RMT = 4 Bytes
+            if (s_ir_parser->input(s_ir_parser, frames, length) == ESP_OK) {
+                if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
+                    ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
+                    num++;
+                }
+            }
+            vRingbufferReturnItem(rb, (void *) frames);
+        } else {
+            ESP_LOGI(TAG, "done");
+            break;
+        }
+    }
 
-TEST_CASE("RMT loop_en test", "[rmt][test_env=UT_T1_RMT][ignore]")
-{
-    rmt_tx_config_t tx_cfg = {
-        .loop_en = true, // set it as true
-        .carrier_duty_percent = 50,
-        .carrier_freq_hz = 38000,
-        .carrier_level = 1,
-        .carrier_en = RMT_TX_CARRIER_EN,
-        .idle_level = 0,
-        .idle_output_en = true,
-    };
-    rmt_config_t rmt_tx = {
-        .channel = RMT_TX_CHANNEL,
-        .gpio_num = RMT_TX_GPIO_NUM,
-        .mem_block_num = 1,
-        .clk_div = RMT_CLK_DIV,
-        .tx_config = tx_cfg,
-        .rmt_mode = 0,
-    };
-    rmt_config(&rmt_tx);
-    rmt_driver_install(rmt_tx.channel, 0, 0);
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-
-    int rx_channel = RMT_RX_CHANNEL;
-    rx_init();
-    RingbufHandle_t rb = NULL;
-    rmt_get_ringbuf_handle(rx_channel, &rb);
-    rmt_rx_start(rx_channel, 1);
-    vTaskDelay(10);
-    tx_init();
-    int tx_channel = RMT_TX_CHANNEL;
-    int tx_num = RMT_TX_DATA_NUM;
-
-    ESP_LOGI(TAG, "RMT TX DATA");
-    size_t size = (sizeof(rmt_item32_t) * DATA_ITEM_NUM * tx_num);
-    rmt_item32_t *item = (rmt_item32_t *)malloc(size);
-    int item_num = DATA_ITEM_NUM * tx_num;
-    memset((void *)item, 0, size);
-    int offset = 0;
-    uint16_t cmd = 0x0;
-    uint16_t addr = 0x11;
-
-    // send data
-    set_tx_data(tx_channel, cmd, addr, item_num, item, offset);
-    rmt_write_items(tx_channel, item, item_num, 0);
-    vTaskDelay(1000 / portTICK_PERIOD_MS);
-    rmt_tx_stop(tx_channel);
-    free(item);
-
-    // receive data
-    uint16_t tmp = get_rx_data(rb);
-    TEST_ASSERT(tmp < RMT_TX_DATA_NUM);
-
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
-    TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL));
+    TEST_ASSERT(num < count);
+    rmt_clean_testbench(tx_channel, rx_channel);
 }
-
-#endif //DISABLED_FOR_TARGETS(ESP32S2)

+ 19 - 5
components/soc/include/hal/rmt_types.h

@@ -18,11 +18,25 @@
 extern "C" {
 #endif
 
+#include "soc/rmt_caps.h"
+
 /**
- * @brief RMT Channel Type
- *
- */
-typedef rmt_channel_id_t rmt_channel_t;
+* @brief RMT channel ID
+*
+*/
+typedef enum {
+    RMT_CHANNEL_0,  /*!< RMT channel number 0 */
+    RMT_CHANNEL_1,  /*!< RMT channel number 1 */
+    RMT_CHANNEL_2,  /*!< RMT channel number 2 */
+    RMT_CHANNEL_3,  /*!< RMT channel number 3 */
+#if RMT_CHANNELS_NUM > 4
+    RMT_CHANNEL_4,  /*!< RMT channel number 4 */
+    RMT_CHANNEL_5,  /*!< RMT channel number 5 */
+    RMT_CHANNEL_6,  /*!< RMT channel number 6 */
+    RMT_CHANNEL_7,  /*!< RMT channel number 7 */
+#endif
+    RMT_CHANNEL_MAX /*!< Number of RMT channels */
+} rmt_channel_t;
 
 /**
  * @brief RMT Internal Memory Owner
@@ -39,7 +53,7 @@ typedef enum {
  *
  */
 typedef enum {
-    RMT_BASECLK_REF, /*!< RMT source clock system reference tick, 1MHz by default (not supported in this version) */
+    RMT_BASECLK_REF, /*!< RMT source clock is REF_TICK, 1MHz by default */
     RMT_BASECLK_APB, /*!< RMT source clock is APB CLK, 80Mhz by default */
     RMT_BASECLK_MAX,
 } rmt_source_clk_t;

+ 1 - 18
components/soc/soc/esp32/include/soc/rmt_caps.h

@@ -18,25 +18,8 @@
 extern "C" {
 #endif
 
-#include <stdint.h>
-
 #define RMT_CHANNEL_MEM_WORDS (64) /*!< Each channel owns 64 words memory */
-
-/**
-* @brief RMT channel ID
-*
-*/
-typedef enum {
-    RMT_CHANNEL_0,  /*!< RMT channel number 0 */
-    RMT_CHANNEL_1,  /*!< RMT channel number 1 */
-    RMT_CHANNEL_2,  /*!< RMT channel number 2 */
-    RMT_CHANNEL_3,  /*!< RMT channel number 3 */
-    RMT_CHANNEL_4,  /*!< RMT channel number 4 */
-    RMT_CHANNEL_5,  /*!< RMT channel number 5 */
-    RMT_CHANNEL_6,  /*!< RMT channel number 6 */
-    RMT_CHANNEL_7,  /*!< RMT channel number 7 */
-    RMT_CHANNEL_MAX /*!< Number of RMT channels */
-} rmt_channel_id_t;
+#define RMT_CHANNELS_NUM (8)       /*!< Total 8 channels */
 
 #ifdef __cplusplus
 }

+ 1 - 14
components/soc/soc/esp32s2/include/soc/rmt_caps.h

@@ -18,21 +18,8 @@
 extern "C" {
 #endif
 
-#include <stdint.h>
-
 #define RMT_CHANNEL_MEM_WORDS (64) /*!< Each channel owns 64 words memory */
-
-/**
-* @brief RMT channel ID
-*
-*/
-typedef enum {
-    RMT_CHANNEL_0,  /*!< RMT channel number 0 */
-    RMT_CHANNEL_1,  /*!< RMT channel number 1 */
-    RMT_CHANNEL_2,  /*!< RMT channel number 2 */
-    RMT_CHANNEL_3,  /*!< RMT channel number 3 */
-    RMT_CHANNEL_MAX /*!< Number of RMT channels */
-} rmt_channel_id_t;
+#define RMT_CHANNELS_NUM (4)       /*!< Total 4 channels */
 
 #ifdef __cplusplus
 }

+ 1 - 1
components/soc/src/esp32/include/hal/rmt_ll.h

@@ -260,7 +260,7 @@ static inline void rmt_ll_get_carrier_high_low_ticks(rmt_dev_t *dev, uint32_t ch
     *low_ticks = dev->carrier_duty_ch[channel].low;
 }
 
-static inline void rmt_ll_enable_tx_carrier(rmt_dev_t *dev, uint32_t channel, bool enable)
+static inline void rmt_ll_enable_carrier(rmt_dev_t *dev, uint32_t channel, bool enable)
 {
     dev->conf_ch[channel].conf0.carrier_en = enable;
 }

+ 1 - 1
components/soc/src/esp32s2/include/hal/rmt_ll.h

@@ -253,7 +253,7 @@ static inline void rmt_ll_get_carrier_high_low_ticks(rmt_dev_t *dev, uint32_t ch
     *low_ticks = dev->carrier_duty_ch[channel].low;
 }
 
-static inline void rmt_ll_enable_tx_carrier(rmt_dev_t *dev, uint32_t channel, bool enable)
+static inline void rmt_ll_enable_carrier(rmt_dev_t *dev, uint32_t channel, bool enable)
 {
     dev->conf_ch[channel].conf0.carrier_en = enable;
 }

+ 5 - 1
docs/en/api-reference/peripherals/rmt.rst

@@ -113,7 +113,11 @@ Common Parameters
 * The RMT **operation mode** - whether this channel is used to transmit or receive data, selected by setting a **rmt_mode** members to one of the values from :cpp:type:`rmt_mode_t`.
 * What is the **pin number** to transmit or receive RMT signals, selected by setting **gpio_num**.
 * How many **memory blocks** will be used by the channel, set with **mem_block_num**.
-* A **clock divider**, that will determine the range of pulse length generated by the RMT transmitter or discriminated by the receiver. Selected by setting **clk_div** to a value within [1 .. 255] range. The RMT source clock is typically APB CLK, 80Mhz by default.
+* Extra miscellaneous parameters for the channel can be set in the **flags**.
+
+    * When **RMT_CHANNEL_FLAGS_ALWAYS_ON** is set, RMT channel will take REF_TICK as source clock. The benefit is, RMT channel can continue work even when APB clock is changing. See :doc:`power_management <../system/power_management>` for more information.
+
+* A **clock divider**, that will determine the range of pulse length generated by the RMT transmitter or discriminated by the receiver. Selected by setting **clk_div** to a value within [1 .. 255] range. The RMT source clock is typically APB CLK, 80Mhz by default. But when **RMT_CHANNEL_FLAGS_ALWAYS_ON** is set in **flags**, RMT source clock is changed to REF_TICK.
 
 .. note::
 

+ 1 - 1
docs/en/api-reference/system/power_management.rst

@@ -77,7 +77,7 @@ The following peripherals work normally even when the APB frequency is changing:
 
 - **UART**: if REF_TICK is used as a clock source. See `use_ref_tick` member of :cpp:class:`uart_config_t`.
 - **LEDC**: if REF_TICK is used as a clock source. See :cpp:func:`ledc_timer_config` function.
-- **RMT**: if REF_TICK is used as a clock source. Although the driver does not support REF_TICK, this feature can be enabled by clearing the ``RMT_REF_ALWAYS_ON_CHx`` bit for the respective channel.
+- **RMT**: if REF_TICK is used as a clock source. See `flags` member of :cpp:class:`rmt_config_t` and macro `RMT_CHANNEL_FLAGS_ALWAYS_ON`.
 
 Currently, the following peripheral drivers are aware of DFS and will use the ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction:
 

+ 1 - 1
docs/zh_CN/api-reference/system/power_management.rst

@@ -72,7 +72,7 @@ ESP32 支持下表中所述的三种电源管理锁。
 
 - **UART**:如果 REF_TICK 用作时钟源,则 UART 不受 APB 频率变更影响。请查看 :cpp:class:`uart_config_t` 中的 `use_ref_tick`。
 - **LEDC**:如果 REF_TICK 用作时钟源,则 LEDC 不受 APB 频率变更影响。请查看 :cpp:func:`ledc_timer_config` 函数。
-- **RMT**:如果 REF_TICK 用作时钟源,则 RMT 不受 APB 频率变更影响。此驱动程序尚不支持 REF_TICK,但可以清除相应通道的 ``RMT_REF_ALWAYS_ON_CHx`` 位来启用该功能
+- **RMT**:如果 REF_TICK 用作时钟源,则 RMT 不受 APB 频率变更影响。请查看 :cpp:class:`rmt_config_t` 结构体中的 `flags` 成员以及 `RMT_CHANNEL_FLAGS_ALWAYS_ON` 宏
 
 目前以下外设驱动程序可感知动态调频,并在调频期间使用 ``ESP_PM_APB_FREQ_MAX`` 锁:
 

+ 2 - 1
tools/unit-test-app/CMakeLists.txt

@@ -2,7 +2,8 @@
 # CMakeLists in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.5)
 
-set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/")
+set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/"
+                         "$ENV{IDF_PATH}/examples/peripherals/rmt/ir_protocols/components")
 
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(unit-test-app)

+ 2 - 0
tools/unit-test-app/Makefile

@@ -5,6 +5,8 @@
 
 PROJECT_NAME := unit-test-app
 
+EXTRA_COMPONENT_DIRS += ${IDF_PATH}/examples/peripherals/rmt/ir_protocols/components
+
 ifeq ($(MAKELEVEL),0)
 # Set default target
 all:

+ 2 - 2
tools/unit-test-app/components/test_utils/ref_clock.c

@@ -49,7 +49,7 @@
 #include "sdkconfig.h"
 
 /* Select which RMT and PCNT channels, and GPIO to use */
-#define REF_CLOCK_RMT_CHANNEL   RMT_CHANNEL_MAX - 1
+#define REF_CLOCK_RMT_CHANNEL   RMT_CHANNELS_NUM - 1
 #define REF_CLOCK_PCNT_UNIT     0
 #define REF_CLOCK_GPIO          21
 
@@ -97,7 +97,7 @@ void ref_clock_init()
     rmt_ll_start_tx(s_rmt.regs, REF_CLOCK_RMT_CHANNEL);
     rmt_ll_set_mem_owner(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, 0);
     rmt_ll_reset_tx_pointer(s_rmt.regs, REF_CLOCK_RMT_CHANNEL);
-    rmt_ll_enable_tx_carrier(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, false);
+    rmt_ll_enable_carrier(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, false);
     rmt_ll_set_counter_clock_div(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, 1);
     rmt_ll_set_mem_blocks(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, 1);
     rmt_ll_set_counter_clock_src(s_rmt.regs, REF_CLOCK_RMT_CHANNEL, 0);