Introduction
This article officially starts learning about the FreeRTOS operating system. I plan to write a series of articles to help everyone quickly get started with FreeRTOS.
1. Introduction to FreeRTOS
FreeRTOS is an open-source, real-time, embedded operating system that is widely used in small embedded systems and microcontrollers. The design goal of FreeRTOS is to provide a lightweight, portable, and easy-to-use real-time operating system to coordinate and manage multiple tasks. By introducing concepts like tasks, semaphores, message queues, and timers, FreeRTOS allows applications to run as expected, facilitating convenient application development.
The core of FreeRTOS is a scheduler that can alternately schedule multiple tasks and ensure they run at appropriate times. Tasks can be periodic, one-time, or event-driven. FreeRTOS supports various architectures, including x86, ARM, AVR, etc., making it adaptable to different hardware platforms and development environments. Additionally, FreeRTOS provides a rich set of APIs and example code, greatly simplifying the development work for developers in embedded systems.
FreeRTOS has the following features:
Memory-efficient: The kernel of FreeRTOS is very small, and the minimum system only requires a few kilobytes of RAM space. The size of task stacks can be dynamically created, so memory is not wasted.
Portability: FreeRTOS can run on different platforms without modifying the code.
Real-time: FreeRTOS provides a real-time scheduler, allowing tasks to respond promptly to external events.
Reliability: FreeRTOS offers priority control for tasks, timers, and error handling functions, making applications more stable.
Ease of use: FreeRTOS provides a rich set of APIs and example code, allowing developers to quickly get started with development.
When using FreeRTOS, developers first need to understand and familiarize themselves with the basic concepts and APIs of FreeRTOS. For example, they need to understand operations like creating, deleting, suspending, and resuming tasks, as well as features of real-time operating systems like semaphores, message queues, and timers. In specific application development, developers can achieve specific application functions by combining and configuring these basic concepts.
In summary, FreeRTOS is a reliable, portable, memory-efficient, and easy-to-use real-time embedded operating system, making it a powerful tool for developing small embedded systems and microcontrollers.
2. Heap
1. Concept Introduction
The heap is a method of dynamic memory allocation in computers, a free storage area that can dynamically allocate and release memory as needed. The characteristic of the heap is that applications can allocate and release memory on the heap based on their needs without reserving and wasting memory space. The implementation of the heap usually relies on the underlying operating system or runtime library.
The data structure of the heap is typically represented by a heap top pointer indicating the starting address of the heap. The memory in the heap is divided into multiple blocks, each with a header to record the current block’s status, size, and other information, and the blocks are arranged in order of size. Applications can dynamically allocate and release memory on the heap by calling the malloc and free functions. When allocating memory on the heap, the malloc function finds a sufficiently large contiguous memory space on the heap and returns its starting address, while the free function marks the released memory block as available and returns it to the heap managed by the heap manager.
2. Simple Implementation
char heap_buf[1024]; // Define a block of free memory space
int pos = 0; // Point to the first allocated space
/*
size: size of memory to allocate
*/
void *my_malloc(int size){
int old_pos = pos;
pos += size; // Move position
return &heap_buf[old_pos]; // Return allocated memory space
}
Here is a diagram to help everyone understand better:
Usage:
char *buf = my_malloc(100);
for (i = 0; i < 100; i++) {
buf[i] = i;
}
3. Stack
In FreeRTOS, the stack plays a very important role. Each task requires a stack to store its runtime context and local variables. The size and allocation method of the stack can affect the execution efficiency of tasks and the stability of the system.
In FreeRTOS, when creating a task, the size of the task’s stack needs to be specified. Before the task starts, the system allocates the specified size of memory space for the task as a stack.
It is clear that the stack is very important in FreeRTOS.
Let’s look at a simple example to see the specific role of the stack:
This program seems very simple; it just calls funA in the main function, which in turn calls funB and funC. However, it contains many knowledge points about the stack.
Here are a few questions to consider for later explanation: how does the program know to jump back to the main function to execute return 0 after finishing funA?
To answer this, we need to introduce a very important register and the concept of the function stack:
Introduction to the LR Register:
The LR register is a type of register in the ARM architecture, known as the Link Register, used to store the return address. In ARM processors, when a function is called, the return address is stored in the LR register. When the function returns, the processor reads the return address from the LR register to jump back to the calling function’s location, thereby achieving function return and program flow transfer.
Introduction to Function Stack:
The stack used during function calls is commonly referred to as the function stack. The function stack is a data structure characterized by Last In First Out (LIFO) and is often created through memory allocation during program execution. The function stack is used to store local variables, function parameters, return addresses, and other necessary stack frame data during the function call process.
Each function needs to create a stack frame in the stack upon entry, which contains all the information required by the function, such as function parameters, local variables, frame pointers of the calling function, return addresses, etc. As the function runs, new stack frames will continually be allocated in the stack, and these frames will be popped in sequence to recover memory space when the function returns.
Common usage scenarios for the function stack include storage of function parameters and local variables, exception handling, and debugging.
void funB(void) {
}
void funC(void) {
}
void funA(void) {
funB();
funC();
}
int main(void) {
funA();
return 0;
}
After introducing these concepts, let’s analyze the above program:
1. First, the funA function will be called, and when calling funA, a stack for the funA function will be created, and the return address of the return 0 code will be saved in the LR register of the funA stack.
2. Upon entering the funA function, the funB function will be called, which will also create a stack for the funB function and save the return address for the funC code in the LR register of the funB stack.
3. Similarly, calling funC will also create a corresponding stack to save the return address.
4. When the funB function finishes executing, it will retrieve the return address saved in the LR from the stack to execute the funC function.
5. When the funA function finishes executing, it will retrieve the return address saved in the LR from the stack to execute the return 0 statement.
Here is a diagram to help everyone better understand the contents stored in the stack:
Each function will have its own stack space, and these stack spaces are independent and do not interfere with each other. After completing the corresponding function, the address saved in the LR register of the function stack will be retrieved to execute the corresponding statement or function.
Conclusion
The stack is very important for creating tasks in FreeRTOS; each task has its corresponding stack, and we will discuss this further later. I welcome everyone to point out errors and hope for your support. I will continue to update, and the subsequent source code will be posted on the public account.