esp_timer: remove legacy ESP32 FRC timer implementation.

This commit is contained in:
Marius Vikhammer 2021-12-14 19:01:19 +08:00
parent ba348f11b8
commit edb76f14d6
31 changed files with 86 additions and 643 deletions

View File

@ -559,7 +559,7 @@ menu "ESP32-specific"
choice ESP32_TIME_SYSCALL
prompt "Timers used for gettimeofday function"
default ESP32_TIME_SYSCALL_USE_RTC_FRC1
default ESP32_TIME_SYSCALL_USE_RTC_HRT
help
This setting defines which hardware timers are used to
implement 'gettimeofday' and 'time' functions in C library.
@ -579,14 +579,14 @@ menu "ESP32-specific"
- When RTC is used for timekeeping, two RTC_STORE registers are
used to keep time in deep sleep mode.
config ESP32_TIME_SYSCALL_USE_RTC_FRC1
config ESP32_TIME_SYSCALL_USE_RTC_HRT
bool "RTC and high-resolution timer"
select ESP_TIME_FUNCS_USE_RTC_TIMER
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32_TIME_SYSCALL_USE_RTC
bool "RTC"
select ESP_TIME_FUNCS_USE_RTC_TIMER
config ESP32_TIME_SYSCALL_USE_FRC1
config ESP32_TIME_SYSCALL_USE_HRT
bool "High-resolution timer"
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32_TIME_SYSCALL_USE_NONE

View File

@ -28,5 +28,7 @@ CONFIG_BROWNOUT_DET_LVL_SEL_5 CONFIG_ESP32_BROWNOUT_DE
CONFIG_BROWNOUT_DET_LVL_SEL_6 CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6
CONFIG_BROWNOUT_DET_LVL_SEL_7 CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7
CONFIG_BROWNOUT_DET_LVL CONFIG_ESP32_BROWNOUT_DET_LVL
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT
CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 CONFIG_ESP32_TIME_SYSCALL_USE_HRT
# SPI RAM config
CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP

View File

@ -274,14 +274,9 @@ menu "ESP32S2-specific"
default 6 if ESP32S2_BROWNOUT_DET_LVL_SEL_6
default 7 if ESP32S2_BROWNOUT_DET_LVL_SEL_7
# Note about the use of "FRC1" name: currently FRC1 timer is not used for
# high resolution timekeeping anymore. Instead the esp_timer API, implemented
# using FRC2 timer, is used.
# FRC1 name in the option name is kept for compatibility.
choice ESP32S2_TIME_SYSCALL
prompt "Timers used for gettimeofday function"
default ESP32S2_TIME_SYSCALL_USE_RTC_FRC1
default ESP32S2_TIME_SYSCALL_USE_RTC_SYSTIMER
help
This setting defines which hardware timers are used to
implement 'gettimeofday' and 'time' functions in C library.
@ -301,14 +296,14 @@ menu "ESP32S2-specific"
- When RTC is used for timekeeping, two RTC_STORE registers are
used to keep time in deep sleep mode.
config ESP32S2_TIME_SYSCALL_USE_RTC_FRC1
config ESP32S2_TIME_SYSCALL_USE_RTC_SYSTIMER
bool "RTC and high-resolution timer"
select ESP_TIME_FUNCS_USE_RTC_TIMER
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32S2_TIME_SYSCALL_USE_RTC
bool "RTC"
select ESP_TIME_FUNCS_USE_RTC_TIMER
config ESP32S2_TIME_SYSCALL_USE_FRC1
config ESP32S2_TIME_SYSCALL_USE_SYSTIMER
bool "High-resolution timer"
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32S2_TIME_SYSCALL_USE_NONE

View File

@ -2,6 +2,8 @@
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
# ESP32-S2 specific
CONFIG_ESP32S2_ULP_COPROC_ENABLED CONFIG_ULP_COPROC_ENABLED
CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM CONFIG_ULP_COPROC_RESERVE_MEM
CONFIG_ESP32S2_ULP_COPROC_RISCV CONFIG_ULP_COPROC_TYPE_RISCV
CONFIG_ESP32S2_ULP_COPROC_ENABLED CONFIG_ULP_COPROC_ENABLED
CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM CONFIG_ULP_COPROC_RESERVE_MEM
CONFIG_ESP32S2_ULP_COPROC_RISCV CONFIG_ULP_COPROC_TYPE_RISCV
CONFIG_ESP32S2_TIME_SYSCALL_USE_RTC_FRC1 CONFIG_ESP32S2_TIME_SYSCALL_USE_RTC_SYSTIMER
CONFIG_ESP32S2_TIME_SYSCALL_USE_FRC1 CONFIG_ESP32S2_TIME_SYSCALL_USE_SYSTIMER

View File

@ -360,14 +360,9 @@ menu "ESP32S3-Specific"
default 6 if ESP32S3_BROWNOUT_DET_LVL_SEL_6
default 7 if ESP32S3_BROWNOUT_DET_LVL_SEL_7
# Note about the use of "FRC1" name: currently FRC1 timer is not used for
# high resolution timekeeping anymore. Instead the esp_timer API, implemented
# using FRC2 timer, is used.
# FRC1 name in the option name is kept for compatibility.
choice ESP32S3_TIME_SYSCALL
prompt "Timers used for gettimeofday function"
default ESP32S3_TIME_SYSCALL_USE_RTC_FRC1
default ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER
help
This setting defines which hardware timers are used to
implement 'gettimeofday' and 'time' functions in C library.
@ -387,14 +382,14 @@ menu "ESP32S3-Specific"
- When RTC is used for timekeeping, two RTC_STORE registers are
used to keep time in deep sleep mode.
config ESP32S3_TIME_SYSCALL_USE_RTC_FRC1
config ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER
bool "RTC and high-resolution timer"
select ESP_TIME_FUNCS_USE_RTC_TIMER
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32S3_TIME_SYSCALL_USE_RTC
bool "RTC"
select ESP_TIME_FUNCS_USE_RTC_TIMER
config ESP32S3_TIME_SYSCALL_USE_FRC1
config ESP32S3_TIME_SYSCALL_USE_SYSTIMER
bool "High-resolution timer"
select ESP_TIME_FUNCS_USE_ESP_TIMER
config ESP32S3_TIME_SYSCALL_USE_NONE

View File

@ -0,0 +1,5 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1 CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER
CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER

View File

@ -509,7 +509,7 @@ void IRAM_ATTR esp_deep_sleep_start(void)
esp_brownout_disable();
#endif //CONFIG_IDF_TARGET_ESP32S2
esp_sync_counters_rtc_and_frc();
esp_sync_timekeeping_timers();
/* Disable interrupts and stall another core in case another task writes
* to RTC memory while we calculate RTC memory CRC.
@ -598,7 +598,7 @@ esp_err_t esp_light_sleep_start(void)
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
uint32_t ccount_at_sleep_start = cpu_ll_get_cycle_count();
uint64_t frc_time_at_start = esp_system_get_time();
uint64_t high_res_time_at_start = esp_system_get_time();
uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);
esp_ipc_isr_stall_other_cpu();
@ -698,23 +698,23 @@ esp_err_t esp_light_sleep_start(void)
s_light_sleep_wakeup = true;
// FRC1 has been clock gated for the duration of the sleep, correct for that.
// System timer has been clock gated for the duration of the sleep, correct for that.
#ifdef CONFIG_IDF_TARGET_ESP32C3
/**
* On esp32c3, rtc_time_get() is non-blocking, esp_system_get_time() is
* blocking, and the measurement data shows that this order is better.
*/
uint64_t frc_time_at_end = esp_system_get_time();
uint64_t high_res_time_at_end = esp_system_get_time();
uint64_t rtc_ticks_at_end = rtc_time_get();
#else
uint64_t rtc_ticks_at_end = rtc_time_get();
uint64_t frc_time_at_end = esp_system_get_time();
uint64_t high_res_time_at_end = esp_system_get_time();
#endif
uint64_t rtc_time_diff = rtc_time_slowclk_to_us(rtc_ticks_at_end - s_config.rtc_ticks_at_sleep_start, s_config.rtc_clk_cal_period);
uint64_t frc_time_diff = frc_time_at_end - frc_time_at_start;
uint64_t high_res_time_diff = high_res_time_at_end - high_res_time_at_start;
int64_t time_diff = rtc_time_diff - frc_time_diff;
int64_t time_diff = rtc_time_diff - high_res_time_diff;
/* Small negative values (up to 1 RTC_SLOW clock period) are possible,
* for very small values of sleep_duration. Ignore those to keep esp_timer
* monotonic.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -368,7 +368,7 @@ static void trigger_deepsleep(void)
// Save start time. Deep sleep.
start = esp_rtc_get_time_us();
esp_sleep_enable_timer_wakeup(1000);
// In function esp_deep_sleep_start() uses function esp_sync_counters_rtc_and_frc()
// In function esp_deep_sleep_start() uses function esp_sync_timekeeping_timers()
// to prevent a negative time after wake up.
esp_deep_sleep_start();
}
@ -388,7 +388,7 @@ static void check_time_deepsleep_1(void)
start = esp_rtc_get_time_us();
esp_sleep_enable_timer_wakeup(1000);
// In function esp_deep_sleep_start() uses function esp_sync_counters_rtc_and_frc()
// In function esp_deep_sleep_start() uses function esp_sync_timekeeping_timers()
// to prevent a negative time after wake up.
esp_deep_sleep_start();
}

View File

@ -20,20 +20,6 @@ menu "Power Management"
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 && ESP_TIMER_IMPL_FRC2 && ESP_TIME_FUNCS_USE_RTC_TIMER)
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

View File

@ -519,7 +519,7 @@ static void trigger_deepsleep(void)
// Save start time. Deep sleep.
gettimeofday(&start, NULL);
esp_sleep_enable_timer_wakeup(1000);
// In function esp_deep_sleep_start() uses function esp_sync_counters_rtc_and_frc()
// In function esp_deep_sleep_start() uses function esp_sync_timekeeping_timers()
// to prevent a negative time after wake up.
esp_deep_sleep_start();
}

View File

@ -4,9 +4,7 @@ set(srcs "src/esp_timer.c"
"src/ets_timer_legacy.c"
"src/system_time.c")
if(CONFIG_ESP_TIMER_IMPL_FRC2)
list(APPEND srcs "src/esp_timer_impl_frc_legacy.c")
elseif(CONFIG_ESP_TIMER_IMPL_TG0_LAC)
if(CONFIG_ESP_TIMER_IMPL_TG0_LAC)
list(APPEND srcs "src/esp_timer_impl_lac.c")
elseif(CONFIG_ESP_TIMER_IMPL_SYSTIMER)
list(APPEND srcs "src/esp_timer_impl_systimer.c")

View File

@ -51,32 +51,14 @@ menu "High resolution timer (esp_timer)"
The ISR dispatch can be used, in some cases, when a callback is very simple
or need a lower-latency.
choice ESP_TIMER_IMPL
prompt "Hardware timer to use for esp_timer"
default ESP_TIMER_IMPL_TG0_LAC if IDF_TARGET_ESP32
default ESP_TIMER_IMPL_SYSTIMER
help
esp_timer APIs can be implemented using different hardware timers.
config ESP_TIMER_IMPL_TG0_LAC
bool
default y
depends on IDF_TARGET_ESP32
- "FRC2 (legacy)" implementation has been used in ESP-IDF v2.x - v4.1.
- "LAC timer of Timer Group 0" implementation is simpler, and has smaller
run time overhead because software handling of timer overflow is not needed.
- "SYSTIMER" implementation is similar to "LAC timer of Timer Group 0" but for non ESP32 chips.
config ESP_TIMER_IMPL_FRC2
bool "FRC2 (legacy) timer"
depends on IDF_TARGET_ESP32
config ESP_TIMER_IMPL_TG0_LAC
bool "LAC timer of Timer Group 0"
depends on IDF_TARGET_ESP32
config ESP_TIMER_IMPL_SYSTIMER
bool "SYSTIMER"
depends on !IDF_TARGET_ESP32
endchoice
config ESP_TIMER_IMPL_SYSTIMER
bool
default y
depends on !IDF_TARGET_ESP32
endmenu # esp_timer

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -27,8 +27,7 @@
* used for simple callback functions, which do not take longer than a few
* microseconds to run.
*
* Implementation note: on the ESP32, esp_timer APIs use the "legacy" FRC2
* timer. Timer callbacks are called from a task running on the PRO CPU.
* Timer callbacks are called from a task running on the PRO CPU.
*/
#include <stdint.h>

View File

@ -1,440 +0,0 @@
/*
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sys/param.h"
#include "esp_timer_impl.h"
#include "esp_timer.h"
#include "esp_err.h"
#include "esp_system.h"
#include "esp_task.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_private/esp_clk.h"
#include "soc/frc_timer_reg.h"
#include "soc/rtc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
/**
* @file esp_timer_frc.c
* @brief Implementation of chip-specific part of esp_timer
*
* This implementation uses FRC2 (legacy) timer of the ESP32. This timer is
* a 32-bit up-counting timer, with a programmable compare value (called 'alarm'
* hereafter). When the timer reaches compare value, interrupt is raised.
* The timer can be configured to produce an edge or a level interrupt.
*
* In this implementation the timer is used for two purposes:
* 1. To generate interrupts at certain moments the upper layer of esp_timer
* uses this to trigger callbacks of esp_timer objects.
*
* 2. To keep track of time relative to application start. This facility is
* used both by the upper layer of esp_timer and by time functions, such as
* gettimeofday.
*
* Whenever an esp_timer timer is armed (configured to fire once or
* periodically), timer_insert function of the upper layer calls
* esp_timer_impl_set_alarm to enable the interrupt at the required moment.
* This implementation sets up the timer interrupt to fire at the earliest of
* two moments:
* a) the time requested by upper layer
* b) the time when the timer count reaches 0xffffffff (i.e. is about to overflow)
*
* Whenever the interrupt fires and timer overflow is detected, interrupt hander
* increments s_time_base_us variable, which is used for timekeeping.
*
* When the interrupt fires, the upper layer is notified, and it dispatches
* the callbacks (if any timers have expired) and sets new alarm value (if any
* timers are still active).
*
* At any point in time, esp_timer_impl_get_time will return the current timer
* value (expressed in microseconds) plus s_time_base_us. To account for the
* case when the timer counter has overflown, but the interrupt has not fired
* yet (for example, because interupts are temporarily disabled),
* esp_timer_impl_get_time will also check timer overflow flag, and will add
* s_timer_us_per_overflow to the returned value.
*
*/
/* Timer is clocked from APB. To allow for integer scaling factor between ticks
* and microseconds, divider 1 is used. 16 or 256 would not work for APB
* frequencies such as 40 or 26 or 2 MHz.
*/
#define TIMER_DIV 1
#define TIMER_DIV_CFG FRC_TIMER_PRESCALER_1
/* ALARM_OVERFLOW_VAL is used as timer alarm value when there are not timers
* enabled which need to fire within the next timer overflow period. This alarm
* is used to perform timekeeping (i.e. to track timer overflows).
* Due to the 0xffffffff cannot recognize the real overflow or the scenario that
* ISR happens follow set_alarm, so change the ALARM_OVERFLOW_VAL to resolve this problem.
* Set it to 0xefffffffUL. The remain 0x10000000UL(about 3 second) is enough to handle ISR.
*/
#define DEFAULT_ALARM_OVERFLOW_VAL 0xefffffffUL
/* Provision to set lower overflow value for unit testing. Lowering the
* overflow value helps check for race conditions which occur near overflow
* moment.
*/
#ifndef ESP_TIMER_DYNAMIC_OVERFLOW_VAL
#define ALARM_OVERFLOW_VAL DEFAULT_ALARM_OVERFLOW_VAL
#else
static uint32_t s_alarm_overflow_val = DEFAULT_ALARM_OVERFLOW_VAL;
#define ALARM_OVERFLOW_VAL (s_alarm_overflow_val)
#endif
static const char* TAG = "esp_timer_impl";
// Interrupt handle returned by the interrupt allocator
static intr_handle_t s_timer_interrupt_handle;
// Function from the upper layer to be called when the interrupt happens.
// Registered in esp_timer_impl_init.
static intr_handler_t s_alarm_handler = NULL;
// Time in microseconds from startup to the moment
// when timer counter was last equal to 0. This variable is updated each time
// when timer overflows, and when APB frequency switch is performed.
static uint64_t s_time_base_us;
// Number of timer ticks per microsecond. Calculated from APB frequency.
static uint32_t s_timer_ticks_per_us;
// Period between timer overflows, in microseconds.
// Equal to 2^32 / s_timer_ticks_per_us.
static uint32_t s_timer_us_per_overflow;
// When frequency switch happens, timer counter is reset to 0, s_time_base_us
// is updated, and alarm value is re-calculated based on the new APB frequency.
// However because the frequency switch can happen before the final
// interrupt handler is invoked, interrupt handler may see a different alarm
// value than the one which caused an interrupt. This can cause interrupt handler
// to consider that the interrupt has happened due to timer overflow, incrementing
// s_time_base_us. To avoid this, frequency switch hook sets this flag if
// it needs to set timer alarm value to ALARM_OVERFLOW_VAL. Interrupt handler
// will not increment s_time_base_us if this flag is set.
static bool s_mask_overflow;
#ifdef CONFIG_PM_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;
//Use FRC_TIMER_LOAD_VALUE(1) instead of UINT32_MAX, convenience to change FRC TIMER for future
#define TIMER_IS_AFTER_OVERFLOW(a) (ALARM_OVERFLOW_VAL < (a) && (a) <= FRC_TIMER_LOAD_VALUE(1))
// Check if timer overflow has happened (but was not handled by ISR yet)
static inline bool IRAM_ATTR timer_overflow_happened(void)
{
return ((REG_READ(FRC_TIMER_CTRL_REG(1)) & FRC_TIMER_INT_STATUS) != 0 &&
((REG_READ(FRC_TIMER_ALARM_REG(1)) == ALARM_OVERFLOW_VAL && TIMER_IS_AFTER_OVERFLOW(REG_READ(FRC_TIMER_COUNT_REG(1))) && !s_mask_overflow) ||
(!TIMER_IS_AFTER_OVERFLOW(REG_READ(FRC_TIMER_ALARM_REG(1))) && TIMER_IS_AFTER_OVERFLOW(REG_READ(FRC_TIMER_COUNT_REG(1))))));
}
static inline void IRAM_ATTR timer_count_reload(void)
{
//this function should be only called the real overflow happened. And the count cannot be very approach to 0xffffffff.
assert(TIMER_IS_AFTER_OVERFLOW(REG_READ(FRC_TIMER_COUNT_REG(1))));
/* Restart the timer count by current time count minus ALARM_OVERFLOW_VAL(0xefffffff), it may cause error, if current tick is near boundary.
* But even if the error happen 100% per overflow(the distance of each real overflow is about 50 second),
* the error is 0.0125us*N per 50s(the FRC time clock is 80MHz), the N is the ticks run by the line following,
* Normally, N is less than 10, assume N is 10, so the error accumulation is only 6.48ms per month.
* In fact, if the CPU frequency is large than 80MHz. The error accumulation will be more less than 6.48ms per month.
* so It can be adopted.
*/
REG_WRITE(FRC_TIMER_LOAD_REG(1), REG_READ(FRC_TIMER_COUNT_REG(1)) - ALARM_OVERFLOW_VAL);
}
void esp_timer_impl_lock(void)
{
portENTER_CRITICAL(&s_time_update_lock);
}
void esp_timer_impl_unlock(void)
{
portEXIT_CRITICAL(&s_time_update_lock);
}
int64_t IRAM_ATTR esp_timer_impl_get_time(void)
{
if (s_alarm_handler == NULL) {
return 0;
}
uint32_t timer_val;
uint64_t time_base;
uint32_t ticks_per_us;
bool overflow;
do {
/* Read all values needed to calculate current time */
timer_val = REG_READ(FRC_TIMER_COUNT_REG(1));
time_base = s_time_base_us;
overflow = timer_overflow_happened();
ticks_per_us = s_timer_ticks_per_us;
/* Read them again and compare */
/* In this function, do not call timer_count_reload() when overflow is true.
* Because there's remain count enough to allow FRC_TIMER_COUNT_REG grow
*/
if (REG_READ(FRC_TIMER_COUNT_REG(1)) > timer_val &&
time_base == *((volatile uint64_t*) &s_time_base_us) &&
ticks_per_us == *((volatile uint32_t*) &s_timer_ticks_per_us) &&
overflow == timer_overflow_happened()) {
break;
}
/* If any value has changed (other than the counter increasing), read again */
} while(true);
uint64_t result = time_base
+ timer_val / ticks_per_us;
return result;
}
int64_t esp_timer_get_time(void) __attribute__((alias("esp_timer_impl_get_time")));
void IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id)
{
static uint64_t timestamp_id[2] = { UINT64_MAX, UINT64_MAX };
portENTER_CRITICAL_SAFE(&s_time_update_lock);
timestamp_id[alarm_id] = timestamp;
timestamp = MIN(timestamp_id[0], timestamp_id[1]);
if (timestamp != UINT64_MAX) {
// Use calculated alarm value if it is less than ALARM_OVERFLOW_VAL.
// Note that if by the time we update ALARM_REG, COUNT_REG value is higher,
// interrupt will not happen for another ALARM_OVERFLOW_VAL timer ticks,
// so need to check if alarm value is too close in the future (e.g. <2 us away).
int32_t offset = s_timer_ticks_per_us * 2;
do {
// Adjust current time if overflow has happened
if (timer_overflow_happened() ||
((REG_READ(FRC_TIMER_COUNT_REG(1)) > ALARM_OVERFLOW_VAL) &&
((REG_READ(FRC_TIMER_CTRL_REG(1)) & FRC_TIMER_INT_STATUS) == 0))) {
// 1. timer_overflow_happened() checks overflow with the interrupt flag.
// 2. During several loops, the counter can be higher than the alarm and even step over ALARM_OVERFLOW_VAL boundary (the interrupt flag is not set).
timer_count_reload();
s_time_base_us += s_timer_us_per_overflow;
}
s_mask_overflow = false;
int64_t cur_count = REG_READ(FRC_TIMER_COUNT_REG(1));
// Alarm time relative to the moment when counter was 0
int64_t time_after_timebase_us = (int64_t)timestamp - s_time_base_us;
// Calculate desired timer compare value (may exceed 2^32-1)
int64_t compare_val = time_after_timebase_us * s_timer_ticks_per_us;
compare_val = MAX(compare_val, cur_count + offset);
uint32_t alarm_reg_val = ALARM_OVERFLOW_VAL;
if (compare_val < ALARM_OVERFLOW_VAL) {
alarm_reg_val = (uint32_t) compare_val;
}
REG_WRITE(FRC_TIMER_ALARM_REG(1), alarm_reg_val);
int64_t delta = (int64_t)alarm_reg_val - (int64_t)REG_READ(FRC_TIMER_COUNT_REG(1));
if (delta <= 0) {
/*
When the timestamp is a bit less than the current counter then the alarm = current_counter + offset.
But due to CPU_freq in some case can be equal APB_freq the offset time can not exceed the overhead
(the alarm will be less than the counter) and it leads to the infinity loop.
To exclude this behavior to the offset was added the delta to have the opportunity to go through it.
*/
offset += llabs(delta) + s_timer_ticks_per_us * 2;
} else {
break;
}
} while (1);
}
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
}
void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
{
esp_timer_impl_set_alarm_id(timestamp, 0);
}
static void IRAM_ATTR timer_alarm_isr(void *arg)
{
portENTER_CRITICAL_ISR(&s_time_update_lock);
// Timekeeping: adjust s_time_base_us if counter has passed ALARM_OVERFLOW_VAL
if (timer_overflow_happened()) {
timer_count_reload();
s_time_base_us += s_timer_us_per_overflow;
}
s_mask_overflow = false;
// Clear interrupt status
REG_WRITE(FRC_TIMER_INT_REG(1), FRC_TIMER_INT_CLR);
// Set alarm to the next overflow moment. Later, upper layer function may
// call esp_timer_impl_set_alarm to change this to an earlier value.
REG_WRITE(FRC_TIMER_ALARM_REG(1), ALARM_OVERFLOW_VAL);
if ((REG_READ(FRC_TIMER_COUNT_REG(1)) > ALARM_OVERFLOW_VAL) &&
((REG_READ(FRC_TIMER_CTRL_REG(1)) & FRC_TIMER_INT_STATUS) == 0)) {
/*
This check excludes the case when the alarm can be less than the counter.
Without this check, it is possible because DPORT uses 4-lvl, and users can use the 5 Hi-interrupt,
they can interrupt this function between FRC_TIMER_INT_CLR and setting the alarm = ALARM_OVERFLOW_VAL
that lead to the counter will go ahead leaving the alarm behind.
*/
timer_count_reload();
s_time_base_us += s_timer_us_per_overflow;
}
portEXIT_CRITICAL_ISR(&s_time_update_lock);
// Call the upper layer handler
(*s_alarm_handler)(arg);
}
void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
{
portENTER_CRITICAL_ISR(&s_time_update_lock);
/* Bail out if the timer is not initialized yet */
if (s_timer_interrupt_handle == NULL) {
portEXIT_CRITICAL_ISR(&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 <= ALARM_OVERFLOW_VAL) {
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_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_USE_RTC_TIMER_REF
s_timer_ticks_per_us = new_ticks_per_us;
s_timer_us_per_overflow = ALARM_OVERFLOW_VAL / new_ticks_per_us;
portEXIT_CRITICAL_ISR(&s_time_update_lock);
}
void esp_timer_impl_advance(int64_t time_us)
{
assert(time_us > 0 && "negative adjustments not supported yet");
portENTER_CRITICAL(&s_time_update_lock);
uint64_t count = REG_READ(FRC_TIMER_COUNT_REG(1));
/* Trigger an ISR to handle past alarms and set new one.
* ISR handler will run once we exit the critical section.
*/
REG_WRITE(FRC_TIMER_ALARM_REG(1), 0);
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
s_time_base_us += count / s_timer_ticks_per_us + time_us;
portEXIT_CRITICAL(&s_time_update_lock);
}
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
{
s_alarm_handler = alarm_handler;
const int interrupt_lvl = (1 << CONFIG_ESP_TIMER_INTERRUPT_LEVEL) & ESP_INTR_FLAG_LEVELMASK;
esp_err_t err = esp_intr_alloc(ETS_TIMER2_INTR_SOURCE,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM | interrupt_lvl,
&timer_alarm_isr, NULL, &s_timer_interrupt_handle);
if (err != ESP_OK) {
ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%0x)", err);
return err;
}
uint32_t apb_freq = rtc_clk_apb_freq_get();
s_timer_ticks_per_us = apb_freq / 1000000 / TIMER_DIV;
assert(s_timer_ticks_per_us > 0
&& apb_freq % TIMER_DIV == 0
&& "APB frequency does not result in a valid ticks_per_us value");
s_timer_us_per_overflow = ALARM_OVERFLOW_VAL / s_timer_ticks_per_us;
s_time_base_us = 0;
REG_WRITE(FRC_TIMER_ALARM_REG(1), ALARM_OVERFLOW_VAL);
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
REG_WRITE(FRC_TIMER_CTRL_REG(1),
TIMER_DIV_CFG | FRC_TIMER_ENABLE | FRC_TIMER_LEVEL_INT);
REG_WRITE(FRC_TIMER_INT_REG(1), FRC_TIMER_INT_CLR);
ESP_ERROR_CHECK( esp_intr_enable(s_timer_interrupt_handle) );
return ESP_OK;
}
void esp_timer_impl_deinit(void)
{
esp_intr_disable(s_timer_interrupt_handle);
REG_WRITE(FRC_TIMER_CTRL_REG(1), 0);
REG_WRITE(FRC_TIMER_ALARM_REG(1), 0);
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
esp_intr_free(s_timer_interrupt_handle);
s_timer_interrupt_handle = NULL;
}
// FIXME: This value is safe for 80MHz APB frequency.
// Should be modified to depend on clock frequency.
uint64_t IRAM_ATTR esp_timer_impl_get_min_period_us(void)
{
return 50;
}
#ifdef ESP_TIMER_DYNAMIC_OVERFLOW_VAL
uint32_t esp_timer_impl_get_overflow_val(void)
{
return s_alarm_overflow_val;
}
void esp_timer_impl_set_overflow_val(uint32_t overflow_val)
{
s_alarm_overflow_val = overflow_val;
/* update alarm value */
esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
}
#endif // ESP_TIMER_DYNAMIC_OVERFLOW_VAL
uint64_t esp_timer_impl_get_counter_reg(void)
{
return (uint64_t)REG_READ(FRC_TIMER_COUNT_REG(1));
}
uint64_t esp_timer_impl_get_alarm_reg(void)
{
return (uint64_t)REG_READ(FRC_TIMER_ALARM_REG(1));
}
void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq")));
void esp_timer_private_advance(int64_t time_us) __attribute__((alias("esp_timer_impl_advance")));
void esp_timer_private_lock(void) __attribute__((alias("esp_timer_impl_lock")));
void esp_timer_private_unlock(void) __attribute__((alias("esp_timer_impl_unlock")));

View File

@ -41,7 +41,7 @@ void esp_timer_impl_init_system_time(void)
{
s_correction_us = esp_rtc_get_time_us() - g_startup_time - esp_timer_impl_get_time();
#if defined(CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER) && defined(CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER)
esp_err_t err = esp_register_shutdown_handler(esp_sync_counters_rtc_and_frc);
esp_err_t err = esp_register_shutdown_handler(esp_sync_timekeeping_timers);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Register shutdown handler failed, err = 0x%x", err);
}

View File

@ -1,7 +1,3 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "../private_include"
PRIV_REQUIRES cmock test_utils)
if(CONFIG_ESP_TIMER_IMPL_FRC2)
idf_build_set_property(COMPILE_DEFINITIONS "-DESP_TIMER_DYNAMIC_OVERFLOW_VAL" APPEND)
endif()

