Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion components/esp_timer/include/esp_timer.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down Expand Up @@ -157,6 +157,22 @@ 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
*
* 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
*/
esp_err_t esp_timer_start_once_at(esp_timer_handle_t timer, uint64_t alarm_us);

/**
* @brief Start a periodic timer
*
Expand All @@ -172,6 +188,26 @@ 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
*
* 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.
*
* @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
*/
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
*
Expand All @@ -190,6 +226,26 @@ 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
*
* 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 `timeout_us` microseconds. Next alarm is at `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 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
*/
esp_err_t esp_timer_restart_at(esp_timer_handle_t timer, uint64_t period_us,
uint64_t alarm_us);

/**
* @brief Stop a running timer
*
Expand Down
81 changes: 75 additions & 6 deletions components/esp_timer/src/esp_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ 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 esp_timer_start_once_at_impl(esp_timer_handle_t timer,
uint64_t alarm_us);
static esp_err_t esp_timer_start_periodic_at_impl(esp_timer_handle_t timer,
uint64_t period_us,
uint64_t first_alarm_us);
static esp_err_t esp_timer_restart_at_impl(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);
static void timer_remove_inactive(esp_timer_handle_t timer);
Expand Down Expand Up @@ -134,8 +141,19 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
*/
esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us)
{
esp_err_t ret = ESP_OK;
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}

if (!is_initialized() || !timer_armed(timer)) {
return ESP_ERR_INVALID_STATE;
}

return esp_timer_restart_at_impl(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 alarm_us)
{
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
Expand All @@ -144,6 +162,14 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64
return ESP_ERR_INVALID_STATE;
}

return esp_timer_restart_at_impl(timer, period_us, alarm_us);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we have to check alarm_us, != 0, and greater than the current time, and return an error.
For all 3 functions.

}

static esp_err_t ESP_TIMER_IRAM_ATTR
esp_timer_restart_at_impl(esp_timer_handle_t timer, uint64_t timeout_us, uint64_t alarm_us)
{
esp_err_t ret = ESP_OK;

esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
timer_list_lock(dispatch_method);

Expand All @@ -169,6 +195,12 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64
timer->alarm = now + timeout_us;
timer->period = 0;
}

/* If the alarm time is explicitly specified, override the calculated one */
if (alarm_us != 0) {
timer->alarm = alarm_us;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Deadlock in esp_timer_restart_at_impl with timer_lock

The esp_timer_restart_at_impl function causes a deadlock by calling timer_remove while already holding the non-recursive timer_list_lock. It also inconsistently ignores an alarm_us value of 0, preventing absolute alarm times at boot, unlike other _at timer functions.

Fix in Cursor Fix in Web

ret = timer_insert(timer, false);
}

Expand All @@ -185,7 +217,23 @@ 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;
return esp_timer_start_once_at_impl(timer, 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)
{
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!is_initialized()) {
return ESP_ERR_INVALID_STATE;
}
return esp_timer_start_once_at_impl(timer, alarm_us);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the check of alarm_us

}

static ESP_TIMER_IRAM_ATTR esp_err_t
esp_timer_start_once_at_impl(esp_timer_handle_t timer, uint64_t alarm_us)
{
esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
esp_err_t err;

Expand All @@ -199,7 +247,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->alarm = alarm_us;
timer->period = 0;
#if WITH_PROFILING
timer->times_armed++;
Expand All @@ -210,7 +258,22 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uin
return err;
}

esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t 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)
{
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!is_initialized()) {
return ESP_ERR_INVALID_STATE;
}

period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
return esp_timer_start_periodic_at_impl(timer, period_us, first_alarm_us);
}

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;
Expand All @@ -219,7 +282,13 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer,
return ESP_ERR_INVALID_STATE;
}
period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
int64_t alarm = esp_timer_get_time() + period_us;
return esp_timer_start_periodic_at_impl(timer, period_us,
esp_timer_get_time() + period_us);
}

static ESP_TIMER_IRAM_ATTR esp_err_t esp_timer_start_periodic_at_impl(
esp_timer_handle_t timer, uint64_t period_us, uint64_t first_alarm_us)
{
esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
esp_err_t err;
timer_list_lock(dispatch_method);
Expand All @@ -228,7 +297,7 @@ esp_err_t ESP_TIMER_IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer,
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++;
Expand Down
99 changes: 68 additions & 31 deletions examples/system/esp_timer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,36 +86,73 @@ If you see the following console output, your example should be running correctl

```
...
I (294) example: Started timers, time since boot: 9662 us
periodic 500000 509644 1 0 0 0
one-shot 0 5009654 1 0 0 0
I (794) example: Periodic timer called, time since boot: 509694 us
I (1294) example: Periodic timer called, time since boot: 1009671 us
I (1794) example: Periodic timer called, time since boot: 1509671 us
I (2294) example: Periodic timer called, time since boot: 2009671 us
periodic 500000 2509644 1 4 0 542
one-shot 0 5009654 1 0 0 0
I (2794) example: Periodic timer called, time since boot: 2509671 us
I (3294) example: Periodic timer called, time since boot: 3009671 us
I (3794) example: Periodic timer called, time since boot: 3509671 us
I (4294) example: Periodic timer called, time since boot: 4009671 us
periodic 500000 4509644 1 8 0 1026
one-shot 0 5009654 1 0 0 0
I (4794) example: Periodic timer called, time since boot: 4509671 us
I (5294) example: Periodic timer called, time since boot: 5009669 us
I (5294) example: One-shot timer called, time since boot: 5009788 us
I (5294) example: Restarted periodic timer with 1s period, time since boot: 5012675 us
I (6294) example: Periodic timer called, time since boot: 6012692 us
periodic 1000000 7012666 2 11 0 1391
one-shot 0 0 1 1 0 11472
I (7294) example: Periodic timer called, time since boot: 7012692 us
I (8294) example: Periodic timer called, time since boot: 8012692 us
periodic 1000000 9012666 2 13 0 1639
one-shot 0 0 1 1 0 11472
I (9294) example: Periodic timer called, time since boot: 9012692 us
I (10294) example: Periodic timer called, time since boot: 10012692 us
I (10314) example: Entering light sleep for 0.5s, time since boot: 10024351 us
I (10314) example: Woke up from light sleep, time since boot: 10525143 us
I (266) example: Started timers, time since boot: 30808 us
Timer stats:
Name Period Alarm Times_armed Times_trigg Times_skip Cb_exec_time
periodic 500000 530773 1 0 0 0
timed periodic 500000 3000000 1 0 0 0
one-shot 0 5030792 1 0 0 0
timed one-shot 0 6000000 1 0 0 0
I (766) example: Periodic timer called, time since boot: 530850 us
I (1266) example: Periodic timer called, time since boot: 1030802 us
I (1766) example: Periodic timer called, time since boot: 1530802 us
I (2266) example: Periodic timer called, time since boot: 2030802 us
Timer stats:
Name Period Alarm Times_armed Times_trigg Times_skip Cb_exec_time
periodic 500000 2530773 1 4 0 999
timed periodic 500000 3000000 1 0 0 0
one-shot 0 5030792 1 0 0 0
timed one-shot 0 6000000 1 0 0 0
I (2766) example: Periodic timer called, time since boot: 2530826 us
I (3236) example: Timed periodic timer called, time since boot: 3000029 us
I (3266) example: Periodic timer called, time since boot: 3030802 us
I (3736) example: Timed periodic timer called, time since boot: 3500029 us
I (3766) example: Periodic timer called, time since boot: 3530802 us
I (4236) example: Timed periodic timer called, time since boot: 4000029 us
I (4266) example: Periodic timer called, time since boot: 4030802 us
Timer stats:
Name Period Alarm Times_armed Times_trigg Times_skip Cb_exec_time
timed periodic 500000 4500000 1 3 0 709
periodic 500000 4530773 1 8 0 1947
one-shot 0 5030792 1 0 0 0
timed one-shot 0 6000000 1 0 0 0
I (4736) example: Timed periodic timer called, time since boot: 4500053 us
I (4766) example: Periodic timer called, time since boot: 4530802 us
I (5236) example: Timed periodic timer called, time since boot: 5000030 us
I (5266) example: Periodic timer called, time since boot: 5030803 us
I (5266) example: One-shot timer called, time since boot: 5031025 us
I (5266) example: Restarted periodic timer with 1s period, time since boot: 5031920 us
I (5736) example: Timed periodic timer called, time since boot: 5500029 us
I (6236) example: Timed one-shot timer called, time since boot: 6000028 us
I (6236) example: Restarted timed periodic timer with 1s period, time since boot: 6000268 us
I (6266) example: Periodic timer called, time since boot: 6031945 us
Timer stats:
Name Period Alarm Times_armed Times_trigg Times_skip Cb_exec_time
timed periodic 1000000 7000000 1 6 0 1438
periodic 1000000 7031916 2 11 0 2644
timed one-shot 0 0 1 1 0 3722
one-shot 0 0 1 1 0 8517
I (7236) example: Timed periodic timer called, time since boot: 7000053 us
I (7266) example: Periodic timer called, time since boot: 7031945 us
I (8236) example: Timed periodic timer called, time since boot: 8000029 us
I (8266) example: Periodic timer called, time since boot: 8031945 us
Timer stats:
Name Period Alarm Times_armed Times_trigg Times_skip Cb_exec_time
timed periodic 1000000 9000000 1 8 0 1940
periodic 1000000 9031916 2 13 0 3106
timed one-shot 0 0 1 1 0 3722
one-shot 0 0 1 1 0 8517
I (9236) example: Timed periodic timer called, time since boot: 9000053 us
I (9266) example: Periodic timer called, time since boot: 9031945 us
I (10236) example: Timed periodic timer called, time since boot: 10000029 us
I (10266) example: Periodic timer called, time since boot: 10031945 us
I (10476) example: Entering light sleep for 0.5s, time since boot: 10239360 us
I (10476) example: Woke up from light sleep, time since boot: 10739673 us
I (10736) example: Timed periodic timer called, time since boot: 11000033 us
I (10766) example: Periodic timer called, time since boot: 11031945 us
I (11736) example: Timed periodic timer called, time since boot: 12000029 us
I (11766) example: Periodic timer called, time since boot: 12031945 us
I (12486) example: Stopped and deleted timers
...
```

Expand All @@ -127,7 +164,7 @@ The subsections below walk you through the important parts of the application ex

### Creating Callback Functions

Timers are used to execute a callback function as a delayed action. So the callback functions `periodic_timer_callback()` and `oneshot_timer_callback()` are crucial parts of this application example.
Timers are used to execute a callback function as a delayed action. So the callback functions `periodic_timer_callback()`, `timed_periodic_timer_callback()`, `oneshot_timer_callback()` and `timed_oneshot_timer_callback()`, are crucial parts of this application example.


### Printing Timer Dumps
Expand Down
Loading