esp32: add implementation of esp_timer based on TG0 LAC timer

Closes: IDF-979
This commit is contained in:
Konstantin Kondrashov 2020-02-06 14:00:18 +08:00 committed by Angus Gratton
parent 67b0a79167
commit 739eb05bb9
63 changed files with 1261 additions and 504 deletions

View File

@ -539,7 +539,7 @@ endif()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}" INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}" PRIV_INCLUDE_DIRS "${priv_include_dirs}"
REQUIRES nvs_flash soc) REQUIRES nvs_flash soc esp_timer)
if(CONFIG_BT_ENABLED) if(CONFIG_BT_ENABLED)
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-const-variable) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-const-variable)

View File

@ -44,7 +44,7 @@ endif()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${includes} INCLUDE_DIRS ${includes}
PRIV_INCLUDE_DIRS "include/driver" PRIV_INCLUDE_DIRS "include/driver"
PRIV_REQUIRES efuse PRIV_REQUIRES efuse esp_timer
REQUIRES esp_ringbuf soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent REQUIRES esp_ringbuf soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent
# uses C11 atomic feature # uses C11 atomic feature

View File

@ -69,6 +69,8 @@ static uint32_t get_clk_en_mask(periph_module_t periph)
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
case PERIPH_USB_MODULE: case PERIPH_USB_MODULE:
return DPORT_USB_CLK_EN; return DPORT_USB_CLK_EN;
case PERIPH_SYSTIMER_MODULE:
return DPORT_SYSTIMER_CLK_EN;
#endif #endif
case PERIPH_I2C0_MODULE: case PERIPH_I2C0_MODULE:
return DPORT_I2C_EXT0_CLK_EN; return DPORT_I2C_EXT0_CLK_EN;
@ -171,6 +173,8 @@ static uint32_t get_rst_en_mask(periph_module_t periph, bool enable)
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
case PERIPH_USB_MODULE: case PERIPH_USB_MODULE:
return DPORT_USB_RST; return DPORT_USB_RST;
case PERIPH_SYSTIMER_MODULE:
return DPORT_SYSTIMER_RST;
#endif #endif
case PERIPH_I2C0_MODULE: case PERIPH_I2C0_MODULE:
return DPORT_I2C_EXT0_RST; return DPORT_I2C_EXT0_RST;

View File

@ -19,7 +19,6 @@ else()
"crosscore_int.c" "crosscore_int.c"
"dport_access.c" "dport_access.c"
"dport_panic_highint_hdl.S" "dport_panic_highint_hdl.S"
"esp_timer_esp32.c"
"esp_himem.c" "esp_himem.c"
"hw_random.c" "hw_random.c"
"int_wdt.c" "int_wdt.c"
@ -33,14 +32,16 @@ else()
"spiram_psram.c" "spiram_psram.c"
"system_api_esp32.c" "system_api_esp32.c"
"task_wdt.c") "task_wdt.c")
set(include_dirs "include") set(include_dirs "include")
set(requires driver efuse soc) #unfortunately rom/uart uses SOC registers directly set(requires driver efuse soc) #unfortunately rom/uart uses SOC registers directly
# driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t
# app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function.
# esp_timer is added here because cpu_start.c uses esp_timer
set(priv_requires app_trace app_update bootloader_support log mbedtls nvs_flash pthread set(priv_requires app_trace app_update bootloader_support log mbedtls nvs_flash pthread
spi_flash vfs espcoredump esp_common perfmon) spi_flash vfs espcoredump esp_common perfmon esp_timer)
set(fragments linker.lf ld/esp32_fragments.lf) set(fragments linker.lf ld/esp32_fragments.lf)
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"

View File

@ -454,8 +454,7 @@ menu "ESP32-specific"
When brownout reset occurs, reduce PHY TX power to keep the code running When brownout reset occurs, reduce PHY TX power to keep the code running
# Note about the use of "FRC1" name: currently FRC1 timer is not used for # Note about the use of "FRC1" name: currently FRC1 timer is not used for
# high resolution timekeeping anymore. Instead the esp_timer API, implemented # high resolution timekeeping anymore. Instead the esp_timer API is used.
# using FRC2 timer, is used.
# FRC1 name in the option name is kept for compatibility. # FRC1 name in the option name is kept for compatibility.
choice ESP32_TIME_SYSCALL choice ESP32_TIME_SYSCALL
prompt "Timers used for gettimeofday function" prompt "Timers used for gettimeofday function"
@ -732,7 +731,7 @@ menu "Power Management"
config PM_USE_RTC_TIMER_REF config PM_USE_RTC_TIMER_REF
bool "Use RTC timer to prevent time drift (EXPERIMENTAL)" bool "Use RTC timer to prevent time drift (EXPERIMENTAL)"
depends on PM_ENABLE && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1) depends on PM_ENABLE && ESP_TIMER_IMPL_FRC2 && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1)
default n default n
help help
When APB clock frequency changes, high-resolution timer (esp_timer) When APB clock frequency changes, high-resolution timer (esp_timer)

View File

@ -33,7 +33,7 @@
#include "esp_private/pm_impl.h" #include "esp_private/pm_impl.h"
#include "esp_private/pm_trace.h" #include "esp_private/pm_trace.h"
#include "esp_private/esp_timer_impl.h" #include "esp_private/esp_timer_private.h"
#include "esp32/pm.h" #include "esp32/pm.h"
#include "esp_sleep.h" #include "esp_sleep.h"
@ -301,7 +301,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80); uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
/* Update APB frequency value used by the timer */ /* Update APB frequency value used by the timer */
if (old_apb_ticks_per_us != apb_ticks_per_us) { if (old_apb_ticks_per_us != apb_ticks_per_us) {
esp_timer_impl_update_apb_freq(apb_ticks_per_us); esp_timer_private_update_apb_freq(apb_ticks_per_us);
} }
/* Calculate new tick divisor */ /* Calculate new tick divisor */

View File

@ -17,7 +17,7 @@
#include <sys/param.h> #include <sys/param.h>
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_private/esp_timer_impl.h" #include "esp_private/esp_timer_private.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp32/clk.h" #include "esp32/clk.h"
#include "esp_newlib.h" #include "esp_newlib.h"
@ -281,11 +281,11 @@ esp_err_t esp_light_sleep_start(void)
{ {
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock); portENTER_CRITICAL(&light_sleep_lock);
/* We will be calling esp_timer_impl_advance inside DPORT access critical /* We will be calling esp_timer_private_advance inside DPORT access critical
* section. Make sure the code on the other CPU is not holding esp_timer * section. Make sure the code on the other CPU is not holding esp_timer
* lock, otherwise there will be deadlock. * lock, otherwise there will be deadlock.
*/ */
esp_timer_impl_lock(); esp_timer_private_lock();
s_config.rtc_ticks_at_sleep_start = rtc_time_get(); s_config.rtc_ticks_at_sleep_start = rtc_time_get();
uint64_t frc_time_at_start = esp_timer_get_time(); uint64_t frc_time_at_start = esp_timer_get_time();
DPORT_STALL_OTHER_CPU_START(); DPORT_STALL_OTHER_CPU_START();
@ -347,11 +347,11 @@ esp_err_t esp_light_sleep_start(void)
* monotonic. * monotonic.
*/ */
if (time_diff > 0) { if (time_diff > 0) {
esp_timer_impl_advance(time_diff); esp_timer_private_advance(time_diff);
} }
esp_set_time_from_rtc(); esp_set_time_from_rtc();
esp_timer_impl_unlock(); esp_timer_private_unlock();
DPORT_STALL_OTHER_CPU_END(); DPORT_STALL_OTHER_CPU_END();
if (!wdt_was_enabled) { if (!wdt_was_enabled) {
rtc_wdt_disable(); rtc_wdt_disable();

View File

@ -13,7 +13,6 @@ if(IDF_TARGET STREQUAL "esp32")
add_dependencies(${COMPONENT_LIB} esp32_test_logo) add_dependencies(${COMPONENT_LIB} esp32_test_logo)
idf_build_set_property(COMPILE_DEFINITIONS "-DESP_TIMER_DYNAMIC_OVERFLOW_VAL" APPEND)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5") target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5")
endif() endif()

View File

@ -17,7 +17,6 @@ else()
"crosscore_int.c" "crosscore_int.c"
"dport_access.c" "dport_access.c"
"dport_panic_highint_hdl.S" "dport_panic_highint_hdl.S"
"esp_timer_esp32s2.c"
"hw_random.c" "hw_random.c"
"int_wdt.c" "int_wdt.c"
"intr_alloc.c" "intr_alloc.c"
@ -30,15 +29,17 @@ else()
"spiram_psram.c" "spiram_psram.c"
"system_api_esp32s2.c" "system_api_esp32s2.c"
"task_wdt.c") "task_wdt.c")
set(include_dirs "include") set(include_dirs "include")
set(requires driver efuse soc) #unfortunately rom/uart uses SOC registers directly set(requires driver efuse soc) #unfortunately rom/uart uses SOC registers directly
# driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t
# app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function.
# esp_timer is added here because cpu_start.c uses esp_timer
set(priv_requires set(priv_requires
app_trace app_update bootloader_support log mbedtls nvs_flash app_trace app_update bootloader_support log mbedtls nvs_flash
pthread spi_flash vfs espcoredump esp_common) pthread spi_flash vfs espcoredump esp_common esp_timer)
set(fragments linker.lf ld/esp32s2_fragments.lf) set(fragments linker.lf ld/esp32s2_fragments.lf)

View File

