diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 11bce0f3b..f00cd2142 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -539,7 +539,7 @@ endif() idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" PRIV_INCLUDE_DIRS "${priv_include_dirs}" - REQUIRES nvs_flash soc) + REQUIRES nvs_flash soc esp_timer) if(CONFIG_BT_ENABLED) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-const-variable) diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 8910e76a7..5163c546c 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -44,7 +44,7 @@ endif() idf_component_register(SRCS "${srcs}" INCLUDE_DIRS ${includes} 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 # uses C11 atomic feature diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index a32d57c54..8a9c9a625 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -69,6 +69,8 @@ static uint32_t get_clk_en_mask(periph_module_t periph) #elif CONFIG_IDF_TARGET_ESP32S2 case PERIPH_USB_MODULE: return DPORT_USB_CLK_EN; + case PERIPH_SYSTIMER_MODULE: + return DPORT_SYSTIMER_CLK_EN; #endif case PERIPH_I2C0_MODULE: 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 case PERIPH_USB_MODULE: return DPORT_USB_RST; + case PERIPH_SYSTIMER_MODULE: + return DPORT_SYSTIMER_RST; #endif case PERIPH_I2C0_MODULE: return DPORT_I2C_EXT0_RST; diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 3f408b1ae..ed15a774f 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -19,7 +19,6 @@ else() "crosscore_int.c" "dport_access.c" "dport_panic_highint_hdl.S" - "esp_timer_esp32.c" "esp_himem.c" "hw_random.c" "int_wdt.c" @@ -33,14 +32,16 @@ else() "spiram_psram.c" "system_api_esp32.c" "task_wdt.c") + set(include_dirs "include") 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 # 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 - spi_flash vfs espcoredump esp_common perfmon) + spi_flash vfs espcoredump esp_common perfmon esp_timer) set(fragments linker.lf ld/esp32_fragments.lf) idf_component_register(SRCS "${srcs}" diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index b1f8b360b..fe8310f8a 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -454,8 +454,7 @@ menu "ESP32-specific" 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 - # high resolution timekeeping anymore. Instead the esp_timer API, implemented - # using FRC2 timer, is used. + # high resolution timekeeping anymore. Instead the esp_timer API is used. # FRC1 name in the option name is kept for compatibility. choice ESP32_TIME_SYSCALL prompt "Timers used for gettimeofday function" @@ -732,7 +731,7 @@ menu "Power Management" config PM_USE_RTC_TIMER_REF bool "Use RTC timer to prevent time drift (EXPERIMENTAL)" - depends on PM_ENABLE && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1) + depends on PM_ENABLE && ESP_TIMER_IMPL_FRC2 && (ESP32_TIME_SYSCALL_USE_RTC || ESP32_TIME_SYSCALL_USE_RTC_FRC1) default n help When APB clock frequency changes, high-resolution timer (esp_timer) diff --git a/components/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index 80008b745..3c7019bb2 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -33,7 +33,7 @@ #include "esp_private/pm_impl.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 "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); /* Update APB frequency value used by the timer */ 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 */ diff --git a/components/esp32/sleep_modes.c b/components/esp32/sleep_modes.c index f280396b0..175a217b4 100644 --- a/components/esp32/sleep_modes.c +++ b/components/esp32/sleep_modes.c @@ -17,7 +17,7 @@ #include #include "esp_attr.h" #include "esp_sleep.h" -#include "esp_private/esp_timer_impl.h" +#include "esp_private/esp_timer_private.h" #include "esp_log.h" #include "esp32/clk.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; 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 * lock, otherwise there will be deadlock. */ - esp_timer_impl_lock(); + esp_timer_private_lock(); s_config.rtc_ticks_at_sleep_start = rtc_time_get(); uint64_t frc_time_at_start = esp_timer_get_time(); DPORT_STALL_OTHER_CPU_START(); @@ -347,11 +347,11 @@ esp_err_t esp_light_sleep_start(void) * monotonic. */ if (time_diff > 0) { - esp_timer_impl_advance(time_diff); + esp_timer_private_advance(time_diff); } esp_set_time_from_rtc(); - esp_timer_impl_unlock(); + esp_timer_private_unlock(); DPORT_STALL_OTHER_CPU_END(); if (!wdt_was_enabled) { rtc_wdt_disable(); diff --git a/components/esp32/test/CMakeLists.txt b/components/esp32/test/CMakeLists.txt index 74ed23aee..d9f57f462 100644 --- a/components/esp32/test/CMakeLists.txt +++ b/components/esp32/test/CMakeLists.txt @@ -13,7 +13,6 @@ if(IDF_TARGET STREQUAL "esp32") 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") endif() diff --git a/components/esp32s2/CMakeLists.txt b/components/esp32s2/CMakeLists.txt index 45b82a747..940e8d1c9 100644 --- a/components/esp32s2/CMakeLists.txt +++ b/components/esp32s2/CMakeLists.txt @@ -17,7 +17,6 @@ else() "crosscore_int.c" "dport_access.c" "dport_panic_highint_hdl.S" - "esp_timer_esp32s2.c" "hw_random.c" "int_wdt.c" "intr_alloc.c" @@ -30,15 +29,17 @@ else() "spiram_psram.c" "system_api_esp32s2.c" "task_wdt.c") + set(include_dirs "include") 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 # 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 spi_flash vfs espcoredump esp_common) + pthread spi_flash vfs espcoredump esp_common esp_timer) set(fragments linker.lf ld/esp32s2_fragments.lf) diff --git a/components/esp32s2/esp_timer_esp32s2.c b/components/esp32s2/esp_timer_esp32s2.c deleted file mode 100644 index d5637b562..000000000 --- a/components/esp32s2/esp_timer_esp32s2.c +++ /dev/null @@ -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 diff --git a/components/esp32s2/pm_esp32s2.c b/components/esp32s2/pm_esp32s2.c index a6735c0ea..7368c7b90 100644 --- a/components/esp32s2/pm_esp32s2.c +++ b/components/esp32s2/pm_esp32s2.c @@ -33,7 +33,7 @@ #include "esp_private/pm_impl.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" /* 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); /* Update APB frequency value used by the timer */ 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 */ diff --git a/components/esp32s2/sleep_modes.c b/components/esp32s2/sleep_modes.c index 996f05032..529b14085 100644 --- a/components/esp32s2/sleep_modes.c +++ b/components/esp32s2/sleep_modes.c @@ -17,7 +17,7 @@ #include #include "esp_attr.h" #include "esp_sleep.h" -#include "esp_private/esp_timer_impl.h" +#include "esp_private/esp_timer_private.h" #include "esp_log.h" #include "esp32s2/clk.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; 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 * lock, otherwise there will be deadlock. */ - esp_timer_impl_lock(); + esp_timer_private_lock(); s_config.rtc_ticks_at_sleep_start = rtc_time_get(); uint64_t frc_time_at_start = esp_timer_get_time(); DPORT_STALL_OTHER_CPU_START(); @@ -331,11 +331,11 @@ esp_err_t esp_light_sleep_start(void) * monotonic. */ if (time_diff > 0) { - esp_timer_impl_advance(time_diff); + esp_timer_private_advance(time_diff); } esp_set_time_from_rtc(); - esp_timer_impl_unlock(); + esp_timer_private_unlock(); DPORT_STALL_OTHER_CPU_END(); if (!wdt_was_enabled) { rtc_wdt_disable(); diff --git a/components/esp32s2/test/CMakeLists.txt b/components/esp32s2/test/CMakeLists.txt index 30d3a2297..41f430e49 100644 --- a/components/esp32s2/test/CMakeLists.txt +++ b/components/esp32s2/test/CMakeLists.txt @@ -13,7 +13,6 @@ if(IDF_TARGET STREQUAL "esp32s2") 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") endif() diff --git a/components/esp_common/CMakeLists.txt b/components/esp_common/CMakeLists.txt index 125400e74..185d8f7e6 100644 --- a/components/esp_common/CMakeLists.txt +++ b/components/esp_common/CMakeLists.txt @@ -9,8 +9,6 @@ else() set(srcs "src/brownout.c" "src/dbg_stubs.c" "src/esp_err_to_name.c" - "src/esp_timer.c" - "src/ets_timer_legacy.c" "src/freertos_hooks.c" "src/mac_addr.c" "src/pm_locks.c" @@ -24,7 +22,7 @@ else() idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include - REQUIRES ${target} + REQUIRES ${target} esp_timer PRIV_REQUIRES soc) set_source_files_properties( diff --git a/components/esp_common/Kconfig b/components/esp_common/Kconfig index a3f617b30..d394393b8 100644 --- a/components/esp_common/Kconfig +++ b/components/esp_common/Kconfig @@ -1,14 +1,5 @@ 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 bool "Enable lookup of error code strings" 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 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 int "Minimal allowed size for shared stack" default 2048 diff --git a/components/esp_common/sdkconfig.rename b/components/esp_common/sdkconfig.rename index c920da0ef..2241ecf33 100644 --- a/components/esp_common/sdkconfig.rename +++ b/components/esp_common/sdkconfig.rename @@ -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_MAIN_TASK_STACK_SIZE CONFIG_ESP_MAIN_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_DEFAULT CONFIG_ESP_CONSOLE_UART_DEFAULT CONFIG_CONSOLE_UART_CUSTOM CONFIG_ESP_CONSOLE_UART_CUSTOM diff --git a/components/esp_event/CMakeLists.txt b/components/esp_event/CMakeLists.txt index 1a0ae0676..6384b8fe3 100644 --- a/components/esp_event/CMakeLists.txt +++ b/components/esp_event/CMakeLists.txt @@ -1,7 +1,7 @@ if(IDF_TARGET STREQUAL "esp32") - set(priv_requires esp_eth) + set(priv_requires esp_eth esp_timer) else() - set(priv_requires) + set(priv_requires esp_timer) endif() idf_component_register(SRCS "default_event_loop.c" diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt index 744e6b207..0755d8f81 100644 --- a/components/esp_http_server/CMakeLists.txt +++ b/components/esp_http_server/CMakeLists.txt @@ -7,4 +7,4 @@ idf_component_register(SRCS "src/httpd_main.c" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "src/port/esp32" "src/util" REQUIRES nghttp # for http_parser.h - PRIV_REQUIRES lwip) + PRIV_REQUIRES lwip esp_timer) diff --git a/components/esp_timer/CMakeLists.txt b/components/esp_timer/CMakeLists.txt new file mode 100644 index 000000000..68be83b9f --- /dev/null +++ b/components/esp_timer/CMakeLists.txt @@ -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}") diff --git a/components/esp_timer/Kconfig b/components/esp_timer/Kconfig new file mode 100644 index 000000000..15fd196bc --- /dev/null +++ b/components/esp_timer/Kconfig @@ -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 diff --git a/components/esp_timer/Makefile.projbuild b/components/esp_timer/Makefile.projbuild new file mode 100644 index 000000000..2ac174b9c --- /dev/null +++ b/components/esp_timer/Makefile.projbuild @@ -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 \ No newline at end of file diff --git a/components/esp_timer/component.mk b/components/esp_timer/component.mk new file mode 100644 index 000000000..43b72dec6 --- /dev/null +++ b/components/esp_timer/component.mk @@ -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 diff --git a/components/esp_timer/include/esp_private/esp_timer_private.h b/components/esp_timer/include/esp_private/esp_timer_private.h new file mode 100644 index 000000000..ecd426411 --- /dev/null +++ b/components/esp_timer/include/esp_private/esp_timer_private.h @@ -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 + +#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 diff --git a/components/esp_common/include/esp_timer.h b/components/esp_timer/include/esp_timer.h similarity index 99% rename from components/esp_common/include/esp_timer.h rename to components/esp_timer/include/esp_timer.h index 769c9fe29..1a39ef0a5 100644 --- a/components/esp_common/include/esp_timer.h +++ b/components/esp_timer/include/esp_timer.h @@ -226,7 +226,6 @@ int64_t esp_timer_get_next_alarm(void); */ esp_err_t esp_timer_dump(FILE* stream); - #ifdef __cplusplus } #endif diff --git a/components/esp_common/include/esp_private/esp_timer_impl.h b/components/esp_timer/private_include/esp_timer_impl.h similarity index 88% rename from components/esp_common/include/esp_private/esp_timer_impl.h rename to components/esp_timer/private_include/esp_timer_impl.h index bee1d0756..f398aaba7 100644 --- a/components/esp_common/include/esp_private/esp_timer_impl.h +++ b/components/esp_timer/private_include/esp_timer_impl.h @@ -15,7 +15,7 @@ #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. * @@ -99,3 +99,21 @@ void esp_timer_impl_lock(void); * @brief counterpart of esp_timer_impl_lock */ 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); diff --git a/components/esp_timer/sdkconfig.rename b/components/esp_timer/sdkconfig.rename new file mode 100644 index 000000000..2b68558a6 --- /dev/null +++ b/components/esp_timer/sdkconfig.rename @@ -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 diff --git a/components/esp_common/src/esp_timer.c b/components/esp_timer/src/esp_timer.c similarity index 99% rename from components/esp_common/src/esp_timer.c rename to components/esp_timer/src/esp_timer.c index 466cd5b71..28977c181 100644 --- a/components/esp_common/src/esp_timer.c +++ b/components/esp_timer/src/esp_timer.c @@ -18,16 +18,16 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_err.h" -#include "esp_timer.h" #include "esp_task.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "freertos/xtensa_api.h" +#include "esp_timer.h" +#include "esp_timer_impl.h" #include "sdkconfig.h" -#include "esp_private/esp_timer_impl.h" #ifdef CONFIG_ESP_TIMER_PROFILING #define WITH_PROFILING 1 diff --git a/components/esp32/esp_timer_esp32.c b/components/esp_timer/src/esp_timer_impl_frc_legacy.c similarity index 96% rename from components/esp32/esp_timer_esp32.c rename to components/esp_timer/src/esp_timer_impl_frc_legacy.c index cfb54556f..c1144a68b 100644 --- a/components/esp32/esp_timer_esp32.c +++ b/components/esp_timer/src/esp_timer_impl_frc_legacy.c @@ -13,15 +13,15 @@ // limitations under the License. #include "sys/param.h" -#include "esp_err.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 "esp_private/esp_timer_impl.h" #include "soc/frc_timer_reg.h" #include "soc/rtc.h" #include "freertos/FreeRTOS.h" @@ -29,7 +29,7 @@ #include "freertos/semphr.h" /** - * @file esp_timer_esp32.c + * @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 @@ -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); } #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"))); diff --git a/components/esp_timer/src/esp_timer_impl_lac.c b/components/esp_timer/src/esp_timer_impl_lac.c new file mode 100644 index 000000000..a61e740a3 --- /dev/null +++ b/components/esp_timer/src/esp_timer_impl_lac.c @@ -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"))); diff --git a/components/esp_timer/src/esp_timer_impl_systimer.c b/components/esp_timer/src/esp_timer_impl_systimer.c new file mode 100644 index 000000000..5bd712b3b --- /dev/null +++ b/components/esp_timer/src/esp_timer_impl_systimer.c @@ -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 +#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"))); diff --git a/components/esp_common/src/ets_timer_legacy.c b/components/esp_timer/src/ets_timer_legacy.c similarity index 99% rename from components/esp_common/src/ets_timer_legacy.c rename to components/esp_timer/src/ets_timer_legacy.c index a856b0cf4..3f35107e9 100644 --- a/components/esp_common/src/ets_timer_legacy.c +++ b/components/esp_timer/src/ets_timer_legacy.c @@ -31,7 +31,6 @@ #include "freertos/xtensa_api.h" #include "sdkconfig.h" #include "esp_timer.h" -#include "esp_private/esp_timer_impl.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/ets_sys.h" #elif CONFIG_IDF_TARGET_ESP32S2 diff --git a/components/esp_timer/test/CMakeLists.txt b/components/esp_timer/test/CMakeLists.txt new file mode 100644 index 000000000..e78a64e2a --- /dev/null +++ b/components/esp_timer/test/CMakeLists.txt @@ -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() \ No newline at end of file diff --git a/components/esp_timer/test/component.mk b/components/esp_timer/test/component.mk new file mode 100644 index 000000000..6c49a0ab2 --- /dev/null +++ b/components/esp_timer/test/component.mk @@ -0,0 +1,6 @@ +# +#Component Makefile +# + +COMPONENT_SRCDIRS := . +COMPONENT_PRIV_INCLUDEDIRS := ../private_include \ No newline at end of file diff --git a/components/esp32/test/test_esp_timer.c b/components/esp_timer/test/test_esp_timer.c similarity index 97% rename from components/esp32/test/test_esp_timer.c rename to components/esp_timer/test/test_esp_timer.c index f9b94a410..a820f04be 100644 --- a/components/esp32/test/test_esp_timer.c +++ b/components/esp_timer/test/test_esp_timer.c @@ -3,21 +3,23 @@ #include #include #include +#include "esp_timer.h" +#include "esp_timer_impl.h" #include "unity.h" #include "soc/frc_timer_reg.h" -#include "esp_timer.h" +#include "soc/timer_group_reg.h" #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "test_utils.h" -#include "esp_private/esp_timer_impl.h" #include "esp_freertos_hooks.h" #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); @@ -37,6 +39,17 @@ 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 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]") { - ref_clock_init(); int64_t t0 = esp_timer_get_time(); const int64_t diff_us = 1000000; esp_timer_impl_advance(diff_us); int64_t t1 = esp_timer_get_time(); 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); - 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]") { +#ifdef CONFIG_ESP_TIMER_IMPL_FRC2 REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); +#endif esp_timer_impl_advance(50331648); // 0xefffffff/80 = 50331647 ets_delay_us(2); portDISABLE_INTERRUPTS(); esp_timer_impl_set_alarm(50331647); - uint32_t alarm_reg = REG_READ(FRC_TIMER_ALARM_REG(1)); - uint32_t count_reg = REG_READ(FRC_TIMER_COUNT_REG(1)); + uint64_t alarm_reg = esp_timer_impl_get_alarm_reg(); + 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 - 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)); } diff --git a/components/esp32/test/test_ets_timer.c b/components/esp_timer/test/test_ets_timer.c similarity index 98% rename from components/esp32/test/test_ets_timer.c rename to components/esp_timer/test/test_ets_timer.c index 7316966c8..0c490d067 100644 --- a/components/esp32/test/test_ets_timer.c +++ b/components/esp_timer/test/test_ets_timer.c @@ -3,11 +3,15 @@ #include #include #include "unity.h" -#include "esp32/rom/ets_sys.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.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]") { diff --git a/components/esp_websocket_client/CMakeLists.txt b/components/esp_websocket_client/CMakeLists.txt index f366a278d..de17d6fdd 100644 --- a/components/esp_websocket_client/CMakeLists.txt +++ b/components/esp_websocket_client/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "esp_websocket_client.c" INCLUDE_DIRS "include" - REQUIRES lwip esp-tls tcp_transport nghttp) + REQUIRES lwip esp-tls tcp_transport nghttp + PRIV_REQUIRES esp_timer) diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index f598e5ba6..4e4b7ff02 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -40,7 +40,7 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS ${include_dirs} PRIV_INCLUDE_DIRS ${private_include_dirs} LDFRAGMENTS linker.lf - REQUIRES app_trace + REQUIRES app_trace esp_timer PRIV_REQUIRES soc) idf_component_get_property(COMPONENT_DIR freertos COMPONENT_DIR) diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt index 15b0d40f8..4274d2a46 100644 --- a/components/mdns/CMakeLists.txt +++ b/components/mdns/CMakeLists.txt @@ -3,5 +3,6 @@ idf_component_register(SRCS "mdns.c" "mdns_networking.c" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "private_include" - REQUIRES lwip mbedtls console esp_netif) + REQUIRES lwip mbedtls console esp_netif + PRIV_REQUIRES esp_timer) diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 2738e130d..01f0234c2 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -24,7 +24,7 @@ list(APPEND ldfragments newlib.lf) idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" REQUIRES vfs - PRIV_REQUIRES soc + PRIV_REQUIRES soc esp_timer LDFRAGMENTS "${ldfragments}") # Toolchain libraries require code defined in this component diff --git a/components/soc/esp32s2/include/soc/periph_defs.h b/components/soc/esp32s2/include/soc/periph_defs.h index d0b69710b..f1072d518 100644 --- a/components/soc/esp32s2/include/soc/periph_defs.h +++ b/components/soc/esp32s2/include/soc/periph_defs.h @@ -59,6 +59,7 @@ typedef enum { PERIPH_WIFI_BT_COMMON_MODULE, PERIPH_BT_BASEBAND_MODULE, PERIPH_BT_LC_MODULE, + PERIPH_SYSTIMER_MODULE, } periph_module_t; typedef enum { diff --git a/components/soc/esp32s2/include/soc/soc.h b/components/soc/esp32s2/include/soc/soc.h index a3badcaad..6db61a2e3 100644 --- a/components/soc/esp32s2/include/soc/soc.h +++ b/components/soc/esp32s2/include/soc/soc.h @@ -72,6 +72,7 @@ #define DR_REG_TIMERGROUP0_BASE 0x3f41F000 #define DR_REG_TIMERGROUP1_BASE 0x3f420000 #define DR_REG_RTC_SLOWMEM_BASE 0x3f421000 +#define DR_REG_SYSTIMER_BASE 0x3f423000 #define DR_REG_SPI2_BASE 0x3f424000 #define DR_REG_SPI3_BASE 0x3f425000 #define DR_REG_SYSCON_BASE 0x3f426000 diff --git a/components/soc/esp32s2/include/soc/systimer_reg.h b/components/soc/esp32s2/include/soc/systimer_reg.h new file mode 100644 index 000000000..0ddb7fe73 --- /dev/null +++ b/components/soc/esp32s2/include/soc/systimer_reg.h @@ -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 +#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 + + diff --git a/components/wifi_provisioning/CMakeLists.txt b/components/wifi_provisioning/CMakeLists.txt index fd8128fda..b261f1e8d 100644 --- a/components/wifi_provisioning/CMakeLists.txt +++ b/components/wifi_provisioning/CMakeLists.txt @@ -19,7 +19,7 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include PRIV_INCLUDE_DIRS src proto-c ../protocomm/proto-c REQUIRES lwip protocomm - PRIV_REQUIRES protobuf-c bt mdns json) + PRIV_REQUIRES protobuf-c bt mdns json esp_timer) # To avoid warning for strncpy set_source_files_properties(src/handlers.c src/scheme_softap.c diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index bf6959b25..8f91b775e 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -90,7 +90,7 @@ set(srcs "port/os_xtensa.c" idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include port/include include/esp_supplicant PRIV_INCLUDE_DIRS src - PRIV_REQUIRES mbedtls) + PRIV_REQUIRES mbedtls esp_timer) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing) target_compile_definitions(${COMPONENT_LIB} PRIVATE diff --git a/docs/Doxyfile b/docs/Doxyfile index 7c163f709..b114de3b6 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -256,7 +256,7 @@ INPUT = \ ../../components/esp_common/include/esp_pm.h \ ../../components/esp32/include/esp32/pm.h \ ### esp_timer, High Resolution Timer - ../../components/esp_common/include/esp_timer.h \ + ../../components/esp_timer/include/esp_timer.h \ ### esp_event, Event Loop Library ../../components/esp_event/include/esp_event.h \ ../../components/esp_event/include/esp_event_base.h \ diff --git a/docs/en/api-reference/system/esp_timer.rst b/docs/en/api-reference/system/esp_timer.rst index 2f8423c0c..98b8ae6e2 100644 --- a/docs/en/api-reference/system/esp_timer.rst +++ b/docs/en/api-reference/system/esp_timer.rst @@ -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. -``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. diff --git a/tools/unit-test-app/configs/default b/tools/unit-test-app/configs/default index 572b6cd8b..70273aa97 100644 --- a/tools/unit-test-app/configs/default +++ b/tools/unit-test-app/configs/default @@ -1 +1 @@ -TEST_COMPONENTS=freertos esp32 driver heap pthread soc spi_flash vfs \ No newline at end of file +TEST_COMPONENTS=freertos esp32 esp_timer driver heap pthread soc spi_flash vfs \ No newline at end of file diff --git a/tools/unit-test-app/configs/default_2 b/tools/unit-test-app/configs/default_2 index 9c00a9f5f..6bf0057d8 100644 --- a/tools/unit-test-app/configs/default_2 +++ b/tools/unit-test-app/configs/default_2 @@ -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 diff --git a/tools/unit-test-app/configs/default_2_s2 b/tools/unit-test-app/configs/default_2_s2 index dc4067ac8..049d2954f 100644 --- a/tools/unit-test-app/configs/default_2_s2 +++ b/tools/unit-test-app/configs/default_2_s2 @@ -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" \ No newline at end of file diff --git a/tools/unit-test-app/configs/default_s2 b/tools/unit-test-app/configs/default_s2 index 77f6779be..726653143 100644 --- a/tools/unit-test-app/configs/default_s2 +++ b/tools/unit-test-app/configs/default_s2 @@ -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" \ No newline at end of file diff --git a/tools/unit-test-app/configs/freertos_compliance b/tools/unit-test-app/configs/freertos_compliance index 2d7a6202c..57b1a2fd0 100644 --- a/tools/unit-test-app/configs/freertos_compliance +++ b/tools/unit-test-app/configs/freertos_compliance @@ -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 diff --git a/tools/unit-test-app/configs/freertos_compliance_s2 b/tools/unit-test-app/configs/freertos_compliance_s2 index f70df4a07..8f9acba04 100644 --- a/tools/unit-test-app/configs/freertos_compliance_s2 +++ b/tools/unit-test-app/configs/freertos_compliance_s2 @@ -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_IDF_TARGET="esp32s2" diff --git a/tools/unit-test-app/configs/psram b/tools/unit-test-app/configs/psram index a00f527f8..6dd17ebed 100644 --- a/tools/unit-test-app/configs/psram +++ b/tools/unit-test-app/configs/psram @@ -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_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_SPIRAM_OCCUPY_NO_HOST=y diff --git a/tools/unit-test-app/configs/psram_2 b/tools/unit-test-app/configs/psram_2 index afa69a06b..2f20137fd 100644 --- a/tools/unit-test-app/configs/psram_2 +++ b/tools/unit-test-app/configs/psram_2 @@ -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_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_SPIRAM_OCCUPY_NO_HOST=y diff --git a/tools/unit-test-app/configs/psram_8m b/tools/unit-test-app/configs/psram_8m index d05646f77..62c9c859c 100644 --- a/tools/unit-test-app/configs/psram_8m +++ b/tools/unit-test-app/configs/psram_8m @@ -1,4 +1,4 @@ -TEST_COMPONENTS=esp32 +TEST_COMPONENTS=esp32 esp_timer CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_SPIRAM_BANKSWITCH_ENABLE=y CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 diff --git a/tools/unit-test-app/configs/release b/tools/unit-test-app/configs/release index 75da1aa80..65758db63 100644 --- a/tools/unit-test-app/configs/release +++ b/tools/unit-test-app/configs/release @@ -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_ASSERTIONS_SILENT=y diff --git a/tools/unit-test-app/configs/release_2 b/tools/unit-test-app/configs/release_2 index d683fe7b3..a666771d1 100644 --- a/tools/unit-test-app/configs/release_2 +++ b/tools/unit-test-app/configs/release_2 @@ -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_ASSERTIONS_SILENT=y diff --git a/tools/unit-test-app/configs/release_2_s2 b/tools/unit-test-app/configs/release_2_s2 index 3bcc868d1..4621aab0b 100644 --- a/tools/unit-test-app/configs/release_2_s2 +++ b/tools/unit-test-app/configs/release_2_s2 @@ -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_ASSERTIONS_SILENT=y CONFIG_IDF_TARGET="esp32s2" diff --git a/tools/unit-test-app/configs/release_s2 b/tools/unit-test-app/configs/release_s2 index 9c3c22f86..ad22a0521 100644 --- a/tools/unit-test-app/configs/release_s2 +++ b/tools/unit-test-app/configs/release_s2 @@ -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_ASSERTIONS_SILENT=y CONFIG_IDF_TARGET="esp32s2" diff --git a/tools/unit-test-app/configs/single_core b/tools/unit-test-app/configs/single_core index 40f7e98f2..99c9bb818 100644 --- a/tools/unit-test-app/configs/single_core +++ b/tools/unit-test-app/configs/single_core @@ -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_FREERTOS_UNICORE=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y diff --git a/tools/unit-test-app/configs/single_core_2 b/tools/unit-test-app/configs/single_core_2 index 4570369aa..77c0f887f 100644 --- a/tools/unit-test-app/configs/single_core_2 +++ b/tools/unit-test-app/configs/single_core_2 @@ -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_FREERTOS_UNICORE=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y diff --git a/tools/unit-test-app/configs/single_core_2_s2 b/tools/unit-test-app/configs/single_core_2_s2 index ef5cc2cbc..ddd515ec5 100644 --- a/tools/unit-test-app/configs/single_core_2_s2 +++ b/tools/unit-test-app/configs/single_core_2_s2 @@ -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_FREERTOS_UNICORE=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y diff --git a/tools/unit-test-app/configs/single_core_s2 b/tools/unit-test-app/configs/single_core_s2 index 5a29a501e..71069137c 100644 --- a/tools/unit-test-app/configs/single_core_s2 +++ b/tools/unit-test-app/configs/single_core_s2 @@ -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_FREERTOS_UNICORE=y CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y