zephyr/doc/kernel/timing/timers.rst

252 lines
7.9 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. 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, measured in milliseconds. It must be greater than zero.
* A :dfn:`period` specifying the time interval between all timer expirations
after the first one, measured in milliseconds. It must be non-negative.
A period of zero 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 :c:macro:`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 :c:macro:`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.
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.
Timer Limitations
=================
Since timers are based on the system clock, the delay values specified
when using a timer are **minimum** values.
(See :ref:`clock_limitations`.)
Implementation
**************
Defining a Timer
================
A timer is defined using a variable of type :c:type:`struct k_timer`.
It must then be initialized by calling :cpp: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), 0);
/* 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), 0);
/* 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.
Use a timer to perform other work while carrying out operations
involving time limits.
.. note::
If a thread has no other work to perform while waiting for time to pass
it should call :cpp:func:`k_sleep()`.
If a thread needs to measure the time required to perform an operation
it can read the :ref:`system clock or the hardware clock <clocks_v2>`
directly, rather than using a timer.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following timer APIs are provided by :file:`kernel.h`:
* :c:macro:`K_TIMER_DEFINE`
* :cpp:func:`k_timer_init()`
* :cpp:func:`k_timer_start()`
* :cpp:func:`k_timer_stop()`
* :cpp:func:`k_timer_status_get()`
* :cpp:func:`k_timer_status_sync()`
* :cpp:func:`k_timer_remaining_get()`