@ -1,407 +0,0 @@
// Copyright 2017 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.
#include "esp_err.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "esp_task.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp32s2/clk.h"
#include "esp_private/esp_timer_impl.h"
#include "soc/frc_timer_reg.h"
#include "soc/rtc.h"
#include "soc/periph_defs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
/**
* @file esp_timer_esp32.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;
// 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;
//The timer_overflow_happened read alarm register to tell if overflow happened.
//However, there is a monent that overflow happens, and before ISR function called
//alarm register is set to another value, then you call timer_overflow_happened,
//it will return false.
//So we store the overflow value when new alarm is to be set.
static bool s_overflow_happened;
#ifdef CONFIG_PM_DFS_USE_RTC_TIMER_REF
// If DFS is enabled, upon the first frequency change this value is set to the
// difference between esp_timer value and RTC timer value. On every subsequent
// frequency change, s_time_base_us is adjusted to maintain the same difference
// between esp_timer and RTC timer. (All mentioned values are in microseconds.)
static uint64_t s_rtc_time_diff = 0;
#endif
// Spinlock used to protect access to static variables above and to the hardware
// registers.
portMUX_TYPE s_time_update_lock = portMUX_INITIALIZER_UNLOCKED;
//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)
{
if (s_overflow_happened) {
return true;
}
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);
}
uint64_t IRAM_ATTR esp_timer_impl_get_time(void)
{
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;
}
void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
// Alarm time relative to the moment when counter was 0
uint64_t time_after_timebase_us = timestamp - s_time_base_us;
// Adjust current time if overflow has happened
bool overflow = timer_overflow_happened();
uint64_t cur_count = REG_READ(FRC_TIMER_COUNT_REG(1));
if (overflow) {
assert(time_after_timebase_us > s_timer_us_per_overflow);
time_after_timebase_us -= s_timer_us_per_overflow;
s_overflow_happened = true;
}
// Calculate desired timer compare value (may exceed 2^32-1)
uint64_t compare_val = time_after_timebase_us * s_timer_ticks_per_us;
uint32_t alarm_reg_val = ALARM_OVERFLOW_VAL;
// 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).
const uint32_t offset = s_timer_ticks_per_us * 2;
if (compare_val < ALARM_OVERFLOW_VAL) {
if (compare_val < cur_count + offset) {
compare_val = cur_count + offset;
if (compare_val > ALARM_OVERFLOW_VAL) {
compare_val = ALARM_OVERFLOW_VAL;
}
}
alarm_reg_val = (uint32_t) compare_val;
}
REG_WRITE(FRC_TIMER_ALARM_REG(1), alarm_reg_val);
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
}
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_overflow_happened = false;
}
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);
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_DFS_USE_RTC_TIMER_REF
// Due to the extra time required to read RTC time, don't attempt this
// adjustment when switching to a higher frequency (which usually
// happens in an interrupt).
if (new_ticks_per_us < s_timer_ticks_per_us) {
uint64_t rtc_time = esp_clk_rtc_time();
uint64_t new_rtc_time_diff = s_time_base_us - rtc_time;
if (s_rtc_time_diff != 0) {
uint64_t correction = new_rtc_time_diff - s_rtc_time_diff;
s_time_base_us -= correction;
} else {
s_rtc_time_diff = new_rtc_time_diff;
}
}
#endif // CONFIG_PM_DFS_USE_RTC_TIMER_REF
s_timer_ticks_per_us = new_ticks_per_us;
s_timer_us_per_overflow = 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;
s_overflow_happened = false;
portEXIT_CRITICAL(&s_time_update_lock);
}
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
{
s_alarm_handler = alarm_handler;
esp_err_t err = esp_intr_alloc(ETS_TIMER2_INTR_SOURCE,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM,
&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

View File

@ -33,7 +33,7 @@
#include "esp_private/pm_impl.h" #include "esp_private/pm_impl.h"
#include "esp_private/pm_trace.h" #include "esp_private/pm_trace.h"
#include "esp_private/esp_timer_impl.h" #include "esp_private/esp_timer_private.h"
#include "esp32s2/pm.h" #include "esp32s2/pm.h"
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work /* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
@ -306,7 +306,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80); uint32_t apb_ticks_per_us = MIN(ticks_per_us, 80);
/* Update APB frequency value used by the timer */ /* Update APB frequency value used by the timer */
if (old_apb_ticks_per_us != apb_ticks_per_us) { if (old_apb_ticks_per_us != apb_ticks_per_us) {
esp_timer_impl_update_apb_freq(apb_ticks_per_us); esp_timer_private_update_apb_freq(apb_ticks_per_us);
} }
/* Calculate new tick divisor */ /* Calculate new tick divisor */

View File

