rmt: simplify rmt source clock configure with clk_tree API

Also decrease the payload size for testing the multi-channel behaviour.
This commit is contained in:
morris 2023-02-08 17:02:21 +08:00
parent 648b1a41c6
commit 1626766abf
9 changed files with 59 additions and 89 deletions

View File

@ -21,7 +21,7 @@
#include "soc/soc_memory_layout.h"
#include "soc/rmt_periph.h"
#include "soc/rmt_struct.h"
#include "esp_private/esp_clk.h"
#include "clk_tree.h"
#include "hal/rmt_hal.h"
#include "hal/rmt_ll.h"
#include "hal/gpio_hal.h"
@ -58,16 +58,6 @@ static const char *TAG = "rmt(legacy)";
#define RMT_DECODE_RX_CHANNEL(encode_chan) ((encode_chan - RMT_RX_CHANNEL_ENCODING_START))
#define RMT_ENCODE_RX_CHANNEL(decode_chan) ((decode_chan + RMT_RX_CHANNEL_ENCODING_START))
#if SOC_RMT_SUPPORT_APB
#define RMT_DEFAULT_CLOCK_FREQ esp_clk_apb_freq()
#elif SOC_RMT_SUPPORT_PLL_F80M
#define RMT_DEFAULT_CLOCK_FREQ (80*1000*1000)
#elif SOC_RMT_SUPPORT_XTAL
#define RMT_DEFAULT_CLOCK_FREQ esp_clk_xtal_freq()
#else
#error "RMT unknow default clock"
#endif
typedef struct {
rmt_hal_context_t hal;
_lock_t rmt_driver_isr_lock;
@ -575,19 +565,20 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
rmt_ll_enable_mem_access_nonfifo(dev, true);
if (rmt_param->flags & RMT_CHANNEL_FLAGS_AWARE_DFS) {
// [clk_tree] TODO: refactor the following code by clk_tree API
#if SOC_RMT_SUPPORT_XTAL
// clock src: XTAL_CLK
rmt_source_clk_hz = esp_clk_xtal_freq();
clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_XTAL, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz);
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_XTAL, 1, 0, 0);
#elif SOC_RMT_SUPPORT_REF_TICK
// clock src: REF_CLK
rmt_source_clk_hz = REF_CLK_FREQ;
clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_REF, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz);
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_REF, 1, 0, 0);
#else
#error "No clock source is aware of DFS"
#endif
} else {
// fallback to use default clock source
rmt_source_clk_hz = RMT_DEFAULT_CLOCK_FREQ;
clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz);
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_DEFAULT, 1, 0, 0);
}
RMT_EXIT_CRITICAL();

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -19,7 +19,7 @@
#include "soc/rmt_periph.h"
#include "hal/rmt_ll.h"
#include "driver/gpio.h"
#include "esp_private/esp_clk.h"
#include "clk_tree.h"
#include "esp_private/periph_ctrl.h"
static const char *TAG = "rmt";
@ -122,65 +122,46 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
"group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
// [clk_tree] TODO: replace the following switch table by clk_tree API
switch (clk_src) {
#if SOC_RMT_SUPPORT_APB
case RMT_CLK_SRC_APB:
periph_src_clk_hz = esp_clk_apb_freq();
#if CONFIG_PM_ENABLE
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, chan->pm_lock_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create APB_FREQ_MAX lock failed");
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for RMT channel (%d,%d)", group->group_id, channel_id);
#endif // CONFIG_PM_ENABLE
break;
#endif // SOC_RMT_SUPPORT_APB
#if SOC_RMT_SUPPORT_PLL_F80M
case RMT_CLK_SRC_PLL_F80M:
periph_src_clk_hz = 80000000;
#if CONFIG_PM_ENABLE
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0
// ESP32C6 PLL_F80M is available even when SOC_ROOT_CLK switches from PLL to XTAL, so using NO_LIGHT_SLEEP lock here is sufficient
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, chan->pm_lock_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed");
ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for RMT channel (%d,%d)", group->group_id, channel_id);
#endif // CONFIG_PM_ENABLE
break;
#endif // SOC_RMT_SUPPORT_PLL_F80M
#if SOC_RMT_SUPPORT_AHB
case RMT_CLK_SRC_AHB:
// TODO: decide which kind of PM lock we should use for such clock
periph_src_clk_hz = 48 * 1000 * 1000;
break;
#endif // SOC_RMT_SUPPORT_AHB
#if SOC_RMT_SUPPORT_XTAL
case RMT_CLK_SRC_XTAL:
periph_src_clk_hz = esp_clk_xtal_freq();
#if CONFIG_PM_ENABLE
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0
// XTAL will be power down in the light sleep (predefined low power modes)
// acquire a NO_LIGHT_SLEEP lock here to prevent the system go into light sleep automatically
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, chan->pm_lock_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed");
ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for RMT channel (%d,%d)", group->group_id, channel_id);
#endif // CONFIG_PM_ENABLE
break;
#endif // SOC_RMT_SUPPORT_XTAL
#if SOC_RMT_SUPPORT_REF_TICK
case RMT_CLK_SRC_REF_TICK:
periph_src_clk_hz = REF_CLK_FREQ;
break;
#endif // SOC_RMT_SUPPORT_REF_TICK
// TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
#if SOC_RMT_SUPPORT_RC_FAST
case RMT_CLK_SRC_RC_FAST:
if (clk_src == RMT_CLK_SRC_RC_FAST) {
// RC_FAST clock is not enabled automatically on start up, we enable it here manually.
// Note there's a ref count in the enable/disable function, we must call them in pair in the driver.
periph_rtc_dig_clk8m_enable();
periph_src_clk_hz = periph_rtc_dig_clk8m_get_freq();
break;
#endif // SOC_RMT_SUPPORT_RC_FAST
default:
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not supported", clk_src);
break;
}
#endif // SOC_RMT_SUPPORT_RC_FAST
// get clock source frequency
ESP_RETURN_ON_ERROR(clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz),
TAG, "get clock source frequency failed");
#if CONFIG_PM_ENABLE
bool need_pm_lock = true;
// to make the RMT work reliable, the source clock must stay alive and unchanged
// driver will create different pm lock for that purpose, according to different clock source
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
#if SOC_RMT_SUPPORT_RC_FAST
if (clk_src == RMT_CLK_SRC_RC_FAST) {
// RC_FAST won't be turn off in sleep and won't change its frequency during DFS
need_pm_lock = false;
}
#endif // SOC_RMT_SUPPORT_RC_FAST
#if SOC_RMT_SUPPORT_APB
if (clk_src == RMT_CLK_SRC_APB) {
// APB clock frequency can be changed during DFS
pm_lock_type = ESP_PM_APB_FREQ_MAX;
}
#endif // SOC_RMT_SUPPORT_APB
if (need_pm_lock) {
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0
ret = esp_pm_lock_create(pm_lock_type, 0, chan->pm_lock_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
}
#endif // CONFIG_PM_ENABLE
// no division for group clock source, to achieve highest resolution
rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0);
group->resolution_hz = periph_src_clk_hz;

