High Resolution Timer (ESP Timer)
Overview
Although FreeRTOS provides software timers, FreeRTOS software timers have a few limitations:
Maximum resolution is equal to the RTOS tick period
Timer callbacks are dispatched from a low-priority timer service (i.e., daemon) task. This task can be preempted by other tasks, leading to decreased precision and accuracy.
Although hardware timers are not subject to the limitations mentioned, they may not be as user-friendly. For instance, application components may require timer events to be triggered at specific future times, but hardware timers typically have only one "compare" value for interrupt generation. This necessitates the creation of an additional system on top of the hardware timer to keep track of pending events and ensure that callbacks are executed when the corresponding hardware interrupts occur.
esp_timer
set of APIs provides one-shot and periodic timers, microsecond time resolution, and 52-bit range.
Internally, esp_timer
uses a 52-bit hardware timer. The exact hardware timer implementation used depends on the target, where SYSTIMER is used for ESP32-P4.
Timer callbacks can be dispatched by two methods:
ESP_TIMER_TASK
.ESP_TIMER_ISR
. Available only if CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is enabled (by default disabled).
ESP_TIMER_TASK
. 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.
If other tasks with a priority higher than esp_timer
are running, callback dispatching will be delayed until the esp_timer
task has a chance to run. For example, this will happen if an SPI Flash operation is in progress.
ESP_TIMER_ISR
. Timer callbacks are dispatched directly from the timer interrupt handler. This method is useful for some simple callbacks which aim for lower latency.
Creating and starting a timer, and dispatching the callback takes some time. Therefore, there is a lower limit to the timeout value of one-shot esp_timer
. If esp_timer_start_once()
is called with a timeout value of less than 20 us, the callback will be dispatched only after approximately 20 us.
Periodic esp_timer
also imposes a 50 us restriction on the minimal timer period. Periodic software timers with a period of less than 50 us are not practical since they would consume most of the CPU time. Consider using dedicated hardware peripherals or DMA features if you find that a timer with a small period is required.
Using esp_timer
APIs
A single timer is represented by esp_timer_handle_t
type. Each timer has a callback function associated with it. This callback function is called from the esp_timer
task each time the timer elapses.
To create a timer, call
esp_timer_create()
.To delete the timer when it is no longer needed, call
esp_timer_delete()
.
The timer can be started in one-shot mode or in periodic mode.
To start the timer in one-shot mode, call
esp_timer_start_once()
, passing the time interval after which the callback should be called. When the callback gets called, the timer is considered to be stopped.To start the timer in periodic mode, call
esp_timer_start_periodic()
, passing the period with which the callback should be called. The timer keeps running untilesp_timer_stop()
is called.
Note that the timer must not be running when esp_timer_start_once()
or esp_timer_start_periodic()
is called. To restart a running timer, call esp_timer_stop()
first, then call one of the start functions.
Callback Functions
Note
Keep the callback functions as short as possible. Otherwise, it will affect all timers.
Timer callbacks that are processed by the ESP_TIMER_ISR
method should not call the context switch call - portYIELD_FROM_ISR()
. Instead, use the esp_timer_isr_dispatch_need_yield()
function. The context switch will be done after all ISR dispatch timers have been processed if required by the system.
esp_timer
During Light-sleep
During Light-sleep, the esp_timer
counter stops and no callback functions are called. Instead, the time is counted by the RTC counter. Upon waking up, the system gets the difference between the counters and calls a function that advances the esp_timer
counter. Since the counter has been advanced, the system starts calling callbacks that were not called during sleep. The number of callbacks depends on the duration of the sleep and the period of the timers. It can lead to the overflow of some queues. This only applies to periodic timers, since one-shot timers are only called once.
This behavior can be changed by calling esp_timer_stop()
before sleeping. In some cases, this can be inconvenient, and instead of the stop function, you can use the skip_unhandled_events
option during esp_timer_create()
. When the skip_unhandled_events
is true, if a periodic timer expires one or more times during Light-sleep, then only one callback is called on wake.
Using the skip_unhandled_events
option with automatic Light-sleep (see Power Management APIs) helps to reduce the power consumption of the system when it is in Light-sleep. The duration of Light-sleep is also in part determined by the next event occurs. Timers with skip_unhandled_events
option does not wake up the system.
Handling Callbacks
esp_timer
is designed to achieve a high-resolution and low-latency timer with the ability to handle delayed events. If the timer is late, then the callback will be called as soon as possible, and it will not be lost. In the worst case, when the timer has not been processed for more than one period (for periodic timers), the callbacks will be called one after the other without waiting for the set period. This can be bad for some applications, and the skip_unhandled_events
option is introduced to eliminate this behavior. If skip_unhandled_events
is set, then a periodic timer that has expired multiple times without being able to call the callback will still result in only one callback event once processing is possible.
Obtaining Current Time
esp_timer
also provides a convenience function to obtain the time passed since start-up, with microsecond precision: esp_timer_get_time()
. This function returns the number of microseconds since esp_timer
was initialized, which usually happens shortly before app_main
function is called.
Unlike gettimeofday
function, values returned by esp_timer_get_time()
:
Start from zero after the chip wakes up from Deep-sleep
Do not have timezone or DST adjustments applied
Application Example
The following example illustrates the usage of esp_timer
APIs: system/esp_timer.
API Reference
Header File
This header file can be included with:
#include "esp_timer.h"
This header file is a part of the API provided by the
esp_timer
component. To declare that your component depends onesp_timer
, add the following to your CMakeLists.txt:REQUIRES esp_timer
or
PRIV_REQUIRES esp_timer
Functions
-
esp_err_t esp_timer_early_init(void)
Minimal initialization of esp_timer.
This function can be called very early in startup process, after this call only esp_timer_get_time function can be used.
Note
This function is called from startup code. Applications do not need to call this function before using other esp_timer APIs.
- Returns
ESP_OK on success
-
esp_err_t esp_timer_init(void)
Initialize esp_timer library.
This function will be called from startup code on every core if CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY is enabled, It allocates the timer ISR on MULTIPLE cores and creates the timer task which can be run on any core.
Note
This function is called from startup code. Applications do not need to call this function before using other esp_timer APIs. Before calling this function, esp_timer_early_init must be called by the startup code.
- Returns
ESP_OK on success
ESP_ERR_NO_MEM if allocation has failed
ESP_ERR_INVALID_STATE if already initialized
other errors from interrupt allocator
-
esp_err_t esp_timer_deinit(void)
De-initialize esp_timer library.
Note
Normally this function should not be called from applications
- Returns
ESP_OK on success
ESP_ERR_INVALID_STATE if not yet initialized
-
esp_err_t esp_timer_create(const esp_timer_create_args_t *create_args, esp_timer_handle_t *out_handle)
Create an esp_timer instance.
Note
When done using the timer, delete it with esp_timer_delete function.
- Parameters
create_args -- Pointer to a structure with timer creation arguments. Not saved by the library, can be allocated on the stack.
out_handle -- [out] Output, pointer to esp_timer_handle_t variable which will hold the created timer handle.
- Returns
ESP_OK on success
ESP_ERR_INVALID_ARG if some of the create_args are not valid
ESP_ERR_INVALID_STATE if esp_timer library is not initialized yet
ESP_ERR_NO_MEM if memory allocation fails
-
esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
Start one-shot timer.
Timer should not be running when this function is called.
- Parameters
timer -- timer handle created using esp_timer_create
timeout_us -- timer timeout, in microseconds relative to the current moment
- Returns
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(esp_timer_handle_t timer, uint64_t period)
Start a periodic timer.
Timer should not be running when this function is called. This function will start the timer which will trigger every 'period' microseconds.
- Parameters
timer -- timer handle created using esp_timer_create
period -- timer period, in microseconds
- Returns
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_restart(esp_timer_handle_t timer, uint64_t timeout_us)
Restart a currently running timer.
If the given timer is a one-shot timer, the timer is restarted immediately and will timeout once in
timeout_us
microseconds. If the given timer is a periodic timer, the timer is restarted immediately with a new period oftimeout_us
microseconds.- Parameters
timer -- timer Handle created using esp_timer_create
timeout_us -- Timeout, in microseconds relative to the current time. In case of a periodic timer, also represents the new period.
- Returns
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_stop(esp_timer_handle_t timer)
Stop the timer.
This function stops the timer previously started using esp_timer_start_once or esp_timer_start_periodic.
- Parameters
timer -- timer handle created using esp_timer_create
- Returns
ESP_OK on success
ESP_ERR_INVALID_STATE if the timer is not running
-
esp_err_t esp_timer_delete(esp_timer_handle_t timer)
Delete an esp_timer instance.
The timer must be stopped before deleting. A one-shot timer which has expired does not need to be stopped.
- Parameters
timer -- timer handle allocated using esp_timer_create
- Returns
ESP_OK on success
ESP_ERR_INVALID_STATE if the timer is running
-
int64_t esp_timer_get_time(void)
Get time in microseconds since boot.
- Returns
number of microseconds since underlying timer has been started
-
int64_t esp_timer_get_next_alarm(void)
Get the timestamp when the next timeout is expected to occur.
- Returns
Timestamp of the nearest timer event, in microseconds. The timebase is the same as for the values returned by esp_timer_get_time.
-
int64_t esp_timer_get_next_alarm_for_wake_up(void)
Get the timestamp when the next timeout is expected to occur skipping those which have skip_unhandled_events flag.
- Returns
Timestamp of the nearest timer event, in microseconds. The timebase is the same as for the values returned by esp_timer_get_time.
-
esp_err_t esp_timer_get_period(esp_timer_handle_t timer, uint64_t *period)
Get the period of a timer.
This function fetches the timeout period of a timer.
Note
The timeout period is the time interval with which a timer restarts after expiry. For one-shot timers, the period is 0 as there is no periodicity associated with such timers.
- Parameters
timer -- timer handle allocated using esp_timer_create
period -- memory to store the timer period value in microseconds
- Returns
ESP_OK on success
ESP_ERR_INVALID_ARG if the arguments are invalid
-
esp_err_t esp_timer_get_expiry_time(esp_timer_handle_t timer, uint64_t *expiry)
Get the expiry time of a one-shot timer.
This function fetches the expiry time of a one-shot timer.
Note
This API returns a valid expiry time only for a one-shot timer. It returns an error if the timer handle passed to the function is for a periodic timer.
- Parameters
timer -- timer handle allocated using esp_timer_create
expiry -- memory to store the timeout value in microseconds
- Returns
ESP_OK on success
ESP_ERR_INVALID_ARG if the arguments are invalid
ESP_ERR_NOT_SUPPORTED if the timer type is periodic
-
esp_err_t esp_timer_dump(FILE *stream)
Dump the list of timers to a stream.
If CONFIG_ESP_TIMER_PROFILING option is enabled, this prints the list of all the existing timers. Otherwise, only the list active timers is printed.
The format is:
name period alarm times_armed times_triggered total_callback_run_time
where:
name — timer name (if CONFIG_ESP_TIMER_PROFILING is defined), or timer pointer period — period of timer, in microseconds, or 0 for one-shot timer alarm - time of the next alarm, in microseconds since boot, or 0 if the timer is not started
The following fields are printed if CONFIG_ESP_TIMER_PROFILING is defined:
times_armed — number of times the timer was armed via esp_timer_start_X times_triggered - number of times the callback was called total_callback_run_time - total time taken by callback to execute, across all calls
- Parameters
stream -- stream (such as stdout) to dump the information to
- Returns
ESP_OK on success
ESP_ERR_NO_MEM if can not allocate temporary buffer for the output
-
void esp_timer_isr_dispatch_need_yield(void)
Requests a context switch from a timer callback function.
This only works for a timer that has an ISR dispatch method. The context switch will be called after all ISR dispatch timers have been processed.
-
bool esp_timer_is_active(esp_timer_handle_t timer)
Returns status of a timer, active or not.
This function is used to identify if the timer is still active or not.
- Parameters
timer -- timer handle created using esp_timer_create
- Returns
1 if timer is still active
0 if timer is not active.
-
esp_err_t esp_timer_new_etm_alarm_event(esp_etm_event_handle_t *out_event)
Get the ETM event handle of esp_timer underlying alarm event.
Note
The created ETM event object can be deleted later by calling
esp_etm_del_event
Note
The ETM event is generated by the underlying hardware — systimer, therefore, if the esp_timer is not clocked by systimer, then no ETM event will be generated.
- Parameters
out_event -- [out] Returned ETM event handle
- Returns
ESP_OK Success
ESP_ERR_INVALID_ARG Parameter error
Structures
-
struct esp_timer_create_args_t
Timer configuration passed to esp_timer_create.
Public Members
-
esp_timer_cb_t callback
Function to call when timer expires.
-
void *arg
Argument to pass to the callback.
-
esp_timer_dispatch_t dispatch_method
Call the callback from task or from ISR.
-
const char *name
Timer name, used in esp_timer_dump function.
-
bool skip_unhandled_events
Skip unhandled events for periodic timers.
-
esp_timer_cb_t callback
Type Definitions
-
typedef struct esp_timer *esp_timer_handle_t
Opaque type representing a single esp_timer.
-
typedef void (*esp_timer_cb_t)(void *arg)
Timer callback function type.
- Param arg
pointer to opaque user-specific data