@ -17,7 +17,7 @@
#include <sys/param.h> #include <sys/param.h>
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_private/esp_timer_impl.h" #include "esp_private/esp_timer_private.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp32s2/clk.h" #include "esp32s2/clk.h"
#include "esp_newlib.h" #include "esp_newlib.h"
@ -266,11 +266,11 @@ esp_err_t esp_light_sleep_start(void)
{ {
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock); portENTER_CRITICAL(&light_sleep_lock);
/* We will be calling esp_timer_impl_advance inside DPORT access critical /* We will be calling esp_timer_private_advance inside DPORT access critical
* section. Make sure the code on the other CPU is not holding esp_timer * section. Make sure the code on the other CPU is not holding esp_timer
* lock, otherwise there will be deadlock. * lock, otherwise there will be deadlock.
*/ */
esp_timer_impl_lock(); esp_timer_private_lock();
s_config.rtc_ticks_at_sleep_start = rtc_time_get(); s_config.rtc_ticks_at_sleep_start = rtc_time_get();
uint64_t frc_time_at_start = esp_timer_get_time(); uint64_t frc_time_at_start = esp_timer_get_time();
DPORT_STALL_OTHER_CPU_START(); DPORT_STALL_OTHER_CPU_START();
@ -331,11 +331,11 @@ esp_err_t esp_light_sleep_start(void)
* monotonic. * monotonic.
*/ */
if (time_diff > 0) { if (time_diff > 0) {
esp_timer_impl_advance(time_diff); esp_timer_private_advance(time_diff);
} }
esp_set_time_from_rtc(); esp_set_time_from_rtc();
esp_timer_impl_unlock(); esp_timer_private_unlock();
DPORT_STALL_OTHER_CPU_END(); DPORT_STALL_OTHER_CPU_END();
if (!wdt_was_enabled) { if (!wdt_was_enabled) {
rtc_wdt_disable(); rtc_wdt_disable();

View File

@ -13,7 +13,6 @@ if(IDF_TARGET STREQUAL "esp32s2")
add_dependencies(${COMPONENT_LIB} esp32s2_test_logo) add_dependencies(${COMPONENT_LIB} esp32s2_test_logo)
idf_build_set_property(COMPILE_DEFINITIONS "-DESP_TIMER_DYNAMIC_OVERFLOW_VAL" APPEND)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5") target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5")
endif() endif()

View File

@ -9,8 +9,6 @@ else()
set(srcs "src/brownout.c" set(srcs "src/brownout.c"
"src/dbg_stubs.c" "src/dbg_stubs.c"
"src/esp_err_to_name.c" "src/esp_err_to_name.c"
"src/esp_timer.c"
"src/ets_timer_legacy.c"
"src/freertos_hooks.c" "src/freertos_hooks.c"
"src/mac_addr.c" "src/mac_addr.c"
"src/pm_locks.c" "src/pm_locks.c"
@ -24,7 +22,7 @@ else()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include INCLUDE_DIRS include
REQUIRES ${target} REQUIRES ${target} esp_timer
PRIV_REQUIRES soc) PRIV_REQUIRES soc)
set_source_files_properties( set_source_files_properties(

View File

@ -1,14 +1,5 @@
menu "Common ESP-related" menu "Common ESP-related"
config ESP_TIMER_PROFILING
bool "Enable esp_timer profiling features"
default n
help
If enabled, esp_timer_dump will dump information such as number of times the timer was started, number of
times the timer has triggered, and the total time it took for the callback to run. This option has some
effect on timer performance and the amount of memory used for timer storage, and should only be used for
debugging/testing purposes.
config ESP_ERR_TO_NAME_LOOKUP config ESP_ERR_TO_NAME_LOOKUP
bool "Enable lookup of error code strings" bool "Enable lookup of error code strings"
default "y" default "y"
@ -63,20 +54,6 @@ menu "Common ESP-related"
same as prior to that of ESP-IDF v4.0, and hence IPC task will run same as prior to that of ESP-IDF v4.0, and hence IPC task will run
at (configMAX_PRIORITIES - 1) priority. at (configMAX_PRIORITIES - 1) priority.
config ESP_TIMER_TASK_STACK_SIZE
int "High-resolution timer task stack size"
default 3584
range 2048 65536
help
Configure the stack size of esp_timer/ets_timer task. This task is used
to dispatch callbacks of timers created using ets_timer and esp_timer
APIs. If you are seing stack overflow errors in timer task, increase
this value.
Note that this is not the same as FreeRTOS timer task. To configure
FreeRTOS timer task size, see "FreeRTOS timer task stack size" option
in "FreeRTOS" menu.
config ESP_MINIMAL_SHARED_STACK_SIZE config ESP_MINIMAL_SHARED_STACK_SIZE
int "Minimal allowed size for shared stack" int "Minimal allowed size for shared stack"
default 2048 default 2048

View File

@ -5,7 +5,6 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE CONFIG_ESP_SYSTEM_EVENT_
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE
CONFIG_MAIN_TASK_STACK_SIZE CONFIG_ESP_MAIN_TASK_STACK_SIZE CONFIG_MAIN_TASK_STACK_SIZE CONFIG_ESP_MAIN_TASK_STACK_SIZE
CONFIG_IPC_TASK_STACK_SIZE CONFIG_ESP_IPC_TASK_STACK_SIZE CONFIG_IPC_TASK_STACK_SIZE CONFIG_ESP_IPC_TASK_STACK_SIZE
CONFIG_TIMER_TASK_STACK_SIZE CONFIG_ESP_TIMER_TASK_STACK_SIZE
CONFIG_CONSOLE_UART CONFIG_ESP_CONSOLE_UART CONFIG_CONSOLE_UART CONFIG_ESP_CONSOLE_UART
CONFIG_CONSOLE_UART_DEFAULT CONFIG_ESP_CONSOLE_UART_DEFAULT CONFIG_CONSOLE_UART_DEFAULT CONFIG_ESP_CONSOLE_UART_DEFAULT
CONFIG_CONSOLE_UART_CUSTOM CONFIG_ESP_CONSOLE_UART_CUSTOM CONFIG_CONSOLE_UART_CUSTOM CONFIG_ESP_CONSOLE_UART_CUSTOM

View File

@ -1,7 +1,7 @@
if(IDF_TARGET STREQUAL "esp32") if(IDF_TARGET STREQUAL "esp32")
set(priv_requires esp_eth) set(priv_requires esp_eth esp_timer)
else() else()
set(priv_requires) set(priv_requires esp_timer)
endif() endif()
idf_component_register(SRCS "default_event_loop.c" idf_component_register(SRCS "default_event_loop.c"

View File

@ -7,4 +7,4 @@ idf_component_register(SRCS "src/httpd_main.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src/port/esp32" "src/util" PRIV_INCLUDE_DIRS "src/port/esp32" "src/util"
REQUIRES nghttp # for http_parser.h REQUIRES nghttp # for http_parser.h
PRIV_REQUIRES lwip) PRIV_REQUIRES lwip esp_timer)

View File

@ -0,0 +1,18 @@
idf_build_get_property(target IDF_TARGET)
set(srcs "src/esp_timer.c"
"src/ets_timer_legacy.c")
if(CONFIG_ESP_TIMER_IMPL_FRC2)
list(APPEND srcs "src/esp_timer_impl_frc_legacy.c")
elseif(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")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
REQUIRES esp_common
PRIV_REQUIRES soc driver "${target}")

View File

@ -0,0 +1,54 @@
menu "High resolution timer (esp_timer)"
config ESP_TIMER_PROFILING
bool "Enable esp_timer profiling features"
default n
help
If enabled, esp_timer_dump will dump information such as number of times the timer was started,
number of times the timer has triggered, and the total time it took for the callback to run.
This option has some effect on timer performance and the amount of memory used for timer
storage, and should only be used for debugging/testing purposes.
config ESP_TIMER_TASK_STACK_SIZE
int "High-resolution timer task stack size"
default 3584
range 2048 65536
help
Configure the stack size of "timer_task" task. This task is used
to dispatch callbacks of timers created using ets_timer and esp_timer
APIs. If you are seing stack overflow errors in timer task, increase
this value.
Note that this is not the same as FreeRTOS timer task. To configure
FreeRTOS timer task size, see "FreeRTOS timer task stack size" option
in "FreeRTOS" menu.
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 if IDF_TARGET_ESP32S2
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 ESP32-S2 chip.
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_ESP32S2
endchoice
endmenu # esp_timer

View File

@ -0,0 +1,6 @@
ifdef CONFIG_ESP_TIMER_IMPL_FRC2
# Enable dynamic esp_timer overflow value if building unit tests
ifneq ("$(filter esp_timer,$(TEST_COMPONENTS_LIST))","")
CPPFLAGS += -DESP_TIMER_DYNAMIC_OVERFLOW_VAL
endif
endif

View File

@ -0,0 +1,22 @@
#
# Component Makefile
#
ifdef CONFIG_IDF_TARGET_ESP32
COMPONENT_SRCDIRS := src
COMPONENT_PRIV_INCLUDEDIRS := private_include
ifdef CONFIG_ESP_TIMER_IMPL_FRC2
# FRC2(legacy) timer is suppoted in esp32
COMPONENT_OBJEXCLUDE := src/esp_timer_impl_lac.o
endif
ifdef CONFIG_ESP_TIMER_IMPL_TG0_LAC
# TG0_LAC timer is suppoted in esp32
COMPONENT_OBJEXCLUDE := src/esp_timer_impl_frc_legacy.o
endif
COMPONENT_OBJEXCLUDE += src/esp_timer_impl_systimer.o
else
$(error esp_timer is only supported by the Make build system for esp32 chip. For other chips, use the Cmake build system)
endif

View File

@ -0,0 +1,70 @@
// Copyright 2017 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.
#pragma once
/**
* @file esp_private/esp_timer_private.h
*
* @brief Interface between common and platform-specific parts of esp_timer.
*
* The functions in this header file are implemented for each supported SoC.
* High level functions defined in esp_timer.c call the functions here to
* interact with the hardware.
*
* Note: The functions from this file are marked as private and are used exclusively
* inside the IDF in the power management and sleep files.
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Notify esp_timer implementation that APB frequency has changed
*
* Called by the frequency switching code.
*
* @param apb_ticks_per_us new number of APB clock ticks per microsecond
*/
void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us);
/**
* @brief Adjust current esp_timer time by a certain value
*
* Called from light sleep code to synchronize esp_timer time with RTC time.
*
* @param time_us adjustment to apply to esp_timer time, in microseconds
*/
void esp_timer_private_advance(int64_t time_us);
/**
* @brief obtain internal critical section used esp_timer implementation
* This can be used when a sequence of calls to esp_timer has to be made,
* and it is necessary that the state of the timer is consistent between
* the calls. Should be treated in the same way as a spinlock.
* Call esp_timer_unlock to release the lock
*/
void esp_timer_private_lock(void);
/**
* @brief counterpart of esp_timer_lock
*/
void esp_timer_private_unlock(void);
#ifdef __cplusplus
}
#endif

View File

@ -226,7 +226,6 @@ int64_t esp_timer_get_next_alarm(void);
*/ */
esp_err_t esp_timer_dump(FILE* stream); esp_err_t esp_timer_dump(FILE* stream);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -15,7 +15,7 @@
#pragma once #pragma once
/** /**
* @file esp_private/esp_timer_impl.h * @file private_include/esp_timer_impl.h
* *
* @brief Interface between common and platform-specific parts of esp_timer. * @brief Interface between common and platform-specific parts of esp_timer.
* *
@ -99,3 +99,21 @@ void esp_timer_impl_lock(void);
* @brief counterpart of esp_timer_impl_lock * @brief counterpart of esp_timer_impl_lock
*/ */
void esp_timer_impl_unlock(void); void esp_timer_impl_unlock(void);
/**
* @brief Get counting register
*
* Bit depth dependents on implementation and can be 32-bit or 64-bit.
*
* @return the value of the counting register
*/
uint64_t esp_timer_impl_get_counter_reg(void);
/**
* @brief Get alarm register
*
* Bit depth dependents on implementation and can be 32-bit or 64-bit.
*
* @return the value of the alarm register
*/
uint64_t esp_timer_impl_get_alarm_reg(void);

View File

@ -0,0 +1,4 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_TIMER_TASK_STACK_SIZE CONFIG_ESP_TIMER_TASK_STACK_SIZE

View File

@ -18,16 +18,16 @@
#include "esp_types.h" #include "esp_types.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_timer.h"
#include "esp_task.h" #include "esp_task.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "freertos/xtensa_api.h" #include "freertos/xtensa_api.h"
#include "esp_timer.h"
#include "esp_timer_impl.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_private/esp_timer_impl.h"
#ifdef CONFIG_ESP_TIMER_PROFILING #ifdef CONFIG_ESP_TIMER_PROFILING
#define WITH_PROFILING 1 #define WITH_PROFILING 1

View File

@ -13,15 +13,15 @@
// limitations under the License. // limitations under the License.
#include "sys/param.h" #include "sys/param.h"
#include "esp_err.h" #include "esp_timer_impl.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_err.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_task.h" #include "esp_task.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp32/clk.h" #include "esp32/clk.h"
#include "esp_private/esp_timer_impl.h"
#include "soc/frc_timer_reg.h" #include "soc/frc_timer_reg.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -29,7 +29,7 @@
#include "freertos/semphr.h" #include "freertos/semphr.h"
/** /**
* @file esp_timer_esp32.c * @file esp_timer_frc.c
* @brief Implementation of chip-specific part of esp_timer * @brief Implementation of chip-specific part of esp_timer
* *
* This implementation uses FRC2 (legacy) timer of the ESP32. This timer is * This implementation uses FRC2 (legacy) timer of the ESP32. This timer is
@ -415,3 +415,18 @@ void esp_timer_impl_set_overflow_val(uint32_t overflow_val)
esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000); esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
} }
#endif // ESP_TIMER_DYNAMIC_OVERFLOW_VAL #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

@ -0,0 +1,282 @@
// Copyright 2020 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.
#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 "esp32/clk.h"
#include "driver/periph_ctrl.h"
#include "soc/soc.h"
#include "soc/timer_group_reg.h"
#include "soc/rtc.h"
#include "freertos/FreeRTOS.h"
/**
* @file esp_timer_lac.c
* @brief Implementation of chip-specific part of esp_timer
*
* This implementation uses TG0 LAC timer of the ESP32. This timer is
* a 64-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.
*/
/* Selects which Timer Group peripheral to use */
#define LACT_MODULE 0
#if LACT_MODULE == 0
#define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE
#define PERIPH_LACT PERIPH_TIMG0_MODULE
#elif LACT_MODULE == 1
#define INTR_SOURCE_LACT ETS_TG1_LACT_LEVEL_INTR_SOURCE
#define PERIPH_LACT PERIPH_TIMG1_MODULE
#else
#error "Incorrect the number of LACT module (only 0 or 1)"
#endif
/* Desired number of timer ticks per microsecond.
* This value should be small enough so that all possible APB frequencies
* could be divided by it without remainder.
* On the other hand, the smaller this value is, the longer we need to wait
* after setting UPDATE_REG before the timer value can be read.
* If TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which
* makes esp_timer_impl_get_time function take too much time.
* The value TICKS_PER_US == 2 allows for most of the APB frequencies, and
* allows reading the counter quickly enough.
*/
#define TICKS_PER_US 2
/* Shorter register names, used in this file */
#define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE))
#define RTC_STEP_REG (TIMG_LACTRTC_REG(LACT_MODULE))
#define ALARM_LO_REG (TIMG_LACTALARMLO_REG(LACT_MODULE))
#define ALARM_HI_REG (TIMG_LACTALARMHI_REG(LACT_MODULE))
#define COUNT_LO_REG (TIMG_LACTLO_REG(LACT_MODULE))
#define COUNT_HI_REG (TIMG_LACTHI_REG(LACT_MODULE))
#define UPDATE_REG (TIMG_LACTUPDATE_REG(LACT_MODULE))
#define LOAD_REG (TIMG_LACTLOAD_REG(LACT_MODULE))
#define LOAD_LO_REG (TIMG_LACTLOADLO_REG(LACT_MODULE))
#define LOAD_HI_REG (TIMG_LACTLOADHI_REG(LACT_MODULE))
#define INT_ENA_REG (TIMG_INT_ENA_TIMERS_REG(LACT_MODULE))
#define INT_ST_REG (TIMG_INT_ST_TIMERS_REG(LACT_MODULE))
#define INT_CLR_REG (TIMG_INT_CLR_TIMERS_REG(LACT_MODULE))
/* Helper type to convert between a 64-bit value and a pair of 32-bit values without shifts and masks */
typedef struct {
union {
struct {
uint32_t lo;
uint32_t hi;
};
uint64_t val;
};
} timer_64b_reg_t;
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;
/* Spinlock used to protect access to the hardware registers. */
portMUX_TYPE s_time_update_lock = portMUX_INITIALIZER_UNLOCKED;
void esp_timer_impl_lock(void)
{
portENTER_CRITICAL(&s_time_update_lock);
}
void esp_timer_impl_unlock(void)
{
portEXIT_CRITICAL(&s_time_update_lock);
}
uint64_t IRAM_ATTR esp_timer_impl_get_counter_reg(void)
{
uint32_t lo, hi;
uint32_t lo_start = REG_READ(COUNT_LO_REG);
uint32_t div = REG_GET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER);
/* The peripheral doesn't have a bit to indicate that the update is done, so we poll the
* lower 32 bit part of the counter until it changes, or a timeout expires.
*/
REG_WRITE(UPDATE_REG, 1);
do {
lo = REG_READ(COUNT_LO_REG);
} while (lo == lo_start && div-- > 0);
/* Since this function is called without a critical section, verify that LO and HI
* registers are consistent. That is, if an interrupt happens between reading LO and
* HI registers, and esp_timer_impl_get_time is called from an ISR, then try to
* detect this by the change in LO register value, and re-read both registers.
*/
do {
lo_start = lo;
hi = REG_READ(COUNT_HI_REG);
lo = REG_READ(COUNT_LO_REG);
} while (lo != lo_start);
timer_64b_reg_t result = {
.lo = lo,
.hi = hi
};
return result.val;
}
uint64_t IRAM_ATTR esp_timer_impl_get_time(void)
{
return esp_timer_impl_get_counter_reg() / TICKS_PER_US;
}
void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
int64_t offset = TICKS_PER_US * 2;
uint64_t now_time = esp_timer_impl_get_counter_reg();
timer_64b_reg_t alarm = { .val = MAX(timestamp * TICKS_PER_US, now_time + offset) };
do {
REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
REG_WRITE(ALARM_LO_REG, alarm.lo);
REG_WRITE(ALARM_HI_REG, alarm.hi);
REG_SET_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
now_time = esp_timer_impl_get_counter_reg();
int64_t delta = (int64_t)alarm.val - (int64_t)now_time;
if (delta <= 0 && REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST) == 0) {
// new alarm is less than the counter and the interrupt flag is not set
offset += abs((int)delta) + TICKS_PER_US * 2;
alarm.val = now_time + offset;
} else {
// finish if either (alarm > counter) or the interrupt flag is already set.
break;
}
} while(1);
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
}
static void IRAM_ATTR timer_alarm_isr(void *arg)
{
/* Clear interrupt status */
REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
/* 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(&s_time_update_lock);
assert(apb_ticks_per_us >= 3 && "divider value too low");
assert(apb_ticks_per_us % TICKS_PER_US == 0 && "APB frequency (in MHz) should be divisible by TICK_PER_US");
REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, apb_ticks_per_us / TICKS_PER_US);
portEXIT_CRITICAL(&s_time_update_lock);
}
void esp_timer_impl_advance(int64_t time_diff_us)
{
portENTER_CRITICAL(&s_time_update_lock);
uint64_t now = esp_timer_impl_get_time();
timer_64b_reg_t dst = { .val = (now + time_diff_us) * TICKS_PER_US };
REG_WRITE(LOAD_LO_REG, dst.lo);
REG_WRITE(LOAD_HI_REG, dst.hi);
REG_WRITE(LOAD_REG, 1);
portEXIT_CRITICAL(&s_time_update_lock);
}
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
{
s_alarm_handler = alarm_handler;
periph_module_enable(PERIPH_LACT);
/* Reset the state */
REG_WRITE(CONFIG_REG, 0);
REG_WRITE(LOAD_LO_REG, 0);
REG_WRITE(LOAD_HI_REG, 0);
REG_WRITE(ALARM_LO_REG, UINT32_MAX);
REG_WRITE(ALARM_HI_REG, UINT32_MAX);
REG_WRITE(LOAD_REG, 1);
REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
esp_err_t err = esp_intr_alloc(INTR_SOURCE_LACT,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM,
&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;
}
/* In theory, this needs a shared spinlock with the timer group driver.
* However since esp_timer_impl_init is called early at startup, this
* will not cause issues in practice.
*/
REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
REG_SET_BIT(CONFIG_REG, TIMG_LACT_INCREASE |
TIMG_LACT_LEVEL_INT_EN |
TIMG_LACT_EN);
// Set the step for the sleep mode when the timer will work
// from a slow_clk frequency instead of the APB frequency.
uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US;
REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us);
ESP_ERROR_CHECK( esp_intr_enable(s_timer_interrupt_handle) );
return ESP_OK;
}
void esp_timer_impl_deinit(void)
{
REG_WRITE(CONFIG_REG, 0);
REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
/* TODO: also clear TIMG_LACT_INT_ENA; however see the note in esp_timer_impl_init. */
esp_intr_disable(s_timer_interrupt_handle);
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;
}
uint64_t esp_timer_impl_get_alarm_reg(void)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
timer_64b_reg_t alarm = {
.lo = REG_READ(ALARM_LO_REG),
.hi = REG_READ(ALARM_HI_REG)
};
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
return alarm.val;
}
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

