FreeRTOS – Delaying Tasks

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks Delay, Tasks

Code Link: Tutorial Source Code Github (vTaskDelay)- Keil

Code Link: Tutorial Source Code Github (vTaskDelayUntil)- Keil

By default any FreeRTOS task should run indefinitely under normal operation. This means a task will always be scheduled to run independent of whether it is required or not as long as it remains in ready-state.

Consider a scenario in which a Microcontroller is connected to a sensor which is read continuously after specific time interval say 1-sec (e.g. a system tracking time in seconds from an RTS). If a FreeRTOS task is scheduled before 1-sec, then it will be useless consumption of valuable processor cycles as 1-measurement/sec is required. This can affect system real-time efficiency in which a task is only run when it is required to run.

In such cases a sensor reading task needs to be delayed/prevent from execution until time of 1-sec is elapsed even if the task is in ready state – Figure-1.

Figure-1: Dummy Task that is required to run after specific Time

In order to meet such requirements, FreeRTOS provides two APIs that delay a task from required amount of time.

  • void vTaskDelay( const TickType_t xTicksToDelay )
  • void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement )

1. vTaskDelay() :

vTaskDelay() specify the amount of time in Tick Timer Ticks during which the task needs to be sent to block (not running) state. For example, specifying a block period of 100 ticks will cause the task to unblock 100 ticks after vTaskDelay() is called!

Parameter NameDescription
xTicksToDelayThe amount of time, in Ticks, that the calling task should block.
Return Valuevoid

Note: vTaskDelay() causes a task to be delayed for at least     xTicksToDelay Ticks. It doesn’t ensure that the task will be immediately scheduled/invoked after xTicksToDelay ticks delay unless there is no other tasks (of same or higher priority) in system. The reason behind this behavior is that the task is delayed only after vTaskDelay() is called. e.g. consider the following sample task using vTaskDelay() API.

/*                                     |
  Sample Task function                 | 
*/                                     |
void myTaskFtn (void * pvParam) {      |
                                       |
    for (;;) {                         |
                                       |
        /* Task implementation */      |  <------ Point-1
                                       |
        ...                            |  <-- [some other task preempt this task here]
                                       |
       vTaskDelay(50);                 |  <------ Point-2
    }                                  |
}                                      |

The myTaskFtn implementation is expected be delayed repeatedly for 50 Ticks. But if by any chance another task preempted the myTaskFtn between Point-1 and Point-2 then the actual delay will be 50-Ticks + the execution time of that other task. More predictable delay can be generated via vTaskDelayUntil().

Note: INCLUDE_vTaskDelay must be defined as 1 in FreeRTOSConfig.h file for this function to be available.

2. vTaskDelayUntil() :

Unlike vTaskDelay() which produces relative time delay from the point onward the vTaskDelay() is called, the vTaskDelayUntil() produces more accurate delay using absolute time reference. The delay produces is independent of when vTaskDelayUntil() is called.

Parameter NameDescription
pxPreviousWakeTimeThe starting point (time stamp) for the vTaskDelayUntil() to count onward. This variable is automatically updated within vTaskDelayUntil() which means it need to be initialized only once in code.
xTimeIncrementThe amount of time, in Ticks, after the starting point i.e. pxPreviousWakeTime that the calling task will remain in block state.
Return Valuevoid

Question: Both vTaskDelay() and vTaskDelayUntil() have xTimeIncrement parameter that specify the time in Ticks that the calling task will be delayed! The question is, Can we specify time in milliseconds instead of Ticks?

Answer: Yes. FreeRTOS defines a MACRO portTICK_PERIOD_MS in portmacro.h that specify time in milliseconds between two consective Tick timer Ticks. So in order to covert any time to equivalent ticks use the following formula.

Formula-1: Converting milliseconds to Ticks

Let’s say we want to delay a task for 50msec. The easy way to use the Delay APIs for the delay in milliseconds is:

