-
Notifications
You must be signed in to change notification settings - Fork 11
Timers
The Contiki system provides a set of timer libraries that are used both by application programs and by the Contiki system itself. The timer libraries contain functionality for checking if a time period has passed, waking up the system from low power mode at scheduled times, and real-time tasks scheduling. The timers are also used by applications to let the system work with other things or enter low power mode for a time period before resuming execution.
Contiki has one clock module and a set of timer modules: timer, stimer, ctimer, etimer, and rtimer. The different timer modules have different uses: some provide long-running timers with low granularity, some provide a high granularity but with a short range, some can be used in interrupt contexts (rtimer) others not.
The clock module provides functionality to handle the system time and also to block the CPU for short time periods. The timer libraries are implemented with the functionality of the clock module as base.
The timer and stimer libraries provides the simplest form of timers and are used to check if a time period has passed. The applications need to ask the timers if they have expired. The difference between these is the resolution of time: timers use system clock ticks while stimers use seconds to allow much longer time periods. Unlike the other timers, the timer and stimer libraries can be safely used from interrupts which makes them especially useful in low level drivers.
The etimer library provides event timers and are used to schedule events to Contiki processes after a period of time. They are used in Contiki processes to wait for a time period while the rest of the system can work or enter low power mode.
The ctimer library provides callback timers and are used to schedule calls to callback functions after a period of time. Like event timers, they are used to wait for some time while the rest of the system can work or enter low power mode. Since the callback timers call a function when a timer expires, they are especially useful in any code that do not have an explicit Contiki process such as protocol implementations. The callback timers are, among other things, used throughout the Rime protocol stack to handle communication timeouts.
The rtimer library provides scheduling of real-time tasks. The rtimer library pre-empt any running Contiki process in order to let the real-time tasks execute at the scheduled time. The real-time tasks are used in time critical code such as the X-MAC implementation where the radio needs to be turned on or off at scheduled times without delay.
The clock module provides functions for handling system time.
The API for the Contiki clock module is shown below. The function clock_time() returns the current system time in clock ticks. The number of clock ticks per second is platform dependent and is specified with the constant CLOCK_SECOND. The system time is specified as the platform dependent type clock_time_t and in most platforms this is a limited unsigned value which wraps around when getting to large. The clock module also provides a function clock_seconds() for getting the system time in seconds as an unsigned long and this time value can become much larger before it wraps around (136 years on MSP430 based platforms). The system time starts from zero when the Contiki system starts.
The clock module provides two functions for blocking the CPU: clock_delay(), which blocks the CPU for a specified delay, and clock_wait(), which blocks the CPU for a specified number of clock ticks. These functions are normally only used in low-level drivers where it sometimes is necessary to wait a short time without giving up the control over the CPU.
The function clock_init() is called by the system during the boot-up procedure to initialize the clock module.
The Clock Module API:
- clock_time_t clock_time() : Get the system time.
- unsigned long clock_seconds() : Get the system time in seconds.
- void clock_delay(unsigned int delay) : Delay the CPU.
- void clock_wait(int delay) : Delay the CPU for a number of clock ticks.
- void clock_init(void) : Initialize the clock module.
- CLOCK_SECOND : The number of ticks per second.
The clock module is platform dependent and is implemented in the file clock.c. Since the clock module handles the system time, the clock module implementation usually also handles the notifications to the etimer library when it is time to check for expired event timers.
The Contiki timer library provides functions for setting, resetting and restarting timers, and for checking if a timer has expired. An application must "manually" check if its timers have expired; this is not done automatically. The timer library use clock_time() in the clock module to get the current system time.
A timer is declared as a struct timer and all access to the timer is made by a pointer to the declared timer.
The API for the Contiki timer library is shown below. A timer is always initialized by a call to timer_set() which sets the timer to expire the specified delay from current time and also stores the time interval in the timer. timer_reset() can then be used to restart the timer from previous expire time and timer_restart() to restart the timer from current time. Both timer_reset() and timer_restart() uses the time interval set in the timer by the call to timer_set(). The difference between these functions is that timer_reset() set the timer to expire at exactly the same time interval while timer_restart() set the timer to expire some time interval from current time, thus allowing time drift.
The function timer_expired() is used to determine if the timer has expired and timer_remaining() to get an estimate of the remaining time until the timer expires. The return value of the latter function is undefined if the timer already has expired.
The timer library can safely be used from interrupts. The code example below shows a simple example how a timer can be used to detect timeouts in an interrupt.
The Timer Library API:
- void timer_set(struct timer *t, clock_time_t interval) : Start the timer.
- void timer_reset(struct timer *t) : Restart the timer from the previous expiration time.
- void timer_restart(struct timer *t) : Restart the timer from current time.
- int timer_expired(struct timer *t) : Check if the timer has expired.
- clock_time_t timer_remaining(struct timer *t) : Get the time until the timer expires.
static struct timer rxtimer; void init(void) { timer_set(&rxtimer, CLOCK_SECOND / 2); } interrupt(UART1RX_VECTOR) uart1_rx_interrupt(void) { if(timer_expired(&rxtimer)) { /* Timeout */ /* ... */ } timer_restart(&rxtimer); /* ... */ }
The Contiki stimer library provides a timer mechanism similar to the timer library but uses time values in seconds, allowing much longer expiration times. The stimer library use clock_seconds() in the clock module to get the current system time in seconds.
The API for the Contiki stimer library is shown below and it is similar to the timer library. The difference is that times are specified as seconds instead of clock ticks.
The stimer library can safely be used from interrupts.
The stimer Library API:
- void stimer_set(struct stimer *t, unsigned long interval) : Start the timer.
- void stimer_reset(struct stimer *t) : Restart the stimer from the previous expiration time.
- void stimer_restart(struct stimer *t) : Restart the stimer from current time.
- int stimer_expired(struct stimer *t) : Check if the stimer has expired.
- unsigned long stimer_remaining(struct stimer *t) : Get the time until the timer expires.
The Contiki etimer library provides a timer mechanism that generate timed events. An event timer will post the event PROCESS_EVENT_TIMER to the process that set the timer when the event timer expires. The etimer library use clock_time() in the clock module to get the current system time.
An event timer is declared as a struct etimer and all access to the event timer is made by a pointer to the declared event timer.
The API for the Contiki etimer library is shown below. Like the previous timers, an event timer is always initialized by a call to etimer_set() which sets the timer to expire the specified delay from current time. etimer_reset() can then be used to restart the timer from previous expire time and etimer_restart() to restart the timer from current time, both using the same time interval that was originally set by etimer_set(). The difference between etimer_reset() and etimer_restart() is that the former schedules the timer from previous expiration time while the latter schedules the timer from current time thus allowing time drift. An event timer can be stopped by a call to etimer_stop() which means it will be immediately expired without posting a timer event. etimer_expired() is used to determine if the event timer has expired.
Note that the timer event is sent to the Contiki process used to schedule the event timer. If an event timer should be scheduled from a callback function or another Contiki process, PROCESS_CONTEXT_BEGIN() and PROCESS_CONTEXT_END() can be used to temporary change the process context. See Processes for more information about process management.
Below is a simple example how an etimer can be used to schedule a process to run once per second.
The etimer library cannot safely be used from interrupts.
The Etimer Library API:
- void etimer_set(struct etimer *t, clock_time_t interval) : Start the timer.
- void etimer_reset(struct etimer *t) : Restart the timer from the previous expiration time.
- void etimer_restart(struct etimer *t) : Restart the timer from current time.
- void etimer_stop(struct etimer *t) : Stop the timer.
- int etimer_expired(struct etimer *t) : Check if the timer has expired.
- int etimer_pending() : Check if there are any non-expired event timers.
- clock_time_t etimer_next_expiration_time() : Get the next event timer expiration time.
- void etimer_request_poll() : Inform the etimer library that the system clock has changed.
PROCESS_THREAD(example_process, ev, data) { static struct etimer et; PROCESS_BEGIN(); /* Delay 1 second */ etimer_set(&et, CLOCK_SECOND); while(1) { PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); /* Reset the etimer to trig again in 1 second */ etimer_reset(&et); /* ... */ } PROCESS_END(); }
The etimer library implementation in core/sys/etimer.c is platform independent but requires callback to etimer_request_poll() for handling the event timers. This allows the etimer library to wakeup the system from low power mode when an event timer expires. The etimer library provides three functions for this:
etimer_pending() Check if there are any non-expired event timers.
etimer_next_expiration_time() Get the next event timer expiration time.
etimer_request_poll() Inform the etimer library that the system clock has changed and that an etimer might have expired. This function is safe to call from interrupts.
The implementation of the clock module usually also handles the callbacks to the etimer library since the module already handles the system time. This can be implemented simply by calling etimer_request_poll() periodically, or by taking advantage of etimer_next_expiration_time() and only notify the etimer library when needed.
The Contiki ctimer library provides a timer mechanism that calls a specified function when a callback timer expires. The ctimer library use clock_time() in the clock module to get the current system time.
The API for the Contiki ctimer library is shown below and is similar to the etimer library. The difference is that ctimer_set() takes a callback function pointer and a data pointer as arguments. When a ctimer expires, it will call the callback function with the data pointer as argument. The code example below shows how a ctimer can be used to schedule a callback to a function once per second.
Note that although the callback timers are calling a specified callback function, the process context for the callback is set to the process used to schedule the ctimer. Do not assume any specific process context in the callback unless you are sure about how the callback timers are scheduled.
The ctimer library can not safely be used from interrupts.
The Ctimer Library API:
- void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr) : Start the timer.
- void ctimer_reset(struct ctimer *t) : Restart the timer from the previous expiration time.
- void ctimer_restart(struct ctimer *t) : Restart the timer from current time.
- void ctimer_stop(struct ctimer *t) : Stop the timer.
- int ctimer_expired(struct ctimer *t) : Check if the timer has expired.
static void callback(void *ptr) { ctimer_reset(&timer); /* ... */ } void init(void) { ctimer_set(&timer, CLOCK_SECOND, callback, NULL); }
The ctimer library is implemented using the etimer library and no further porting is needed.
The Contiki rtimer library provides scheduling and execution of real-time tasks (with predictable execution times). The rtimer library uses its own clock module for scheduling to allow higher clock resolution. The function rtimer_time() is used to get the current system time in ticks and RTIMER_SECOND specifies the number of ticks per second.
Unlike the other timer libraries in Contiki, the real-time tasks pre-empt normal execution for the task to execute immediately. This sets some constraints for what can be done in real-time tasks because most functions do not handle pre-emption. Interrupt-safe functions such as process_poll() are always safe to use in real-time tasks but anything that might conflict with normal execution must be synchronized.
There are two types of real-time tasks: hard and soft. The hard real-time tasks have priority over the soft that will be delayed when needed to allow the hard real-time tasks to execute on time.
A maximal execution time (duration) must be specified when scheduling real-time tasks. This is needed for the scheduler to determine when it is safe to schedule soft real-time tasks. A real-time task can use the function rtimer_last_duration() to retrieve the execution time required when the task was executed last time.
The API for the Contiki rtimer library is shown below. A real time task is always initialized by a call to the function rtimer_setup() which sets the type (hard or soft), callback function pointer, and data pointer. The rtimer can then be scheduled to expire after a specified time using rtimer_schedule(). After a rtimer has expired, it can also be rescheduled using rtimer_reschedule(). The difference between rtimer_reschedule() and rtimer_schedule() is that the former schedules the timer from previous expiration time while the latter schedules the timer from current time thus allowing time drift. Note that rtimer_reschedule() will fail if the next scheduled expire time has already passed due to a real time task being late and it is important to always check the return code. Once scheduled, real-time tasks can not be rescheduled until they have expired. When a rtimer expires, it calls a callback function with a pointer to the expired rtimer, a pre-set data pointer, and a status as arguments. The status is RTIMER_ONTIME in the normal case and RTIMER_DELAYED if the real-time task by some reason failed to execute on time. The code example below shows a simple example how a real-time task can be setup to execute four times per second.
The rtimer library can be safely used from interrupts.
The Rtimer Library API:
- void rtimer_setup(struct rtimer *task, int type, rtimer_callback_t func, void *ptr) : Setup the real-time task.
- int rtimer_schedule(struct rtimer *task, rtimer_clock_t delay, rtimer_clock_t duration) : Schedule the real-time task after the specified delay and with the specified duration.
- int rtimer_reschedule(struct rtimer *task, rtimer_clock_t delta_time, rtimer_clock_t duration) : Schedule the real-time task based on its current time plus delta.
- rtimer_schedule_time(task) : Get the scheduled time for the real-time task
- rtimer_last_duration(task) : Get the task execution duration for last execution.
- rtimer_time() : Get the current time.
- RTIMER_CLOCK_LT(now,t0,t1) : Check if the time t0 is less than the time t1 with respect to the current time now.
- RTIMER_SECOND : The number of ticks per second.
- void rtimer_init(void) : Initialize the rtimer library.
- void rtimer_run_next(void) : Called by the rtimer scheduler to run next real-time task.
static struct rtimer task; static void callback(struct rtimer *t, void *ptr, int status) { if(rtimer_reschedule(&task, RTIMER_SECOND / 4, DURATION) != RTIMER_OK) { /* Failed to reschedule timer. Recover by rescheduling from current time. */ rtimer_schedule(&task, RTIMER_SECOND / 4, DURATION); } /* ... */ } void init(void) { rtimer_setup(&task, RTIMER_HARD, callback, NULL); rtimer_schedule(&task, RTIMER_SECOND / 4, DURATION); }
The rtimer library implementation in core/sys/rtimer.c is platform independent and depends on the rtimer-arch.c to handle the platform dependent functionality such as scheduling. The following three functions need to be implemented when porting the rtimer library.
rtimer_arch_init() is called by the rtimer library to initialize the rtimer architecture code.
rtimer_time() is used to get the current rtimer system time.
rtimer_arch_schedule() is used to schedule a call to rtimer_run_next() at a specific time. rtimer_arch_schedule() takes two times as arguments, now and wakeup_time, where the first is the start time of the schedule request and the latter is the requested time for the wakeup callback. Scheduling is only possible when the current time is between the start time and the wakeup time to ensure that the wakeup time has not already been passed. (Two times are needed to separate short time intervals from long time intervals on platforms where the times are represented as unsigned values that wrap around when they become too large.) rtimer_arch_schedule() returns 0 if it was not possible to schedule the wakeup callback and 1 otherwise. The wakeup callback is normally implemented using an interrupt to ensure that rtimer_run_next() is called at the requested time.
Besides these three functions, the rtimer architecture code needs to define RTIMER_ARCH_SECOND as the number of ticks per second and the data type rtimer_clock_t that is used for rtimer times. These are declared in the file rtimer-arch.h.
Platform dependent functions for the rtimer library:
- RTIMER_ARCH_SECOND : The number of ticks per second.
- void rtimer_arch_init(void) : Initialize the rtimer architecture code.
- rtimer_clock_t rtimer_time() : Get the current time.
- int rtimer_arch_schedule(rtimer_clock_t now, rtimer_clock_t wakeup_time): Schedule a call to rtimer_run_next().
Contiki contains a set of timer libraries that are used both by core Contiki modules and applications. The timer libraries are used to detect timeouts as well as schedule process events and function callbacks to allow the system to work with other things, or enter low power mode, for a time period before resuming execution.