feat(esp_timer): Support absolute timing APIs

Merges https://github.com/espressif/esp-idf/pull/17807
This commit is contained in:
Huelsenfrucht
2025-10-31 12:46:42 -05:00
committed by Konstantin Kondrashov
parent d18019de5b
commit b3ccc1316d
6 changed files with 280 additions and 83 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -157,6 +157,23 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
*/
esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
/**
* @brief Start a one-shot timer with absolute alarm time
*
* This function starts a one-shot timer that will trigger once at the specified absolute time.
* To start a timer relative to the current time, see esp_timer_start_once().
* Timer represented by `timer` should not be running when this function is
* called.
*
* @param timer timer handle created using esp_timer_create()
* @param alarm_us timer alarm time, in absolute microseconds (as returned by esp_timer_get_time())
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the handle is invalid
* - ESP_ERR_INVALID_STATE if the timer is already running or in the past
*/
esp_err_t esp_timer_start_once_at(esp_timer_handle_t timer, uint64_t alarm_us);
/**
* @brief Start a periodic timer
*
@@ -172,6 +189,24 @@ esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
*/
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
/**
* @brief Start a periodic timer with absolute alarm time
*
* Timer represented by `timer` should not be running when this function is called.
* This function starts the timer which will trigger every `period` microseconds.
* The first alarm will be triggered at `first_alarm_us` time.
* To start a periodic timer relative to the current time, see esp_timer_start_periodic().
*
* @param timer timer handle created using esp_timer_create()
* @param period_us timer period, in microseconds
* @param first_alarm_us timer first alarm time, in absolute microseconds (as returned by esp_timer_get_time())
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the handle is invalid
* - ESP_ERR_INVALID_STATE if the timer is already running or in the past
*/
esp_err_t esp_timer_start_periodic_at(esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us);
/**
* @brief Restart a currently running timer
*
@@ -190,6 +225,24 @@ esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
*/
esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us);
/**
* @brief Restart a currently running timer with absolute alarm time
*
* Type of `timer` | Action
* --------------- | ------
* One-shot timer | Restarted immediately and times out once at `alarm_us` microseconds
* Periodic timer | Restarted immediately with a new period of `period_us` microseconds. Next alarm is at `first_alarm_us` microseconds
*
* @param timer timer handle created using esp_timer_create()
* @param period_us In case of a periodic timer, represents the new period.
* @param first_alarm_us timer alarm time, in absolute microseconds (as returned by esp_timer_get_time())
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the handle is invalid
* - ESP_ERR_INVALID_STATE if the timer is not running or in the past
*/
esp_err_t esp_timer_restart_at(esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us);
/**
* @brief Stop a running timer
*

View File

@@ -65,6 +65,7 @@ static esp_err_t timer_remove(esp_timer_handle_t timer);
static bool timer_armed(esp_timer_handle_t timer);
static void timer_list_lock(esp_timer_dispatch_t timer_type);
static void timer_list_unlock(esp_timer_dispatch_t timer_type);
static esp_err_t timer_restart(esp_timer_handle_t timer, uint64_t timeout_us, uint64_t alarm_us);
#if WITH_PROFILING
static void timer_insert_inactive(esp_timer_handle_t timer);
@@ -133,6 +134,20 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
* in IRAM when PM_SLP_IRAM_OPT = y and ESP_TASK_WDT USE ESP_TIMER = y.
*/
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us)
{
return timer_restart(timer, timeout_us, 0);
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart_at(esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us)
{
const uint64_t min_overhead_us = esp_timer_impl_get_min_period_us();
if (first_alarm_us + min_overhead_us < esp_timer_impl_get_time()) {
return ESP_ERR_INVALID_ARG;
}
return timer_restart(timer, period_us, first_alarm_us);
}
static esp_err_t ESP_TIMER_IRAM_ATTR timer_restart(esp_timer_handle_t timer, uint64_t timeout_us, uint64_t first_alarm_us)
{
esp_err_t ret = ESP_OK;
@@ -163,11 +178,11 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64
/* Remove function got rid of the alarm and period fields, restore them */
const uint64_t min_period = esp_timer_impl_get_min_period_us();
const uint64_t new_period = MAX(timeout_us, min_period);
timer->alarm = now + new_period;
timer->alarm = (first_alarm_us != 0) ? first_alarm_us : now + new_period;
timer->period = new_period;
} else {
/* The new one-shot alarm shall be triggered timeout_us after the current time */
timer->alarm = now + timeout_us;
timer->alarm = (first_alarm_us != 0) ? first_alarm_us : now + timeout_us;
timer->period = 0;
}
ret = timer_insert(timer, false);
@@ -178,7 +193,7 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64
return ret;
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
static esp_err_t ESP_TIMER_IRAM_ATTR timer_init(esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us)
{
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
@@ -186,7 +201,7 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uin
if (!is_initialized()) {
return ESP_ERR_INVALID_STATE;
}
int64_t alarm = esp_timer_get_time() + timeout_us;
esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
esp_err_t err;
@@ -200,37 +215,7 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uin
if (timer_armed(timer)) {
err = ESP_ERR_INVALID_STATE;
} else {
timer->alarm = alarm;
timer->period = 0;
#if WITH_PROFILING
timer->times_armed++;
#endif
err = timer_insert(timer, false);
}
timer_list_unlock(dispatch_method);
return err;
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
{
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!is_initialized()) {
return ESP_ERR_INVALID_STATE;
}
uint64_t min_period = esp_timer_impl_get_min_period_us();
period_us = MAX(period_us, min_period);
int64_t alarm = esp_timer_get_time() + period_us;
esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
esp_err_t err;
timer_list_lock(dispatch_method);
/* Check if the timer is armed once the list is locked to avoid a data race */
if (timer_armed(timer)) {
err = ESP_ERR_INVALID_STATE;
} else {
timer->alarm = alarm;
timer->alarm = first_alarm_us;
timer->period = period_us;
#if WITH_PROFILING
timer->times_armed++;
@@ -242,6 +227,37 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer,
return err;
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
{
return timer_init(timer, 0, esp_timer_get_time() + timeout_us);
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once_at(esp_timer_handle_t timer, uint64_t alarm_us)
{
const uint64_t min_overhead_us = esp_timer_impl_get_min_period_us();
if (alarm_us + min_overhead_us < esp_timer_impl_get_time()) {
return ESP_ERR_INVALID_ARG;
}
return timer_init(timer, 0, alarm_us);
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
{
uint64_t min_period = esp_timer_impl_get_min_period_us();
period_us = MAX(period_us, min_period);
return timer_init(timer, period_us, esp_timer_get_time() + period_us);
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic_at(esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us)
{
const uint64_t min_overhead_us = esp_timer_impl_get_min_period_us();
if (first_alarm_us + min_overhead_us < esp_timer_impl_get_time()) {
return ESP_ERR_INVALID_ARG;
}
period_us = MAX(period_us, min_overhead_us);
return timer_init(timer, period_us, first_alarm_us);
}
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
{
if (timer == NULL) {