mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
gptimer: unify clock setting with clk_tree API
This commit is contained in:
parent
080fd7e14f
commit
044a114e66
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -15,7 +15,7 @@
|
||||
#include "hal/timer_ll.h"
|
||||
#include "hal/check.h"
|
||||
#include "soc/timer_periph.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "clk_tree.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
@ -76,42 +76,12 @@ esp_err_t timer_get_counter_time_sec(timer_group_t group_num, timer_idx_t timer_
|
||||
ESP_RETURN_ON_FALSE(p_timer_obj[group_num][timer_num] != NULL, ESP_ERR_INVALID_ARG, TIMER_TAG, TIMER_NEVER_INIT_ERROR);
|
||||
uint64_t timer_val = timer_hal_capture_and_get_counter_value(&p_timer_obj[group_num][timer_num]->hal);
|
||||
uint32_t div = p_timer_obj[group_num][timer_num]->divider;
|
||||
// [clk_tree] TODO: replace the following switch table by clk_tree API
|
||||
switch (p_timer_obj[group_num][timer_num]->clk_src) {
|
||||
#if SOC_TIMER_GROUP_SUPPORT_APB
|
||||
case TIMER_SRC_CLK_APB:
|
||||
*time = (double)timer_val * div / esp_clk_apb_freq();
|
||||
break;
|
||||
#endif
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
case TIMER_SRC_CLK_XTAL:
|
||||
*time = (double)timer_val * div / esp_clk_xtal_freq();
|
||||
break;
|
||||
#endif
|
||||
#if SOC_TIMER_GROUP_SUPPORT_AHB
|
||||
case TIMER_SRC_CLK_AHB:
|
||||
*time = (double)timer_val * div / (48 * 1000 * 1000);
|
||||
break;
|
||||
#endif
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F40M
|
||||
case TIMER_SRC_CLK_PLL_F40M:
|
||||
*time = (double)timer_val * div / (40 * 1000 * 1000);
|
||||
break;
|
||||
#endif
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F48M
|
||||
case TIMER_SRC_CLK_PLL_F48M:
|
||||
*time = (double)timer_val * div / (48 * 1000 * 1000);
|
||||
break;
|
||||
#endif
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F80M
|
||||
case TIMER_SRC_CLK_PLL_F80M:
|
||||
*time = (double)timer_val * div / (80 * 1000 * 1000);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TIMER_TAG, "invalid clock source");
|
||||
break;
|
||||
}
|
||||
// get clock source frequency
|
||||
uint32_t counter_src_hz = 0;
|
||||
ESP_RETURN_ON_ERROR(clk_tree_src_get_freq_hz((soc_module_clk_t)p_timer_obj[group_num][timer_num]->clk_src,
|
||||
CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz),
|
||||
TIMER_TAG, "get clock source frequency failed");
|
||||
*time = (double)timer_val * div / counter_src_hz;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -340,9 +310,14 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
|
||||
TIMER_ENTER_CRITICAL(&timer_spinlock[group_num]);
|
||||
timer_hal_init(hal, group_num, timer_num);
|
||||
timer_hal_set_counter_value(hal, 0);
|
||||
|
||||
timer_src_clk_t clk_src = TIMER_SRC_CLK_DEFAULT;
|
||||
if (config->clk_src) {
|
||||
clk_src = config->clk_src;
|
||||
}
|
||||
// although `clk_src` is of `timer_src_clk_t` type, but it's binary compatible with `gptimer_clock_source_t`,
|
||||
// as the underlying enum entries come from the same `soc_module_clk_t`
|
||||
timer_ll_set_clock_source(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, (gptimer_clock_source_t)config->clk_src);
|
||||
timer_ll_set_clock_source(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, (gptimer_clock_source_t)clk_src);
|
||||
timer_ll_set_clock_prescale(hal->dev, timer_num, config->divider);
|
||||
timer_ll_set_count_direction(p_timer_obj[group_num][timer_num]->hal.dev, timer_num, config->counter_dir);
|
||||
timer_ll_enable_intr(hal->dev, TIMER_LL_EVENT_ALARM(timer_num), false);
|
||||
@ -350,7 +325,7 @@ esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, const timer
|
||||
timer_ll_enable_alarm(hal->dev, timer_num, config->alarm_en);
|
||||
timer_ll_enable_auto_reload(hal->dev, timer_num, config->auto_reload);
|
||||
timer_ll_enable_counter(hal->dev, timer_num, config->counter_en);
|
||||
p_timer_obj[group_num][timer_num]->clk_src = config->clk_src;
|
||||
p_timer_obj[group_num][timer_num]->clk_src = clk_src;
|
||||
p_timer_obj[group_num][timer_num]->alarm_en = config->alarm_en;
|
||||
p_timer_obj[group_num][timer_num]->auto_reload_en = config->auto_reload;
|
||||
p_timer_obj[group_num][timer_num]->direction = config->counter_dir;
|
||||
|
@ -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
|
||||
*/
|
||||
@ -27,6 +27,7 @@
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "clk_ctrl_os.h"
|
||||
#include "clk_tree.h"
|
||||
#include "gptimer_priv.h"
|
||||
|
||||
static const char *TAG = "gptimer";
|
||||
@ -402,67 +403,58 @@ static void gptimer_release_group_handle(gptimer_group_t *group)
|
||||
|
||||
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz)
|
||||
{
|
||||
unsigned int counter_src_hz = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t counter_src_hz = 0;
|
||||
int timer_id = timer->timer_id;
|
||||
// [clk_tree] TODO: replace the following switch table by clk_tree API
|
||||
switch (src_clk) {
|
||||
#if SOC_TIMER_GROUP_SUPPORT_APB
|
||||
case GPTIMER_CLK_SRC_APB:
|
||||
counter_src_hz = esp_clk_apb_freq();
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, timer->pm_lock_name, &timer->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create APB_FREQ_MAX lock failed");
|
||||
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for timer (%d,%d)", timer->group->group_id, timer_id);
|
||||
#endif
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_APB
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F40M
|
||||
case GPTIMER_CLK_SRC_PLL_F40M:
|
||||
counter_src_hz = 40 * 1000 * 1000;
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
// on ESP32C2, PLL_F40M is unavailable when CPU clock source switches from PLL to XTAL, so we're acquiring a "APB" lock here to prevent the clock switch
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, timer->pm_lock_name, &timer->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create APB_FREQ_MAX lock failed");
|
||||
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for timer (%d,%d)", timer->group->group_id, timer_id);
|
||||
#endif
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_PLL_F40M
|
||||
#if SOC_TIMER_GROUP_SUPPORT_PLL_F80M
|
||||
case GPTIMER_CLK_SRC_PLL_F80M:
|
||||
counter_src_hz = 80 * 1000 * 1000;
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
// ESP32C6 PLL_F80M is available when SOC_ROOT_CLK switches to XTAL
|
||||
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, timer->pm_lock_name, &timer->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed");
|
||||
ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for timer (%d,%d)", timer->group->group_id, timer_id);
|
||||
#endif
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_PLL_F80M
|
||||
#if SOC_TIMER_GROUP_SUPPORT_AHB
|
||||
case GPTIMER_CLK_SRC_AHB:
|
||||
// TODO: decide which kind of PM lock we should use for such clock
|
||||
counter_src_hz = 48 * 1000 * 1000;
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_AHB
|
||||
#if SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
case GPTIMER_CLK_SRC_XTAL:
|
||||
counter_src_hz = esp_clk_xtal_freq();
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
|
||||
// TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
|
||||
#if SOC_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
case GPTIMER_CLK_SRC_RC_FAST:
|
||||
if (src_clk == GPTIMER_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();
|
||||
counter_src_hz = periph_rtc_dig_clk8m_get_freq();
|
||||
break;
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not support", src_clk);
|
||||
break;
|
||||
}
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
|
||||
// get clock source frequency
|
||||
ESP_RETURN_ON_ERROR(clk_tree_src_get_freq_hz((soc_module_clk_t)src_clk, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz),
|
||||
TAG, "get clock source frequency failed");
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
bool need_pm_lock = true;
|
||||
// to make the gptimer 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_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
if (src_clk == GPTIMER_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_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
|
||||
#if SOC_TIMER_GROUP_SUPPORT_APB
|
||||
if (src_clk == GPTIMER_CLK_SRC_APB) {
|
||||
// APB clock frequency can be changed during DFS
|
||||
pm_lock_type = ESP_PM_APB_FREQ_MAX;
|
||||
}
|
||||
#endif // SOC_TIMER_GROUP_SUPPORT_APB
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C2
|
||||
if (src_clk == GPTIMER_CLK_SRC_PLL_F40M) {
|
||||
// although PLL_F40M clock is a fixed PLL clock, which is unchangeable
|
||||
// on ESP32C2, PLL_F40M can be turned off even during DFS (unlike other PLL clocks)
|
||||
// so we're acquiring a fake "APB" lock here to prevent the system from doing DFS
|
||||
pm_lock_type = ESP_PM_APB_FREQ_MAX;
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32C2
|
||||
|
||||
if (need_pm_lock) {
|
||||
sprintf(timer->pm_lock_name, "gptimer_%d_%d", timer->group->group_id, timer_id); // e.g. gptimer_0_0
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, timer->pm_lock_name, &timer->pm_lock),
|
||||
TAG, "create pm lock failed");
|
||||
}
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
timer_ll_set_clock_source(timer->hal.dev, timer_id, src_clk);
|
||||
timer->clk_src = src_clk;
|
||||
unsigned int prescale = counter_src_hz / resolution_hz; // potential resolution loss here
|
||||
@ -471,7 +463,7 @@ static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_sou
|
||||
if (timer->resolution_hz != resolution_hz) {
|
||||
ESP_LOGW(TAG, "resolution lost, expect %"PRIu32", real %"PRIu32, resolution_hz, timer->resolution_hz);
|
||||
}
|
||||
return ret;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Put the default ISR handler in the IRAM for better performance
|
||||
|
@ -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
|
||||
*/
|
||||
@ -134,11 +134,15 @@ TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
between the alarm triggering and the execution of the callback that actually stops the gptimer.
|
||||
*/
|
||||
#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 50
|
||||
/**
|
||||
* @note Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
* 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
|
||||
#else
|
||||
#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 50
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_stop_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
@ -215,11 +219,15 @@ TEST_CASE("gptimer_stop_on_alarm", "[gptimer]")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
between the alarm triggering and the capturing of the counter's value in the subsequent ISR.
|
||||
*/
|
||||
#define GPTIMER_AUTO_RELOAD_ON_ALARM_COUNT_DELTA 30
|
||||
/**
|
||||
* @note Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
* between the alarm triggering and the capturing of the counter's value in the subsequent ISR.
|
||||
*/
|
||||
#if CONFIG_PM_ENABLE
|
||||
#define GPTIMER_AUTO_RELOAD_ON_ALARM_COUNT_DELTA 200
|
||||
#else
|
||||
#define GPTIMER_AUTO_RELOAD_ON_ALARM_COUNT_DELTA 30
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_reload_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
@ -275,6 +283,7 @@ TEST_CASE("gptimer_auto_reload_on_alarm", "[gptimer]")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_normal_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
TaskHandle_t task_handle = (TaskHandle_t)user_data;
|
||||
@ -285,6 +294,17 @@ TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_normal_callback(gptimer_
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
* between the alarm triggering and the capturing of the counter's value in the subsequent ISR.
|
||||
* Also should account for the inaccuracy of the systick during DFS.
|
||||
*/
|
||||
#if CONFIG_PM_ENABLE
|
||||
#define GPTIMER_ONE_SHOT_ALARM_COUNT_DELTA 15000
|
||||
#else
|
||||
#define GPTIMER_ONE_SHOT_ALARM_COUNT_DELTA 1000
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
TEST_CASE("gptimer_one_shot_alarm", "[gptimer]")
|
||||
{
|
||||
TaskHandle_t task_handle = xTaskGetCurrentTaskHandle();
|
||||
@ -319,7 +339,7 @@ TEST_CASE("gptimer_one_shot_alarm", "[gptimer]")
|
||||
// the alarm is stopped, but the counter should still work
|
||||
uint64_t value = 0;
|
||||
TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value));
|
||||
TEST_ASSERT_UINT_WITHIN(1000, 1100000, value); // 1100000 = 100ms alarm + 1s delay
|
||||
TEST_ASSERT_UINT_WITHIN(GPTIMER_ONE_SHOT_ALARM_COUNT_DELTA, 1100000, value); // 1100000 = 100ms alarm + 1s delay
|
||||
TEST_ESP_OK(gptimer_stop(timers[i]));
|
||||
}
|
||||
|
||||
@ -403,11 +423,15 @@ TEST_CASE("gptimer_update_alarm_dynamically", "[gptimer]")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Delta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
between the alarm triggering and the capturing of the counter's value in the subsequent ISR.
|
||||
*/
|
||||
#define GPTIMER_COUNT_DOWN_RELOAD_DELTA 30
|
||||
/**
|
||||
* @noteDelta of the timer count after the triggering of the alarm. Delta must be sufficient large to account for the latency
|
||||
* between the alarm triggering and the capturing of the counter's value in the subsequent ISR.
|
||||
*/
|
||||
#if CONFIG_PM_ENABLE
|
||||
#define GPTIMER_COUNT_DOWN_RELOAD_DELTA 200
|
||||
#else
|
||||
#define GPTIMER_COUNT_DOWN_RELOAD_DELTA 30
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_count_down_reload_alarm_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -503,10 +503,6 @@ config SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_SUPPORT_PLL_F40M
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
int
|
||||
default 1
|
||||
|
@ -242,7 +242,6 @@
|
||||
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (1U)
|
||||
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_PLL_F40M (1)
|
||||
#define SOC_TIMER_GROUP_TOTAL_TIMERS (1U)
|
||||
|
||||
/*-------------------------- eFuse CAPS----------------------------*/
|
||||
|
@ -871,10 +871,6 @@ config SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_SUPPORT_PLL_F80M
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_SUPPORT_RC_FAST
|
||||
bool
|
||||
default y
|
||||
|
@ -366,7 +366,6 @@
|
||||
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (1U)
|
||||
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_PLL_F80M (1)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_RC_FAST (1)
|
||||
#define SOC_TIMER_GROUP_TOTAL_TIMERS (2)
|
||||
#define SOC_TIMER_SUPPORT_ETM (1)
|
||||
|
@ -731,10 +731,6 @@ config SOC_TIMER_GROUP_SUPPORT_XTAL
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_SUPPORT_PLL_F48M
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
int
|
||||
default 2
|
||||
|
@ -355,7 +355,6 @@
|
||||
#define SOC_TIMER_GROUP_TIMERS_PER_GROUP (1U)
|
||||
#define SOC_TIMER_GROUP_COUNTER_BIT_WIDTH (54)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_XTAL (1)
|
||||
#define SOC_TIMER_GROUP_SUPPORT_PLL_F48M (1)
|
||||
// #define SOC_TIMER_GROUP_SUPPORT_RC_FAST (1) // TODO: IDF-6265
|
||||
#define SOC_TIMER_GROUP_TOTAL_TIMERS (2)
|
||||
#define SOC_TIMER_SUPPORT_ETM (1)
|
||||
|
@ -280,11 +280,9 @@ Alarm value can be updated dynamically inside the ISR handler callback, by chang
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into Light-sleep mode, thus potentially changing the period of a GPTimer's counting step and leading to inaccurate time keeping.
|
||||
There're some power management strategies, which might turn off or change the frequency of GPTimer's source clock to save power consumption. For example, during DFS, APB clock will be scaled down. If light-sleep is also enabled, PLL and XTAL clocks will be powered off. Both of them can result in an inaccurate time keeping.
|
||||
|
||||
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates a GPTimer instance that has selected :cpp:enumerator:`GPTIMER_CLK_SRC_APB` as its clock source, the driver will guarantee that the power management lock is acquired when enabling the timer by :cpp:func:`gptimer_enable`. Likewise, the driver releases the lock when :cpp:func:`gptimer_disable` is called for that timer.
|
||||
|
||||
If other gptimer clock sources are selected such as :cpp:enumerator:`GPTIMER_CLK_SRC_XTAL`, then the driver will not install power management lock. The XTAL clock source is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||
The driver can prevent the above situation from happening by creating different power management lock according to different clock source. The driver will increase the reference count of that power management lock in the :cpp:func:`gptimer_enable` and decrease it in the :cpp:func:`gptimer_disable`. So we can ensure the clock source is stable between :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable`.
|
||||
|
||||
.. _gptimer-iram-safe:
|
||||
|
||||
|
@ -278,13 +278,11 @@
|
||||
.. _gptimer-power-management:
|
||||
|
||||
电源管理
|
||||
^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^
|
||||
|
||||
当使能电源管理时(即 :ref:`CONFIG_PM_ENABLE` 已打开),系统将在进入 Light-sleep 模式之前调整 APB 频率,从而可能会改变通用定时器的计数步骤周期,导致计时不准确。
|
||||
有些电源管理的策略会在某些时刻关闭时钟源,或者改变时钟源的频率,以求降低功耗。比如在启用 DFS 后, APB 时钟源会降低频率。如果浅睡眠(light sleep) 模式也被开启, PLL 和 XTAL 时钟都会被默认关闭,从而导致 GPTimer 的计时不准确。
|
||||
|
||||
然而,驱动程序可以通过获取类型为 :cpp:enumerator:`ESP_PM_APB_FREQ_MAX` 的电源管理锁来阻止系统更改 APB 频率。每当驱动程序创建一个通用定时器实例,且该实例选择 :cpp:enumerator:`GPTIMER_CLK_SRC_APB` 作为其时钟源的时,驱动程序会确保在通过 :cpp:func:`gptimer_enable` 使能定时器时,已经获取了电源管理锁。同样,当为该定时器调用 :cpp:func:`gptimer_disable` 时,驱动程序会释放电源管理锁。
|
||||
|
||||
如果选择 :cpp:enumerator:`GPTIMER_CLK_SRC_XTAL` 等其他时钟源,那么驱动程序不会安装电源管理锁。只要时钟源仍可提供足够的分辨率,XTAL 时钟源就更适合低功耗应用。
|
||||
驱动程序会根据具体的时钟源选择,通过创建不同的电源锁来避免上述情况的发生。驱动会在 :cpp:func:`gptimer_enable` 函数中增加电源锁的引用计数,并在 :cpp:func:`gptimer_disable` 函数中减少电源锁的引用计数,从而保证了在 :cpp:func:`gptimer_enable` 和 :cpp:func:`gptimer_disable` 之间, GPTimer 的时钟源始处于稳定工作的状态。
|
||||
|
||||
.. _gptimer-iram-safe:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user