diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 39775a980c..2629d864a5 100644 --- a/components/driver/include/driver/rmt.h +++ b/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, \ diff --git a/components/driver/rmt.c b/components/driver/rmt.c index 5ed12c49f9..656f605f31 100644 --- a/components/driver/rmt.c +++ b/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; } diff --git a/components/driver/test/CMakeLists.txt b/components/driver/test/CMakeLists.txt index dbbffcdca9..11e9c0474d 100644 --- a/components/driver/test/CMakeLists.txt +++ b/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 - ) \ No newline at end of file + REQUIRES unity test_utils driver nvs_flash esp_serial_slave_link infrared_tools) diff --git a/components/driver/test/test_rmt.c b/components/driver/test/test_rmt.c index 766c8f8f7e..9e6cdb5e22 100644 --- a/components/driver/test/test_rmt.c +++ b/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 #include #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" +#include "driver/rmt.h" +#include "ir_tools.h" +#include "unity.h" +#include "test_utils.h" + +// 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 -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2) -//No runners static const char *TAG = "RMT.test"; +static ir_builder_t *s_ir_builder = NULL; +static ir_parser_t *s_ir_parser = NULL; -#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) +static void rmt_setup_testbench(int tx_channel, int rx_channel, uint32_t flags) { - item->level0 = 1; - item->duration0 = (high_us) / 10 * RMT_TICK_10_US; - item->level1 = 0; - item->duration1 = (low_us) / 10 * RMT_TICK_10_US; -} + // 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 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); -} + 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)); + } -/* - * @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); -} + // 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 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); -} + // install driver + if (tx_channel >= 0) { + TEST_ESP_OK(rmt_driver_install(tx_channel, 0, 0)); -/** - * @brief Generate NEC end signal: positive 0.56ms - */ -static void fill_item_end(rmt_item32_t *item) -{ - fill_item_level(item, BIT_END, 0x7fff); -} + 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); + } -/** - * @brief Check whether duration is around target_us - */ -static inline bool check_in_range(int duration_ticks, int target_us, int margin_us) -{ - if ((ITEM_DURATION(duration_ticks) < (target_us + margin_us)) && - (ITEM_DURATION(duration_ticks) > (target_us - margin_us))) { - return true; - } else { - return false; + if (rx_channel >= 0) { + TEST_ESP_OK(rmt_driver_install(rx_channel, 3000, 0)); + + 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); } } -/** - * @brief Check whether this value represents an NEC header - */ -static bool header_if(rmt_item32_t *item) +static void rmt_clean_testbench(int tx_channel, int rx_channel) { - 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 (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 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; -} - -/** - * @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; -} - -/** - * @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; - } - 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; -} - -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; + 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; } } -static int get_rx_data(RingbufHandle_t rb) +TEST_CASE("RMT wrong configuration", "[rmt][error]") { - 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; - } - } - return tmp; + rmt_config_t correct_config = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 0); + rmt_config_t wrong_config = correct_config; + + wrong_config.clk_div = 0; + TEST_ASSERT(rmt_config(&wrong_config) == ESP_ERR_INVALID_ARG); + + wrong_config = correct_config; + wrong_config.channel = RMT_CHANNELS_NUM; + TEST_ASSERT(rmt_config(&wrong_config) == ESP_ERR_INVALID_ARG); + + 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); } -/** - * @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); -} - -/** - * @brief RMT receiver initialization - */ -static void rx_init(void) -{ - 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); -} - -//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); -} - -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); -} - -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)); -} - -// 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_get_mem_block_num(channel, &memNum)); - TEST_ESP_OK(rmt_get_rx_idle_thresh(channel, &idleThreshold)); - - 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_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_get_mem_block_num(channel, &memNum)); + TEST_ASSERT_EQUAL_UINT8(2, memNum); + + 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_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_set_memory_owner(channel, RMT_MEM_OWNER_RX)); - - 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_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_driver_uninstall(channel)); -} - -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; - - 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_ASSERT_EQUAL_INT8(loop_en, 0); - TEST_ASSERT_EQUAL_UINT8(div_cnt, RMT_CLK_DIV); - TEST_ASSERT_EQUAL_UINT8(memNum, 1); - - //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)); - 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)); + rmt_clean_testbench(channel, -1); - 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); + // RX related functions + rmt_setup_testbench(-1, channel, 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)); + 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_ESP_OK(rmt_set_rx_filter(channel, 1, 100)); + + rmt_clean_testbench(-1, channel); } -TEST_CASE("RMT use multi channel", "[rmt][test_env=UT_T1_RMT]") +TEST_CASE("RMT multiple channels", "[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 tx_cfg1 = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 0); - rmt_config_t rmt_tx2 = rmt_tx1; - rmt_tx2.channel = 2; - rmt_config(&rmt_tx2); - rmt_driver_install(rmt_tx2.channel, 0, 0); + TEST_ESP_OK(rmt_config(&tx_cfg1)); + TEST_ESP_OK(rmt_driver_install(tx_cfg1.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); + 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_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)); + 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; - 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)); + uint32_t test_flags[] = {0, RMT_CHANNEL_FLAGS_ALWAYS_ON}; // test REF_TICK clock source - 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)); -} + // 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]); -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 + // 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++; + } + + // 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_ASSERT_EQUAL(0x30, cmd); + rmt_clean_testbench(tx_channel, rx_channel); } - TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL)); } -TEST_CASE("RMT basic TX and RX", "[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); - // wait until tx done - rmt_write_items(RMT_TX_CHANNEL, items, num_items, 1); + 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 write item wait some ticks", "[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..."); + 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; - 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)); + rmt_setup_testbench(tx_channel, rx_channel, 0); - vTaskDelay(pdMS_TO_TICKS(2000)); + // 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); - ESP_LOGI(TAG, "Sending RMT data..."); + // 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)); - // 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); - free(items); + vTaskDelay(pdMS_TO_TICKS(1000)); - // 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)); + // 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)); + + // 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)); + + // 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_ASSERT(num < count); + rmt_clean_testbench(tx_channel, rx_channel); } - -TEST_CASE("RMT TX stop test", "[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); - rmt_write_items(RMT_TX_CHANNEL, items, num_items, 0); - vTaskDelay(1000 / portTICK_PERIOD_MS); - rmt_tx_stop(RMT_TX_CHANNEL); - 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_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)); -} - -#endif //DISABLED_FOR_TARGETS(ESP32S2) \ No newline at end of file diff --git a/components/soc/include/hal/rmt_types.h b/components/soc/include/hal/rmt_types.h index 4e463cdfd1..14ecf48d3d 100644 --- a/components/soc/include/hal/rmt_types.h +++ b/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; diff --git a/components/soc/soc/esp32/include/soc/rmt_caps.h b/components/soc/soc/esp32/include/soc/rmt_caps.h index 7b3eaf4ade..a1c4e7eb54 100644 --- a/components/soc/soc/esp32/include/soc/rmt_caps.h +++ b/components/soc/soc/esp32/include/soc/rmt_caps.h @@ -18,25 +18,8 @@ extern "C" { #endif -#include - #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 } diff --git a/components/soc/soc/esp32s2/include/soc/rmt_caps.h b/components/soc/soc/esp32s2/include/soc/rmt_caps.h index 8313d3463b..9973964e72 100644 --- a/components/soc/soc/esp32s2/include/soc/rmt_caps.h +++ b/components/soc/soc/esp32s2/include/soc/rmt_caps.h @@ -18,21 +18,8 @@ extern "C" { #endif -#include - #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 } diff --git a/components/soc/src/esp32/include/hal/rmt_ll.h b/components/soc/src/esp32/include/hal/rmt_ll.h index 1749b2be37..64820e2de1 100644 --- a/components/soc/src/esp32/include/hal/rmt_ll.h +++ b/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; } diff --git a/components/soc/src/esp32s2/include/hal/rmt_ll.h b/components/soc/src/esp32s2/include/hal/rmt_ll.h index 46dcac68b7..434b6c4243 100644 --- a/components/soc/src/esp32s2/include/hal/rmt_ll.h +++ b/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; } diff --git a/docs/en/api-reference/peripherals/rmt.rst b/docs/en/api-reference/peripherals/rmt.rst index e27af877a8..9f2f1e2053 100644 --- a/docs/en/api-reference/peripherals/rmt.rst +++ b/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:: diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index b26602d524..462294b946 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/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: diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 4f28ae725f..5072ef25b1 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/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`` 锁: diff --git a/tools/unit-test-app/CMakeLists.txt b/tools/unit-test-app/CMakeLists.txt index a1861793e1..50e000d5e1 100644 --- a/tools/unit-test-app/CMakeLists.txt +++ b/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) diff --git a/tools/unit-test-app/Makefile b/tools/unit-test-app/Makefile index 1217f78204..37c68111be 100644 --- a/tools/unit-test-app/Makefile +++ b/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: diff --git a/tools/unit-test-app/components/test_utils/ref_clock.c b/tools/unit-test-app/components/test_utils/ref_clock.c index cecb5aed29..3e912e3544 100644 --- a/tools/unit-test-app/components/test_utils/ref_clock.c +++ b/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);