View File

@ -90,7 +90,7 @@ TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]")
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
// convert the raw count to us
value = value * 1000000 / timer_resolution_hz[i];
TEST_ASSERT_UINT_WITHIN(1000, 20000, value);
TEST_ASSERT_UINT_WITHIN(1100, 20000, value);
}
printf("stop timers\r\n");
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
@ -139,7 +139,7 @@ TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]")
* between the alarm triggering and the execution of the callback that actually stops the gptimer.
*/
#if CONFIG_PM_ENABLE
#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 100
#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 150
#else
#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 50
#endif // CONFIG_PM_ENABLE

View File

@ -1,4 +1,7 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
# CONFIG_ESP_TASK_WDT_INIT is not set
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
# Disable nano printf, because we need to print the timer count in %llu format
CONFIG_NEWLIB_NANO_FORMAT=n

View File

@ -464,13 +464,9 @@ static bool test_rmt_tx_done_cb_record_time(rmt_channel_handle_t channel, const
static void test_rmt_multi_channels_trans(size_t channel0_mem_block_symbols, size_t channel1_mem_block_symbols, bool channel0_with_dma, bool channel1_with_dma)
{
#define TEST_RMT_CHANS 2
#define TEST_LED_NUM 24
#define TEST_STOP_TIME_NO_SYNCHRO_DELTA 150
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
#define TEST_STOP_TIME_SYNCHRO_DELTA 400
#else
#define TEST_STOP_TIME_SYNCHRO_DELTA 10
#endif // #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
#define TEST_LED_NUM 1
#define TEST_STOP_TIME_NO_SYNCHRO_DELTA 250
#define TEST_STOP_TIME_SYNCHRO_DELTA 60
rmt_tx_channel_config_t tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)

View File

@ -1,5 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -1,4 +1,7 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
# CONFIG_ESP_TASK_WDT_INIT is not set
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_UNITY_ENABLE_64BIT=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096

View File

@ -615,10 +615,6 @@ config SOC_RMT_SUPPORT_XTAL
bool
default y
config SOC_RMT_SUPPORT_PLL_F80M
bool
default y
config SOC_RMT_SUPPORT_RC_FAST
bool
default y

View File

@ -262,7 +262,6 @@
#define SOC_RMT_SUPPORT_TX_SYNCHRO 1 /*!< Support coordinate a group of TX channels to start simultaneously */
#define SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY 1 /*!< TX carrier can be modulated to data phase only */
#define SOC_RMT_SUPPORT_XTAL 1 /*!< Support set XTAL clock as the RMT clock source */
#define SOC_RMT_SUPPORT_PLL_F80M 1 /*!< Support set PLL_F80M as the RMT clock source */
#define SOC_RMT_SUPPORT_RC_FAST 1 /*!< Support set RC_FAST as the RMT clock source */
/*-------------------------- MCPWM CAPS --------------------------------------*/