소스 검색

rmt: support REF_TICK as channel clock source

Closes https://github.com/espressif/esp-idf/pull/3952
morris 6 년 전
부모
커밋
0e4d82bc55

+ 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,         \

+ 14 - 9
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;
 }

+ 55 - 29
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;
 

+ 1 - 1
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;

+ 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

@@ -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`` 锁: