From 0e4d82bc55e1741016c27aaa5bfaa7991b87b370 Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 14 Feb 2020 14:33:07 +0800 Subject: [PATCH] rmt: support REF_TICK as channel clock source Closes https://github.com/espressif/esp-idf/pull/3952 --- components/driver/include/driver/rmt.h | 5 ++ components/driver/rmt.c | 23 +++-- components/driver/test/test_rmt.c | 84 ++++++++++++------- components/soc/include/hal/rmt_types.h | 2 +- docs/en/api-reference/peripherals/rmt.rst | 6 +- .../api-reference/system/power_management.rst | 2 +- .../api-reference/system/power_management.rst | 2 +- 7 files changed, 82 insertions(+), 42 deletions(-) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 39775a980..2629d864a 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 5ed12c49f..634f45880 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; @@ -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,20 @@ 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_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; @@ -990,7 +995,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/test_rmt.c b/components/driver/test/test_rmt.c index 766c8f8f7..c43a4dd89 100644 --- a/components/driver/test/test_rmt.c +++ b/components/driver/test/test_rmt.c @@ -19,17 +19,16 @@ #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_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 @@ -42,10 +41,16 @@ static const char *TAG = "RMT.test"; #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) */ +#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) */ + +static uint32_t s_rmt_10us_ticks = RMT_TICK_10_US; + +static inline uint32_t item_duration(uint32_t duration_us) +{ + return (duration_us & 0x7fff) * 10 / s_rmt_10us_ticks; +} /** * @brief Build register value of waveform for NEC one data bit @@ -53,9 +58,9 @@ static const char *TAG = "RMT.test"; 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->duration0 = (high_us) / 10 * s_rmt_10us_ticks; item->level1 = 0; - item->duration1 = (low_us) / 10 * RMT_TICK_10_US; + item->duration1 = (low_us) / 10 * s_rmt_10us_ticks; } /** @@ -95,8 +100,8 @@ static void fill_item_end(rmt_item32_t *item) */ 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))) { + if ((item_duration(duration_ticks) < (target_us + margin_us)) && + (item_duration(duration_ticks) > (target_us - margin_us))) { return true; } else { return false; @@ -265,7 +270,7 @@ static int get_rx_data(RingbufHandle_t rb) /** * @brief RMT transmitter initialization */ -static void tx_init(void) +static void tx_init(bool always_on) { // the sender once it send something, its frq is 38kHz, and the duty cycle is 50% rmt_tx_config_t tx_cfg = { @@ -285,6 +290,11 @@ static void tx_init(void) .tx_config = tx_cfg, .rmt_mode = 0, }; + if (always_on) { + rmt_tx.flags |= RMT_CHANNEL_FLAGS_ALWAYS_ON; + rmt_tx.clk_div = 10; + s_rmt_10us_ticks = 1; // REF_TICK / DIV / 1e5 + } rmt_config(&rmt_tx); rmt_driver_install(rmt_tx.channel, 0, 0); } @@ -292,7 +302,7 @@ static void tx_init(void) /** * @brief RMT receiver initialization */ -static void rx_init(void) +static void rx_init(bool always_on) { rmt_rx_config_t rx_cfg = { .filter_en = true, @@ -307,6 +317,11 @@ static void rx_init(void) .rmt_mode = RMT_MODE_RX, .rx_config = rx_cfg, }; + if (always_on) { + rmt_rx.flags |= RMT_CHANNEL_FLAGS_ALWAYS_ON; + rmt_rx.clk_div = 10; + s_rmt_10us_ticks = 1; // REF_TICK / DIV / 1e5 + } rmt_config(&rmt_rx); rmt_driver_install(rmt_rx.channel, (sizeof(rmt_item32_t) * DATA_ITEM_NUM * (RMT_TX_DATA_NUM + 6)), 0); } @@ -314,7 +329,7 @@ static void rx_init(void) //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(); + tx_init(false); 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}}}; @@ -448,7 +463,7 @@ TEST_CASE("RMT rx set and get properties", "[rmt][test_env=UT_T1_RMT]") uint16_t idleThreshold; rmt_mem_owner_t owner; - rx_init(); + rx_init(false); TEST_ESP_OK(rmt_get_clk_div(channel, &div_cnt)); TEST_ESP_OK(rmt_get_mem_block_num(channel, &memNum)); @@ -486,7 +501,7 @@ TEST_CASE("RMT tx set and get properties", "[rmt][test_env=UT_T1_RMT]") bool loop_en; rmt_mem_owner_t owner; - tx_init(); + tx_init(false); 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)); @@ -616,7 +631,7 @@ TEST_CASE("RMT memory block test", "[rmt][test_env=UT_T1_RMT]") TEST_CASE("RMT send waveform(logic analyzer)", "[rmt][test_env=UT_T1_RMT][ignore]") { - tx_init(); + tx_init(false); rmt_item32_t items[1]; items[0].duration0 = 300 / 10 * RMT_TICK_10_US; //300us items[0].level0 = 1; @@ -629,15 +644,15 @@ TEST_CASE("RMT send waveform(logic analyzer)", "[rmt][test_env=UT_T1_RMT][ignore TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL)); } -TEST_CASE("RMT basic TX and RX", "[rmt][test_env=UT_T1_RMT]") +static void rmt_test_tx_rx(bool always_on) { - rx_init(); + rx_init(always_on); 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(); + tx_init(always_on); uint16_t cmd = 0x0; uint16_t addr = 0x11; int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM; @@ -658,15 +673,26 @@ TEST_CASE("RMT basic TX and RX", "[rmt][test_env=UT_T1_RMT]") TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL)); } +TEST_CASE("RMT basic TX and RX", "[rmt][test_env=UT_T1_RMT]") +{ + rmt_test_tx_rx(false); +} + +TEST_CASE("RMT channel clock always on", "[rmt][test_env=UT_T1_RMT]") +{ + rmt_test_tx_rx(true); + s_rmt_10us_ticks = RMT_TICK_10_US; // restore default APB clock source for other tests +} + TEST_CASE("RMT TX write item wait some ticks", "[rmt][test_env=UT_T1_RMT]") { - rx_init(); + rx_init(false); 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(); + tx_init(false); uint16_t cmd = 0x0; uint16_t addr = 0x11; int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM; @@ -691,13 +717,13 @@ TEST_CASE("RMT TX write item wait some ticks", "[rmt][test_env=UT_T1_RMT]") TEST_CASE("RMT TX stop test", "[rmt][test_env=UT_T1_RMT]") { - rx_init(); + rx_init(false); 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(); + tx_init(false); uint16_t cmd = 0x0; uint16_t addr = 0x11; int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM; @@ -746,12 +772,12 @@ TEST_CASE("RMT loop_en test", "[rmt][test_env=UT_T1_RMT][ignore]") TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL)); int rx_channel = RMT_RX_CHANNEL; - rx_init(); + rx_init(false); RingbufHandle_t rb = NULL; rmt_get_ringbuf_handle(rx_channel, &rb); rmt_rx_start(rx_channel, 1); vTaskDelay(10); - tx_init(); + tx_init(false); int tx_channel = RMT_TX_CHANNEL; int tx_num = RMT_TX_DATA_NUM; diff --git a/components/soc/include/hal/rmt_types.h b/components/soc/include/hal/rmt_types.h index 0e2e3735f..14ecf48d3 100644 --- a/components/soc/include/hal/rmt_types.h +++ b/components/soc/include/hal/rmt_types.h @@ -53,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/docs/en/api-reference/peripherals/rmt.rst b/docs/en/api-reference/peripherals/rmt.rst index e27af877a..9f2f1e205 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 b26602d52..462294b94 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 86a02ca0d..2c13c2a3e 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -78,7 +78,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`` 锁: