mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/remove_frc2_timer' into 'master'
esp_timer: remove legacy ESP32 FRC timer implementation Closes IDF-3805 See merge request espressif/esp-idf!16406
This commit is contained in:
commit
5ef585b1b9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -5,3 +5,5 @@
|
||||
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
|
||||
|
@ -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
|
||||
|
5
components/esp32s3/sdkconfig.rename
Normal file
5
components/esp32s3/sdkconfig.rename
Normal 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
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
||||
- "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"
|
||||
bool
|
||||
default y
|
||||
depends on IDF_TARGET_ESP32
|
||||
|
||||
config ESP_TIMER_IMPL_SYSTIMER
|
||||
bool "SYSTIMER"
|
||||
bool
|
||||
default y
|
||||
depends on !IDF_TARGET_ESP32
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu # esp_timer
|
||||
|
@ -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>
|
||||
|
@ -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")));
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
Loading…
x
Reference in New Issue
Block a user