TNeoKernel
v1.02
|
Timer is a kernel object that is used to ask the kernel to call some user-provided function at a particular time in the future, based on the system timer tick.
If you need to repeatedly wake up particular task, you can create semaphore which you should wait for in the task, and signal in the timer callback (remember that you should use tn_sem_isignal()
in this callback, since it is called from an ISR).
If you need to perform rather fast action (such as toggle some pin, or the like), consider doing that right in the timer callback, in order to avoid context switch overhead.
The timer callback approach provides ultimate flexibility.
In the spirit of TNeoKernel, timers are as lightweight as possible. That's why there is only one type of timer: the single-shot timer. If you need your timer to fire repeatedly, you can easily restart it from the timer function by the tn_timer_start()
, so it's not a problem.
When timer fires, the user-provided function is called. Be aware of the following:
tn_tick_int_processing()
);Consequently:
tn_arch_sr_save_int_dis()
and tn_arch_sr_restore()
if you need.See TN_TimerFunc
for the prototype of the function that could be scheduled.
Although you don't have to understand the implementation of timers to use them, it is probably worth knowing, particularly because the kernel have an option TN_TICK_LISTS_CNT
to customize the balance between performance of tn_tick_int_processing()
and memory occupied by timers.
The easiest implementation of timers could be something like this: we have just a single list with all active timers, and at every system tick we should walk through all the timers in this list, and do the following with each timer:
This approach has drawbacks:
The latter is probably not so critical in the embedded world since large amount of timers is unlikely there; whereas the former is actually notable.
So, different approach was applied. The main idea is taken from the mainline Linux kernel source, but the implementation was simplified much because (1) embedded systems have much less resources, and (2) the kernel doesn't need to scale as well as Linux does. You can read about Linux timers implementation in the book "Linux Device Drivers", 3rd edition:
This book is freely available at http://lwn.net/Kernel/LDD3/ .
So, TNeoKernel's implementation:
We have configurable value N
that is a power of two, typical values are 4
, 8
or 16
.
If the timer expires in the next 1
to (N - 1)
system ticks, it is added to one of the N
lists (the so-called "tick" lists) devoted to short-range timers using the least significant bits of the timeout
value. If it expires farther in the future, it is added to the "generic" list.
Each N
-th system tick, all the timers from "generic" list are walked through, and the following is performed with each timer:
timeout
value decremented by N
timeout
is less than N
, timer is moved to the appropriate "tick" list.At every system tick, all the timers from current "tick" list are fired unconditionally. This is an efficient and nice solution.
The attentive reader may want to ask why do we use (N - 1)
"tick" lists if we actually have N
lists. That's because, again, we want to be able to modify timers from the timer function. If we use N
lists, and user wants to add new timer with timeout
equal to N
, then new timer will be added to the same list which is iterated through at the moment, and things will be mixed up.
If we use (N - 1)
lists, we are guaranteed that new timers can't be added to the current "tick" list while we are iterating through it. (although timer can be deleted from that list, but it's ok)
The N
in the TNeoKernel is configured by the compile-time option TN_TICK_LISTS_CNT
.
Definition in file tn_timer.h.
Go to the source code of this file.
Data Structures | |
struct | TN_Timer |
Timer. More... | |
Typedefs | |
typedef void( | TN_TimerFunc )(struct TN_Timer *timer, void *p_user_data) |
Prototype of the function that should be called by timer. More... | |
Functions | |
enum TN_RCode | tn_timer_create (struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data) |
Construct the timer. More... | |
enum TN_RCode | tn_timer_delete (struct TN_Timer *timer) |
Destruct the timer. More... | |
enum TN_RCode | tn_timer_start (struct TN_Timer *timer, TN_Timeout timeout) |
Start or restart the timer: that is, schedule the timer's function (given to tn_timer_create() ) to be called later by the kernel. More... | |
enum TN_RCode | tn_timer_cancel (struct TN_Timer *timer) |
If timer is active, cancel it. More... | |
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. More... | |
enum TN_RCode | tn_timer_is_active (struct TN_Timer *timer, BOOL *p_is_active) |
Returns whether given timer is active or inactive. More... | |
enum TN_RCode | tn_timer_time_left (struct TN_Timer *timer, TN_Timeout *p_time_left) |
Returns how many system timer ticks (at most) is left for the timer to expire. More... | |
typedef void( TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data) |
Prototype of the function that should be called by timer.
When timer fires, the user-provided function is called. Be aware of the following:
tn_tick_int_processing()
);Consequently:
timer | Timer that caused function to be called |
p_user_data | The user-provided pointer given to tn_timer_create() . |
Definition at line 197 of file tn_timer.h.
enum TN_RCode tn_timer_create | ( | struct TN_Timer * | timer, |
TN_TimerFunc * | func, | ||
void * | p_user_data | ||
) |
Construct the timer.
id_timer
field should not contain TN_ID_TIMER
, otherwise, TN_RC_WPARAM
is returned.
timer | Pointer to already allocated struct TN_Timer |
func | Function to be called by timer, can't be NULL . See TN_TimerFunc() |
p_user_data | User data pointer that is given to user-provided func . |
TN_RC_OK
if timer was successfully created;TN_RC_WPARAM
if wrong params were given. Destruct the timer.
If the timer is active, it is cancelled first.
timer | timer to destruct |
TN_RC_OK
if timer was successfully deleted;TN_RC_WCONTEXT
if called from wrong context;TN_CHECK_PARAM
is non-zero, additional return codes are available: TN_RC_WPARAM
and TN_RC_INVALID_OBJ
. enum TN_RCode tn_timer_start | ( | struct TN_Timer * | timer, |
TN_Timeout | timeout | ||
) |
Start or restart the timer: that is, schedule the timer's function (given to tn_timer_create()
) to be called later by the kernel.
See TN_TimerFunc()
.
It is legal to restart already active timer. In this case, the timer will be cancelled first.
timer | Timer to start |
timeout | Number of system ticks after which timer should fire (i.e. function should be called). Note that timeout can't be TN_WAIT_INFINITE or 0 . |
TN_RC_OK
if timer was successfully started;TN_RC_WCONTEXT
if called from wrong context;TN_RC_WPARAM
if wrong params were given: say, timeout
is either TN_WAIT_INFINITE
or 0
.TN_CHECK_PARAM
is non-zero, additional return code is available: TN_RC_INVALID_OBJ
. If timer is active, cancel it.
If timer is already inactive, nothing is changed.
timer | Timer to cancel |
TN_RC_OK
if timer was successfully cancelled;TN_RC_WCONTEXT
if called from wrong context;TN_CHECK_PARAM
is non-zero, additional return codes are available: TN_RC_WPARAM
and TN_RC_INVALID_OBJ
. 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.
Can be called if timer is either active or inactive.
timer | Pointer to timer |
func | Function to be called by timer, can't be NULL . See TN_TimerFunc() |
p_user_data | User data pointer that is given to user-provided func . |
TN_RC_OK
if operation was successfull;TN_RC_WPARAM
if wrong params were given. Returns whether given timer is active or inactive.
timer | Pointer to timer |
p_is_active | Pointer to BOOL variable in which resulting value should be stored |
TN_RC_OK
if operation was successfull;TN_RC_WPARAM
if wrong params were given. enum TN_RCode tn_timer_time_left | ( | struct TN_Timer * | timer, |
TN_Timeout * | p_time_left | ||
) |
Returns how many system timer ticks (at most) is left for the timer to expire.
If timer is inactive, 0 is returned.
timer | Pointer to timer |
p_time_left | Pointer to TN_Timeout variable in which resulting value should be stored |
TN_RC_OK
if operation was successfull;TN_RC_WPARAM
if wrong params were given.