High level timer abstraction layer. More...
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().
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.)
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:
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.
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.
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.
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:
ztimer_now(ZTIMER_USEC);
They also need to be added to USEMODULE using the names ztimer_usec
, ztimer_msec
and ztimer_sec
.
Modules | |
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 | |
ztimer stop watch | |
Measure time with ztimer. | |
ztimer_xtimer_compat: xtimer wrapper | |
Files | |
file | ztimer.h |
ztimer API | |
file | config.h |
ztimer default configuration | |
file | periodic.h |
Periodic 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... | |
Macros | |
#define | ZTIMER_CLOCK_NO_REQUIRED_PM_MODE (UINT8_MAX) |
Disables interaction with pm_layered for a clock. | |
#define | MSG_ZTIMER 0xc83e |
msg type used by ztimer_msg_receive_timeout | |
Typedefs | |
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 | |
Functions | |
void | ztimer_handler (ztimer_clock_t *clock) |
main ztimer callback handler | |
bool | ztimer_acquire (ztimer_clock_t *clock) |
Acquire a clock. | |
bool | ztimer_release (ztimer_clock_t *clock) |
Release a clock. | |
uint32_t | ztimer_set (ztimer_clock_t *clock, ztimer_t *timer, uint32_t val) |
Set a timer on a clock. | |
unsigned | ztimer_is_set (const ztimer_clock_t *clock, const ztimer_t *timer) |
Check if a timer is currently active. | |
bool | ztimer_remove (ztimer_clock_t *clock, ztimer_t *timer) |
Remove a timer from a clock. | |
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. | |
int | ztimer_msg_receive_timeout (ztimer_clock_t *clock, msg_t *msg, uint32_t timeout) |
receive a message (blocking, with timeout) | |
ztimer_now_t | _ztimer_now_extend (ztimer_clock_t *clock) |
ztimer_now() for extending timers | |
void | _ztimer_assert_clock_active (ztimer_clock_t *clock) |
asserts the given clock to be active | |
static ztimer_now_t | ztimer_now (ztimer_clock_t *clock) |
Get the current time from a clock. | |
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 ) | |
void | ztimer_sleep (ztimer_clock_t *clock, uint32_t duration) |
Put the calling thread to sleep for the specified number of ticks. | |
static void | ztimer_spin (ztimer_clock_t *clock, uint32_t duration) |
Busy-wait specified duration. | |
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. | |
void | ztimer_set_timeout_flag (ztimer_clock_t *clock, ztimer_t *timer, uint32_t timeout) |
Set timeout thread flag after timeout . | |
void | ztimer_mutex_unlock (ztimer_clock_t *clock, ztimer_t *timer, uint32_t timeout, mutex_t *mutex) |
Unlock mutex after 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 . | |
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 . | |
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. | |
Variables | |
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) | |
ztimer_clock_t *const | ZTIMER_MSEC_BASE |
Base ztimer for the millisecond clock (ZTIMER_MSEC) | |
#define MSG_ZTIMER 0xc83e |
#define ZTIMER_CLOCK_NO_REQUIRED_PM_MODE (UINT8_MAX) |
typedef struct ztimer_base ztimer_base_t |
typedef void(* ztimer_callback_t) (void *arg) |
typedef struct ztimer_clock ztimer_clock_t |
bool ztimer_acquire | ( | ztimer_clock_t * | clock | ) |
Acquire a clock.
This will indicate the the underlying clock is required to be running. If time differences are measured using ztimer_now this will make sure ztimer won't turn of the clock source.
[in] | clock | ztimer clock to operate on |
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.
|
inlinestatic |
unsigned ztimer_is_set | ( | const ztimer_clock_t * | clock, |
const ztimer_t * | timer | ||
) |
Check if a timer is currently active.
[in] | clock | ztimer clock to operate on |
[in] | timer | timer to check |
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.
[in] | clock | ztimer clock to operate on |
[out] | msg | pointer to buffer which will be filled if a message is received |
[in] | timeout | relative timeout, in clock time units |
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] | clock | ztimer clock to operate on |
[in,out] | mutex | Mutex object to lock |
[in] | timeout | timeout after which to give up |
0 | Success, caller has the mutex |
-ECANCELED | Failed to obtain mutex within timeout |
void ztimer_mutex_unlock | ( | ztimer_clock_t * | clock, |
ztimer_t * | timer, | ||
uint32_t | timeout, | ||
mutex_t * | mutex | ||
) |
Unlock mutex after timeout
.
This function will unlock the given mutex after the timeout has passed.
[in] | clock | ztimer clock to operate on |
[in] | timer | timer struct to use |
[in] | timeout | timeout in ztimer_clock's ticks |
[in] | mutex | mutex to unlock after timeout |
|
inlinestatic |
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):
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.
Two values can only be compared when the clock has been continuously active between the first and the second reading.
Calling ztimer_acquire before using ztimer_now()
is the preferred way to guarantee that a clock is continuously active. Make sure to call the corresponding ztimer_release after the last ztimer_now()
call.
A clock is also 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.
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:
start = ztimer_now(ZTIMER_MSEC)
is set in the handler.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:
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.)
clock
) before fetching the clock's current time.[in] | clock | ztimer clock to operate on |
clock
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.
clock
) before making use of ztimer_periodic_wakeup. After usage ztimer_release(clock
) should be called.[in] | clock | ztimer clock to operate on |
[in] | last_wakeup | base time stamp for the wakeup |
[in] | period | time in ticks that will be added to last_wakeup |
bool ztimer_release | ( | ztimer_clock_t * | clock | ) |
Release a clock.
This will indicate the the underlying clock isn't required to be running anymore and may be turned off.
[in] | clock | ztimer clock to operate on |
bool ztimer_remove | ( | ztimer_clock_t * | clock, |
ztimer_t * | timer | ||
) |
Remove a timer from a clock.
This will remove timer
from the timer targets queue for clock
.
This function does nothing if timer
is not found in the timer queue of clock
.
[in] | clock | ztimer clock to operate on |
[in] | timer | timer entry to remove |
true | The timer was removed (and thus its callback neither was nor will be called by ztimer). |
false | The timer fired previously or is not set on the clock at all. |
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] | clock | ztimer clock to operate on |
[in,out] | rmutex | rmutex object to lock |
[in] | timeout | timeout after which to give up |
0 | Success, caller has the rmutex |
-ECANCELED | Failed to obtain rmutex within timeout |
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
.
timer
is not copied and must remain in scope until the callback is fired or the timer is removed via ztimer_remove[in] | clock | ztimer clock to operate on |
[in] | timer | timer entry to set |
[in] | val | timer target (relative ticks from now) |
timer
was set against (now() + @p val = absolute trigger time
). 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.
timer
and msg
will not be copied, i.e. *timer
and *msg
needs to remain valid until the timer has triggered.[in] | clock | ztimer clock to operate on |
[in] | timer | ztimer timer struct to use |
[in] | offset | ticks from now |
[in] | msg | pointer to msg that will be sent |
[in] | target_pid | pid the message will be sent to |
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] | clock | ztimer clock to operate on |
[in] | timer | timer struct to use |
[in] | timeout | timeout in ztimer_clock's ticks |
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] | clock | ztimer clock to operate on |
[in] | timer | timer struct to work with. |
[in] | offset | clock ticks from now |
[in] | pid | pid of the thread that will be woken up |
void ztimer_sleep | ( | ztimer_clock_t * | clock, |
uint32_t | duration | ||
) |
Put the calling thread to sleep for the specified number of ticks.
[in] | clock | ztimer clock to use |
[in] | duration | duration of sleep, in ztimer time units |
|
inlinestatic |
|
extern |
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.
|
extern |
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.