ztimer high level timer abstraction layer

High level timer abstraction layer. More...

Detailed Description

High level timer abstraction layer.


ztimer provides a high level abstraction of hardware timers for application timing needs.

The basic functions of the ztimer module are ztimer_now(), ztimer_sleep(), ztimer_set() and ztimer_remove().

They all take a pointer to a clock device (or virtual timer device) as first parameter.

RIOT provides ZTIMER_USEC, ZTIMER_MSEC, ZTIMER_SEC by default, which can be used in an application by depending on the modules ztimer_usec, ztimer_msec or ztimer_sec. They will then automatically get configured.

Every ztimer clock allows multiple timeouts to be scheduled. They all provide unsigned 32bit range. In this documentation, a timeout or its corresponding struct will be called timer, and when the time out has passed, it has triggered.

As ztimer can use arbitrarily configurable backends, a ztimer clock instance can run at configurable frequencies. Throughout this documentation, one clock step is called tick. For the pre-defined clocks ZTIMER_USEC, ZTIMER_MSEC and ZTIMER_SEC, one clock tick corresponds to one microsecond, one millisecond or one second, respectively.

ztimer_now() returns the current clock tick count as uint32_t.

ztimer_sleep() pauses the current thread for the passed amount of clock ticks. E.g., ztimer_sleep(ZTIMER_SEC, 5); will suspend the currently running thread for five seconds.

ztimer_set() takes a ztimer_t object (containing a function pointer and void * argument) and an interval as arguments. After at least the interval (in number of ticks for the corresponding clock) has passed, the callback will be called in interrupt context. A timer can be cancelled using ztimer_remove().


#include "ztimer.h"
static void callback(void *arg)
int main()
ztimer_t timeout = { .callback=callback, .arg="Hello ztimer!" };
ztimer_set(ZTIMER_SEC, &timeout, 2);
ztimer_clock_t *const ZTIMER_SEC
Default ztimer second clock.
uint32_t ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val)
Set a timer on a clock.
void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration)
Put the calling thread to sleep for the specified number of ticks.
ztimer structure
Definition: ztimer.h:332
ztimer API

ztimer best practices

  1. Don't use ZTIMER_USEC unless the increased resolution is really needed. ZTIMER_USEC will, on most platforms, prevent low-power sleep modes.
  2. Clear ztimer_t structs before use. Example:
    ztimer_t foo = { 0 };
    This ensures ztimer knows the timer is not already set, possibly preventing an unnecessary full ztimer list traversal. (ztimer will ensure that a removed timer is sufficiently cleared.)
  3. Don't compare ztimer_now() values from different clocks. The clocks are almost certainly not synchronized.


clocks, virtual timers, chaining

The system is composed of clocks (virtual ztimer devices) which can be chained to create an abstract view of a hardware timer/counter device. Each ztimer clock acts as a operation on the next clock in the chain. At the end of each ztimer chain there is always some kind of counter device object.

Each clock device handles multiplexing (allowing multiple timers to be set) and extension to full 32bit.

Hardware interface submodules:

Filter submodules:

A common chain could be:

  1. ztimer_periph_timer (e.g., on top of an 1024Hz 16bit hardware timer)
  2. ztimer_convert_frac (to convert 1024 to 1000Hz)

This is how e.g., the clock ZTIMER_MSEC might be configured on a specific system.

Every clock in the chain can always be used on its own. E.g. in the example above, the ztimer_periph object can be used as ztimer clock with 1024Hz ticks in addition to the ztimer_convert_frac with 1000Hz.

Timer handling

Timers in ztimer are stored in a clock using a linked list for which each entry stores the difference to the previous entry in the timer (T[n]). The clock also stores the absolute time on which the relative offsets are based (B), effectively storing the absolute target time for each entry (as B + sum(T[0-n])). Storing the entries in this way allows all entries to use the full width of the used uint32_t, compared to storing the absolute time.

In order to prevent timer processing offset to add up, whenever a timer triggers, the list's absolute base time is set to the expected trigger time (B + T[0]). The underlying clock is then set to alarm at (now() + (now() - B) + T[1]). Thus even though the list is keeping relative offsets, the time keeping is done by keeping track of the absolute times.

Currently, a sorted singly linked list is used for storing the timers. This choice has some implications:

By making the list doubly-linked, removal of timer objects could be easily made a constant operation, at the price of another pointer per timer object (for "previous" element).

If deemed necessary, the linked list can be exchanged our augmented with another data structure providing better algorithmic guarantees. It remains to be shown whether the increased complexity would lead to better performance for any reasonable amount of active timers.

Clock extension

The API always allows setting full 32bit relative offsets for every clock.

In some cases (e.g., a hardware timer only allowing getting/setting smaller values or a conversion which would overflow uint32_t for large intervals), ztimer takes care of extending timers. This is enabled automatically for every ztimer clock that has a "max_value" setting smaller than 2**32-1. If a ztimer_set() would overflow that value, intermediate intervals of length (max_value / 2) are set until the remaining interval fits into max_value. If extension is enabled for a clock, ztimer_now() uses interval checkpointing, storing the current time and corresponding clock tick value on each call and using that information to calculate the current time. This ensures correct ztimer_now() values if ztimer_now() is called at least once every "max_value" ticks. This is ensured by scheduling intermediate callbacks every (max_value / 2) ticks (even if no timeout is configured).


Care has been taken to avoid any unexpected behaviour of ztimer. In particular, ztimer tries hard to avoid underflows (setting a backend timer to a value at or behind the current time, causing the timer interrupt to trigger one whole timer period too late). This is done by always setting relative timeouts to backend timers, with interrupts disabled and ensuring that very small values don't cause underflows.

Configuration and convention

As timer hardware and capabilities is diverse and ztimer allows configuring and using arbitrary clock backends and conversions, it is envisioned to provide default configurations that application developers can assume to be available.

These are implemented by using pointers to ztimer clocks using default names.

For now, there are:

ZTIMER_USEC: clock providing microsecond ticks, always uses a basic timer (ztimer_periph_timer)

ZTIMER_MSEC: clock providing millisecond ticks, using a low power timer (ztimer_periph_rtt) if it is available on the platform and it running at 1kHz or above else it uses the same basic timer as ZTIMER_USEC does.

ZTIMER_SEC: clock providing second time, possibly using epoch semantics, it will use a low power timer (ztimer_periph_rtt) if it is available on the platform alternately it uses ztimer_periph_rtc if it is available and configured if if these are missing it will use same basic timer as ZTIMER_USEC does.

If periph_rtt is required with direct access by another MODULE or application, ztimer_no_periph_rtt can be included to avoid automatic selection of ztimer_periph_rtt as a backend for ZTIMER_SEC and ZTIMER_MSEC. i.e.: USEMODULE += ztimer_no_periph_rtt.

These pointers are defined in ztimer.h and can be used like this:


They also need to be added to USEMODULE using the names ztimer_usec, ztimer_msec and ztimer_sec.

Some notes on ztimer's accuracy

  1. ztimer should wait "at least" the specified timeout
  2. due to its implementation details, expect +-1 clock tick systemic inaccuracy for all clocks.
  3. for the predefined clocks (ZTIMER_USEC, ZTIMER_MSEC, ZTIMER_SEC), tick conversion might be applied using ztimer_convert_*, causing errors due to integer conversion and rounding. In particular, most RTT's closest match for milliseconds are 1024Hz, which will be converted using convert_frac to provide the 1ms clock.
  4. Some platforms don't have any timer that can be configured to 1us. E.g., the fe310 (hifive1/b) only supports a 32kHz timer, and most atmegas only support 250kHz. In order to not completely break all applications using ZTIMER_USEC, that clock will only provide ~30.5ms respectively 4us maximum accuracy on those boards. With DEVELHELP=1, a warning will be printed at boot time.
  5. Due to +-1 systemic inaccuracies, it is advisable to use ZTIMER_MSEC for second timers up to 49 days (instead of ZTIMER_SEC).


 ztimer 64bit version
 ztimer 64bit version
 ztimer frequency conversion modules
 ztimer frequency conversion modules
 ztimer mock clock backend
 ztimer mock clock backend
 ztimer overhead utility
 ztimer overhead measurement functionality
 ztimer periph/ptp backend
 ztimer periph/ptp backend
 ztimer periph/rtc backend
 ztimer periph/rtc backend
 ztimer periph/rtt backend
 ztimer periph/rtt backend
 ztimer periph/timer backend
 ztimer periph/timer backend
 64-bit timestamp support