View File

@ -17,45 +17,11 @@
#define SEC (1000000)
#if CONFIG_ESP_TIMER_IMPL_FRC2
#include "soc/frc_timer_reg.h"
#endif
#ifdef CONFIG_ESP_TIMER_PROFILING
#define WITH_PROFILING 1
#endif
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
extern uint32_t esp_timer_impl_get_overflow_val(void);
extern void esp_timer_impl_set_overflow_val(uint32_t overflow_val);
static uint32_t s_old_overflow_val;
static void setup_overflow(void)
{
s_old_overflow_val = esp_timer_impl_get_overflow_val();
/* Overflow every 0.1 sec.
* Chosen so that it is 0 modulo s_timer_ticks_per_us (which is 80),
* to prevent roundoff error on each overflow.
*/
esp_timer_impl_set_overflow_val(8000000);
}
static void teardown_overflow(void)
{
esp_timer_impl_set_overflow_val(s_old_overflow_val);
}
#else
static void setup_overflow(void)
{
}
static void teardown_overflow(void)
{
}
#endif // CONFIG_ESP_TIMER_IMPL_FRC2
static void dummy_cb(void* arg)
{
@ -68,7 +34,6 @@ TEST_CASE("esp_timer orders timers correctly", "[esp_timer]")
const size_t num_timers = sizeof(timeouts)/sizeof(timeouts[0]);
esp_timer_handle_t handles[num_timers];
char* names[num_timers];
setup_overflow();
for (size_t i = 0; i < num_timers; ++i) {
asprintf(&names[i], "timer%d", i);
esp_timer_create_args_t args = {
@ -78,7 +43,6 @@ TEST_CASE("esp_timer orders timers correctly", "[esp_timer]")
TEST_ESP_OK(esp_timer_create(&args, &handles[i]));
TEST_ESP_OK(esp_timer_start_once(handles[i], timeouts[i] * 100));
}
teardown_overflow();
char* stream_str[1024];
FILE* stream = fmemopen(stream_str, sizeof(stream_str), "r+");
TEST_ESP_OK(esp_timer_dump(stream));
@ -139,7 +103,6 @@ static void set_alarm_task(void* arg)
TEST_CASE("esp_timer_impl_set_alarm stress test", "[esp_timer]")
{
SemaphoreHandle_t done = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
setup_overflow();
xTaskCreatePinnedToCore(&set_alarm_task, "set_alarm_0", 4096, done, UNITY_FREERTOS_PRIORITY, NULL, 0);
#if portNUM_PROCESSORS == 2
xTaskCreatePinnedToCore(&set_alarm_task, "set_alarm_1", 4096, done, UNITY_FREERTOS_PRIORITY, NULL, 1);
@ -149,7 +112,6 @@ TEST_CASE("esp_timer_impl_set_alarm stress test", "[esp_timer]")
#if portNUM_PROCESSORS == 2
TEST_ASSERT(xSemaphoreTake(done, test_time_sec * 2 * 1000 / portTICK_PERIOD_MS));
#endif
teardown_overflow();
vSemaphoreDelete(done);
}
@ -174,7 +136,6 @@ TEST_CASE("esp_timer produces correct delay", "[esp_timer]")
const size_t delays_count = sizeof(delays_ms)/sizeof(delays_ms[0]);
ref_clock_init();
setup_overflow();
for (size_t i = 0; i < delays_count; ++i) {
t_end = 0;
int64_t t_start = ref_clock_get();
@ -188,7 +149,6 @@ TEST_CASE("esp_timer produces correct delay", "[esp_timer]")
TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, delays_ms[i], ms_diff);
}
teardown_overflow();
ref_clock_deinit();
TEST_ESP_OK( esp_timer_dump(stdout) );
@ -236,7 +196,6 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
};
TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
ref_clock_init();
setup_overflow();
args.timer = timer1;
args.t_start = ref_clock_get();
args.done = xSemaphoreCreateBinary();
@ -248,7 +207,6 @@ TEST_CASE("periodic esp_timer produces correct delays", "[esp_timer]")
for (size_t i = 0; i < NUM_INTERVALS; ++i) {
TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, (i + 1) * delay_ms, args.intervals[i]);
}
teardown_overflow();
ref_clock_deinit();
TEST_ESP_OK( esp_timer_dump(stdout) );
@ -405,7 +363,6 @@ TEST_CASE("esp_timer for very short intervals", "[esp_timer]")
esp_timer_handle_t timer1, timer2;
ESP_ERROR_CHECK( esp_timer_create(&timer_args, &timer1) );
ESP_ERROR_CHECK( esp_timer_create(&timer_args, &timer2) );
setup_overflow();
const int timeout_ms = 10;
for (int timeout_delta_us = -150; timeout_delta_us < 150; timeout_delta_us++) {
printf("delta=%d", timeout_delta_us);
@ -418,7 +375,6 @@ TEST_CASE("esp_timer for very short intervals", "[esp_timer]")
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_timer_stop(timer2));
}
teardown_overflow();
vSemaphoreDelete(semaphore);
TEST_ESP_OK(esp_timer_delete(timer1));
TEST_ESP_OK(esp_timer_delete(timer2));
@ -499,7 +455,6 @@ static void timer_test_monotonic_values_task(void* arg) {
TEST_CASE("esp_timer_get_time returns monotonic values", "[esp_timer]")
{
ref_clock_init();
setup_overflow();
test_monotonic_values_state_t states[portNUM_PROCESSORS] = {0};
SemaphoreHandle_t done = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
@ -517,7 +472,6 @@ TEST_CASE("esp_timer_get_time returns monotonic values", "[esp_timer]")
}
vSemaphoreDelete(done);
teardown_overflow();
ref_clock_deinit();
for (int i = 0; i < portNUM_PROCESSORS; ++i) {
@ -716,7 +670,6 @@ TEST_CASE("Can start/stop timer from ISR context", "[esp_timer]")
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_ESP32_DPORT_WORKAROUND)
#include "soc/dport_reg.h"
#include "soc/frc_timer_reg.h"
static bool task_stop;
static bool time_jumped;
@ -846,9 +799,6 @@ TEST_CASE("esp_timer_impl_set_alarm and using start_once do not lead that the Sy
TEST_CASE("Test case when esp_timer_impl_set_alarm needs set timer < now_time", "[esp_timer]")
{
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
#endif
esp_timer_impl_advance(50331648); // 0xefffffff/80 = 50331647
esp_rom_delay_us(2);
@ -859,27 +809,12 @@ TEST_CASE("Test case when esp_timer_impl_set_alarm needs set timer < now_time",
uint64_t count_reg = esp_timer_impl_get_counter_reg();
portENABLE_INTERRUPTS();
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
const uint32_t offset = 80 * 2; // s_timer_ticks_per_us
#else
const uint32_t offset = 2;
#endif
printf("alarm_reg = 0x%llx, count_reg 0x%llx\n", alarm_reg, count_reg);
TEST_ASSERT(alarm_reg <= (count_reg + offset));
}
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
TEST_CASE("Test esp_timer_impl_set_alarm when the counter is near an overflow value", "[esp_timer]")
{
for (int i = 0; i < 1024; ++i) {
uint32_t count_reg = 0xeffffe00 + i;
REG_WRITE(FRC_TIMER_LOAD_REG(1), count_reg);
printf("%d) count_reg = 0x%x\n", i, count_reg);
esp_timer_impl_set_alarm(1); // timestamp is expired
}
}
#endif
static void timer_callback5(void* arg)
{

View File

@ -102,8 +102,8 @@ menu "Log output"
by the tick period. This time will reset after a software reboot.
e.g. (90000)
- System time is taken from POSIX time functions which use the ESP32's
RTC and FRC1 timers to maintain an accurate time. The system time is
- System time is taken from POSIX time functions which use the chip's
RTC and high resoultion timers to maintain an accurate time. The system time is
initialized to 0 on startup, it can be set with an SNTP sync, or with
POSIX time functions. This time will not reset after a software reboot.
e.g. (00:01:30.000)

View File

@ -71,7 +71,7 @@
#define CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0 1
#define CONFIG_ESP32_BROWNOUT_DET_LVL 0
#define CONFIG_ESP32_REDUCE_PHY_TX_POWER 1
#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1
#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT 1
#define CONFIG_ESP32_RTC_CLK_SRC_INT_RC 1
#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024
#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ESP_NEWLIB_H__
#define __ESP_NEWLIB_H__
@ -53,9 +45,12 @@ void esp_setup_syscall_table(void) __attribute__((deprecated("Please call esp_ne
void esp_set_time_from_rtc(void);
/*
* Sync counters RTC and FRC. Update boot_time.
* Sync timekeeping timers, RTC and high-resolution timer. Update boot_time.
*/
void esp_sync_counters_rtc_and_frc(void);
void esp_sync_timekeeping_timers(void);
/* Kept for backward compatibility */
#define esp_sync_counters_rtc_and_frc esp_sync_timekeeping_timers
/**
* Initialize newlib static locks

View File

@ -43,7 +43,7 @@
// Offset between FRC timer and the RTC.
// Offset between High resolution timer and the RTC.
// Initialized after reset or light sleep.
#if defined(CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER) && defined(CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER)
int64_t s_microseconds_offset = 0;
@ -119,7 +119,7 @@ void esp_set_time_from_rtc(void)
#endif // CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER && CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER
}
void esp_sync_counters_rtc_and_frc(void)
void esp_sync_timekeeping_timers(void)
{
#if defined( CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER ) && defined( CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER )
struct timeval tv;

View File

@ -549,14 +549,14 @@ typedef enum {
static void print_counters(void)
{
int64_t frc = esp_system_get_time();
int64_t high_res_time = esp_system_get_time();
int64_t rtc = esp_rtc_get_time_us();
uint64_t boot_time = esp_time_impl_get_boot_time();
printf("\tFRC %lld (us)\n", frc);
printf("\tHigh-res time %lld (us)\n", high_res_time);
printf("\tRTC %lld (us)\n", rtc);
printf("\tBOOT %lld (us)\n", boot_time);
printf("\ts_microseconds_offset %lld (us)\n", s_microseconds_offset);
printf("delta RTC - FRC counters %lld (us)\n", rtc - frc);
printf("delta RTC - high-res time counters %lld (us)\n", rtc - high_res_time);
}
static void set_initial_condition(type_reboot_t type_reboot, int error_time)
@ -575,7 +575,7 @@ static void set_initial_condition(type_reboot_t type_reboot, int error_time)
print_counters();
printf("FRC counter increased to %d (s)\n", error_time);
printf("High res counter increased to %d (s)\n", error_time);
esp_timer_private_advance(error_time * 1000000ULL);
print_counters();
@ -590,7 +590,7 @@ static void set_initial_condition(type_reboot_t type_reboot, int error_time)
if (type_reboot == TYPE_REBOOT_ABORT) {
printf("Update boot time based on diff\n");
esp_sync_counters_rtc_and_frc();
esp_sync_timekeeping_timers();
print_counters();
printf("reboot as abort\n");
abort();
@ -628,7 +628,7 @@ static void check_time(void)
TEST_ASSERT_LESS_OR_EQUAL(latency_before_run_ut, dt);
}
TEST_CASE_MULTIPLE_STAGES("Timestamp after abort is correct in case RTC & FRC have + big error", "[newlib][reset=abort,SW_CPU_RESET]", set_timestamp1, check_time);
TEST_CASE_MULTIPLE_STAGES("Timestamp after restart is correct in case RTC & FRC have + big error", "[newlib][reset=SW_CPU_RESET]", set_timestamp2, check_time);
TEST_CASE_MULTIPLE_STAGES("Timestamp after restart is correct in case RTC & FRC have - big error", "[newlib][reset=SW_CPU_RESET]", set_timestamp3, check_time);
TEST_CASE_MULTIPLE_STAGES("Timestamp after abort is correct in case RTC & High-res timer have + big error", "[newlib][reset=abort,SW_CPU_RESET]", set_timestamp1, check_time);
TEST_CASE_MULTIPLE_STAGES("Timestamp after restart is correct in case RTC & High-res timer have + big error", "[newlib][reset=SW_CPU_RESET]", set_timestamp2, check_time);
TEST_CASE_MULTIPLE_STAGES("Timestamp after restart is correct in case RTC & High-res timer have - big error", "[newlib][reset=SW_CPU_RESET]", set_timestamp3, check_time);
#endif // CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER && CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER

View File

@ -1,6 +1,8 @@
High Resolution Timer
=====================
{IDF_TARGET_HR_TIMER:default = "SYSTIMER", esp32 = "LAC timer"}
Overview
--------
@ -15,17 +17,7 @@ An interrupt level of the handler depends on the :ref:`CONFIG_ESP_TIMER_INTERRUP
``esp_timer`` set of APIs provides one-shot and periodic timers, microsecond time resolution, and 64-bit range.
Internally, ``esp_timer`` uses a 64-bit hardware timer, where the implementation depends on :ref:`CONFIG_ESP_TIMER_IMPL`. Available options are:
.. list::
:esp32: - LAC timer
:esp32: - (legacy) FRC2 timer
:not esp32: - SYSTIMER
.. only:: esp32
.. note:: The FRC2 is a legacy option for ESP32 until v4.2, a 32-bit hardware timer was used. Starting at v4.2, use the new LAC timer option instead, it has a simpler implementation, and has smaller run time overhead because software handling of timer overflow is not needed.
Internally, ``esp_timer`` uses a 64-bit hardware timer, where the implementation depends on the target. {IDF_TARGET_HR_TIMER} is used for {IDF_TARGET_NAME}.
Timer callbacks can be dispatched by two methods:

View File

@ -51,3 +51,9 @@ APP Trace
---------
One of the timestamp sources has changed from the legacy timer group driver to the new :doc:`GPTimer <../api-reference/peripherals/gptimer>`. Kconfig choices like ``APPTRACE_SV_TS_SOURCE_TIMER00`` has been changed to ``APPTRACE_SV_TS_SOURCE_GPTIMER``. User doesn't need to choose the group and timer ID any more.
ESP Timer
---------
Removed the FRC2 based legacy implementation of esp_timer available on ESP32. The simpler and more efficient implementation based on the LAC timer is now the only option.

View File

@ -36,11 +36,11 @@ Once time is synchronized, ESP32 will perform timekeeping using built-in timers.
- RTC clock is used to maintain accurate time when chip is in deep sleep mode
- FRC1 timer is used to provide time at microsecond accuracy when ESP32 is running.
- High-resolution timer is used to provide time at microsecond accuracy when ESP32 is running.
Timekeeping using RTC timer is demonstrated in this example by going into deep sleep mode. After wake up, ESP32 will print current time without connecting to WiFi.
To use this functionality, make sure "Timers used for gettimeofday function" option in "ESP32-specific config" menu of menuconfig is set to "RTC and FRC1" or "RTC".
To use this functionality, make sure "Timers used for gettimeofday function" option in "ESP32-specific config" menu of menuconfig is set to "RTC and high-resolution timer" or "RTC".
## Working with time
@ -89,14 +89,14 @@ This example can use 3 time synchronization method:
## Adjtime()
`int adjtime(const struct timeval *delta, struct timeval *outdelta)`
`adjtime()` is a libc function that is called automatically in "smooth" time update mode, but can also be called from custom time synchronization code.
`adjtime()` is a libc function that is called automatically in "smooth" time update mode, but can also be called from custom time synchronization code.
If the time error is less than 35 minutes then `adjtime` function will start smooth adjusting otherwise the return value is -1.
This function speeds up or slows down the system clock in order to make a gradual adjustment. This ensures that the calendar time reported by the system clock is always monotonically increasing, which might not happen if you simply set the clock. If adjusting the system clock by `adjtime()` is already done during the second call `adjtime()`, and the delta of the second call is not NULL, the earlier tuning is stopped, but the already completed part of the adjustment is not canceled.
The delta argument specifies a relative adjustment to be made to the clock time. If negative, the system clock is slowed down for a while until it has lost this much elapsed time. If positive, the system clock is speeded up for a while.
The delta argument specifies a relative adjustment to be made to the clock time. If negative, the system clock is slowed down for a while until it has lost this much elapsed time. If positive, the system clock is speeded up for a while.
If the olddelta argument is not a null pointer, the adjtime function returns information about any previous time adjustment that has not yet completed.
If the olddelta argument is not a null pointer, the adjtime function returns information about any previous time adjustment that has not yet completed.
The return value is 0 on success and -1 on failure.

View File

@ -8,7 +8,7 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80
CONFIG_ESP32_ULP_COPROC_ENABLED=y
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=512
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y
# ESP32S2-specific config

View File

@ -13,6 +13,6 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80
CONFIG_ESP32_ULP_COPROC_ENABLED=y
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=512
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y

View File

@ -2,6 +2,6 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80
CONFIG_ESP32_ULP_COPROC_ENABLED=y
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=512
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y

View File

@ -4,8 +4,6 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
CONFIG_PM_ENABLE=y
# Enable tickless idle mode
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
# Use RTC timer as reference
CONFIG_PM_USE_RTC_TIMER_REF=y
# Put related source code in IRAM
CONFIG_PM_SLP_IRAM_OPT=y
CONFIG_PM_RTOS_IDLE_OPT=y

View File

@ -1242,7 +1242,6 @@ components/newlib/abort.c
components/newlib/assert.c
components/newlib/platform_include/assert.h
components/newlib/platform_include/errno.h
components/newlib/platform_include/esp_newlib.h
components/newlib/platform_include/net/if.h
components/newlib/platform_include/pthread.h
components/newlib/platform_include/sys/dirent.h
@ -1426,7 +1425,6 @@ components/soc/esp32/include/soc/emac_ext_struct.h
components/soc/esp32/include/soc/emac_mac_struct.h
components/soc/esp32/include/soc/fe_reg.h
components/soc/esp32/include/soc/flash_encryption_reg.h
components/soc/esp32/include/soc/frc_timer_reg.h
components/soc/esp32/include/soc/gpio_pins.h
components/soc/esp32/include/soc/gpio_reg.h
components/soc/esp32/include/soc/gpio_sd_reg.h
@ -1641,7 +1639,6 @@ components/soc/esp32s2/include/soc/efuse_reg.h
components/soc/esp32s2/include/soc/efuse_struct.h
components/soc/esp32s2/include/soc/extmem_reg.h
components/soc/esp32s2/include/soc/fe_reg.h
components/soc/esp32s2/include/soc/frc_timer_reg.h
components/soc/esp32s2/include/soc/gpio_pins.h
components/soc/esp32s2/include/soc/gpio_reg.h
components/soc/esp32s2/include/soc/gpio_sd_reg.h

View File

@ -175,9 +175,9 @@ CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5=
CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6=
CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7=
CONFIG_ESP32_BROWNOUT_DET_LVL=0
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32_TIME_SYSCALL_USE_RTC=
CONFIG_ESP32_TIME_SYSCALL_USE_FRC1=
CONFIG_ESP32_TIME_SYSCALL_USE_HRT=
CONFIG_ESP32_TIME_SYSCALL_USE_NONE=
CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y
CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS=