3. Using delays


 

In this tutorial, we will illustrate the need for an OS-aware waiting mechanism in the tasks. Let-us compare the effect of using of the vTaskDelay() OS function to a stupid delay implementation using the usual loop-counting idea.

Edit vTask2() function as follows:

/*
 *	Task1 toggles LED every 30ms
 */
void vTask1 (void *pvParameters)
{
	while(1)
	{
		BSP_LED_Toggle();
		vTaskDelay(30);
	}
}

/*
 *	Task2 sends a message to console every 100ms
 */
void vTask2 (void *pvParameters)
{
	uint16_t 	count;
	uint32_t	i;

	count = 0;

	while(1)
	{
		my_printf("Hello %2d from task2\r\n", count);
		count++;

		// Let us try a stupid delay
		//vTaskDelay(100);
		for(i=0; i<1000000; i++);
	}
}

 

Save all , build , start the debugger  et run the application. You'll get the console message:

image_002.png

but no blinking LED. Why is that?

 

Only you know that the counting loop is there for a delay. The OS scheduler just considers this is something the task has to do. Since the never ending loop of Task_2 never pauses, and because Task_2 is of highest priority, there is no chance for Task_1 to ever execute.

Below is the trace properties after several seconds of recording. Only 40 events have been captured, for a total duration of 75µs. How come?

image_000.png
 
 
The time line provides an explanation: startup events are first recorded, as expected. Then Task_2 is started. From this point, no kernel event never occurs because Task_2 never stops. There's nothing left to record, and there is even no evidence of a Task_1 waiting somewhere...
 
image_001.png

 

What if we apply the same idea to vTask1() instead?

/*
 *	Task1 toggles LED every 30ms
 */
void vTask1 (void *pvParameters)
{
	uint32_t	i;

	while(1)
	{
		BSP_LED_Toggle();

		// Let us try the stupid delay here, now.
		//vTaskDelay(30);
		for(i=0; i<100000; i++);
	}
}

/*
 *	Task2 sends a message to console every 100ms
 */
void vTask2 (void *pvParameters)
{
	uint16_t 	count;

	count = 0;

	while(1)
	{
		my_printf("Hello %2d from task2\r\n", count);
		count++;
		vTaskDelay(100);
	}
}

 

This time, the application seems to work fine, both processes are executed as expected. Nevertheless, looking at the execution trace, on can see that Task_1 takes more than 90% of the CPU time... for blinking a LED (what a shame)! And it only works because Task_1 has the lowest priority level. Any task with a priority level lower than the one of Task_1 would never execute.

image_003.png

 

As a conclusion, this little experiment clearly shows that you need, somewhere in every task loop, an OS aware waiting mechanism, otherwise there is no CPU time left to execute other tasks. When the task only need to wait for a known amount of time, vTaskDelay(), or vTaskDelayUntil() must be used, instead of any other non-OS delay functions.

When the task needs to wait an unknown time, for something to happen, say an event coming from other tasks, or from an external input, then other kernel objects are used. This includes semaphores, message queues, events, timers...

Stay tuned!