Microcontroller RTOS Real-Time Task Scheduling: Multi-Task Cooperation and Priority Management
Today, we will discuss something “advanced”: the Real-Time Operating System (RTOS) for microcontrollers.If you have used a microcontroller to run bare-metal code, you may have encountered the problem of needing to handle multiple tasks simultaneously, such as key detection, sensor data collection, and serial communication. Using a <span>while</span>
loop for polling these tasks can quickly drive you crazy. RTOS (Real-Time Operating System) is the tool to solve such multi-tasking problems. This article will guide you step-by-step into the world of RTOS from the perspective of task scheduling and priority management.
1. What is RTOS? Why use RTOS?
Basic Concept Explanation
RTOS is essentially a small operating system designed specifically for embedded devices, with the main function of managing the runtime of multiple tasks. Unlike the bare-metal programming we commonly use, RTOS allows multiple tasks to “appear to run simultaneously.” Its core is the task scheduler, which is responsible for arranging the execution order of tasks according to certain rules.
For example, RTOS is like a strict class teacher, responsible for scheduling each student’s (task’s) speaking time, who speaks and for how long is determined by the teacher.
Why use RTOS?
- Solving Multi-Task Problems: Key detection, sensor data collection, display refresh, and other tasks can run independently without interference.
- Improving Code Maintainability: Writing each function as a separate task makes the logic clearer.
- Real-Time Performance: RTOS can ensure that critical tasks are completed on time based on their priority.
2. The Core of Task Scheduling: Time-Slice Round Robin vs. Priority Preemption
1. Time-Slice Round Robin Scheduling
Time-slice round robin (Round Robin) is a simple task scheduling method that allows each task to run in turn for a period of time. For example, if there are three tasks:
- Task A: Key scanning
- Task B: Sensor sampling
- Task C: Serial communication
RTOS will let each of them run for 10ms before switching to the next task, making it look like they are “working simultaneously.”
Characteristics of Time-Slice Round Robin:
- Suitable for tasks with the same priority, such as equally important key and display refresh.
- Simple and easy to use, but not very friendly for tasks with high real-time requirements.
2. Priority Preemption Scheduling
Priority preemption (Priority Preemptive) is a more commonly used scheduling method in RTOS. Each task is assigned a priority, and higher priority tasks can interrupt lower priority tasks. For example:
- High priority task: Emergency alarm signal
- Low priority task: Display refresh
When the alarm signal task needs to execute, RTOS will immediately pause the refresh task to prioritize the alarm task.
Characteristics of Priority Preemption:
- Strong real-time performance: Critical tasks can be completed first.
- Requires careful priority design to avoid “high priority tasks consuming too many resources.”
3. Practice: Implementing Multi-Task Scheduling with FreeRTOS
FreeRTOS is a very popular open-source RTOS that supports various microcontrollers. Below, we will use it to implement a simple multi-task scheduling.
Hardware Environment
- Microcontroller: STM32F103C8T6
- Development Tools: Keil or STM32CubeIDE
- Peripherals: 2 LEDs, 1 button
Task Description
- Task A: LED1 Blinking (low priority, periodic task).
- Task B: Key Detection (high priority, response task).
- Task C: LED2 Indicating Key Status (medium priority, cooperative task).
Hardware Wiring Diagram
Pin | Function |
---|---|
PA0 | Button Input |
PA1 | LED1 Output |
PA2 | LED2 Output |
FreeRTOS Code Example
Below is a code example for implementing multi-task scheduling:
1#include "FreeRTOS.h"
2#include "task.h"
3#include "stm32f1xx_hal.h"
4
5/* Define the pins for LED and button */
6#define LED1_PIN GPIO_PIN_1
7#define LED2_PIN GPIO_PIN_2
8#define BUTTON_PIN GPIO_PIN_0
9#define GPIO_PORT GPIOA
10
11/* Task handles */
12TaskHandle_t TaskAHandle, TaskBHandle, TaskCHandle;
13
14/* Task A: LED1 Blinking */
15void TaskA(void *pvParameters) {
16 while (1) {
17 HAL_GPIO_TogglePin(GPIO_PORT, LED1_PIN);
18 vTaskDelay(pdMS_TO_TICKS(500)); // Delay 500ms
19 }
20}
21
22/* Task B: Key Detection */
23void TaskB(void *pvParameters) {
24 while (1) {
25 if (HAL_GPIO_ReadPin(GPIO_PORT, BUTTON_PIN) == GPIO_PIN_SET) {
26 xTaskNotifyGive(TaskCHandle); // Notify Task C
27 }
28 vTaskDelay(pdMS_TO_TICKS(50)); // Delay 50ms
29 }
30}
31
32/* Task C: LED2 Indicating Key Status */
33void TaskC(void *pvParameters) {
34 while (1) {
35 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for notification from Task B
36 HAL_GPIO_TogglePin(GPIO_PORT, LED2_PIN);
37 }
38}
39
40/* Main function */
41int main(void) {
42 HAL_Init();
43 SystemClock_Config();
44 MX_GPIO_Init();
45
46 /* Create tasks */
47 xTaskCreate(TaskA, "TaskA", 128, NULL, 1, &TaskAHandle);
48 xTaskCreate(TaskB, "TaskB", 128, NULL, 2, &TaskBHandle);
49 xTaskCreate(TaskC, "TaskC", 128, NULL, 3, &TaskCHandle);
50
51 /* Start scheduler */
52 vTaskStartScheduler();
53
54 while (1) {
55 // This line will never be reached
56 }
57}
Code Explanation
- Task A: Blinks LED1 every 500ms.
- Task B: Detects button status and notifies Task C.
- Task C: Switches LED2 status upon receiving notification.
Notes:
- Priority Assignment: Task C has a higher priority than Task A to ensure timely button response.
- Task Stack Size: Each task’s stack is allocated 128 bytes; in actual applications, this should be adjusted based on task complexity.
- Notification Mechanism: Tasks synchronize using
<span>xTaskNotifyGive</span>
and<span>ulTaskNotifyTake</span>
, which is safer than using global variables.
4. Practical Application Cases
A typical RTOS application scenario is smart home systems. For example:
- Task A: Periodically collect temperature and humidity data.
- Task B: Detect door magnetic sensor status and trigger alarms.
- Task C: Upload data to the server via WiFi module.
Through priority preemption scheduling, we can ensure that the alarm task (Task B) is executed first, while data uploading (Task C) can be completed during idle time.
5. Common Problems and Solutions
-
Improper Task Priority Design
- High priority tasks consuming too much CPU time can lead to low priority tasks being “starved.”
- Solution: Use task delays (
<span>vTaskDelay</span>
) or time-slice round robin.
Task Stack Overflow
- Insufficient stack space can cause program crashes.
- Solution: Enable stack overflow detection in FreeRTOS configuration and allocate task stacks appropriately.
Deadlock Issues
- Two tasks waiting for each other to release resources can cause the system to freeze.
- Solution: Avoid nested locks and use FreeRTOS’s built-in deadlock detection tools.
6. Practical Recommendations
- In actual projects, first list all tasks and their priorities, adjusting scheduling strategies based on the real-time requirements of the tasks.
- Learn about other features of FreeRTOS, such as queues, semaphores, and mutexes, to further optimize task cooperation.
- Utilize debugging tools (such as FreeRTOS task monitoring) to observe task running states and identify issues promptly.
When developing with RTOS, starting simple and gradually optimizing is key for beginners to get started.