Principles of FreeRTOS Task Priority Configuration

In the field of embedded development, FreeRTOS has become the preferred real-time operating system for many developers due to its lightweight and flexible characteristics. The configuration of task priorities is akin to assigning “priority levels” to various tasks within the system, directly determining the execution order of tasks and the overall scheduling efficiency of the system. For those developing in the VSCode ESP-IDF environment, mastering the principles of FreeRTOS task priority configuration can lead to smoother and more stable operation of our embedded systems. Let’s discuss these principles in detail.

01

Task Priority Configuration Principles

1.Importance Priority:

Assign priorities based on the functional importance of tasks within the system. For example, tasks related to critical system functions, such as safety monitoring and emergency fault handling, should be assigned higher priorities to ensure timely execution and avoid serious system issues.

2.Real-time Priority:

For time-sensitive tasks that require quick responses, such as sensor data acquisition (real-time retrieval of critical data) and task scheduling after interrupt handling, high priority should be assigned to meet the system’s real-time requirements.

3.Avoid Priority Inversion:

Efforts should be made to prevent priority inversion, where a high-priority task waits for a low-priority task to release resources, causing the high-priority task to be unable to execute for an extended period. This can be mitigated through proper resource management (e.g., using priority inheritance mutexes).

4.Short Execution for High-Priority Tasks:

High-priority tasks should run for a short duration to prevent long CPU occupation, which could lead to “starvation” of low-priority tasks (not getting execution opportunities for a long time). If a high-priority task needs to run for an extended period, it can be split into multiple short execution segments, yielding the CPU appropriately in between.

5.Priority Layering:

Tasks should be layered according to functional modules or execution frequency, with tasks within the same layer having similar priorities and clear distinctions between different layers, facilitating management and scheduling.

02

Configuration Principles

FreeRTOS employs a preemptive scheduling algorithm (default, can also be configured for cooperative scheduling), with task priority typically ranging from 0 to configMAX_PRIORITIES – 1 (in ESP-IDF, configMAX_PRIORITIES is generally set to 32 by default), where a higher numerical value indicates a higher priority.

When multiple tasks are in the ready state, the scheduler selects the highest priority task to run. If a high-priority task enters the ready state, regardless of the state of the currently running task (as long as it is not in an interrupt service routine where scheduling is disabled), the scheduler will immediately suspend the current task and switch to the high-priority task. The previously suspended task will only resume execution once the high-priority task enters a blocked state (e.g., waiting for a semaphore, delay, etc.) or is deleted.

Example Code:

Defined high, medium, and low priority task functions: high_priority_task, medium_priority_task, and low_priority_task, each printing information in a loop and calling vTaskDelay to simulate task execution and yielding the CPU.

After the program runs, the high-priority task will execute first. After the high-priority task briefly executes and yields the CPU, the medium-priority task will have the opportunity to execute, and after the medium-priority task yields the CPU, the low-priority task will execute, demonstrating the effect of priority scheduling.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
// Log tag
static const char *TAG = "PRIORITY_DEMO";
// High priority task
void high_priority_task(void *pvParameters) {
    while (1) {
        ESP_LOGI(TAG, "High priority task running");
        // Simulate quick execution, yield CPU after a short delay
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}
// Medium priority task
void medium_priority_task(void *pvParameters) {
    while (1) {
        ESP_LOGI(TAG, "Medium priority task running");
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
// Low priority task
void low_priority_task(void *pvParameters) {
    while (1) {
        ESP_LOGI(TAG, "Low priority task running");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
void app_main(void) {
    TaskHandle_t high_handle = NULL;
    TaskHandle_t medium_handle = NULL;
    TaskHandle_t low_handle = NULL;
    // Create high priority task, set priority to 5
    BaseType_t ret_high = xTaskCreate(
        high_priority_task,
        "HighPriorityTask",
        2048,
        NULL,
        5,
        &high_handle
    );
    // Create medium priority task, set priority to 3
    BaseType_t ret_medium = xTaskCreate(
        medium_priority_task,
        "MediumPriorityTask",
        2048,
        NULL,
        3,
        &medium_handle
    );
    // Create low priority task, set priority to 1
    BaseType_t ret_low = xTaskCreate(
        low_priority_task,
        "LowPriorityTask",
        2048,
        NULL,
        1,
        &low_handle
    );
    if (ret_high != pdPASS || ret_medium != pdPASS || ret_low != pdPASS) {
        ESP_LOGE(TAG, "Task creation failed");
    }
}

03

Conclusion

Mastering the principles of FreeRTOS task priority configuration is key to effective task scheduling in embedded systems. By assigning priorities based on importance and real-time requirements, avoiding priority inversion, allowing high-priority tasks to run for short durations, and managing tasks in layers, we can achieve more efficient scheduling and stable operation of our embedded systems.

Previous Articles:

Introduction to FreeRTOS Tasks

MCU Low Power Modes

ADXL345 Three-Axis Accelerometer Driver

ESP32 Infrared Remote Control Technology

ESP32 I2S Driver MAX98357 Amplifier Program Design

ESP32-S3 WIFI UDP Communication

Leave a Comment