@ -0,0 +1,249 @@
// Copyright 2017 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.
#include <sys/param.h>
#include "esp_timer_impl.h"
#include "esp_err.h"
#include "esp_timer.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "soc/systimer_reg.h"
#include "soc/periph_defs.h"
#include "freertos/FreeRTOS.h"
/**
* @file esp_timer_systimer.c
* @brief Implementation of chip-specific part of esp_timer
*
* This implementation uses SYSTIMER of the ESP32-S2. This timer is
* a 64-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.
*/
/* esp_timer uses the 2 compare unit of SYSTIMER. */
#define INTR_SOURCE_LACT (ETS_SYSTIMER_TARGET2_EDGE_INTR_SOURCE)
// Registers
#define COUNT_LO_REG (SYSTIMER_VALUE_LO_REG)
#define COUNT_HI_REG (SYSTIMER_VALUE_HI_REG)
#define LOAD_LO_REG (SYSTIMER_LOAD_LO_REG)
#define LOAD_HI_REG (SYSTIMER_LOAD_HI_REG)
#define ALARM_LO_REG (SYSTIMER_TARGET2_LO_REG)
#define ALARM_HI_REG (SYSTIMER_TARGET2_HI_REG)
// Macros
#define ENABLE_CLK() (REG_SET_BIT(SYSTIMER_CONF_REG, SYSTIMER_CLK_EN))
#define ENABLE_INT() (REG_SET_BIT(SYSTIMER_INT_ENA_REG, SYSTIMER_INT2_ENA))
#define DISABLE_INT() (REG_CLR_BIT(SYSTIMER_INT_ENA_REG, SYSTIMER_INT2_ENA))
#define GET_INT_FLAG() (REG_GET_FIELD(SYSTIMER_INT_RAW_REG, SYSTIMER_INT2_RAW))
#define CLEAR_INT() (REG_WRITE(SYSTIMER_INT_CLR_REG, SYSTIMER_INT2_CLR))
#define DISABLE_COMPARE_UNIT() (REG_WRITE(SYSTIMER_TARGET2_CONF_REG, 0))
#define ENABLE_COMPARE_UNIT() (REG_WRITE(SYSTIMER_TARGET2_CONF_REG, SYSTIMER_TARGET2_WORK_EN))
#define APPLY_LOADED_VAL() (REG_SET_BIT(SYSTIMER_LOAD_REG, SYSTIMER_TIMER_LOAD))
#define SETTING_STEP_FOR_PLL_SRC(step) (REG_SET_FIELD(SYSTIMER_STEP_REG, SYSTIMER_TIMER_PLL_STEP, step))
#define SETTING_STEP_FOR_XTAL_SRC(step) (REG_SET_FIELD(SYSTIMER_STEP_REG, SYSTIMER_TIMER_XTAL_STEP, step))
#define UPDATE_COUNT_REG() (REG_WRITE(SYSTIMER_UPDATE_REG, SYSTIMER_TIMER_UPDATE))
#define GET_FLAG_UPDATED_COUNT_REG() (REG_GET_BIT(SYSTIMER_UPDATE_REG, SYSTIMER_TIMER_VALUE_VALID))
/* Helper type to convert between a 64-bit value and a pair of 32-bit values without shifts and masks */
typedef struct {
union {
struct {
uint32_t lo;
uint32_t hi;
};
uint64_t val;
};
} timer_64b_reg_t;
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;
/* Number of timer ticks per microsecond. */
#define TICKS_PER_US (APB_CLK_FREQ / 1000000)
/* Spinlock used to protect access to the hardware registers. */
portMUX_TYPE s_time_update_lock = portMUX_INITIALIZER_UNLOCKED;
void esp_timer_impl_lock(void)
{
portENTER_CRITICAL(&s_time_update_lock);
}
void esp_timer_impl_unlock(void)
{
portEXIT_CRITICAL(&s_time_update_lock);
}
uint64_t IRAM_ATTR esp_timer_impl_get_counter_reg(void)
{
uint32_t lo, lo_start, hi;
/* Set the "update" bit and wait for acknowledgment */
UPDATE_COUNT_REG();
while (GET_FLAG_UPDATED_COUNT_REG() == 0) {
;
}
/* Read LO, HI, then LO again, check that LO returns the same value.
* This accounts for the case when an interrupt may happen between reading
* HI and LO values, and this function may get called from the ISR.
* In this case, the repeated read will return consistent values.
*/
lo_start = REG_READ(COUNT_LO_REG);
do {
lo = lo_start;
hi = REG_READ(COUNT_HI_REG);
lo_start = REG_READ(COUNT_LO_REG);
} while (lo_start != lo);
timer_64b_reg_t result = {
.lo = lo,
.hi = hi
};
return result.val;
}
uint64_t IRAM_ATTR esp_timer_impl_get_time(void)
{
return esp_timer_impl_get_counter_reg() / TICKS_PER_US;
}
void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
int64_t offset = TICKS_PER_US * 2;
uint64_t now_time = esp_timer_impl_get_counter_reg();
timer_64b_reg_t alarm = { .val = MAX(timestamp * TICKS_PER_US, now_time + offset) };
do {
DISABLE_COMPARE_UNIT();
REG_WRITE(ALARM_LO_REG, alarm.lo);
REG_WRITE(ALARM_HI_REG, alarm.hi);
ENABLE_COMPARE_UNIT();
now_time = esp_timer_impl_get_counter_reg();
int64_t delta = (int64_t)alarm.val - (int64_t)now_time;
if (delta <= 0 && GET_INT_FLAG() == 0) {
// new alarm is less than the counter and the interrupt flag is not set
offset += abs((int)delta) + TICKS_PER_US * 2;
alarm.val = now_time + offset;
} else {
// finish if either (alarm > counter) or the interrupt flag is already set.
break;
}
} while(1);
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
}
static void IRAM_ATTR timer_alarm_isr(void *arg)
{
// clear the interrupt
CLEAR_INT();
/* Call the upper layer handler */
(*s_alarm_handler)(arg);
}
void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
{
/* If this function was called when switching APB clock to PLL, don't need
* do anything: the SYSTIMER_TIMER_PLL_STEP is already correct.
* If this was called when switching APB clock to XTAL, need to adjust
* XTAL_STEP value accordingly.
*/
if (apb_ticks_per_us != TICKS_PER_US) {
assert((TICKS_PER_US % apb_ticks_per_us) == 0 && "TICK_PER_US should be divisible by APB frequency (in MHz)");
SETTING_STEP_FOR_XTAL_SRC(TICKS_PER_US / apb_ticks_per_us);
}
}
void esp_timer_impl_advance(int64_t time_us)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
timer_64b_reg_t new_count = { .val = esp_timer_impl_get_counter_reg() + time_us * TICKS_PER_US };
REG_WRITE(LOAD_LO_REG, new_count.lo);
REG_WRITE(LOAD_HI_REG, new_count.hi);
APPLY_LOADED_VAL();
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
}
esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
{
s_alarm_handler = alarm_handler;
esp_err_t err = esp_intr_alloc(INTR_SOURCE_LACT,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_EDGE,
&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;
}
ENABLE_CLK();
/* Configure the counter:
* - increment by 1 when running from PLL (80 ticks per microsecond),
* - increment by 2 when running from XTAL (40 ticks per microsecond).
* Note that if the APB frequency is derived from XTAL with divider != 1,
* XTAL_STEP needs to be adjusted accordingly. For example, if
* the APB frequency is XTAL/4 = 10 MHz, then XTAL_STEP should be set to 8.
* This is handled in esp_timer_impl_update_apb_freq function above.
*/
assert(rtc_clk_xtal_freq_get() == 40 && TICKS_PER_US == 80
&& "update the following code to support other XTAL:APB frequency ratios");
SETTING_STEP_FOR_PLL_SRC(1);
SETTING_STEP_FOR_XTAL_SRC(2);
/* TODO: if SYSTIMER is used for anything else, access to SYSTIMER_INT_ENA_REG has to be
* protected by a shared spinlock. Since this code runs as part of early startup, this
* is practically not an issue. Same applies to SYSTIMER_CLK_EN above.
*/
ENABLE_INT();
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);
DISABLE_COMPARE_UNIT();
/* TODO: may need a spinlock, see the note related to SYSTIMER_INT_ENA_REG in esp_timer_impl_init */
DISABLE_INT();
esp_intr_free(s_timer_interrupt_handle);
s_timer_interrupt_handle = NULL;
}
uint64_t IRAM_ATTR esp_timer_impl_get_min_period_us(void)
{
return 50;
}
uint64_t esp_timer_impl_get_alarm_reg(void)
{
portENTER_CRITICAL_SAFE(&s_time_update_lock);
timer_64b_reg_t alarm = {
.lo = REG_READ(ALARM_LO_REG),
.hi = REG_READ(ALARM_HI_REG)
};
portEXIT_CRITICAL_SAFE(&s_time_update_lock);
return alarm.val;
}
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

