| .. _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()` |