10. Software Timers

 
 
The vTaskDelay() function has been used before to get a task into the blocked state for a given amount of time. This is perfectly valid to activate a task at regular time intervals. You can achieve the same thing, and more with Sotware Timers. A Software Timer can be configured for one-shot or periodic wake-ups. It can be created, deleted, started and stopped from anywhere it is known.
 
As usual, let us start with a practical example. First, we declare a Handle to a timer. The ticks variable is not mandatory, we're just using it in this example to display timer state.
 
// FreeRTOS tasks
void vTask1 		(void *pvParameters);
void vTask2 		(void *pvParameters);

// Timer callback function
void vTaskTimer		(TimerHandle_t xTimer);

// Declare Timer Object
TimerHandle_t	my_timer;
TickType_t	ticks;

// Trace User Events Channels
traceString ue1;

 

Next, we create and start the timer in the init section of main(). It is possible to start a timer before or after the scheduler is launched. The pdTRUE argument set in xTimerCreate() tells the timer to auto-reload when it expires. Doing so, we'll get 200ms (actually 200 OS ticks) periodic wake-ups. vTaskTimer is a pointer to a so-called callback function. It is basically the name of the function to be called every time the timer reaches the set delay.

...
	// Create Timer object
	my_timer = xTimerCreate("my_timer", 200, pdTRUE, NULL, vTaskTimer);

	// Start Timer
	xTimerStart(my_timer, 0);
	ticks = xTimerGetExpiryTime(my_timer);

	// Register the Trace User Event Channels
	ue1 = xTraceRegisterString("ticks");

	// Create Tasks
	xTaskCreate(vTask1,		"Task_1", 		256, NULL, 1, NULL);
	xTaskCreate(vTask2,		"Task_2", 		256, NULL, 2, NULL);
...

 

An example of timer callback function is provided below. You need to make sure that timer callback cannot enter, for any reason, into blocked state. It is therefore better to avoid using any FreeRTOS API function here (if you need, then make sure timeouts are set to zero). More generally, the callback function is just a regular function that returns and that should be kept as short as possible. You can consider it as an interrupt handler, even if it's not...

In this example, the timer callback just sends a console message, prints the timer next-stop value into the trace recorder, and then toggles the LED.

/*
 * Timer Callback
 */
void vTaskTimer (TimerHandle_t xTimer)
{
	vTracePrintF(ue1, (char *)"%d", (uint32_t)ticks);
	
	my_printf("\tTimer callback\r\n");
	
	ticks = xTimerGetExpiryTime(my_timer);
	BSP_LED_Toggle();
}

 

Creation of a software timer actually creates a task, just like a regular OS task, called Timer Service. The application communicates with this this task using a regular Message Queue, althrough this Message Queue is hidden behind timer API functions like xTimerStart(). Because the Timer Service is just another task, you may want to set its priority. This is done in FreeRTOSConfih.h:

/* Software timer definitions. */
#define configUSE_TIMERS		  1
#define configTIMER_TASK_PRIORITY	( 3 )
#define configTIMER_QUEUE_LENGTH	  5
#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE )

 

Finally, Task_1 and Task_2 are left for demo purposes, but do nothing really useful.

/*
 *	Task_1
 */
void vTask1 (void *pvParameters)
{
	while(1)
	{
		my_printf("-");

		// Wait for 1s
		vTaskDelay(1000);
	}
}

/*
 *	Task_2
 */
void vTask2 (void *pvParameters)
{
	while(1)
	{
		my_printf("#");

		// Wait for 500ms
		vTaskDelay(500);
	}
}

 

Before firing the debugger, you might be interested in setting the trace recorder in order to capture timer events ( in trcConfig.h):

/*****************************************************************************
 * TRC_CFG_INCLUDE_TIMER_EVENTS
 *
 * Macro which should be defined as either zero (0) or one (1).
 *
 * If this is zero (0), the trace will exclude any Timer events.
 *
 * Default value is 0 since dependent on timers.c
 *****************************************************************************/
#define TRC_CFG_INCLUDE_TIMER_EVENTS 1

 

Now the result:

image_000.png
 
 
In the recorded trace, you can see a new lane corresponding the the Timer Service task with a priority level 3. You may also note that a Message Queue object is involved in the task management.
 
image_001.png