Tasks

Tasks 

FreeRTOS tasks are the building blocks for FreeRTOS based applications.  Tasks are semi-independent sections of code that are used to manage resources, implement control logic, and respond to events in a time-sensitive manner. Each task operates with its own execution context, allowing the system to perform multiple functions concurrently while maintaining modularity and responsiveness. 

Implementing a Task 

A FreeRTOS task is implemented using a standard C function.  Each task will be assigned its own independent stack from SRAM.  The primary constraint when defining a task is that it must not return—instead, it should contain an infinite loop that keeps the task active throughout the system’s operation. 

You can examine the example code below that implements a task that blinks an LED every 500mS. 

This piece of code shows how to implement a very simple task.  Because each task has its own execution context and stack, you can allocate local variables (led_blink_count) just like any other C function.  It also illustrates how the task is not allowed to issue a return statement.  All FreeRTOS tasks should contain an infinite loop that never returns. 

One important thing to note is the use of vTaskDelay(pdMS_TO_TICKS(500)).  This function is used by FreeRTOS to place a task into the Blocked state for the number of requested milliseconds.  By placing the task in the Blocked state, other tasks are allowed to run if they are not blocked waiting for another resource.   

To highlight the impact of proper task blocking, consider the version of the LED blink task shown below. Unlike the earlier example that used vTaskDelay(), this version uses a simple for loop to create a delay   

While the change may seem very small, the impacts are significant.  In this version, the task continues to execute code during the delay—even though it’s not performing useful work.  Because the task remains in the Running state, it holds on to the CPU and prevents lower-priority tasks from executing. By contrast, using vTaskDelay() explicitly places the task in the Blocked state, allowing the scheduler to dispatch other ready tasks and maintain real-time responsiveness 

Registering a Task with the Scheduler 

A FreeRTOS task must be registered with the scheduler.  You can call the xTaskCreate() to register the task with the scheduler.   

 

The list of parameters and their intended purpose has been summarized below.  

Parameter #  Parameter Name  Purpose 
1  pvTaskCode  Identifies which C function will be used to implement the task. 
2  pcName  A string used to identify the task during debugging 
3  usStackDepth  Tells FreeRTOS the size of task’s stack 
4  pvParameters  Optional Function parameter 
5  uxPriority  The priority at which the task will execute 
6  pxCreatedTask  Used to initialize an optional task handle 

 

Make sure to check the return values when calling xTaskCreate() . The return value of pdPASS indicates that the task was successfully created.  The return value of pdFAIL indicates that the task has not been created because there is insufficient heap memory available for FreeRTOS to allocate enough RAM to hold the task data structures and stack 

Idle Task 

In FreeRTOS, the idle task is a special system task that is automatically created when the scheduler starts. It runs at the lowest possible priority, ensuring it only executes when no other tasks are ready to run. The idle task exists primarily to keep the CPU occupied when the system has no other work to do, preventing the processor from entering an undefined state. Designers can customize the idle task to perform low-priority background operations, such as power management (e.g., putting the CPU into a low-power sleep mode), system monitoring, or memory cleanup (like reclaiming memory from deleted tasks). However, care must be taken to ensure that any added functionality does not block or delay the idle task, as it plays a critical role in system stability. 

Starting the Scheduler 

A standard initialization flow for a FreeRTOS-based application typically involves the following steps: 

  1. Initialize Hardware Resources: Configure all peripherals required by the application (e.g., GPIO, UART, I2C, ADC).
  2. Initialize FreeRTOS Resources: Set up synchronization and communication primitives such as queues, semaphores, mutexes, and event groups. 
  3. Create and Register Tasks: Define application logic as separate tasks using xTaskCreate(), assigning priorities and stack sizes. 
  4. Start the Scheduler: Invoke vTaskStartScheduler() to hand control to FreeRTOS. From this point on, task execution is governed by the scheduler. 

The code below illustrates a simple example of this initialization process.