file  config.h
 ztimer default configuration
file  periodic.h
 Periodic ztimer API.
file  ztimer.h
 ztimer API

Data Structures

struct  ztimer_base
 Minimum information for each timer. More...
struct  ztimer_t
 ztimer structure More...
struct  ztimer_ops_t
 ztimer backend method structure More...
struct  ztimer_clock
 ztimer device structure More...


 Disables interaction with pm_layered for a clock.
#define MSG_ZTIMER   0xc83e
 msg type used by ztimer_msg_receive_timeout


typedef struct ztimer_base ztimer_base_t
 ztimer_base_t forward declaration
typedef struct ztimer_clock ztimer_clock_t
 ztimer_clock_t forward declaration
typedef void(* ztimer_callback_t) (void *arg)
 Type of callbacks in timers.
typedef uint32_t ztimer_now_t
 type for ztimer_now() result More...


void ztimer_handler (ztimer_clock_t *clock)
 main ztimer callback handler More...
uint32_t ztimer_set (ztimer_clock_t *clock, ztimer_t *timer, uint32_t val)
 Set a timer on a clock. More...
unsigned ztimer_is_set (const ztimer_clock_t *clock, const ztimer_t *timer)
 Check if a timer is currently active. More...
bool ztimer_remove (ztimer_clock_t *clock, ztimer_t *timer)
 Remove a timer from a clock. More...
void ztimer_set_msg (ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, msg_t *msg, kernel_pid_t target_pid)
 Post a message after a delay. More...
int ztimer_msg_receive_timeout (ztimer_clock_t *clock, msg_t *msg, uint32_t timeout)
 receive a message (blocking, with timeout) More...
ztimer_now_t _ztimer_now_extend (ztimer_clock_t *clock)
 ztimer_now() for extending timers
static ztimer_now_t ztimer_now (ztimer_clock_t *clock)
 Get the current time from a clock. More...
void ztimer_periodic_wakeup (ztimer_clock_t *clock, uint32_t *last_wakeup, uint32_t period)
 Suspend the calling thread until the time (last_wakeup + period) More...
void ztimer_sleep (ztimer_clock_t *clock, uint32_t duration)
 Put the calling thread to sleep for the specified number of ticks. More...
static void ztimer_spin (ztimer_clock_t *clock, uint32_t duration)
 Busy-wait specified duration. More...
void ztimer_set_wakeup (ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, kernel_pid_t pid)
 Set a timer that wakes up a thread. More...
void ztimer_set_timeout_flag (ztimer_clock_t *clock, ztimer_t *timer, uint32_t timeout)
 Set timeout thread flag after timeout. More...
int ztimer_mutex_lock_timeout (ztimer_clock_t *clock, mutex_t *mutex, uint32_t timeout)
 Try to lock the given mutex, but give up after timeout. More...
int ztimer_rmutex_lock_timeout (ztimer_clock_t *clock, rmutex_t *rmutex, uint32_t timeout)
 Try to lock the given rmutex, but give up after timeout. More...
void ztimer_init (void)
 Initialize the board-specific default ztimer configuration.
static void ztimer_init_extend (ztimer_clock_t *clock)
 Initialize possible ztimer extension intermediate timer. More...


ztimer_clock_t *const ZTIMER_USEC
 Default ztimer microsecond clock.
ztimer_clock_t *const ZTIMER_MSEC
 Default ztimer millisecond clock.
ztimer_clock_t *const ZTIMER_SEC
 Default ztimer second clock.