@ -31,7 +31,6 @@
#include "freertos/xtensa_api.h" #include "freertos/xtensa_api.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_private/esp_timer_impl.h"
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/ets_sys.h" #include "esp32/rom/ets_sys.h"
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2

View File

@ -0,0 +1,7 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "../private_include"
REQUIRES unity test_utils)
if(CONFIG_ESP_TIMER_IMPL_FRC2)
idf_build_set_property(COMPILE_DEFINITIONS "-DESP_TIMER_DYNAMIC_OVERFLOW_VAL" APPEND)
endif()

View File

@ -0,0 +1,6 @@
#
#Component Makefile
#
COMPONENT_SRCDIRS := .
COMPONENT_PRIV_INCLUDEDIRS := ../private_include

View File

@ -3,21 +3,23 @@
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/param.h> #include <sys/param.h>
#include "esp_timer.h"
#include "esp_timer_impl.h"
#include "unity.h" #include "unity.h"
#include "soc/frc_timer_reg.h" #include "soc/frc_timer_reg.h"
#include "esp_timer.h" #include "soc/timer_group_reg.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "test_utils.h" #include "test_utils.h"
#include "esp_private/esp_timer_impl.h"
#include "esp_freertos_hooks.h" #include "esp_freertos_hooks.h"
#ifdef CONFIG_ESP_TIMER_PROFILING #ifdef CONFIG_ESP_TIMER_PROFILING
#define WITH_PROFILING 1 #define WITH_PROFILING 1
#endif #endif
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
extern uint32_t esp_timer_impl_get_overflow_val(void); extern uint32_t esp_timer_impl_get_overflow_val(void);
extern void esp_timer_impl_set_overflow_val(uint32_t overflow_val); extern void esp_timer_impl_set_overflow_val(uint32_t overflow_val);
@ -37,6 +39,17 @@ static void teardown_overflow(void)
{ {
esp_timer_impl_set_overflow_val(s_old_overflow_val); 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
TEST_CASE("esp_timer orders timers correctly", "[esp_timer]") TEST_CASE("esp_timer orders timers correctly", "[esp_timer]")
{ {
@ -595,15 +608,13 @@ TEST_CASE("Can delete timer from a separate task, triggered from callback", "[es
TEST_CASE("esp_timer_impl_advance moves time base correctly", "[esp_timer]") TEST_CASE("esp_timer_impl_advance moves time base correctly", "[esp_timer]")
{ {
ref_clock_init();
int64_t t0 = esp_timer_get_time(); int64_t t0 = esp_timer_get_time();
const int64_t diff_us = 1000000; const int64_t diff_us = 1000000;
esp_timer_impl_advance(diff_us); esp_timer_impl_advance(diff_us);
int64_t t1 = esp_timer_get_time(); int64_t t1 = esp_timer_get_time();
int64_t t_delta = t1 - t0; int64_t t_delta = t1 - t0;
printf("diff_us=%lld t1-t0=%lld\n", diff_us, t_delta); printf("diff_us=%lld t0=%lld t1=%lld t1-t0=%lld\n", diff_us, t0, t1, t_delta);
TEST_ASSERT_INT_WITHIN(1000, diff_us, (int) t_delta); TEST_ASSERT_INT_WITHIN(1000, diff_us, (int) t_delta);
ref_clock_deinit();
} }
@ -821,19 +832,26 @@ 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]") 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); REG_WRITE(FRC_TIMER_LOAD_REG(1), 0);
#endif
esp_timer_impl_advance(50331648); // 0xefffffff/80 = 50331647 esp_timer_impl_advance(50331648); // 0xefffffff/80 = 50331647
ets_delay_us(2); ets_delay_us(2);
portDISABLE_INTERRUPTS(); portDISABLE_INTERRUPTS();
esp_timer_impl_set_alarm(50331647); esp_timer_impl_set_alarm(50331647);
uint32_t alarm_reg = REG_READ(FRC_TIMER_ALARM_REG(1)); uint64_t alarm_reg = esp_timer_impl_get_alarm_reg();
uint32_t count_reg = REG_READ(FRC_TIMER_COUNT_REG(1)); uint64_t count_reg = esp_timer_impl_get_counter_reg();
portENABLE_INTERRUPTS(); portENABLE_INTERRUPTS();
#ifdef CONFIG_ESP_TIMER_IMPL_FRC2
const uint32_t offset = 80 * 2; // s_timer_ticks_per_us const uint32_t offset = 80 * 2; // s_timer_ticks_per_us
printf("alarm_reg = 0x%x, count_reg 0x%x\n", alarm_reg, count_reg); #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)); TEST_ASSERT(alarm_reg <= (count_reg + offset));
} }

View File

@ -3,11 +3,15 @@
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include "unity.h" #include "unity.h"
#include "esp32/rom/ets_sys.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/ets_sys.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/ets_sys.h"
#endif
TEST_CASE("ets_timer produces correct delay", "[ets_timer]") TEST_CASE("ets_timer produces correct delay", "[ets_timer]")
{ {

View File

@ -1,3 +1,4 @@
idf_component_register(SRCS "esp_websocket_client.c" idf_component_register(SRCS "esp_websocket_client.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES lwip esp-tls tcp_transport nghttp) REQUIRES lwip esp-tls tcp_transport nghttp
PRIV_REQUIRES esp_timer)

View File

@ -40,7 +40,7 @@ idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${include_dirs} INCLUDE_DIRS ${include_dirs}
PRIV_INCLUDE_DIRS ${private_include_dirs} PRIV_INCLUDE_DIRS ${private_include_dirs}
LDFRAGMENTS linker.lf LDFRAGMENTS linker.lf
REQUIRES app_trace REQUIRES app_trace esp_timer
PRIV_REQUIRES soc) PRIV_REQUIRES soc)
idf_component_get_property(COMPONENT_DIR freertos COMPONENT_DIR) idf_component_get_property(COMPONENT_DIR freertos COMPONENT_DIR)

View File

@ -3,5 +3,6 @@ idf_component_register(SRCS "mdns.c"
"mdns_networking.c" "mdns_networking.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include" PRIV_INCLUDE_DIRS "private_include"
REQUIRES lwip mbedtls console esp_netif) REQUIRES lwip mbedtls console esp_netif
PRIV_REQUIRES esp_timer)

View File

@ -24,7 +24,7 @@ list(APPEND ldfragments newlib.lf)
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}" INCLUDE_DIRS "${include_dirs}"
REQUIRES vfs REQUIRES vfs
PRIV_REQUIRES soc PRIV_REQUIRES soc esp_timer
LDFRAGMENTS "${ldfragments}") LDFRAGMENTS "${ldfragments}")
# Toolchain libraries require code defined in this component # Toolchain libraries require code defined in this component

View File