void vTaskDelay( 50 / portTICK_PERIOD_MS )


void vTaskDelayUntil( pxPreviousWakeTime , 50 / portTICK_PERIOD_MS )

Tutorial Scenario:

This tutorial consists of two parts. Each part implements a single FreeRTOS Task that is responsible for counting the number of elapsed time in seconds. In the first part we will use vTaskDelay() Delay API while in the second part we will use vTaskDelayUntil() API to generate 1-sec delay.

Let’s get back to programming. Following are the steps.

Steps:

1. The first step is to create a FreeRTOS Task via xTaskCreate() API which will be scheduled and will run after each 1-sec delay.

  /*
     Simple FreeRTOS Task
  */
  xTaskCreate (vTask, "T1", 150, NULL, 1, NULL);

Note: If you don’t know how to create freeRTOS Tasks, refer to the following Tutorial.

The subsequent steps are to implement first part of tutorial i.e. using xTaskCreate() API to generate 1-second delay.

2. The next step is to implement vTask function given as an argument to xTaskCreate() API in the step-1. The vTask function for vTaskDelay() API (first part of tutorial) is given bellow.

/*
    Task function for vTaskDelay() API demonstration
*/
void vTask(void * pvParams) {

  printf("Clock Task Start Running...\n");
 
  for (;;) {
    
    printf("Seconds Count: %d\n", counter++);
 
    /*
       create 1-second (1000-msec) delay.
    */
    vTaskDelay(1000/ portTICK_PERIOD_MS);

  }
  
}

As can be seen from the above function implementation, when the Task is executed for the first time, it prints message Clock Task Start Running… as an indication of start seconds counting clock/task. It then prints the value of a global variable counter which holds the number of seconds elapsed (see source code from github link).

Once the task prints the seconds counts message, it sends the task to block state for 1-second by calling FreeRTOS delay API vTaskDelay() discussed before. Once 1-second is elapsed, it calls back prints the seconds being counted and the cycle repeats.

The problem with this approach is that to delay API is called after the print message i.e. printf("Seconds Count: %d\n", counter++);. which means the total time taken will be 1-second (generated by vTaskDelay()) plus the time taken to by the printf("Seconds Count: %d\n", counter++); statement. Thats the reason why vTaskDelay() API is not suitable for accurate time delay with reference to scheduler time line.

3. The last step is to start the scheduler to allow created tasks to run.

/*
    Start the Scheduler
*/
vTaskStartScheduler();

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

Video demonstration:

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.

Now let’s move to second part to implement vTaskDelayUntil() API to generate 1-second delay. We will just repeat 2,3 parts of previous first part.

2. Following is the vTask function implementation for vTaskDelayUntil() API.

/*
    Task function for vTaskDelayUntil() API demonstration
*/
void vTask(void * pvParams) {
  
  TickType_t xLastWakeTime;
 
  /*
    1000-msec in terms of tick
  */
  const TickType_t xFrequency = 1000/ portTICK_PERIOD_MS;

  /*
    stamp current time... It is only required once.
    it will be updated automatically with every call
    to vTaskDelayUntil() API.
  */
  xLastWakeTime = xTaskGetTickCount();
  
  printf("Clock Task Start Running...\n");
 
  for (;;) {
    
    printf("Seconds Count: %d\n", counter++);
 
    /*
       create 1-second (1000-msec) delay.
    */
    vTaskDelayUntil(&amp;xLastWakeTime, xFrequency);
  }
  
}

The code is quite self explanatory. For any doubt refer to the vTaskDelayUntil() API explanation, discussed earlier.

3. The last step is to start the scheduler to allow created tasks to run.

/*
    Start the Scheduler
*/
vTaskStartScheduler();

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

Video demonstration:

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.

References:

[1] – FreeRTOS Official Website



79 thoughts on “FreeRTOS – Delaying Tasks”

Leave a Reply

Your email address will not be published. Required fields are marked *