esp_timer: add support for frequency scaling
This commit is contained in:
parent
3f818f4862
commit
eb0c34e5c9
3 changed files with 80 additions and 0 deletions
|
@ -970,6 +970,19 @@ config PM_DFS_INIT_AUTO
|
|||
If disabled, DFS will not be active until the application
|
||||
configures it using esp_pm_configure function.
|
||||
|
||||
config PM_USE_RTC_TIMER_REF
|
||||
bool "Use RTC timer to prevent time drift (EXPERIMENTAL)"
|
||||
depends on PM_ENABLE && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1)
|
||||
default n
|
||||
help
|
||||
When APB clock frequency changes, high-resolution timer (esp_timer)
|
||||
scale and base value need to be adjusted. Each adjustment may cause
|
||||
small error, and over time such small errors may cause time drift.
|
||||
If this option is enabled, RTC timer will be used as a reference to
|
||||
compensate for the drift.
|
||||
It is recommended that this option is only used if 32k XTAL is selected
|
||||
as RTC clock source.
|
||||
|
||||
config PM_PROFILING
|
||||
bool "Enable profiling counters for PM locks"
|
||||
depends on PM_ENABLE
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "esp_attr.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_clk.h"
|
||||
#include "esp_timer_impl.h"
|
||||
#include "soc/frc_timer_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
|
@ -112,6 +113,14 @@ static uint32_t s_timer_us_per_overflow;
|
|||
// will not increment s_time_base_us if this flag is set.
|
||||
static bool s_mask_overflow;
|
||||
|
||||
#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
// If DFS is enabled, upon the first frequency change this value is set to the
|
||||
// difference between esp_timer value and RTC timer value. On every subsequent
|
||||
// frequency change, s_time_base_us is adjusted to maintain the same difference
|
||||
// between esp_timer and RTC timer. (All mentioned values are in microseconds.)
|
||||
static uint64_t s_rtc_time_diff = 0;
|
||||
#endif
|
||||
|
||||
// Spinlock used to protect access to static variables above and to the hardware
|
||||
// registers.
|
||||
portMUX_TYPE s_time_update_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
@ -208,6 +217,55 @@ static void IRAM_ATTR timer_alarm_isr(void *arg)
|
|||
(*s_alarm_handler)(arg);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
|
||||
{
|
||||
portENTER_CRITICAL(&s_time_update_lock);
|
||||
/* Bail out if the timer is not initialized yet */
|
||||
if (s_timer_interrupt_handle == NULL) {
|
||||
portEXIT_CRITICAL(&s_time_update_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t new_ticks_per_us = apb_ticks_per_us / TIMER_DIV;
|
||||
uint32_t alarm = REG_READ(FRC_TIMER_ALARM_REG(1));
|
||||
uint32_t count = REG_READ(FRC_TIMER_COUNT_REG(1));
|
||||
uint64_t ticks_to_alarm = alarm - count;
|
||||
uint64_t new_ticks = (ticks_to_alarm * new_ticks_per_us) / s_timer_ticks_per_us;
|
||||
uint32_t new_alarm_val;
|
||||
if (alarm > count && new_ticks <= FRC_TIMER_LOAD_VALUE(1)) {
|
||||
new_alarm_val = new_ticks;
|
||||
} else {
|
||||
new_alarm_val = ALARM_OVERFLOW_VAL;
|
||||
if (alarm != ALARM_OVERFLOW_VAL) {
|
||||
s_mask_overflow = true;
|
||||
}
|
||||
}
|
||||
REG_WRITE(FRC_TIMER_ALARM_REG(1), new_alarm_val);
|
||||
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
|
||||
|
||||
s_time_base_us += count / s_timer_ticks_per_us;
|
||||
|
||||
#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
// Due to the extra time required to read RTC time, don't attempt this
|
||||
// adjustment when switching to a higher frequency (which usually
|
||||
// happens in an interrupt).
|
||||
if (new_ticks_per_us < s_timer_ticks_per_us) {
|
||||
uint64_t rtc_time = esp_clk_rtc_time();
|
||||
uint64_t new_rtc_time_diff = s_time_base_us - rtc_time;
|
||||
if (s_rtc_time_diff != 0) {
|
||||
uint64_t correction = new_rtc_time_diff - s_rtc_time_diff;
|
||||
s_time_base_us -= correction;
|
||||
} else {
|
||||
s_rtc_time_diff = new_rtc_time_diff;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_PM_DFS_USE_RTC_TIMER_REF
|
||||
|
||||
s_timer_ticks_per_us = new_ticks_per_us;
|
||||
s_timer_us_per_overflow = FRC_TIMER_LOAD_VALUE(1) / new_ticks_per_us;
|
||||
|
||||
portEXIT_CRITICAL(&s_time_update_lock);
|
||||
}
|
||||
|
||||
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,15 @@ void esp_timer_impl_deinit();
|
|||
*/
|
||||
void esp_timer_impl_set_alarm(uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* @brief Notify esp_timer implementation that APB frequency has changed
|
||||
*
|
||||
* Called by the frequency switching code.
|
||||
*
|
||||
* @param apb_ticks_per_us new number of APB clock ticks per microsecond
|
||||
*/
|
||||
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us);
|
||||
|
||||
/**
|
||||
* @brief Get time, in microseconds, since esp_timer_impl_init was called
|
||||
* @return timestamp in microseconds
|
||||
|
|
Loading…
Reference in a new issue