@ -59,6 +59,7 @@ typedef enum {
PERIPH_WIFI_BT_COMMON_MODULE, PERIPH_WIFI_BT_COMMON_MODULE,
PERIPH_BT_BASEBAND_MODULE, PERIPH_BT_BASEBAND_MODULE,
PERIPH_BT_LC_MODULE, PERIPH_BT_LC_MODULE,
PERIPH_SYSTIMER_MODULE,
} periph_module_t; } periph_module_t;
typedef enum { typedef enum {

View File

@ -72,6 +72,7 @@
#define DR_REG_TIMERGROUP0_BASE 0x3f41F000 #define DR_REG_TIMERGROUP0_BASE 0x3f41F000
#define DR_REG_TIMERGROUP1_BASE 0x3f420000 #define DR_REG_TIMERGROUP1_BASE 0x3f420000
#define DR_REG_RTC_SLOWMEM_BASE 0x3f421000 #define DR_REG_RTC_SLOWMEM_BASE 0x3f421000
#define DR_REG_SYSTIMER_BASE 0x3f423000
#define DR_REG_SPI2_BASE 0x3f424000 #define DR_REG_SPI2_BASE 0x3f424000
#define DR_REG_SPI3_BASE 0x3f425000 #define DR_REG_SPI3_BASE 0x3f425000
#define DR_REG_SYSCON_BASE 0x3f426000 #define DR_REG_SYSCON_BASE 0x3f426000

View File

@ -0,0 +1,404 @@
// Copyright 2020 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.
#pragma once
#include <stdint.h>
#include "soc/soc.h"
#ifdef __cplusplus
extern "C" {
#endif
/** SYSTIMER_CONF_REG register
* Configure system timer clock
*/
#define SYSTIMER_CONF_REG (DR_REG_SYSTIMER_BASE + 0x0)
/** SYSTIMER_CLK_FO : R/W; bitpos: [0]; default: 0;
* system timer force clock enable
*/
#define SYSTIMER_CLK_FO (BIT(0))
#define SYSTIMER_CLK_FO_M (SYSTIMER_CLK_FO_V << SYSTIMER_CLK_FO_S)
#define SYSTIMER_CLK_FO_V 0x00000001
#define SYSTIMER_CLK_FO_S 0
/** SYSTIMER_CLK_EN : R/W; bitpos: [31]; default: 0;
* register clock enable
*/
#define SYSTIMER_CLK_EN (BIT(31))
#define SYSTIMER_CLK_EN_M (SYSTIMER_CLK_EN_V << SYSTIMER_CLK_EN_S)
#define SYSTIMER_CLK_EN_V 0x00000001
#define SYSTIMER_CLK_EN_S 31
/** SYSTIMER_LOAD_REG register
* load value to system timer
*/
#define SYSTIMER_LOAD_REG (DR_REG_SYSTIMER_BASE + 0x4)
/** SYSTIMER_TIMER_LOAD : WO; bitpos: [31]; default: 0;
* load value to system timer
*/
#define SYSTIMER_TIMER_LOAD (BIT(31))
#define SYSTIMER_TIMER_LOAD_M (SYSTIMER_TIMER_LOAD_V << SYSTIMER_TIMER_LOAD_S)
#define SYSTIMER_TIMER_LOAD_V 0x00000001
#define SYSTIMER_TIMER_LOAD_S 31
/** SYSTIMER_LOAD_HI_REG register
* High 32-bit load to system timer
*/
#define SYSTIMER_LOAD_HI_REG (DR_REG_SYSTIMER_BASE + 0x8)
/** SYSTIMER_TIMER_LOAD_HI : R/W; bitpos: [32:0]; default: 0;
* High 32-bit load to system timer
*/
#define SYSTIMER_TIMER_LOAD_HI 0xFFFFFFFF
#define SYSTIMER_TIMER_LOAD_HI_M (SYSTIMER_TIMER_LOAD_HI_V << SYSTIMER_TIMER_LOAD_HI_S)
#define SYSTIMER_TIMER_LOAD_HI_V 0xFFFFFFFF
#define SYSTIMER_TIMER_LOAD_HI_S 0
/** SYSTIMER_LOAD_LO_REG register
* Low 32-bit load to system timer
*/
#define SYSTIMER_LOAD_LO_REG (DR_REG_SYSTIMER_BASE + 0xc)
/** SYSTIMER_TIMER_LOAD_LO : R/W; bitpos: [32:0]; default: 0;
* Low 32-bit load to system timer
*/
#define SYSTIMER_TIMER_LOAD_LO 0xFFFFFFFF
#define SYSTIMER_TIMER_LOAD_LO_M (SYSTIMER_TIMER_LOAD_LO_V << SYSTIMER_TIMER_LOAD_LO_S)
#define SYSTIMER_TIMER_LOAD_LO_V 0xFFFFFFFF
#define SYSTIMER_TIMER_LOAD_LO_S 0
/** SYSTIMER_STEP_REG register
* system timer accumulation step
*/
#define SYSTIMER_STEP_REG (DR_REG_SYSTIMER_BASE + 0x10)
/** SYSTIMER_TIMER_XTAL_STEP : R/W; bitpos: [10:0]; default: 80;
* system timer accumulation step when using XTAL
*/
#define SYSTIMER_TIMER_XTAL_STEP 0x000003FF
#define SYSTIMER_TIMER_XTAL_STEP_M (SYSTIMER_TIMER_XTAL_STEP_V << SYSTIMER_TIMER_XTAL_STEP_S)
#define SYSTIMER_TIMER_XTAL_STEP_V 0x000003FF
#define SYSTIMER_TIMER_XTAL_STEP_S 0
/** SYSTIMER_TIMER_PLL_STEP : R/W; bitpos: [20:10]; default: 1;
* system timer accumulation step when using PLL
*/
#define SYSTIMER_TIMER_PLL_STEP 0x000003FF
#define SYSTIMER_TIMER_PLL_STEP_M (SYSTIMER_TIMER_PLL_STEP_V << SYSTIMER_TIMER_PLL_STEP_S)
#define SYSTIMER_TIMER_PLL_STEP_V 0x000003FF
#define SYSTIMER_TIMER_PLL_STEP_S 10
/** SYSTIMER_TARGET0_HI_REG register
* System timer target0 high 32-bit
*/
#define SYSTIMER_TARGET0_HI_REG (DR_REG_SYSTIMER_BASE + 0x14)
/** SYSTIMER_TIMER_TARGET0_HI : R/W; bitpos: [32:0]; default: 0;
* System timer target0 high 32-bit
*/
#define SYSTIMER_TIMER_TARGET0_HI 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET0_HI_M (SYSTIMER_TIMER_TARGET0_HI_V << SYSTIMER_TIMER_TARGET0_HI_S)
#define SYSTIMER_TIMER_TARGET0_HI_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET0_HI_S 0
/** SYSTIMER_TARGET0_LO_REG register
* System timer target0 low 32-bit
*/
#define SYSTIMER_TARGET0_LO_REG (DR_REG_SYSTIMER_BASE + 0x18)
/** SYSTIMER_TIMER_TARGET0_LO : R/W; bitpos: [32:0]; default: 0;
* System timer target0 low 32-bit
*/
#define SYSTIMER_TIMER_TARGET0_LO 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET0_LO_M (SYSTIMER_TIMER_TARGET0_LO_V << SYSTIMER_TIMER_TARGET0_LO_S)
#define SYSTIMER_TIMER_TARGET0_LO_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET0_LO_S 0
/** SYSTIMER_TARGET1_HI_REG register
* System timer target1 high 32-bit
*/
#define SYSTIMER_TARGET1_HI_REG (DR_REG_SYSTIMER_BASE + 0x1c)
/** SYSTIMER_TIMER_TARGET1_HI : R/W; bitpos: [32:0]; default: 0;
* System timer target1 high 32-bit
*/
#define SYSTIMER_TIMER_TARGET1_HI 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET1_HI_M (SYSTIMER_TIMER_TARGET1_HI_V << SYSTIMER_TIMER_TARGET1_HI_S)
#define SYSTIMER_TIMER_TARGET1_HI_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET1_HI_S 0
/** SYSTIMER_TARGET1_LO_REG register
* System timer target1 low 32-bit
*/
#define SYSTIMER_TARGET1_LO_REG (DR_REG_SYSTIMER_BASE + 0x20)
/** SYSTIMER_TIMER_TARGET1_LO : R/W; bitpos: [32:0]; default: 0;
* System timer target1 low 32-bit
*/
#define SYSTIMER_TIMER_TARGET1_LO 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET1_LO_M (SYSTIMER_TIMER_TARGET1_LO_V << SYSTIMER_TIMER_TARGET1_LO_S)
#define SYSTIMER_TIMER_TARGET1_LO_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET1_LO_S 0
/** SYSTIMER_TARGET2_HI_REG register
* System timer target2 high 32-bit
*/
#define SYSTIMER_TARGET2_HI_REG (DR_REG_SYSTIMER_BASE + 0x24)
/** SYSTIMER_TIMER_TARGET2_HI : R/W; bitpos: [32:0]; default: 0;
* System timer target2 high 32-bit
*/
#define SYSTIMER_TIMER_TARGET2_HI 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET2_HI_M (SYSTIMER_TIMER_TARGET2_HI_V << SYSTIMER_TIMER_TARGET2_HI_S)
#define SYSTIMER_TIMER_TARGET2_HI_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET2_HI_S 0
/** SYSTIMER_TARGET2_LO_REG register
* System timer target2 low 32-bit
*/
#define SYSTIMER_TARGET2_LO_REG (DR_REG_SYSTIMER_BASE + 0x28)
/** SYSTIMER_TIMER_TARGET2_LO : R/W; bitpos: [32:0]; default: 0;
* System timer target2 low 32-bit
*/
#define SYSTIMER_TIMER_TARGET2_LO 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET2_LO_M (SYSTIMER_TIMER_TARGET2_LO_V << SYSTIMER_TIMER_TARGET2_LO_S)
#define SYSTIMER_TIMER_TARGET2_LO_V 0xFFFFFFFF
#define SYSTIMER_TIMER_TARGET2_LO_S 0
/** SYSTIMER_TARGET0_CONF_REG register
* Configure system timer target0 work mode
*/
#define SYSTIMER_TARGET0_CONF_REG (DR_REG_SYSTIMER_BASE + 0x2c)
/** SYSTIMER_TARGET0_PERIOD : R/W; bitpos: [30:0]; default: 0;
* System timer target0 alarm period
*/
#define SYSTIMER_TARGET0_PERIOD 0x3FFFFFFF
#define SYSTIMER_TARGET0_PERIOD_M (SYSTIMER_TARGET0_PERIOD_V << SYSTIMER_TARGET0_PERIOD_S)
#define SYSTIMER_TARGET0_PERIOD_V 0x3FFFFFFF
#define SYSTIMER_TARGET0_PERIOD_S 0
/** SYSTIMER_TARGET0_PERIOD_MODE : R/W; bitpos: [30]; default: 0;
* Whether system timer target0 work in period mode
*/
#define SYSTIMER_TARGET0_PERIOD_MODE (BIT(30))
#define SYSTIMER_TARGET0_PERIOD_MODE_M (SYSTIMER_TARGET0_PERIOD_MODE_V << SYSTIMER_TARGET0_PERIOD_MODE_S)
#define SYSTIMER_TARGET0_PERIOD_MODE_V 0x00000001
#define SYSTIMER_TARGET0_PERIOD_MODE_S 30
/** SYSTIMER_TARGET0_WORK_EN : R/W; bitpos: [31]; default: 0;
* system timer target0 work enable
*/
#define SYSTIMER_TARGET0_WORK_EN (BIT(31))
#define SYSTIMER_TARGET0_WORK_EN_M (SYSTIMER_TARGET0_WORK_EN_V << SYSTIMER_TARGET0_WORK_EN_S)
#define SYSTIMER_TARGET0_WORK_EN_V 0x00000001
#define SYSTIMER_TARGET0_WORK_EN_S 31
/** SYSTIMER_TARGET1_CONF_REG register
* Configure system timer target1 work mode
*/
#define SYSTIMER_TARGET1_CONF_REG (DR_REG_SYSTIMER_BASE + 0x30)
/** SYSTIMER_TARGET1_PERIOD : R/W; bitpos: [30:0]; default: 0;
* System timer target1 alarm period
*/
#define SYSTIMER_TARGET1_PERIOD 0x3FFFFFFF
#define SYSTIMER_TARGET1_PERIOD_M (SYSTIMER_TARGET1_PERIOD_V << SYSTIMER_TARGET1_PERIOD_S)
#define SYSTIMER_TARGET1_PERIOD_V 0x3FFFFFFF
#define SYSTIMER_TARGET1_PERIOD_S 0
/** SYSTIMER_TARGET1_PERIOD_MODE : R/W; bitpos: [30]; default: 0;
* Whether system timer target1 work in period mode
*/
#define SYSTIMER_TARGET1_PERIOD_MODE (BIT(30))
#define SYSTIMER_TARGET1_PERIOD_MODE_M (SYSTIMER_TARGET1_PERIOD_MODE_V << SYSTIMER_TARGET1_PERIOD_MODE_S)
#define SYSTIMER_TARGET1_PERIOD_MODE_V 0x00000001
#define SYSTIMER_TARGET1_PERIOD_MODE_S 30
/** SYSTIMER_TARGET1_WORK_EN : R/W; bitpos: [31]; default: 0;
* system timer target1 work enable
*/
#define SYSTIMER_TARGET1_WORK_EN (BIT(31))
#define SYSTIMER_TARGET1_WORK_EN_M (SYSTIMER_TARGET1_WORK_EN_V << SYSTIMER_TARGET1_WORK_EN_S)
#define SYSTIMER_TARGET1_WORK_EN_V 0x00000001
#define SYSTIMER_TARGET1_WORK_EN_S 31
/** SYSTIMER_TARGET2_CONF_REG register
* Configure system timer target2 work mode
*/
#define SYSTIMER_TARGET2_CONF_REG (DR_REG_SYSTIMER_BASE + 0x34)
/** SYSTIMER_TARGET2_PERIOD : R/W; bitpos: [30:0]; default: 0;
* System timer target2 alarm period
*/
#define SYSTIMER_TARGET2_PERIOD 0x3FFFFFFF
#define SYSTIMER_TARGET2_PERIOD_M (SYSTIMER_TARGET2_PERIOD_V << SYSTIMER_TARGET2_PERIOD_S)
#define SYSTIMER_TARGET2_PERIOD_V 0x3FFFFFFF
#define SYSTIMER_TARGET2_PERIOD_S 0
/** SYSTIMER_TARGET2_PERIOD_MODE : R/W; bitpos: [30]; default: 0;
* Whether system timer target2 work in period mode
*/
#define SYSTIMER_TARGET2_PERIOD_MODE (BIT(30))
#define SYSTIMER_TARGET2_PERIOD_MODE_M (SYSTIMER_TARGET2_PERIOD_MODE_V << SYSTIMER_TARGET2_PERIOD_MODE_S)
#define SYSTIMER_TARGET2_PERIOD_MODE_V 0x00000001
#define SYSTIMER_TARGET2_PERIOD_MODE_S 30
/** SYSTIMER_TARGET2_WORK_EN : R/W; bitpos: [31]; default: 0;
* system timer target2 work enable
*/
#define SYSTIMER_TARGET2_WORK_EN (BIT(31))
#define SYSTIMER_TARGET2_WORK_EN_M (SYSTIMER_TARGET2_WORK_EN_V << SYSTIMER_TARGET2_WORK_EN_S)
#define SYSTIMER_TARGET2_WORK_EN_V 0x00000001
#define SYSTIMER_TARGET2_WORK_EN_S 31
/** SYSTIMER_UPDATE_REG register
* Read out system timer value
*/
#define SYSTIMER_UPDATE_REG (DR_REG_SYSTIMER_BASE + 0x38)
/** SYSTIMER_TIMER_VALUE_VALID : RO; bitpos: [30]; default: 0;
* If it is valid to read out timer value from register
*/
#define SYSTIMER_TIMER_VALUE_VALID (BIT(30))
#define SYSTIMER_TIMER_VALUE_VALID_M (SYSTIMER_TIMER_VALUE_VALID_V << SYSTIMER_TIMER_VALUE_VALID_S)
#define SYSTIMER_TIMER_VALUE_VALID_V 0x00000001
#define SYSTIMER_TIMER_VALUE_VALID_S 30
/** SYSTIMER_TIMER_UPDATE : WO; bitpos: [31]; default: 0;
* Update system timer value to register
*/
#define SYSTIMER_TIMER_UPDATE (BIT(31))
#define SYSTIMER_TIMER_UPDATE_M (SYSTIMER_TIMER_UPDATE_V << SYSTIMER_TIMER_UPDATE_S)
#define SYSTIMER_TIMER_UPDATE_V 0x00000001
#define SYSTIMER_TIMER_UPDATE_S 31
/** SYSTIMER_VALUE_HI_REG register
* system timer high 32-bit
*/
#define SYSTIMER_VALUE_HI_REG (DR_REG_SYSTIMER_BASE + 0x3c)
/** SYSTIMER_TIMER_VALUE_HI : RO; bitpos: [32:0]; default: 0;
* system timer high 32-bit
*/
#define SYSTIMER_TIMER_VALUE_HI 0xFFFFFFFF
#define SYSTIMER_TIMER_VALUE_HI_M (SYSTIMER_TIMER_VALUE_HI_V << SYSTIMER_TIMER_VALUE_HI_S)
#define SYSTIMER_TIMER_VALUE_HI_V 0xFFFFFFFF
#define SYSTIMER_TIMER_VALUE_HI_S 0
/** SYSTIMER_VALUE_LO_REG register
* system timer low 32-bit
*/
#define SYSTIMER_VALUE_LO_REG (DR_REG_SYSTIMER_BASE + 0x40)
/** SYSTIMER_TIMER_VALUE_LO : RO; bitpos: [32:0]; default: 0;
* system timer low 32-bit
*/
#define SYSTIMER_TIMER_VALUE_LO 0xFFFFFFFF
#define SYSTIMER_TIMER_VALUE_LO_M (SYSTIMER_TIMER_VALUE_LO_V << SYSTIMER_TIMER_VALUE_LO_S)
#define SYSTIMER_TIMER_VALUE_LO_V 0xFFFFFFFF
#define SYSTIMER_TIMER_VALUE_LO_S 0
/** SYSTIMER_INT_ENA_REG register
* system timer interrupt enable
*/
#define SYSTIMER_INT_ENA_REG (DR_REG_SYSTIMER_BASE + 0x44)
/** SYSTIMER_INT0_ENA : R/W; bitpos: [0]; default: 0;
* system timer target0 interrupt enable
*/
#define SYSTIMER_INT0_ENA (BIT(0))
#define SYSTIMER_INT0_ENA_M (SYSTIMER_INT0_ENA_V << SYSTIMER_INT0_ENA_S)
#define SYSTIMER_INT0_ENA_V 0x00000001
#define SYSTIMER_INT0_ENA_S 0
/** SYSTIMER_INT1_ENA : R/W; bitpos: [1]; default: 0;
* system timer target1 interrupt enable
*/
#define SYSTIMER_INT1_ENA (BIT(1))
#define SYSTIMER_INT1_ENA_M (SYSTIMER_INT1_ENA_V << SYSTIMER_INT1_ENA_S)
#define SYSTIMER_INT1_ENA_V 0x00000001
#define SYSTIMER_INT1_ENA_S 1
/** SYSTIMER_INT2_ENA : R/W; bitpos: [2]; default: 0;
* system timer target2 interrupt enable
*/
#define SYSTIMER_INT2_ENA (BIT(2))
#define SYSTIMER_INT2_ENA_M (SYSTIMER_INT2_ENA_V << SYSTIMER_INT2_ENA_S)
#define SYSTIMER_INT2_ENA_V 0x00000001
#define SYSTIMER_INT2_ENA_S 2
/** SYSTIMER_INT_RAW_REG register
* system timer interrupt raw
*/
#define SYSTIMER_INT_RAW_REG (DR_REG_SYSTIMER_BASE + 0x48)
/** SYSTIMER_INT0_RAW : RO; bitpos: [0]; default: 0;
* system timer target0 interrupt raw
*/
#define SYSTIMER_INT0_RAW (BIT(0))
#define SYSTIMER_INT0_RAW_M (SYSTIMER_INT0_RAW_V << SYSTIMER_INT0_RAW_S)
#define SYSTIMER_INT0_RAW_V 0x00000001
#define SYSTIMER_INT0_RAW_S 0
/** SYSTIMER_INT1_RAW : RO; bitpos: [1]; default: 0;
* system timer target1 interrupt raw
*/
#define SYSTIMER_INT1_RAW (BIT(1))
#define SYSTIMER_INT1_RAW_M (SYSTIMER_INT1_RAW_V << SYSTIMER_INT1_RAW_S)
#define SYSTIMER_INT1_RAW_V 0x00000001
#define SYSTIMER_INT1_RAW_S 1
/** SYSTIMER_INT2_RAW : RO; bitpos: [2]; default: 0;
* system timer target2 interrupt raw
*/
#define SYSTIMER_INT2_RAW (BIT(2))
#define SYSTIMER_INT2_RAW_M (SYSTIMER_INT2_RAW_V << SYSTIMER_INT2_RAW_S)
#define SYSTIMER_INT2_RAW_V 0x00000001
#define SYSTIMER_INT2_RAW_S 2
/** SYSTIMER_INT_CLR_REG register
* system timer interrupt clear
*/
#define SYSTIMER_INT_CLR_REG (DR_REG_SYSTIMER_BASE + 0x4c)
/** SYSTIMER_INT0_CLR : WO; bitpos: [0]; default: 0;
* system timer target0 interrupt clear
*/
#define SYSTIMER_INT0_CLR (BIT(0))
#define SYSTIMER_INT0_CLR_M (SYSTIMER_INT0_CLR_V << SYSTIMER_INT0_CLR_S)
#define SYSTIMER_INT0_CLR_V 0x00000001
#define SYSTIMER_INT0_CLR_S 0
/** SYSTIMER_INT1_CLR : WO; bitpos: [1]; default: 0;
* system timer target1 interrupt clear
*/
#define SYSTIMER_INT1_CLR (BIT(1))
#define SYSTIMER_INT1_CLR_M (SYSTIMER_INT1_CLR_V << SYSTIMER_INT1_CLR_S)
#define SYSTIMER_INT1_CLR_V 0x00000001
#define SYSTIMER_INT1_CLR_S 1
/** SYSTIMER_INT2_CLR : WO; bitpos: [2]; default: 0;
* system timer target2 interrupt clear
*/
#define SYSTIMER_INT2_CLR (BIT(2))
#define SYSTIMER_INT2_CLR_M (SYSTIMER_INT2_CLR_V << SYSTIMER_INT2_CLR_S)
#define SYSTIMER_INT2_CLR_V 0x00000001
#define SYSTIMER_INT2_CLR_S 2
/** SYSTIMER_DATE_REG register
* system timer register version
*/
#define SYSTIMER_DATE_REG (DR_REG_SYSTIMER_BASE + 0xfc)
/** SYSTIMER_DATE : R/W; bitpos: [32:0]; default: 25194848;
* system timer register version
*/
#define SYSTIMER_DATE 0xFFFFFFFF
#define SYSTIMER_DATE_M (SYSTIMER_DATE_V << SYSTIMER_DATE_S)
#define SYSTIMER_DATE_V 0xFFFFFFFF
#define SYSTIMER_DATE_S 0
#ifdef __cplusplus
}
#endif

View File

@ -19,7 +19,7 @@ idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include INCLUDE_DIRS include
PRIV_INCLUDE_DIRS src proto-c ../protocomm/proto-c PRIV_INCLUDE_DIRS src proto-c ../protocomm/proto-c
REQUIRES lwip protocomm REQUIRES lwip protocomm
PRIV_REQUIRES protobuf-c bt mdns json) PRIV_REQUIRES protobuf-c bt mdns json esp_timer)
# To avoid warning for strncpy # To avoid warning for strncpy
set_source_files_properties(src/handlers.c src/scheme_softap.c set_source_files_properties(src/handlers.c src/scheme_softap.c

View File

@ -90,7 +90,7 @@ set(srcs "port/os_xtensa.c"
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include port/include include/esp_supplicant INCLUDE_DIRS include port/include include/esp_supplicant
PRIV_INCLUDE_DIRS src PRIV_INCLUDE_DIRS src
PRIV_REQUIRES mbedtls) PRIV_REQUIRES mbedtls esp_timer)
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing)
target_compile_definitions(${COMPONENT_LIB} PRIVATE target_compile_definitions(${COMPONENT_LIB} PRIVATE

View File

@ -256,7 +256,7 @@ INPUT = \
../../components/esp_common/include/esp_pm.h \ ../../components/esp_common/include/esp_pm.h \
../../components/esp32/include/esp32/pm.h \ ../../components/esp32/include/esp32/pm.h \
### esp_timer, High Resolution Timer ### esp_timer, High Resolution Timer
../../components/esp_common/include/esp_timer.h \ ../../components/esp_timer/include/esp_timer.h \
### esp_event, Event Loop Library ### esp_event, Event Loop Library
../../components/esp_event/include/esp_event.h \ ../../components/esp_event/include/esp_event.h \
../../components/esp_event/include/esp_event_base.h \ ../../components/esp_event/include/esp_event_base.h \

View File

@ -11,7 +11,15 @@ Although FreeRTOS provides software timers, these timers have a few limitations:
Hardware timers are free from both of the limitations, but often they are less convenient to use. For example, application components may need timer events to fire at certain times in the future, but the hardware timer only contains one "compare" value used for interrupt generation. This means that some facility needs to be built on top of the hardware timer to manage the list of pending events can dispatch the callbacks for these events as corresponding hardware interrupts happen. Hardware timers are free from both of the limitations, but often they are less convenient to use. For example, application components may need timer events to fire at certain times in the future, but the hardware timer only contains one "compare" value used for interrupt generation. This means that some facility needs to be built on top of the hardware timer to manage the list of pending events can dispatch the callbacks for these events as corresponding hardware interrupts happen.
``esp_timer`` set of APIs provide such facility. Internally, ``esp_timer`` uses a 32-bit hardware timer (FRC1, "legacy" timer). ``esp_timer`` provides one-shot and periodic timers, microsecond time resolution, and 64-bit range. ``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 :ref:`CONFIG_ESP_TIMER_IMPL`:
- LAC timer (ESP32)
- (legacy) FRC2 timer (ESP32)
- SYSTIMER for (ESP32-S2)
.. 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.
Timer callbacks are dispatched from a high-priority ``esp_timer`` task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower priority task using a queue instead. Timer callbacks are dispatched from a high-priority ``esp_timer`` task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower priority task using a queue instead.

View File

@ -1 +1 @@
TEST_COMPONENTS=freertos esp32 driver heap pthread soc spi_flash vfs TEST_COMPONENTS=freertos esp32 esp_timer driver heap pthread soc spi_flash vfs

View File

@ -1 +1 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 driver heap pthread soc spi_flash vfs test_utils TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_timer driver heap pthread soc spi_flash vfs test_utils

View File

@ -1,2 +1,2 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 driver heap pthread soc spi_flash vfs TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"

View File

@ -1,2 +1,2 @@
TEST_COMPONENTS=freertos esp32s2 driver heap pthread soc spi_flash vfs TEST_COMPONENTS=freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"

View File

@ -1,2 +1,2 @@
TEST_COMPONENTS=driver esp32 spi_flash TEST_COMPONENTS=driver esp32 esp_timer spi_flash
CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y

View File

@ -1,3 +1,3 @@
TEST_COMPONENTS=driver esp32s2 spi_flash TEST_COMPONENTS=driver esp32s2 esp_timer spi_flash
CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"

View File

@ -1,4 +1,4 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update driver esp32 freertos mbedtls spi_flash test_utils TEST_EXCLUDE_COMPONENTS=libsodium bt app_update driver esp32 esp_timer freertos mbedtls spi_flash test_utils
CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
CONFIG_SPIRAM_OCCUPY_NO_HOST=y CONFIG_SPIRAM_OCCUPY_NO_HOST=y

View File

@ -1,4 +1,4 @@
TEST_COMPONENTS=driver esp32 freertos mbedtls spi_flash TEST_COMPONENTS=driver esp32 esp_timer freertos mbedtls spi_flash
CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
CONFIG_SPIRAM_OCCUPY_NO_HOST=y CONFIG_SPIRAM_OCCUPY_NO_HOST=y

View File

@ -1,4 +1,4 @@
TEST_COMPONENTS=esp32 TEST_COMPONENTS=esp32 esp_timer
CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_BANKSWITCH_ENABLE=y CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 CONFIG_SPIRAM_BANKSWITCH_RESERVE=8

View File

@ -1,3 +1,3 @@
TEST_COMPONENTS=freertos esp32 driver heap pthread soc spi_flash vfs TEST_COMPONENTS=freertos esp32 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -1,3 +1,3 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 driver heap pthread soc spi_flash vfs test_utils TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_timer driver heap pthread soc spi_flash vfs test_utils
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -1,4 +1,4 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 driver heap pthread soc spi_flash vfs test_utils TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs test_utils
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"

View File

@ -1,4 +1,4 @@
TEST_COMPONENTS=freertos esp32s2 driver heap pthread soc spi_flash vfs TEST_COMPONENTS=freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"

View File

@ -1,4 +1,4 @@
TEST_COMPONENTS=freertos esp32 driver heap pthread soc spi_flash vfs TEST_COMPONENTS=freertos esp32 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_MEMMAP_SMP=n CONFIG_MEMMAP_SMP=n
CONFIG_FREERTOS_UNICORE=y CONFIG_FREERTOS_UNICORE=y
CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y

View File

@ -1,4 +1,4 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 driver heap pthread soc spi_flash vfs test_utils TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32 esp_timer driver heap pthread soc spi_flash vfs test_utils
CONFIG_MEMMAP_SMP=n CONFIG_MEMMAP_SMP=n
CONFIG_FREERTOS_UNICORE=y CONFIG_FREERTOS_UNICORE=y
CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y

View File

@ -1,4 +1,4 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 driver heap pthread soc spi_flash vfs TEST_EXCLUDE_COMPONENTS=libsodium bt app_update freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs
CONFIG_MEMMAP_SMP=n CONFIG_MEMMAP_SMP=n
CONFIG_FREERTOS_UNICORE=y CONFIG_FREERTOS_UNICORE=y
CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y

View File

@ -1,4 +1,4 @@
TEST_COMPONENTS=freertos esp32s2 driver heap pthread soc spi_flash vfs test_utils TEST_COMPONENTS=freertos esp32s2 esp_timer driver heap pthread soc spi_flash vfs test_utils
CONFIG_MEMMAP_SMP=n CONFIG_MEMMAP_SMP=n
CONFIG_FREERTOS_UNICORE=y CONFIG_FREERTOS_UNICORE=y
CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y