Detailed Description

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:

See TN_TimerFunc for the prototype of the function that could be scheduled.

Implementation of timers

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:

Decrement timeout by 1

If new timeout is 0, then remove that timer from the list (i.e. make timer inactive), and fire the appropriate timer function.

This approach has drawbacks:

We can't manage timers from the function called by timer. If we do so (say, if we start new timer), then the timer list gets modified. But we are currently iterating through this list, so, it's quite easy to mix things up.

It is inefficient on rather large amount of timers and/or with large timeouts: we should iterate through all of them each system tick.

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:

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

if resulting 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)