In the last tutorial, we have learned to use FreeRTOS software timers. We learned to create and start one-shot and auto-reload type software timers. But we didn’t discuss the use of timer ID tag while creating software timer with xTimerCreate() function. We assigned zero value to the argument of xTimerCreate() function that is responsible to assign ID to each software timer of FreeRTOS. In this tutorial, we will discuss what a timer ID? Why do we need it and How to assign ID tags?
Software Timer ID Introuction
As you know that each software timer has an associated callback function. This callback function executes each timer a software timer expires or after the specified period. In the last tutorial, we assigned only one callback function to each software timer. But in FreeRTOS, we can assign a single callback function to multiple timers. In such cases, a software timer ID tag becomes useful. But application developers can also use an ID tag for other purposes also.
The software timer ID is placed inside a void pointer (void *). Hence, we can use this void pointer to store to integer value. It can point to any other object such as a function pointer.
How to assign ID to a Software Timer?
As we have seen in the last tutorial, we can assign ID to software timer while creating software timer with the help of xTimerCreate() API of FreeRTOS. As you can see from the prototype of xTimerCreate() method, the fourth input argument is used to assign ID to software timer.
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
But on top of that, FreeRTOS software timer API also provides functions that can be used to update software ID and query its values after the FreeRTOS scheduler has started. These functions are vTimerSetTimerID() and pvTimerGetTimerID().
Update Software Timer ID
vTimerSetTimerID() API Function is used to update software timers with a new ID. This is a prototype of FreeRTOS API function:
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );
It accepts two input arguments and returns nothing. First input argument “xTimer” is a reference handle of the software timer being updated with a new ID value. The handle will have been returned from the call to xTimerCreate() used to create the software timer.
The second argument is the value to which the software timer’s ID will be set.
Query FreeRTOS Software Timer ID
We can also get software timer ID value using pvTimerGetTimerID() function. This is a prototype of ID query function:
void *pvTimerGetTimerID( TimerHandle_t xTimer );
The input argument to this function is a handle to the software timer and the return value is an ID.
FreeRTOS Software Timer ID Arduino Example
In this section, we will see an example application to check the use of a software timer ID. In the last tutorial, we have used different callback functions for two different timers. We use one callback function with a one-shot timer and another callback function with auto-reload timer. But we can also assign one callback function to more than on software timers with FreeRTOS API. Therefore, whenever a timer period expires, it executes the same callback function.
In this example, we will create similar functionality to that created in the last tutorial, but assigns a single callback function to both software timers.
Arduino Code
The Arduino code given below is almost similar to the one used in the last tutorial. The only difference is that we assigned the same callback function (prvTimerCallback) to both timers such as one-shot and auto-reload timers.
#include <Arduino_FreeRTOS.h>
#include <timers.h>
/* The periods assigned to the one-shot and auto-reload timers are 6 second and one econd respectively. */
#define mainONE_SHOT_TIMER_PERIOD pdMS_TO_TICKS( 3333 )
#define mainAUTO_RELOAD_TIMER_PERIOD pdMS_TO_TICKS( 500 )
TimerHandle_t xAutoReloadTimer, xOneShotTimer;
BaseType_t xTimer1Started, xTimer2Started;
void setup()
{
Serial.begin(9600); // Enable serial communication library.
/* Create the one shot timer, storing the handle to the created timer in xOneShotTimer. */
xOneShotTimer = xTimerCreate(
/* Text name for the software timer - not used by FreeRTOS. */
"OneShot",
/* The software timer's period in ticks. */
mainONE_SHOT_TIMER_PERIOD,
/* Setting uxAutoRealod to pdFALSE creates a one-shot software timer. */
pdFALSE,
/* This example does not use the timer id. */
0,
/* The callback function to be used by the software timer being created. */
prvTimerCallback);
/* Create the auto-reload timer, storing the handle to the created timer in xAutoReloadTimer. */
xAutoReloadTimer = xTimerCreate(
/* Text name for the software timer - not used by FreeRTOS. */
"AutoReload",
/* The software timer's period in ticks. */
mainAUTO_RELOAD_TIMER_PERIOD,
/* Setting uxAutoRealod to pdTRUE creates an auto-reload timer. */
pdTRUE,
/* This example does not use the timer id. */
0,
/* The callback function to be used by the software timer being created. */
prvTimerCallback);
/* Check the software timers were created. */
if( ( xOneShotTimer != NULL ) && ( xAutoReloadTimer != NULL ) )
{
/* Start the software timers, using a block time of 0 (no block time). The scheduler has
not been started yet so any block time specified here would be ignored anyway. */
xTimer1Started = xTimerStart( xOneShotTimer, 0 );
xTimer2Started = xTimerStart( xAutoReloadTimer, 0 );
/* The implementation of xTimerStart() uses the timer command queue, and xTimerStart()
will fail if the timer command queue gets full. The timer service task does not get
created until the scheduler is started, so all commands sent to the command queue will
stay in the queue until after the scheduler has been started. Check both calls to
xTimerStart() passed. */
if( ( xTimer1Started == pdPASS ) && ( xTimer2Started == pdPASS ) )
{
/* Start the scheduler. */
vTaskStartScheduler();
}
}
}
void loop()
{
// put your main code here, to run repeatedly:
}
static void prvTimerCallback( TimerHandle_t xTimer )
{
TickType_t xTimeNow;
uint32_t ulExecutionCount;
/* A count of the number of times this software timer has expired is stored in the timer's
ID. Obtain the ID, increment it, then save it as the new ID value. The ID is a void
pointer, so is cast to a uint32_t. */
ulExecutionCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
ulExecutionCount++;
vTimerSetTimerID( xTimer, ( void * ) ulExecutionCount );
/* Obtain the current tick count. */
xTimeNow = xTaskGetTickCount();
/* The handle of the one-shot timer was stored in xOneShotTimer when the timer was created.
Compare the handle passed into this function with xOneShotTimer to determine if it was the
one-shot or auto-reload timer that expired, then output a string to show the time at which
the callback was executed. */
if( xTimer == xOneShotTimer )
{
Serial.print("One-shot timer callback executing ");
Serial.println(xTimeNow/31);
}
else
{
/* xTimer did not equal xOneShotTimer, so it must have been the auto-reload timer that
expired. */
Serial.print("Auto-reload timer callback executing ");
Serial.println( xTimeNow/31 );
if( ulExecutionCount == 5 )
{
/* Stop the auto-reload timer after it has executed 5 times. This callback function
executes in the context of the RTOS daemon task so must not call any functions that
might place the daemon task into the Blocked state. Therefore a block time of 0 is
used. */
xTimerStop( xTimer, 0 );
}
}
}
prvTimerCallback() function will execute one either one-shot or auto-reload timer expires. The input argument of prvTimerCallback() checks whether it is called due to one-shot or auto-reload timer. Inside the prvTimerCallback() function, we also provide an example to show the usage of the software timer ID.
Demonstration with Arduino
Now copy this code to Arduino IDE and Upload code to Arduino board. You will see output like this on the Arduino Serial monitor. As you can see auto-reload timer executes 5 times and one-shot timer only executes one.