TNeo  v1.07
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tn_timer.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeo: real-time kernel initially based on TNKernel
4  *
5  * TNKernel: copyright © 2004, 2013 Yuri Tiomkin.
6  * PIC32-specific routines: copyright © 2013, 2014 Anders Montonen.
7  * TNeo: copyright © 2014 Dmitry Frank.
8  *
9  * TNeo was born as a thorough review and re-implementation of
10  * TNKernel. The new kernel has well-formed code, inherited bugs are fixed
11  * as well as new features being added, and it is tested carefully with
12  * unit-tests.
13  *
14  * API is changed somewhat, so it's not 100% compatible with TNKernel,
15  * hence the new name: TNeo.
16  *
17  * Permission to use, copy, modify, and distribute this software in source
18  * and binary forms and its documentation for any purpose and without fee
19  * is hereby granted, provided that the above copyright notice appear
20  * in all copies and that both that copyright notice and this permission
21  * notice appear in supporting documentation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY FRANK OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 /**
38  * \file
39  *
40  * Timer is a kernel object that is used to ask the kernel to call some
41  * user-provided function at a particular time in the future, based on the
42  * $(TN_SYS_TIMER_LINK) tick.
43  *
44  * If you need to repeatedly wake up particular task, you can create semaphore
45  * which you should \ref tn_sem_wait() "wait for" in the task, and \ref
46  * tn_sem_isignal() "signal" in the timer callback (remember that you should
47  * use `tn_sem_isignal()` in this callback, since it is called from an ISR).
48  *
49  * If you need to perform rather fast action (such as toggle some pin, or the
50  * like), consider doing that right in the timer callback, in order to avoid
51  * context switch overhead.
52  *
53  * The timer callback approach provides ultimate flexibility.
54  *
55  * In the spirit of TNeo, timers are as lightweight as possible. That's
56  * why there is only one type of timer: the single-shot timer. If you need your
57  * timer to fire repeatedly, you can easily restart it from the timer function
58  * by the `tn_timer_start()`, so it's not a problem.
59  *
60  * When timer fires, the user-provided function is called. Be aware of the
61  * following:
62  *
63  * - Function is called from an ISR context (namely, from $(TN_SYS_TIMER_LINK)
64  * ISR, by the `tn_tick_int_processing()`);
65  * - Function is called with global interrupts disabled.
66  *
67  * Consequently:
68  *
69  * - It's legal to call interrupt services from this function;
70  * - You should make sure that your interrupt stack is enough for this
71  * function;
72  * - The function should be as fast as possible;
73  * - The function should not enable interrupts unconditionally. Consider using
74  * `tn_arch_sr_save_int_dis()` and `tn_arch_sr_restore()` if you need.
75  *
76  * See `#TN_TimerFunc` for the prototype of the function that could be
77  * scheduled.
78  *
79  * TNeo offers two implementations of timers: static and dynamic. Refer
80  * to the page \ref time_ticks for details.
81  *
82  * \section timers_static_implementation Implementation of static timers
83  *
84  * Although you don't have to understand the implementation of timers to use
85  * them, it is probably worth knowing, particularly because the kernel have an
86  * option `#TN_TICK_LISTS_CNT` to customize the balance between performance of
87  * `tn_tick_int_processing()` and memory occupied by timers.
88  *
89  * The easiest implementation of timers could be something like this: we
90  * have just a single list with all active timers, and at every system tick
91  * we should walk through all the timers in this list, and do the following
92  * with each timer:
93  *
94  * - Decrement timeout by 1
95  * - If new timeout is 0, then remove that timer from the list (i.e. make timer
96  * inactive), and fire the appropriate timer function.
97  *
98  * This approach has drawbacks:
99  *
100  * - We can't manage timers from the function called by timer. If we do so
101  * (say, if we start new timer), then the timer list gets modified. But we
102  * are currently iterating through this list, so, it's quite easy to mix
103  * things up.
104  * - It is inefficient on rather large amount of timers and/or with large
105  * timeouts: we should iterate through all of them each system tick.
106  *
107  * The latter is probably not so critical in the embedded world since large
108  * amount of timers is unlikely there; whereas the former is actually notable.
109  *
110  * So, different approach was applied. The main idea is taken from the mainline
111  * Linux kernel source, but the implementation was simplified much because (1)
112  * embedded systems have much less resources, and (2) the kernel doesn't need
113  * to scale as well as Linux does. You can read about Linux timers
114  * implementation in the book "Linux Device Drivers", 3rd edition:
115  *
116  * - Time, Delays, and Deferred Work
117  * - Kernel Timers
118  * - The Implementation of Kernel Timers
119  *
120  * This book is freely available at http://lwn.net/Kernel/LDD3/ .
121  *
122  * So, TNeo's implementation:
123  *
124  * We have configurable value `N` that is a power of two, typical values are
125  * `4`, `8` or `16`.
126  *
127  * If the timer expires in the next `1` to `(N - 1)` system ticks, it is added
128  * to one of the `N` lists (the so-called "tick" lists) devoted to short-range
129  * timers using the least significant bits of the `timeout` value. If it
130  * expires farther in the future, it is added to the "generic" list.
131  *
132  * Each `N`-th system tick, all the timers from "generic" list are walked
133  * through, and the following is performed with each timer:
134  *
135  * - `timeout` value decremented by `N`
136  * - if resulting `timeout` is less than `N`, timer is moved to the appropriate
137  * "tick" list.
138  *
139  * At *every* system tick, all the timers from current "tick" list are fired
140  * unconditionally. This is an efficient and nice solution.
141  *
142  * The attentive reader may want to ask why do we use `(N - 1)` "tick" lists if
143  * we actually have `N` lists. That's because, again, we want to be able to
144  * modify timers from the timer function. If we use `N` lists, and user wants
145  * to add new timer with `timeout` equal to `N`, then new timer will be added
146  * to the same list which is iterated through at the moment, and things will be
147  * mixed up.
148  *
149  * If we use `(N - 1)` lists, we are guaranteed that new timers can't be added
150  * to the current "tick" list while we are iterating through it.
151  * (although timer can be deleted from that list, but it's ok)
152  *
153  * The `N` in the TNeo is configured by the compile-time option
154  * `#TN_TICK_LISTS_CNT`.
155  */
156 
157 
158 #ifndef _TN_TIMER_H
159 #define _TN_TIMER_H
160 
161 /*******************************************************************************
162  * INCLUDED FILES
163  ******************************************************************************/
164 
165 #include "tn_list.h"
166 #include "tn_common.h"
167 
168 
169 
170 #ifdef __cplusplus
171 extern "C" { /*}*/
172 #endif
173 
174 /*******************************************************************************
175  * PUBLIC TYPES
176  ******************************************************************************/
177 
178 struct TN_Timer;
179 
180 
181 /**
182  * Prototype of the function that should be called by timer.
183  *
184  * When timer fires, the user-provided function is called. Be aware of the
185  * following:
186  * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK)
187  * ISR, by the `tn_tick_int_processing()`);
188  * - Function is called with global interrupts disabled.
189  *
190  * Consequently:
191  *
192  * - It's legal to call interrupt services from this function;
193  * - The function should be as fast as possible.
194  *
195  * @param timer
196  * Timer that caused function to be called
197  * @param p_user_data
198  * The user-provided pointer given to `tn_timer_create()`.
199  */
200 typedef void (TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data);
201 
202 /**
203  * Timer
204  */
205 struct TN_Timer {
206  ///
207  /// id for object validity verification.
208  /// This field is in the beginning of the structure to make it easier
209  /// to detect memory corruption.
211  ///
212  /// A list item to be included in the $(TN_SYS_TIMER_LINK) queue
214  ///
215  /// Function to be called by timer
217  ///
218  /// User data pointer that is given to user-provided `func`.
219  void *p_user_data;
220 
221 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
222  ///
223  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
224  ///
225  /// Tick count value when timer was started
227  ///
228  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
229  ///
230  /// Timeout value (it is set just once, and stays unchanged until timer is
231  /// expired, cancelled or restarted)
233 #endif
234 
235 #if !TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
236  ///
237  /// $(TN_IF_ONLY_DYNAMIC_TICK_NOT_SET)
238  ///
239  /// Current (left) timeout value
241 #endif
242 };
243 
244 
245 
246 
247 
248 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
249 
250 /**
251  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
252  *
253  * Prototype of callback function that should schedule next time to call
254  * `tn_tick_int_processing()`.
255  *
256  * See `tn_callback_dyn_tick_set()`
257  *
258  * @param timeout
259  * Timeout after which `tn_tick_int_processing()` should be called next
260  * time. Note the following:
261  * - It might be `#TN_WAIT_INFINITE`, which means that there are no active
262  * timeouts, and so, there's no need for tick interrupt at all.
263  * - It might be `0`; in this case, it's <i>already</i> time to call
264  * `tn_tick_int_processing()`. You might want to set interrupt request
265  * bit then, in order to get to it as soon as possible.
266  * - In other cases, the function should schedule next call to
267  * `tn_tick_int_processing()` in the `timeout` tick periods.
268  *
269  */
270 typedef void (TN_CBTickSchedule)(TN_TickCnt timeout);
271 
272 /**
273  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
274  *
275  * Prototype of callback function that should return current system tick
276  * counter value.
277  *
278  * See `tn_callback_dyn_tick_set()`
279  *
280  * @return current system tick counter value.
281  */
282 typedef TN_TickCnt (TN_CBTickCntGet)(void);
283 
284 #endif
285 
286 
287 
288 
289 /*******************************************************************************
290  * PROTECTED GLOBAL DATA
291  ******************************************************************************/
292 
293 /*******************************************************************************
294  * DEFINITIONS
295  ******************************************************************************/
296 
297 /*******************************************************************************
298  * PUBLIC FUNCTION PROTOTYPES
299  ******************************************************************************/
300 
301 /**
302  * Construct the timer. `id_timer` field should not contain
303  * `#TN_ID_TIMER`, otherwise, `#TN_RC_WPARAM` is returned.
304  *
305  * $(TN_CALL_FROM_TASK)
306  * $(TN_CALL_FROM_ISR)
307  * $(TN_LEGEND_LINK)
308  *
309  * @param timer
310  * Pointer to already allocated `struct TN_Timer`
311  * @param func
312  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
313  * @param p_user_data
314  * User data pointer that is given to user-provided `func`.
315  *
316  * @return
317  * * `#TN_RC_OK` if timer was successfully created;
318  * * `#TN_RC_WPARAM` if wrong params were given.
319  */
321  struct TN_Timer *timer,
322  TN_TimerFunc *func,
323  void *p_user_data
324  );
325 
326 /**
327  * Destruct the timer. If the timer is active, it is cancelled first.
328  *
329  * $(TN_CALL_FROM_TASK)
330  * $(TN_CALL_FROM_ISR)
331  * $(TN_LEGEND_LINK)
332  *
333  * @param timer timer to destruct
334  *
335  * @return
336  * * `#TN_RC_OK` if timer was successfully deleted;
337  * * `#TN_RC_WCONTEXT` if called from wrong context;
338  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
339  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
340  */
341 enum TN_RCode tn_timer_delete(struct TN_Timer *timer);
342 
343 /**
344  * Start or restart the timer: that is, schedule the timer's function (given to
345  * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`.
346  *
347  * It is legal to restart already active timer. In this case, the timer will be
348  * cancelled first.
349  *
350  * $(TN_CALL_FROM_TASK)
351  * $(TN_CALL_FROM_ISR)
352  * $(TN_LEGEND_LINK)
353  *
354  * @param timer
355  * Timer to start
356  * @param timeout
357  * Number of system ticks after which timer should fire (i.e. function
358  * should be called). **Note** that `timeout` can't be `#TN_WAIT_INFINITE` or
359  * `0`.
360  *
361  * @return
362  * * `#TN_RC_OK` if timer was successfully started;
363  * * `#TN_RC_WCONTEXT` if called from wrong context;
364  * * `#TN_RC_WPARAM` if wrong params were given: say, `timeout` is either
365  * `#TN_WAIT_INFINITE` or `0`.
366  * * If `#TN_CHECK_PARAM` is non-zero, additional return code
367  * is available: `#TN_RC_INVALID_OBJ`.
368  */
369 enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_TickCnt timeout);
370 
371 /**
372  * If timer is active, cancel it. If timer is already inactive, nothing is
373  * changed.
374  *
375  * $(TN_CALL_FROM_TASK)
376  * $(TN_CALL_FROM_ISR)
377  * $(TN_LEGEND_LINK)
378  *
379  * @param timer
380  * Timer to cancel
381  *
382  * @return
383  * * `#TN_RC_OK` if timer was successfully cancelled;
384  * * `#TN_RC_WCONTEXT` if called from wrong context;
385  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
386  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
387  */
388 enum TN_RCode tn_timer_cancel(struct TN_Timer *timer);
389 
390 /**
391  * Set user-provided function and pointer to user data for the timer.
392  * Can be called if timer is either active or inactive.
393  *
394  * $(TN_CALL_FROM_TASK)
395  * $(TN_CALL_FROM_ISR)
396  * $(TN_LEGEND_LINK)
397  *
398  * @param timer
399  * Pointer to timer
400  * @param func
401  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
402  * @param p_user_data
403  * User data pointer that is given to user-provided `func`.
404  *
405  * @return
406  * * `#TN_RC_OK` if operation was successfull;
407  * * `#TN_RC_WPARAM` if wrong params were given.
408  */
410  struct TN_Timer *timer,
411  TN_TimerFunc *func,
412  void *p_user_data
413  );
414 
415 /**
416  * Returns whether given timer is active or inactive.
417  *
418  * $(TN_CALL_FROM_TASK)
419  * $(TN_CALL_FROM_ISR)
420  * $(TN_LEGEND_LINK)
421  *
422  * @param timer
423  * Pointer to timer
424  * @param p_is_active
425  * Pointer to `#TN_BOOL` variable in which resulting value should be stored
426  *
427  * @return
428  * * `#TN_RC_OK` if operation was successfull;
429  * * `#TN_RC_WPARAM` if wrong params were given.
430  */
431 enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, TN_BOOL *p_is_active);
432 
433 /**
434  * Returns how many $(TN_SYS_TIMER_LINK) ticks (at most) is left for the timer
435  * to expire. If timer is inactive, 0 is returned.
436  *
437  * $(TN_CALL_FROM_TASK)
438  * $(TN_CALL_FROM_ISR)
439  * $(TN_LEGEND_LINK)
440  *
441  * @param timer
442  * Pointer to timer
443  * @param p_time_left
444  * Pointer to `#TN_TickCnt` variable in which resulting value should be
445  * stored
446  *
447  * @return
448  * * `#TN_RC_OK` if operation was successfull;
449  * * `#TN_RC_WPARAM` if wrong params were given.
450  */
452  struct TN_Timer *timer,
453  TN_TickCnt *p_time_left
454  );
455 
456 #ifdef __cplusplus
457 } /* extern "C" */
458 #endif
459 
460 #endif // _TN_TIMER_H
461 
462 /*******************************************************************************
463  * end of file
464  ******************************************************************************/
465 
466 
enum TN_RCode tn_timer_cancel(struct TN_Timer *timer)
If timer is active, cancel it.
enum TN_RCode tn_timer_create(struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data)
Construct the timer.
struct TN_ListItem timer_queue
A list item to be included in the system timer queue.
Definition: tn_timer.h:213
enum TN_RCode tn_timer_delete(struct TN_Timer *timer)
Destruct the timer.
Circular doubly linked list, for internal kernel usage.
enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, TN_BOOL *p_is_active)
Returns whether given timer is active or inactive.
TN_RCode
Result code returned by kernel services.
Definition: tn_common.h:81
TN_ObjId
Magic number for object validity verification.
Definition: tn_common.h:65
unsigned long TN_TickCnt
Type for system tick count, it is used by the kernel to represent absolute tick count value as well a...
Definition: tn_common.h:188
enum TN_RCode tn_timer_set_func(struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data)
Set user-provided function and pointer to user data for the timer.
Definitions used through the whole kernel.
void( TN_CBTickSchedule)(TN_TickCnt timeout)
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:270
TN_TickCnt start_tick_cnt
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:226
enum TN_ObjId id_timer
id for object validity verification.
Definition: tn_timer.h:210
TN_TickCnt( TN_CBTickCntGet)(void)
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:282
Timer.
Definition: tn_timer.h:205
TN_TimerFunc * func
Function to be called by timer.
Definition: tn_timer.h:216
TN_TickCnt timeout
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:232
TN_TickCnt timeout_cur
Available if only TN_DYNAMIC_TICK is not set.
Definition: tn_timer.h:240
enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_TickCnt timeout)
Start or restart the timer: that is, schedule the timer's function (given to tn_timer_create()) to be...
void * p_user_data
User data pointer that is given to user-provided func.
Definition: tn_timer.h:219
enum TN_RCode tn_timer_time_left(struct TN_Timer *timer, TN_TickCnt *p_time_left)
Returns how many system timer ticks (at most) is left for the timer to expire.
Circular doubly linked list item, for internal kernel usage.
Definition: tn_list.h:63
void( TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data)
Prototype of the function that should be called by timer.
Definition: tn_timer.h:200
#define TN_BOOL
boolean type definition
Definition: tn_common.h:213