Index
Task scheduling is a crucial feature in FreeRTOS that determines how tasks are executed on the CPU. FreeRTOS primarily uses two scheduling algorithms: round-robin scheduling and priority-based scheduling.
1. Round-Robin Scheduling
In round-robin scheduling, all tasks with the same priority are given equal CPU time in a cyclic manner. When the time slice (a defined time period for a task to run) expires for a task, the scheduler switches to the next task in the queue of the same priority. This method ensures that all tasks get a chance to execute without any one task hogging the CPU.
Key Characteristics of Round-Robin Scheduling:
- Tasks of the same priority share CPU time equally.
- The scheduler rotates through the tasks in a circular fashion.
- Useful when you have multiple tasks of the same importance.
2. Priority-Based Scheduling
In priority-based scheduling, each task is assigned a priority level. The FreeRTOS scheduler always runs the highest-priority task that is in the Ready state. If a higher-priority task becomes ready while a lower-priority task is running, the lower-priority task is preempted, and the higher-priority task is executed immediately.
Key Characteristics of Priority-Based Scheduling:
- Tasks with higher priority preempt lower-priority tasks.
- Tasks with the same priority use round-robin scheduling.
- Ensures critical tasks are executed promptly.
3. Time Slicing
Time slicing is a feature of both round-robin and priority-based scheduling. It allows tasks to execute for a defined time period (time slice) before the scheduler decides to switch to another task. In FreeRTOS, the duration of time slices is generally determined by the system tick timer.
4. Task Starvation
Task starvation occurs when a lower-priority task is perpetually denied CPU time because higher-priority tasks are always ready to run. This can lead to situations where low-priority tasks may not get executed at all if the system is heavily loaded with high-priority tasks.
Example: Task Scheduling in FreeRTOS
Let’s create a simple FreeRTOS example that demonstrates both round-robin and priority-based scheduling. We will create three tasks: two of equal priority that will use round-robin scheduling and one with a higher priority that will preempt the other two.
Step 1: Set Up the Environment
Make sure you have ESP-IDF installed and your environment set up:
. $HOME/esp/esp-idf/export.sh
Step 2: Create a New Project
Create a new folder for your project and initialize it.
Step 3: Write the Code
Here’s a simple FreeRTOS application with three tasks:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Task handles
TaskHandle_t task1_handle = NULL;
TaskHandle_t task2_handle = NULL;
// Function for Task 1 (Low Priority)
void task1(void *pvParameter) {
while (1) {
printf("Task 1 is running.\n");
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay for 500 ms
}
}
// Function for Task 2 (Low Priority)
void task2(void *pvParameter) {
while (1) {
printf("Task 2 is running.\n");
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay for 500 ms
}
}
// Function for Task 3 (High Priority)
void task3(void *pvParameter) {
while (1) {
printf("Task 3 (High Priority) is running.\n");
vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay for 1 second
}
}
// Main application entry point
void app_main(void) {
// Create Task 1 with priority 1
xTaskCreate(task1, "Task 1", 2048, NULL, 1, &task1_handle);
// Create Task 2 with priority 1 (same as Task 1)
xTaskCreate(task2, "Task 2", 2048, NULL, 1, &task2_handle);
// Create Task 3 with priority 2 (higher than Task 1 and Task 2)
xTaskCreate(task3, "Task 3", 2048, NULL, 2, NULL);
}
Explanation of the Code
- Task Handles: Two task handles (
task1_handle
andtask2_handle
) are defined to manage the two low-priority tasks. - Task Functions:
- task1: This task prints “Task 1 is running.” and delays for 500 ms.
- task2: This task prints “Task 2 is running.” and also delays for 500 ms.
- task3: This high-priority task prints “Task 3 (High Priority) is running.” and delays for 1 second.
- Task Creation:
- Both
task1
andtask2
are created with priority 1, so they will share CPU time in a round-robin manner. task3
is created with priority 2, so it will preempttask1
andtask2
when it becomes ready to run.
- Both
Step 4: Build and Flash the Project
- Build the project:
idf.py build
2. Flash the project to the ESP32:
idf.py -p /dev/ttyUSB0 flash
After flashing, you should observe the output in the terminal. Task 3
will print its messages, preempting Task 1
and Task 2
, which will run in a round-robin fashion when Task 3
is delayed.
Output Explanation
You will see outputs like:
Task 3 (High Priority) is running.
Task 1 is running.
Task 2 is running.
Task 3 (High Priority) is running.
Task 1 is running.
Task 2 is running.
...
Here, Task 3
will run more frequently than Task 1
and Task 2
due to its higher priority. When Task 3
is executing, Task 1
and Task 2
may have to wait for their turn, demonstrating priority-based scheduling. Both Task 1
and Task 2
will print their messages every 500 ms, but their execution may be interrupted by Task 3
.
Summary
In this example, we illustrated how task scheduling works in FreeRTOS, focusing on round-robin and priority-based scheduling, time slicing, and task starvation. Understanding these concepts is crucial for managing tasks effectively in embedded systems, ensuring critical tasks receive the attention they need while still allowing for responsive multitasking.