ztimer_clock_t *const ZTIMER_USEC_BASE
 Base ztimer for the microsecond clock (ZTIMER_USEC) More...
ztimer_clock_t *const ZTIMER_MSEC_BASE
 Base ztimer for the millisecond clock (ZTIMER_MSEC) More...

Typedef Documentation

◆ ztimer_now_t

type for ztimer_now() result

use ztimer_now() returning uint32_t or alternatively use module ztimer64 with ztimer64_now() returning uint64_t. Will be removed after 2022.10 release.

Definition at line 323 of file ztimer.h.

Function Documentation

◆ ztimer_handler()

void ztimer_handler ( ztimer_clock_t clock)

main ztimer callback handler

This gets called by clock implementations, and must only be called by them with interrupts disabled.

◆ ztimer_init_extend()

static void ztimer_init_extend ( ztimer_clock_t clock)

Initialize possible ztimer extension intermediate timer.

This will basically just set a timer to (clock->max_value >> 1), if max_value is not UINT32_MAX.

This is called automatically by all ztimer backends and extension modules.

Definition at line 731 of file ztimer.h.

◆ ztimer_is_set()

unsigned ztimer_is_set ( const ztimer_clock_t clock,
const ztimer_t timer 

Check if a timer is currently active.

[in]clockztimer clock to operate on
[in]timertimer to check
> 0 if timer is active
0 if timer is not active

◆ ztimer_msg_receive_timeout()

int ztimer_msg_receive_timeout ( ztimer_clock_t clock,
msg_t msg,
uint32_t  timeout 

receive a message (blocking, with timeout)

Similar to msg_receive(), but with a timeout parameter. The function will return after waiting at most timeout ticks.

: This might function might leave a message with type MSG_ZTIMER in the thread's message queue, which must be handled (ignored).
[in]clockztimer clock to operate on
[out]msgpointer to buffer which will be filled if a message is received
[in]timeoutrelative timeout, in clock time units
>=0 if a message was received
-ETIME on timeout

◆ ztimer_mutex_lock_timeout()

int ztimer_mutex_lock_timeout ( ztimer_clock_t clock,
mutex_t mutex,
uint32_t  timeout 

Try to lock the given mutex, but give up after timeout.

[in]clockztimer clock to operate on
[in,out]mutexMutex object to lock
[in]timeouttimeout after which to give up
Return values
0Success, caller has the mutex
-ECANCELEDFailed to obtain mutex within timeout

◆ ztimer_now()

static ztimer_now_t ztimer_now ( ztimer_clock_t clock)

Get the current time from a clock.

There are several caveats to consider when using values returned by ztimer_now() (or comparing those values to results of ztimer_set, which are compatible unless MODULE_ZTMIER_NOW64 is in use):

  • A single value has no meaning of its own. Meaningful results are only ever produced when subtracting values from each other (in the wrapping fashion implied by the use of unsigned integers in C).

    For example, even though it may be the case in some scenarios, the value does not indicate time since system startup.

  • Only values obtained from the same clock can be compared.
  • Two values can only be compared when the clock has been continuously active between the first and the second reading.

    A clock is guaranteed to be active from the time any timer is set (the first opportunity to get a "now" value from it is the return value of ztimer_set) until the time the timer's callback returns. The clock also stays active when timers are set back-to-back (which is the case when the first timer's callback sets the second timer), or when they overlap (which can be known by starting the second timer and afterwards observing that ztimer_is_set or ztimer_remove returns true in a low-priority context).

    In contrast, the clock is not guaranteed to be active if a timer is removed and then a second one is started (even if the thread does not block between these events), or when an expiring timer wakes up a thread that then sets the second timer.

    If the clock was active, then the difference between the second value and the first is then the elapsed time in the clock's unit, modulo 2³² ticks (or 2⁶⁴ when using the ZTIMER_NOW64 module).

  • A difference between two values (calculated in the usual wrapping way) is guaranteed to be exactly the elapsed time (not just modulo 2³²) if there exists a single timer that is continuously set while both readings are taken (which in particular means that the clock was continuously active), and the timer is observed to be still set when after the second reading an execution context with lower priority than the ZTimer interrupt has run. (In particular, this is the case in a thread context when interrupts are enabled).

    For example, this sequence of events will return usable values:

    • In a thread, a timer is set.
    • Some interrupt fires, and start = ztimer_now(ZTIMER_MSEC) is set in the handler.
    • The interrupt fires again, and duration = start - ztimer_now(ZTIMER_MSEC) is stored.
    • Back in the thread context, ztimer_remove on the timer returns true.

      Only now, duration can be known to be a duration in milliseconds.

    (By comparison, if the timer were removed right inside the second interrupt, then duration might either be correct, or it might be 5 milliseconds when really 2³² + 5 milliseconds have elapsed)

    The requirement of the execution contexts can be dispensed with, if the set timer is shorter than the wrap-around time of the clock by at least the maximum duration the full system is allowed to spend between interrupt servicing opportunities. That time varies by setup, but an upper bound of 1 minute is conservative enough for system modules to use.

    For example, this sequence of events will also return usable values:

    • A mutex is locked, and a timer is set to unlock it on the millisecond timer after 1 hour. (This is way less than the wrap-around time of around 50 days).
    • The return value of setting the timer is noted as start time.
    • Some interrupt fires, and ztimer_now() is taken. Then (still inside the ISR), mutex_trylock is used to test for whether the interrupt is still locked (indicating that the timer has not been processed). If locking failed, the difference is valid and can be used immediately. Otherwise, the mutex needs to be freed again, and the difference is discarded (it can be stored as "longer than 1 hour").
  • To compare two values T1 and T2 without additional knowledge (eg. of a maximum time difference between them), it has to be known which value was read earlier, so that the earlier can be subtracted from the later.

    If that is not known, an easy solution is to store a base value T0 inside the same single-timer window as T1 and T2, and then compare (T2 - T0) and (T1 - T0) to see which of the events occurred earlier.

The above criteria are conservative API guarantees of ztimer_now. There can be additional properties of a system that allow additional usage patterns; these need to be evaluated case-by-case. (For example, a ZTimer backed by a timer that never stops might be comparable even without a running timer.)

All the above need to be considered before using the results of this function. Not considering them may give results that appear to be valid, but that can change without prior warning, e.g. when unrelated components are altered that change the systems's power management behavior.
[in]clockztimer clock to operate on
Current count on clock

Definition at line 600 of file ztimer.h.

◆ ztimer_periodic_wakeup()

void ztimer_periodic_wakeup ( ztimer_clock_t clock,
uint32_t *  last_wakeup,
uint32_t  period 

Suspend the calling thread until the time (last_wakeup + period)

This function can be used to create periodic wakeups.

When the function returns, last_wakeup is set to (last_wakeup + period).

last_wakeup should be set to ztimer_now(clock) before first call of the function.

If the time (last_wakeup + period) has already passed, the function sets last_wakeup to last_wakeup + period and returns immediately.

[in]clockztimer clock to operate on
[in]last_wakeupbase time stamp for the wakeup
[in]periodtime in ticks that will be added to last_wakeup

◆ ztimer_remove()

bool ztimer_remove ( ztimer_clock_t clock,
ztimer_t timer 

Remove a timer from a clock.

This will place timer in the timer targets queue for clock.

This function does nothing if timer is not found in the timer queue of clock.

[in]clockztimer clock to operate on
[in]timertimer entry to remove
Return values
trueThe timer was removed (and thus its callback neither was nor will be called by ztimer).
falseThe timer fired previously or is not set on the clock at all.

◆ ztimer_rmutex_lock_timeout()

int ztimer_rmutex_lock_timeout ( ztimer_clock_t clock,
rmutex_t rmutex,
uint32_t  timeout 

Try to lock the given rmutex, but give up after timeout.

[in]clockztimer clock to operate on
[in,out]rmutexrmutex object to lock
[in]timeouttimeout after which to give up
Return values
0Success, caller has the rmutex
-ECANCELEDFailed to obtain rmutex within timeout

◆ ztimer_set()

uint32_t ztimer_set ( ztimer_clock_t clock,
ztimer_t timer,
uint32_t  val 

Set a timer on a clock.

This will place timer in the timer targets queue of clock.

The memory pointed to by timer is not copied and must remain in scope until the callback is fired or the timer is removed via ztimer_remove
[in]clockztimer clock to operate on
[in]timertimer entry to set
[in]valtimer target (relative ticks from now)
The value of ztimer_now() that timer was set against (now() + @p val = absolute trigger time).

◆ ztimer_set_msg()

void ztimer_set_msg ( ztimer_clock_t clock,
ztimer_t timer,
uint32_t  offset,
msg_t msg,
kernel_pid_t  target_pid 

Post a message after a delay.

This function sets a timer that will send a message offset ticks from now.

The memory pointed to by timer and msg will not be copied, i.e. *timer and *msg needs to remain valid until the timer has triggered.
[in]clockztimer clock to operate on
[in]timerztimer timer struct to use
[in]offsetticks from now
[in]msgpointer to msg that will be sent
[in]target_pidpid the message will be sent to

◆ ztimer_set_timeout_flag()

void ztimer_set_timeout_flag ( ztimer_clock_t clock,
ztimer_t timer,
uint32_t  timeout 

Set timeout thread flag after timeout.

This function will set THREAD_FLAG_TIMEOUT on the current thread after timeout usec have passed.

[in]clockztimer clock to operate on
[in]timertimer struct to use
[in]timeouttimeout in ztimer_clock's ticks

◆ ztimer_set_wakeup()

void ztimer_set_wakeup ( ztimer_clock_t clock,
ztimer_t timer,
uint32_t  offset,
kernel_pid_t  pid 

Set a timer that wakes up a thread.

This function sets a timer that will wake up a thread when the timer has expired.

[in]clockztimer clock to operate on
[in]timertimer struct to work with.
[in]offsetclock ticks from now
[in]pidpid of the thread that will be woken up

◆ ztimer_sleep()

void ztimer_sleep ( ztimer_clock_t clock,
uint32_t  duration 

Put the calling thread to sleep for the specified number of ticks.

[in]clockztimer clock to use
[in]durationduration of sleep, in ztimer time units

◆ ztimer_spin()

static void ztimer_spin ( ztimer_clock_t clock,
uint32_t  duration 

Busy-wait specified duration.

: This blocks lower priority threads. Use only for very short delays.
[in]clockztimer clock to use
[in]durationduration to spin, in clock time units

Definition at line 653 of file ztimer.h.

Variable Documentation


ztimer_clock_t* const ZTIMER_MSEC_BASE

Base ztimer for the millisecond clock (ZTIMER_MSEC)

This ztimer will reference the counter device object at the end of the chain of ztimer_clock_t for ZTIMER_MSEC.

If ztimer_periph_rtt is not used then ZTIMER_MSEC_BASE will reference the same base as ZTIMER_USEC_BASE.

If the base counter device object's frequency (CONFIG_ZTIMER_MSEC_BASE_FREQ) is not 1KHz then ZTIMER_MSEC will be converted on top of this one. Otherwise they will reference the same ztimer_clock.

To avoid chained conversions its better to base new ztimer_clock on top of ZTIMER_MSEC_BASE running at CONFIG_ZTIMER_MSEC_BASE_FREQ.


ztimer_clock_t* const ZTIMER_USEC_BASE

Base ztimer for the microsecond clock (ZTIMER_USEC)

This ztimer will reference the counter device object at the end of the chain of ztimer_clock_t for ZTIMER_USEC.

If the base counter device object's frequency (CONFIG_ZTIMER_USEC_BASE_FREQ) is not 1MHz then ZTIMER_USEC will be converted on top of this one. Otherwise they will reference the same ztimer_clock.

To avoid chained conversions its better to base new ztimer_clock on top of ZTIMER_USEC_BASE running at CONFIG_ZTIMER_USEC_BASE_FREQ.