The Principles and Implementation of the Embedded Operating System FreeRTOS
2026-04-06 06:48:25··#1
In the embedded field, embedded real-time operating systems are being used more and more widely. Using an embedded real-time operating system (RTOS) allows for more rational and efficient use of CPU resources, simplifies application software design, shortens system development time, and better ensures system real-time performance and reliability. Because RTOS requires certain system resources (especially RAM), only a few real-time operating systems such as μC/OS-II, emboss, salvo, and FreeRTOS can run on microcontrollers with small RAM. Compared to commercial operating systems like μC/OS-II and emboss, FreeRTOS is a completely free operating system with open source code, portability, customizability, and flexible scheduling strategies. It can be easily ported to various microcontrollers, and its latest version is 2.6. 1. FreeRTOS Operating System Functions As a lightweight operating system, FreeRTOS provides functions including task management, time management, semaphores, message queues, memory management, and logging, which can basically meet the needs of smaller systems. The FreeRTOS kernel supports priority scheduling algorithms, where each task can be assigned a priority based on its importance. The CPU always allows the highest-priority task in the ready state to run first. The FreeRTOS kernel also supports round-robin scheduling, allowing different tasks to use the same priority. When no higher-priority task is ready, tasks of the same priority share CPU time. The FreeRTOS kernel can be configured as either a preemptive or non-preemptive kernel according to user needs. When FreeRTOS is configured as a preemptive kernel, a high-priority task in the ready state can preempt the CPU usage rights of a low-priority task, ensuring the system meets real-time requirements. When FreeRTOS is configured as a non-preemptive kernel, a high-priority task in the ready state can only run after the currently running task actively releases its CPU usage rights, thus improving CPU efficiency. 2. Principles and Implementation of the FreeRTOS Operating System 2.1 Implementation of the Task Scheduling Mechanism The task scheduling mechanism is an important concept and core technology of embedded real-time operating systems. For a preemptive kernel, a high-priority task, once ready, can preempt the CPU usage rights of a low-priority task, improving the system's real-time responsiveness. Unlike μc/os-ii, FreeRTOS has no limit on the number of system tasks and supports both priority scheduling and round-robin scheduling algorithms. Therefore, FreeRTOS uses a doubly linked list instead of looking up the task ready table for task scheduling. The system-defined linked list and linked list node data structures are as follows: `typedef struct xlist { // Defines the linked list structure unsigned portshorpt usnumberofitems; // usnumberofitems is the length of the linked list, 0 indicates the linked list is empty volatile xlistitem * pxhead; // pxhead is the head pointer of the linked list volatile xlistitem * pxindex; // pxindex points to the pointer of the current node in the linked list volatile xlistitem xlistend; // xlistend is the tail node of the linked list } xlist; struct xlist_item { // Defines the structure of the linked list node port tick type xitem value; // The value of xitem value is used for time management // port tick type is the clock tick data type, // which can be selected as 16-bit or 32-bit as needed volatile struct xlist_item * pxnext; // Points to the previous node of the linked list void * pvowner; // Points to the task control block where this linked list node is located void *` pvcontainer; // Points to the linked list containing this node }; In FreeRTOS, each task corresponds to a task control block (TCB), which is defined as follows: typedef struct tsktaskcontrolblock { portstack_type * pxtopofstack; // Points to the end of the task stack portstack_type * pxstack; // Points to the beginning of the task stack unsigned portshort usstackdepth; // Defines the stack depth signed portchar pctaskname[tskmax_task_name_len]; // Task name unsigned portchar ucpriority; // Task priority xlistitem xgenericlistitem; // Used to insert the TCB into the ready list or waiting list xlistitem xeventlistitem; // Used to insert the TCB into the event list (such as a message queue) unsigned portchar uctcbnumber; // Used for recording functions } tsktcb; FreeRTOS defines the ready task linked list array as xlist `pxready_taskslists[portmax_priorities]`. Here, `portmax_priorities` represents the system-defined maximum priority. To make a task with priority `n` ready, the node `xgenericlistltem` in the corresponding `tcb` needs to be inserted into the linked list `pxreadytaskslists[n]`, and the `pvcontainer` in `xgenericlistitem` needs to point to `pxreadytaskslists[n]`. During task scheduling, the scheduling algorithm first implements priority scheduling. The system searches the ready task linked list array for the first non-zero priority value `usnumberofitems` in descending order of priority. This priority is the current highest ready priority, and priority scheduling is implemented accordingly. If there is only one ready task under this priority, this ready task enters the running state; if there are multiple ready tasks under this priority, a round-robin scheduling algorithm is used to implement multi-task execution in turn. If the round-robin scheduling algorithm is executed under priority n, the system first obtains the next node pointed to by the current node by executing (pxreadytaskslists[n]) → pxindex = (pxreadytasks-lists[n]) → pxlndex → pxnext, then obtains the corresponding task control block through the pvowner pointer of this node, and finally makes the task corresponding to this task control block enter the running state. It can be seen that in FreeRTOS, the switching time between tasks with the same priority is one clock cycle. Taking Figure 1 as an example, let the maximum number of tasks in the system be portmax_priorities. When task scheduling is performed at a certain moment, pxreadytaskslists[i], usnumberofitems=0 (i=2...portmax_priorities) and pxreadytaskslists[1] are obtained. usnumberofitems=3. From this, the kernel knows that the current highest ready priority is l, and three tasks under this priority have already entered the ready state. Since there are multiple ready tasks under the highest ready priority, the system needs to execute a round-robin scheduling algorithm to switch tasks. The pointer `pxlndex` indicates that task 1 is the current task, and the `pxnext` node of task 1 points to task 2. Therefore, the system sets `pxindex` to point to task 2 and executes task 2 to achieve task scheduling. When the next clock tick arrives, if the highest ready priority is still 1, as shown in Figure 1, the system will set `pxindex` to point to task 3 and execute task 3. To speed up task scheduling, Frecrtos tracks the highest currently ready priority using the variable `uctopreadypriority`. When a task is added to the ready list, if its priority is higher than `uctopreadypriority`, then the priority of this task is assigned to `uctopreadypriority`. Thus, when performing priority scheduling, the scheduling algorithm starts searching from `uctopreadypriority` instead of `portmax_priorities`. This speeds up the search and shortens the kernel shutdown time. 2.2 Implementation of Task Management Effective management of multiple tasks is a major function of the operating system. FreeRTOS allows for tasks such as creating, deleting, suspending, resuming, prioritizing, and retrieving task information. The following discussion focuses on task creation and deletion in FreeRTOS. When the `staskcreate()` function is called to create a new task, FreeRTOS first allocates the necessary memory. If memory allocation is successful, it initializes the task name, stack depth, and priority of the task control block, and then initializes the task control block's stack according to the stack's growth direction. Next, FreeRTOS adds the newly created task to the ready task list. If the current task has the highest priority, this priority is assigned to the variable `uctopreadypriority` (its function is explained in Section 2.1). If the task scheduler is already running and the currently created task has the highest priority, a task switch occurs. Unlike μC/OS-II, task deletion in FreeRTOS is performed in two steps. When a user calls the `vtaskdelete()` function, the first step of task deletion is performed: FreeRTOS first removes the task to be deleted from the ready task list and the event waiting list, and then adds the task to the task deletion list. If the task to be deleted is a currently running task, the system executes the task scheduling function, thus completing the first step of task deletion. When the system idle task, i.e., the `prvldletask()` function, runs, if it finds a task waiting to be deleted in the task deletion list, the second step of task deletion is performed: the memory space occupied by the task is released, and the task is removed from the task deletion list, thus completely deleting the task. It is worth noting that in FreeRTOS, when the system is configured with a non-preemptive kernel, the idle task also has the function of switching between tasks. By comparing the specific code of μc/os-ii and FreeRTOS, it is found that the two-step deletion strategy is beneficial to reduce kernel shutdown time and the execution time of the task deletion function, especially when deleting multiple tasks. 2.3 Implementation of Time Management The typical time management function provided by FreeRTOS is `vtaskdelay()`, which can be used to delay a task for a specific period of time. In FreeRT0S, if a task needs to be delayed by `xtickstodelay` clock ticks, the kernel adds `xtickstodelay` to the total number of clock ticks currently running (defined as `xtickcount`, 32 bits long) to obtain the number of clock ticks `xtimetowake` for the task's next wakeup. Then, the kernel removes the task's control block from the ready list, assigns `xtimetowake` as the node value to the task's `xitemvalue`, and inserts the task control block into different linked lists according to the value of `xtimetowake`. If `xtimetowake` > `xtickcount`, meaning there is no overflow in the calculation, the kernel inserts the task control block into the `pxdelayedtasklist` linked list; if `xtimetowake` > `xtickcount`, meaning there is no overflow in the calculation, the kernel inserts the task control block into the `pxdelayedtasklist` linked list; otherwise, it inserts it into the `pxdelayedtasklist` linked list. 3. Conclusion As an open-source operating system, learning FreeRTOS allows for a better understanding of the implementation principles of embedded real-time operating systems. As a free operating system, using FreeRTOS can reduce system costs and simplify development while meeting the basic needs of smaller systems. In practice, the temperature control system built using the FreeRTOS operating system and the MSP430 microcontroller is stable and reliable, achieving good control results. It is believed that with time, FreeRTOS will continue to improve its functionality to better meet people's requirements for the real-time performance, reliability, and ease of use of embedded operating systems.