A software timer (or just a 'timer') allows a function to be executed at a set time in the future. The function executed by the timer is called the timer’s callback function. The time between a timer being started, and its callback function being executed, is called the timer’s period. Put simply, the timer's callback function is executed when the timer's period expires.
The FreeRTOS implementation does not execute timer callback functions from an interrupt context, does not consume any processing time unless a timer has actually expired, does not add any processing overhead to the tick interrupt, and does not walk any link list structures while interrupts are disabled.
Before we could use a timer we must create an instance, for this purpose we use the FreeRTOS function xTimerCreate
. The function allocates the storage required by the new timer, initialises the new timers internal state, and returns a handle by which the new timer can be referenced.
TimerHandle_t xTimerCreate(
const char * const pcTimerName,
const TickType_t xTimerPeriod,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction
);
Starting a Timer
Starting a timer ensures the timer is in the active state. If the timer is not stopped, deleted, or reset in the mean time, the callback function associated with the timer will get called 'n 'ticks after xTimerStart() was called, where 'n' is the timers defined period. xTimerStart
function starts a timer that was previously created using the xTimerCreate()
API function
BaseType_t xTimerStart (
TimerHandle_t xTimer,
TickType_t xBlockTime );
It is valid to call xTimerStart()
before the RTOS scheduler has been started, but when this is done the timer will not actually start until the RTOS scheduler is started, and the timers expiry time will be relative to when the RTOS scheduler is started, not relative to when xTimerStart()
was called.
The timer callback is the function to call when the timer expires. Callback functions must have the prototype defined by TimerCallbackFunction_t type, which is
void vCallbackFunction( TimerHandle_t xTimer );
Timer callback functions execute in the context of the timer service task. It is therefore essential that timer callback functions never attempt to block. For example, a timer callback function must not call vTaskDelay()
, vTaskDelayUntil()
, or specify a non zero block time when accessing a queue or a semaphore.
TimerHandle_t xTimer; /*Timer handler*/
/*Timer callback function*/
void vCallback( TimerHandle_t pxTimer );
int main( void )
{
/*create a new timer*/
xTimer = xTimerCreate(“timer”, 240, pdTRUE, TMRID, vCallback);
xTimerStart( xTimer, 0 ); /*start timer*/
vTaskStartScheduler();
}
void vCallback( TimerHandle_t pxTimer )
{
vPrintString(“Timeout\n”);
}
Timer Demon task
Timer functionality is optional, and not part of the core FreeRTOS kernel. It is instead provided by a timer service (or daemon) task.
static void prvTimerTask( void *pvParameters )
{
for( ;; )
{
/*get the nearest time from timer to expire*/
xNextExpireTime = prvGetNextExpireTime(..);
/*block until a timer expire*/
prvProcessTimerOrBlockTask(..);
/*process a command in case of received*/
prvProcessReceivedCommands();
}
}
Many of the public FreeRTOS timer API functions send commands to the timer service task through a queue called the timer command queue. The timer command queue is private to the RTOS kernel itself and is not directly accessible to application code. The length of the timer command queue is set by the configTIMER_QUEUE_LENGTH
configuration constant.
In order to use the timer API function it must be declared the following definitions in FreeRTOSConfig.h
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( 240 )
One Shot and Auto Reload
There are two types of timers, one-shot timers, and auto-reload timers. Once started, a one-shot timer will execute its callback function only once. Conversely, once started, an auto-reload timer will automatically re-start itself after each execution of its callback function, resulting in periodic callback execution.
Resetting a timer results in the timer recalculating its expiry time so the expiry time becomes relative to when the timer was reset, and not when the timer was originally started
xTimerReset()
re-starts a timer that was previously created. If the timer had already been started and was already in the active state, then xTimerReset()
will cause the timer to re-evaluate its expiry time relative to when xTimerReset()
was called.
BaseType_t xTimerReset (
TimerHandle_t xTimer,
TickType_t xBlockTime );
Code Snippets
- TIMER: Timer creation with reload mode enable
- TIMER: Array of Timers
- TIMER: Timer without reload execution
- TIMER: Change period using buttons
- TIMER: Change reload mode
Exercises (printf)
- Create 3 timers with different times and with the reload mode disabled, timers should call the same callback function. The first timer should be initiated just the first time and with the timeout init the second timer, the timeout of the second timer must init the third timer, the timeout must init the first timer, and so on.
- Create a Task and a Timer, the timer should have a timeout of 1sec and must indicate using a Queue that a timeout has occurred to the task, the task should count the timeouts of the timer to after be displayed.
- Create a task that communicates each 5sec to another task the activation of a timer. The timer should display a message each 200ms for a space of 2 seconds.
- Modify the last program, the first task should send to the second task a different timeout value for the timer every 5 seconds. (Use the values 200ms, 300ms, 500ms, and 1000ms).
- Investigate the functions xTimerReset, xTimerIsTimerActive and xTimerDelete.
Exercises
- Create a program, to create a Timer with reload mode to make LED blinks.
- Modify the last program to rotate the LED.
- Modify the first program to add a button, if the button is pressed the LED must stop blinking (the Timer is stopped ), and if the button is pressed again the timer can run normally. A task should be used to read the button pressed.
- Create a program to send a message to a Queue each 500ms, timer should be used to send the message. The Queue message must be read for a task to process it. The message must contain a pin LED to control the LED state.
- Modify the last program to send different LED pins each timeout, when the timer sends the last pin, the next execution must be the first pin, restarting messages. This time the LED state just changes until the pin message is restarted.
- Create a program that uses a button to activate a timer. The timer must be created with the reload mode disabled, and a task must be created to read the button, each time the button is pressed the timer must run for 100ms to print a message.
- Modify the last program, this time the Timer should blink an LED, the button now changes the reload mode of the timer, it means that when the button is pressed the timer has reload mode enabled, and if is pressed again the timer changes to reload mode disabled.