zephyr/doc/kernel/services/timing/timers.rst

246 lines
8.0 KiB
ReStructuredText

.. _timers_v2:
Timers
######
A :dfn:`timer` is a kernel object that measures the passage of time
using the kernel's system clock. When a timer's specified time limit
is reached it can perform an application-defined action,
or it can simply record the expiration and wait for the application
to read its status.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of timers can be defined (limited only by available RAM). Each timer
is referenced by its memory address.
A timer has the following key properties:
* A :dfn:`duration` specifying the time interval before the timer
expires for the first time. This is a ``k_timeout_t`` value that
may be initialized via different units.
* A :dfn:`period` specifying the time interval between all timer
expirations after the first one, also a ``k_timeout_t``. It must be
non-negative. A period of ``K_NO_WAIT`` (i.e. zero) or
``K_FOREVER`` means that the timer is a one shot timer that stops
after a single expiration. (For example then, if a timer is started
with a duration of 200 and a period of 75, it will first expire
after 200ms and then every 75ms after that.)
* An :dfn:`expiry function` that is executed each time the timer expires.
The function is executed by the system clock interrupt handler.
If no expiry function is required a ``NULL`` function can be specified.
* A :dfn:`stop function` that is executed if the timer is stopped prematurely
while running. The function is executed by the thread that stops the timer.
If no stop function is required a ``NULL`` function can be specified.
* A :dfn:`status` value that indicates how many times the timer has expired
since the status value was last read.
A timer must be initialized before it can be used. This specifies its
expiry function and stop function values, sets the timer's status to zero,
and puts the timer into the **stopped** state.
A timer is **started** by specifying a duration and a period.
The timer's status is reset to zero, then the timer enters
the **running** state and begins counting down towards expiry.
Note that the timer's duration and period parameters specify
**minimum** delays that will elapse. Because of internal system timer
precision (and potentially runtime interactions like interrupt delay)
it is possible that more time may have passed as measured by reads
from the relevant system time APIs. But at least this much time is
guaranteed to have elapsed.
When a running timer expires its status is incremented
and the timer executes its expiry function, if one exists;
If a thread is waiting on the timer, it is unblocked.
If the timer's period is zero the timer enters the stopped state;
otherwise the timer restarts with a new duration equal to its period.
A running timer can be stopped in mid-countdown, if desired.
The timer's status is left unchanged, then the timer enters the stopped state
and executes its stop function, if one exists.
If a thread is waiting on the timer, it is unblocked.
Attempting to stop a non-running timer is permitted,
but has no effect on the timer since it is already stopped.
A running timer can be restarted in mid-countdown, if desired.
The timer's status is reset to zero, then the timer begins counting down
using the new duration and period values specified by the caller.
If a thread is waiting on the timer, it continues waiting.
A timer's status can be read directly at any time to determine how many times
the timer has expired since its status was last read.
Reading a timer's status resets its value to zero.
The amount of time remaining before the timer expires can also be read;
a value of zero indicates that the timer is stopped.
A thread may read a timer's status indirectly by **synchronizing**
with the timer. This blocks the thread until the timer's status is non-zero
(indicating that it has expired at least once) or the timer is stopped;
if the timer status is already non-zero or the timer is already stopped
the thread continues without waiting. The synchronization operation
returns the timer's status and resets it to zero.
.. note::
Only a single user should examine the status of any given timer,
since reading the status (directly or indirectly) changes its value.
Similarly, only a single thread at a time should synchronize
with a given timer. ISRs are not permitted to synchronize with timers,
since ISRs are not allowed to block.
Implementation
**************
Defining a Timer
================
A timer is defined using a variable of type :c:struct:`k_timer`.
It must then be initialized by calling :c:func:`k_timer_init`.
The following code defines and initializes a timer.
.. code-block:: c
struct k_timer my_timer;
extern void my_expiry_function(struct k_timer *timer_id);
k_timer_init(&my_timer, my_expiry_function, NULL);
Alternatively, a timer can be defined and initialized at compile time
by calling :c:macro:`K_TIMER_DEFINE`.
The following code has the same effect as the code segment above.
.. code-block:: c
K_TIMER_DEFINE(my_timer, my_expiry_function, NULL);
Using a Timer Expiry Function
=============================
The following code uses a timer to perform a non-trivial action on a periodic
basis. Since the required work cannot be done at interrupt level,
the timer's expiry function submits a work item to the
:ref:`system workqueue <workqueues_v2>`, whose thread performs the work.
.. code-block:: c
void my_work_handler(struct k_work *work)
{
/* do the processing that needs to be done periodically */
...
}
K_WORK_DEFINE(my_work, my_work_handler);
void my_timer_handler(struct k_timer *dummy)
{
k_work_submit(&my_work);
}
K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);
...
/* start periodic timer that expires once every second */
k_timer_start(&my_timer, K_SECONDS(1), K_SECONDS(1));
Reading Timer Status
====================
The following code reads a timer's status directly to determine
if the timer has expired on not.
.. code-block:: c
K_TIMER_DEFINE(my_status_timer, NULL, NULL);
...
/* start one shot timer that expires after 200 ms */
k_timer_start(&my_status_timer, K_MSEC(200), K_NO_WAIT);
/* do work */
...
/* check timer status */
if (k_timer_status_get(&my_status_timer) > 0) {
/* timer has expired */
} else if (k_timer_remaining_get(&my_status_timer) == 0) {
/* timer was stopped (by someone else) before expiring */
} else {
/* timer is still running */
}
Using Timer Status Synchronization
==================================
The following code performs timer status synchronization to allow a thread
to do useful work while ensuring that a pair of protocol operations
are separated by the specified time interval.
.. code-block:: c
K_TIMER_DEFINE(my_sync_timer, NULL, NULL);
...
/* do first protocol operation */
...
/* start one shot timer that expires after 500 ms */
k_timer_start(&my_sync_timer, K_MSEC(500), K_NO_WAIT);
/* do other work */
...
/* ensure timer has expired (waiting for expiry, if necessary) */
k_timer_status_sync(&my_sync_timer);
/* do second protocol operation */
...
.. note::
If the thread had no other work to do it could simply sleep
between the two protocol operations, without using a timer.
Suggested Uses
**************
Use a timer to initiate an asynchronous operation after a specified
amount of time.
Use a timer to determine whether or not a specified amount of time has
elapsed. In particular, timers should be used when higher precision
and/or unit control is required than that afforded by the simpler
:c:func:`k_sleep` and :c:func:`k_usleep` calls.
Use a timer to perform other work while carrying out operations
involving time limits.
.. note::
If a thread needs to measure the time required to perform an operation
it can read the :ref:`system clock or the hardware clock <kernel_timing>`
directly, rather than using a timer.
Configuration Options
*********************
Related configuration options:
* None
API Reference
*************
.. doxygengroup:: timer_apis