FreeRTOS (pronounced "free-arr-toss") is an open source real-time operating system for embedded systems. FreeRTOS is designed to be "small, simple, and easy to use" and can support many different hardware architectures and cross compilers. Since the development of Richard Barry in 2002, FreeRTOS has been actively developed. As for me, I am not a developer or contributor to FreeRTOS, I am just an end user and a fan. Therefore, this chapter will focus on the "what" and "how" of the FreeRTOS architecture, and less "why" than the other chapters of the book. Like all operating systems, the main job of FreeRTOS is to perform tasks. Most FreeRTOS code involves priority, scheduling, and performing user-defined tasks. But unlike all other operating systems, FreeRTOS is a real-time operating system that runs on embedded systems. By the end of this chapter, I hope you can understand the basic architecture of FreeRTOS. Most FreeRTOS is dedicated to performing tasks, so you can see how it works. If this is your first time to get to know an operating system, I still hope that you can learn how the most basic operating system works. FreeRTOS is relatively simple, especially compared to Windows, Linux, or OS X, but all operating systems have the same concepts and goals, so no matter which operating system you learn, it is inspiring and interesting. "Embedded" and "real time" represent different understandings for different people, so let's define them like FreeRTOS users. An embedded system is a computer system designed to do simple things like a TV remote, a car GPS, an electronic watch, or a pacemaker. Embedded systems are significantly different than general purpose computer systems in that they are smaller and slower, and are generally cheaper. A typical low-end embedded system might have an 8-bit CPU running at 25MHz, a few kilobytes of memory, and perhaps 32KB of flash. A high-end embedded system might have a 32-bit CPU running at 750MHz, a GB of memory, and a few gigabytes of flash. Real-time systems are designed to do things for a certain amount of time, and they ensure that these things are done when they should. Cardiac pacemakers are an excellent example of a real-time embedded system. The pacemaker must contract the heart muscle at the right time to save your life; it can't be too busy to respond in time. Cardiac pacemakers and other real-time embedded systems must be carefully designed to perform their tasks in a timely manner at all times. FreeRTOS is a relatively small application. The minimized FreeRTOS kernel consists of only three (.c) files and a few header files, totaling less than 9,000 lines of code, including comments and blank lines. A typical post-compilation (binary) code image is less than 10KB. The FreeRTOS code can be broken down into three main blocks: task, communication, and hardware interface. • Task: About half of the core code of FreeRTOS is used to handle the primary concerns of most operating systems: tasks. A task is a user-defined C function given a priority. Task.c and task.h complete all the heavy lifting of creating, scheduling, and maintaining tasks. ◠Communication: Tasks are important, but it is more important to communicate with each other! It brings us the second task of FreeRTOS: communication. About 40% of the FreeRTOS core code is used to handle communications. Queue.c and queue.h are responsible for handling FreeRTOS communications. Tasks and interrupts use queues to send data to each other and use semaphores and mutual exclusion to send critical resource usage. ◠Hardware interface: Nearly 9000 lines of code piece together the basic FreeRTOS, is hardware-independent; the same code can run, regardless of whether FreeRTOS is running on the inconspicuous 8051, or the latest and most dazzling ARM core. About 6% of FreeRTOS's core code plays a role as a shim between the hardware-independent FreeRTOS kernel and hardware-related code. We will discuss hardware-related code in the next section. Hardware considerations The hardware-independent FreeRTOS layer is above the hardware-related layer. The hardware-related layer declares what chip architecture you choose. Figure 3.1 shows the layers of FreeRTOS. Figure 3.1: Software layer of FreeRTOS FreeRTOS contains all the hardware-independent and hardware-related code you need to start a very running system. It supports many compilers (CodeWarrior, GCC, IAR, etc.) and also supports many processor architectures (ARM7, ARM Cortex-M3, PICs series, Silicon Labs 8051, x86, etc.). See the FreeRTOS website for a list of processor and compiler support. FreeRTOS is a highly configurable design. FreeRTOS can be compiled to fit a single CPU, minimalist RTOS, operating system that supports only a few tasks, and can be compiled into a multi-core powerful combination of TCP/IP, file system, and USB monsters. Configuration options can be selected in the FreeRTOSConfig.h file by setting different #defines. Clock speed, heap size, mutual exclusion, and API subsets, along with many other options, can be configured in this file. Here are a few examples of setting the maximum number of task priorities, the frequency of the CPU, the frequency of the system metronome, the minimum stack size, and the total heap size: For different cross compilers and CPU architectures, hardware related code is distributed across multiple files. For example, if you use the ARM Cortex-M3 chip and the IAR compiler works, then the hardware-related code exists in the FreeRTOS/Source/portable/IAR/ARM_CM3/ directory. The portmacro.h file declares all hardware-specific features, and port.c and portasm.s contain all the actual hardware-related code. The hardware-independent header file portable.h uses #include's to introduce the correct portmacro.h file at compile time. FreeRTOS uses #define'd to call the hardware-specific functions declared in portmacro.h. Let's look at an example of how FreeRTOS calls a hardware-related function. Hardware-independent files tasks.c often need to insert a critical section of code to prevent preemption. The performance of inserting a critical section is different on different architectures, and the hardware-independent task.c does not need to know the hardware-related details. So task.c calls the global macro portENTER_CRITICAL(), and I am happy to ignore how it actually does. Suppose we use the IAR compiler to compile FreeRTOS on the ARM Crotex-M3 chip, using the file /Source/portable/IAR/ARM_CM3/portmacro.h that defines portENTER_CRITICAL(), as shown below: vPortEnterCritical() is actually defined in FreeRTOS/Source/portable/IAR/ARM_CM3/port.c. This port.c file is a hardware-related file that contains code files for the IAR compiler and Cortex-M3 chip. The vPortEnterCritical() function takes advantage of this hardware-specific knowledge to enter the critical section and returns to the hardware-independent task.c. The protmacro.h file also defines the basic architecture of a data type. This data type includes the basic integer variables, pointers, and data types of the system clock metronome. When using the IAR compiler on ARM Cortex-M3 chips, the following definitions are used: #define portBASE_TYPE long // Basic integer variable type As we have seen, FreeRTOS uses C's preprocessor macro #define to implement hardware-related functions. FreeRTOS also uses #define to handle a large amount of hardware-independent code. Frequent use of #define for non-embedded applications is a serious mistake, but in many small embedded systems this overhead is negligible compared to the functionality provided by "real time". Task priority and ready list All tasks have a user-specified priority, from 0 (lowest priority) to a compile time value of configMAX_PRIORITIES-1 (highest priority). For example, if configMAX_PRIORITIES is set to 5, when FreeRTOS uses 5 priority levels: 0 (lowest priority), 1, 2, 3, and 4 (highest priority). FreeRTOS uses a "ready list" to keep track of all the tasks that are ready to run. It implements a ready list like an array of task lists, as follows: Static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; /* Prioritised ready tasks. */ Fiber Patch Cord /Pigtail,Fiber Optic Patch Cord,Fiber Optic Patch Cable,Fiber Jumper CIXI LANGUANG PHOTOELECTRIC TECHNOLOGY CO..LTD , https://www.cxblueray.com
#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 )
#define configCPU_CLOCK_HZ ( 12000000UL )
#define configTICK_RATE_HZ ( ( portTIckType ) 1000 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 100 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 4 * 1024 ) )
#define portENTER_CRITICAL() vPortEnterCritical()
#define portSTACK_TYPE unsigned long // Pointers to memory locations
Typedef unsigned portLONG portTickType; // The system timer tick type
This way of using data types, and functions through the small layer of #defines, looks a bit more complicated, but it allows FreeRTOS to be recompiled on a completely different system architecture, just by modifying these hardware-related file. At the same time, if you want FreeRTOS to run on a architecture that is not currently supported, you only need to implement hardware-related functions, which is much less than the hardware-independent part of FreeRTOS.
pxReadyTasksLists[0] is a list of all prepared priority tasks, pxReadyTasksLists[1] is a list of all prepared priority tasks, and so on, until pxReadyTasksLists[configMAX_PRIORITIES-1].