Try the new version: www.pomad.fr/POMAD_2024/welcome

3. Using delays

Submitted by admin on Thu, 07/15/2021 - 22:37

 

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 saveall_edit, build build_exec, start the debugger  and run resume_co 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 20 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 (in the recorder) 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, that's insane! 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...

gitlab commit  Commit name "Wrong delay"
push  Push onto Gitlab

Stay tuned!

 

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.