TNeo  v1.06
 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  /// A list item to be included in the $(TN_SYS_TIMER_LINK) queue
209  ///
210  /// Function to be called by timer
212  ///
213  /// User data pointer that is given to user-provided `func`.
214  void *p_user_data;
215 
216 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
217  ///
218  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
219  ///
220  /// Tick count value when timer was started
222  ///
223  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
224  ///
225  /// Timeout value (it is set just once, and stays unchanged until timer is
226  /// expired, cancelled or restarted)
228 #endif
229 
230 #if !TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
231  ///
232  /// $(TN_IF_ONLY_DYNAMIC_TICK_NOT_SET)
233  ///
234  /// Current (left) timeout value
236 #endif
237 
238  ///
239  /// id for object validity verification
241 };
242 
243 
244 
245 
246 
247 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
248 
249 /**
250  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
251  *
252  * Prototype of callback function that should schedule next time to call
253  * `tn_tick_int_processing()`.
254  *
255  * See `tn_callback_dyn_tick_set()`
256  *
257  * @param timeout
258  * Timeout after which `tn_tick_int_processing()` should be called next
259  * time. Note the following:
260  * - It might be `#TN_WAIT_INFINITE`, which means that there are no active
261  * timeouts, and so, there's no need for tick interrupt at all.
262  * - It might be `0`; in this case, it's <i>already</i> time to call
263  * `tn_tick_int_processing()`. You might want to set interrupt request
264  * bit then, in order to get to it as soon as possible.
265  * - In other cases, the function should schedule next call to
266  * `tn_tick_int_processing()` in the `timeout` tick periods.
267  *
268  */
269 typedef void (TN_CBTickSchedule)(TN_TickCnt timeout);
270 
271 /**
272  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
273  *
274  * Prototype of callback function that should return current system tick
275  * counter value.
276  *
277  * See `tn_callback_dyn_tick_set()`
278  *
279  * @return current system tick counter value.
280  */
281 typedef TN_TickCnt (TN_CBTickCntGet)(void);
282 
283 #endif
284 
285 
286 
287 
288 /*******************************************************************************
289  * PROTECTED GLOBAL DATA
290  ******************************************************************************/
291 
292 /*******************************************************************************
293  * DEFINITIONS
294  ******************************************************************************/
295 
296 /*******************************************************************************
297  * PUBLIC FUNCTION PROTOTYPES
298  ******************************************************************************/
299 
300 /**
301  * Construct the timer. `id_timer` field should not contain
302  * `#TN_ID_TIMER`, otherwise, `#TN_RC_WPARAM` is returned.
303  *
304  * $(TN_CALL_FROM_TASK)
305  * $(TN_CALL_FROM_ISR)
306  * $(TN_LEGEND_LINK)
307  *
308  * @param timer
309  * Pointer to already allocated `struct TN_Timer`
310  * @param func
311  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
312  * @param p_user_data
313  * User data pointer that is given to user-provided `func`.
314  *
315  * @return
316  * * `#TN_RC_OK` if timer was successfully created;
317  * * `#TN_RC_WPARAM` if wrong params were given.
318  */
320  struct TN_Timer *timer,
321  TN_TimerFunc *func,
322  void *p_user_data
323  );
324 
325 /**
326  * Destruct the timer. If the timer is active, it is cancelled first.
327  *
328  * $(TN_CALL_FROM_TASK)
329  * $(TN_CALL_FROM_ISR)
330  * $(TN_LEGEND_LINK)
331  *
332  * @param timer timer to destruct
333  *
334  * @return
335  * * `#TN_RC_OK` if timer was successfully deleted;
336  * * `#TN_RC_WCONTEXT` if called from wrong context;
337  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
338  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
339  */
340 enum TN_RCode tn_timer_delete(struct TN_Timer *timer);
341 
342 /**
343  * Start or restart the timer: that is, schedule the timer's function (given to
344  * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`.
345  *
346  * It is legal to restart already active timer. In this case, the timer will be
347  * cancelled first.
348  *
349  * $(TN_CALL_FROM_TASK)
350  * $(TN_CALL_FROM_ISR)
351  * $(TN_LEGEND_LINK)
352  *
353  * @param timer
354  * Timer to start
355  * @param timeout
356  * Number of system ticks after which timer should fire (i.e. function
357  * should be called). **Note** that `timeout` can't be `#TN_WAIT_INFINITE` or
358  * `0`.
359  *
360  * @return
361  * * `#TN_RC_OK` if timer was successfully started;
362  * * `#TN_RC_WCONTEXT` if called from wrong context;
363  * * `#TN_RC_WPARAM` if wrong params were given: say, `timeout` is either
364  * `#TN_WAIT_INFINITE` or `0`.
365  * * If `#TN_CHECK_PARAM` is non-zero, additional return code
366  * is available: `#TN_RC_INVALID_OBJ`.
367  */
368 enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_TickCnt timeout);
369 
370 /**
371  * If timer is active, cancel it. If timer is already inactive, nothing is
372  * changed.
373  *
374  * $(TN_CALL_FROM_TASK)
375  * $(TN_CALL_FROM_ISR)
376  * $(TN_LEGEND_LINK)
377  *
378  * @param timer
379  * Timer to cancel
380  *
381  * @return
382  * * `#TN_RC_OK` if timer was successfully cancelled;
383  * * `#TN_RC_WCONTEXT` if called from wrong context;
384  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
385  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
386  */
387 enum TN_RCode tn_timer_cancel(struct TN_Timer *timer);
388 
389 /**
390  * Set user-provided function and pointer to user data for the timer.
391  * Can be called if timer is either active or inactive.
392  *
393  * $(TN_CALL_FROM_TASK)
394  * $(TN_CALL_FROM_ISR)
395  * $(TN_LEGEND_LINK)
396  *
397  * @param timer
398  * Pointer to timer
399  * @param func
400  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
401  * @param p_user_data
402  * User data pointer that is given to user-provided `func`.
403  *
404  * @return
405  * * `#TN_RC_OK` if operation was successfull;
406  * * `#TN_RC_WPARAM` if wrong params were given.
407  */
409  struct TN_Timer *timer,
410  TN_TimerFunc *func,
411  void *p_user_data
412  );
413 
414 /**
415  * Returns whether given timer is active or inactive.
416  *
417  * $(TN_CALL_FROM_TASK)
418  * $(TN_CALL_FROM_ISR)
419  * $(TN_LEGEND_LINK)
420  *
421  * @param timer
422  * Pointer to timer
423  * @param p_is_active
424  * Pointer to `#TN_BOOL` variable in which resulting value should be stored
425  *
426  * @return
427  * * `#TN_RC_OK` if operation was successfull;
428  * * `#TN_RC_WPARAM` if wrong params were given.
429  */
430 enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, TN_BOOL *p_is_active);
431 
432 /**
433  * Returns how many $(TN_SYS_TIMER_LINK) ticks (at most) is left for the timer
434  * to expire. If timer is inactive, 0 is returned.
435  *
436  * $(TN_CALL_FROM_TASK)
437  * $(TN_CALL_FROM_ISR)
438  * $(TN_LEGEND_LINK)
439  *
440  * @param timer
441  * Pointer to timer
442  * @param p_time_left
443  * Pointer to `#TN_TickCnt` variable in which resulting value should be
444  * stored
445  *
446  * @return
447  * * `#TN_RC_OK` if operation was successfull;
448  * * `#TN_RC_WPARAM` if wrong params were given.
449  */
451  struct TN_Timer *timer,
452  TN_TickCnt *p_time_left
453  );
454 
455 #ifdef __cplusplus
456 } /* extern "C" */
457 #endif
458 
459 #endif // _TN_TIMER_H
460 
461 /*******************************************************************************
462  * end of file
463  ******************************************************************************/
464 
465 
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:208
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:269
TN_TickCnt start_tick_cnt
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:221
enum TN_ObjId id_timer
id for object validity verification
Definition: tn_timer.h:240
TN_TickCnt( TN_CBTickCntGet)(void)
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:281
Timer.
Definition: tn_timer.h:205
TN_TimerFunc * func
Function to be called by timer.
Definition: tn_timer.h:211
TN_TickCnt timeout
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:227
TN_TickCnt timeout_cur
Available if only TN_DYNAMIC_TICK is not set.
Definition: tn_timer.h:235
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:214
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:206