Introduction
I wrote this porting guide because a colleague from my WeChat public account asked me to write a tutorial on porting, so I wrote this article at their request.
Preparation
Before porting, we first need to obtain the official source package of FreeRTOS. Here are two download links:
One is the official website: http://www.freertos.org/ and the other is the code hosting site: https://sourceforge.net/projects/freertos/files/FreeRTOS/
After opening the website link, we select the latest version of FreeRTOS, V9.0.0 (2016). Although the version of FreeRTOS has been updated to V10.0.1, we still choose V9.0.0 because the kernel is very stable and there is a lot of information available online. The versions after V10.0.0 were released after Amazon acquired FreeRTOS, mainly adding some cloud components. The FreeRTOS we discuss in this book is a real-time kernel, and using version V9.0.0 is sufficient.
Brief Introduction to FreeRTOS
FreeRTOS includes demo examples and kernel source code (which is quite important, as we need to extract most of the files from this directory). The Source folder contains the source code of the FreeRTOS kernel, which we need when porting FreeRTOS; the Demo folder contains the complete project code that FreeRTOS has ported for various microcontrollers. To promote itself, FreeRTOS provides complete project programs for evaluation boards from various semiconductor manufacturers, and these programs are placed in the Demo directory, which is very valuable for reference.

Source Folder
Here we will focus on analyzing the files in the FreeRTOS/Source folder. ① and ③ contain the general header files and C files of FreeRTOS, which are applicable to various compilers and processors. The header files and C files that need to be ported are placed in the ② portblle folder.

The portblle folder contains files related to the compiler, and different support files are used in different compilers. ① contains KEIL, which is the compiler we use. In fact, the contents of KEIL are the same as those in RVDS, so we only need the contents of the ③ RVDS folder, which contains various processor-related folders. From the names of the folders, we are very familiar with them; the STM32 we study has various series such as M0, M3, M4, etc. FreeRTOS is software, and the microcontroller is hardware. For FreeRTOS to run on a microcontroller, they must be associated together. The MemMang folder contains source files related to memory management.

Porting Process
Extracting Source Code
-
First, create a new folder named “FreeRTOS” in the root directory of our STM32 bare-metal project template, and create two empty folders under the FreeRTOS folder, named “src” and “port”. The src folder is used to store the core source files of FreeRTOS, which are the ‘.c files’ we often refer to, and the port folder is used to store memory management and processor architecture-related code. These codes have been provided to us by FreeRTOS and can be used directly. As mentioned earlier, FreeRTOS is software, and our development board is hardware. There must be a bridge to connect the software and hardware. The codes related to the processor architecture can be referred to as the RTOS hardware interface layer, and they are located in the FreeRTOS/Source/Portable folder.
-
Open the FreeRTOS V9.0.0 source code, find all the ‘.c files’ in the “FreeRTOSv9.0.0\FreeRTOS\Source” directory, and copy them to the newly created src folder.
Insert image description here -
Open the FreeRTOS V9.0.0 source code, find the “MemMang” folder and the “RVDS” folder in the “FreeRTOSv9.0.0\FreeRTOS\Source\portable” directory, and copy them to the newly created port folder.
Insert image description here -
Open the FreeRTOS V9.0.0 source code, find the “include” folder in the “FreeRTOSv9.0.0\FreeRTOS\Source” directory, which contains some header files we need to use FreeRTOS, and copy it directly to our newly created FreeRTOS folder. After completing this step, we can see that our newly created FreeRTOS folder now has three folders, which contain the core files of FreeRTOS. Thus, the source code of FreeRTOS has been extracted.
Insert image description here
Adding to the Project
Add FreeRTOSConfig.h File The FreeRTOSConfig.h file is the project configuration file for FreeRTOS. Since FreeRTOS is a real-time operating kernel that can be trimmed for different processor platforms, users can modify this configuration header file to trim the functions of FreeRTOS. Therefore, we copy it to the user folder. Open the FreeRTOS V9.0.0 source code, find the “FreeRTOSv9.0.0\FreeRTOS\Demo” folder, double-click to open it, and find the “FreeRTOSConfig.h” file in its root directory, then copy it to the user folder of our project. We will need to modify this file later.
Create Project Groups Next, we create two group folders in MDK: FreeRTOS/src and FreeRTOS/port. The FreeRTOS/src folder is used to store the contents of the src folder, and FreeRTOS/port is used to store the contents of the port\MemMang folder and port\RVDS\ARM_CM3 folder. Then we add the FreeRTOS content to the project according to the newly created groups. In the FreeRTOS/port group, we only need to add one file from the MemMang folder, and we choose “heap_4.c”, which is a memory management source file of FreeRTOS. After adding:

Add Header File Paths The FreeRTOS source code has been added to the group folders in the development environment. When compiling, we need to specify the header file paths for these source files; otherwise, compilation will report errors. The FreeRTOS source code only has header files in the FreeRTOS\include and FreeRTOS\port\RVDS\ARM_CM3 folders, so we only need to specify the paths of these two header file folders in the development environment. At the same time, we also copied the FreeRTOSConfig.h header file to the user folder in the root directory of the project, so the path of the user folder also needs to be added to the development environment.

Modify FreeRTOSConfig.h
The FreeRTOSConfig.h file is copied directly from the demo folder. This header file defines macros for trimming the required functions of the entire FreeRTOS. Some macros are enabled, while others are disabled. Initially, we only need to configure the simplest functions. To configure FreeRTOS functions freely, we must understand the meanings of these macro definitions. Below, we will briefly introduce the meanings of these macro definitions and then modify them.
1#ifndef FREERTOS_CONFIG_H
2#define FREERTOS_CONFIG_H
3
4#include "stm32f10x.h"
5#include "bsp_usart.h"
6
7
8// Call different stdint.h files for different compilers
9#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
10 #include <stdint.h>
11 extern uint32_t SystemCoreClock;
12#endif
13
14// Assertion
15#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
16#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
17
18/************************************************************************
19 * FreeRTOS Basic Configuration Options
20 *********************************************************************/
21/* Set 1: RTOS uses preemptive scheduling; set 0: RTOS uses cooperative scheduling (time slice)
22 *
23 * Note: In multi-task management mechanisms, operating systems can be divided into preemptive and cooperative types.
24 * Cooperative operating systems switch to the next task after the task actively releases the CPU.
25 * The timing of task switching completely depends on the currently running task.
26 */
27#define configUSE_PREEMPTION 1
28
29// 1 enables time-slice scheduling (enabled by default)
30#define configUSE_TIME_SLICING 1
31
32/* Some hardware running FreeRTOS have two methods to select the next task to execute:
33 * General method and hardware-specific method (hereinafter referred to as "special method").
34 *
35 * General method:
36 * 1. configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 or the hardware does not support this special method.
37 * 2. Can be used for all hardware supported by FreeRTOS
38 * 3. Fully implemented in C, slightly less efficient than the special method.
39 * 4. No maximum available priority number limit is enforced.
40 * Special method:
41 * 1. configUSE_PORT_OPTIMISED_TASK_SELECTION must be set to 1.
42 * 2. Relies on one or more architecture-specific assembly instructions (usually similar to the leading zero count [CLZ] instruction).
43 * 3. More efficient than the general method.
44 * 4. Generally enforces a maximum available priority number of 32.
45 * Generally, if the hardware does not have these hardware instructions, this macro should be set to 0!
46 */
47#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
48
49/* Set 1: Enable low-power tickless mode; set 0: Keep the system tick interrupt running
50 * If low power is enabled, it may cause download issues because the program is in sleep mode. The following methods can be used to solve this:
51 *
52 * Download methods:
53 * 1. Connect the development board properly
54 * 2. Press and hold the reset button, and release it at the moment of clicking download
55 *
56 * 1. Use a jumper cap to connect BOOT 0 to high level (3.3V)
57 * 2. Power on again and download
58 *
59 * 1. Use FlyMcu to erase the chip, then download
60 * STMISP -> Clear chip(z)
61 */
62#define configUSE_TICKLESS_IDLE 0
63
64/*
65 * Write the actual CPU core clock frequency, which is the CPU instruction execution frequency, usually referred to as Fclk
66 * Fclk is the clock signal supplied to the CPU core. The CPU main frequency we refer to is XX MHz,
67 * which refers to this clock signal. Correspondingly, 1/Fclk is the CPU clock cycle;
68 */
69#define configCPU_CLOCK_HZ (SystemCoreClock)
70
71// The frequency of the RTOS system tick interrupt. That is, the number of interrupts per second, and each interrupt will perform task scheduling in RTOS
72#define configTICK_RATE_HZ (( TickType_t )1000)
73
74// Maximum available priority
75#define configMAX_PRIORITIES (32)
76
77// Stack size used by idle task
78#define configMINIMAL_STACK_SIZE ((unsigned short)128)
79
80// Task name string length
81#define configMAX_TASK_NAME_LEN (16)
82
83 // Data type of the system tick counter variable, 1 indicates 16-bit unsigned integer, 0 indicates 32-bit unsigned integer
84#define configUSE_16_BIT_TICKS 0
85
86// Idle task yields CPU usage to other tasks of the same priority
87#define configIDLE_SHOULD_YIELD 1
88
89// Enable queues
90#define configUSE_QUEUE_SETS 1
91
92// Enable task notification function, enabled by default
93#define configUSE_TASK_NOTIFICATIONS 1
94
95// Use mutexes
96#define configUSE_MUTEXES 1
97
98// Use recursive mutexes
99#define configUSE_RECURSIVE_MUTEXES 1
100
101// Set to 1 to use counting semaphores
102#define configUSE_COUNTING_SEMAPHORES 1
103
104/* Set the number of semaphores and message queues that can be registered */
105#define configQUEUE_REGISTRY_SIZE 10
106
107#define configUSE_APPLICATION_TASK_TAG 0
108
109
110/*****************************************************************
111 FreeRTOS Memory Allocation Configuration Options
112*****************************************************************/
113// Support dynamic memory allocation
114#define configSUPPORT_DYNAMIC_ALLOCATION 1
115// Support static memory
116#define configSUPPORT_STATIC_ALLOCATION 0
117// Total heap size of the system
118#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
119
120
121/***************************************************************
122 FreeRTOS Hook Function Configuration Options
123**************************************************************/
124/* Set 1: Use idle hook (Idle Hook is similar to a callback function); set 0: Ignore idle hook
125 *
126 * The idle task hook is a function implemented by the user,
127 * FreeRTOS specifies the function name and parameters: void vApplicationIdleHook(void ),
128 * this function will be called during each idle task cycle.
129 * For RTOS tasks that have been deleted, the idle task can release the stack memory allocated to them.
130 * Therefore, it must be ensured that the idle task can be executed by the CPU.
131 * It is common to use the idle hook function to set the CPU into low-power mode.
132 * API functions that may block the idle task cannot be called.
133 */
134#define configUSE_IDLE_HOOK 0
135
136/* Set 1: Use tick hook (Tick Hook); set 0: Ignore tick hook
137 *
138 * The tick hook is a function implemented by the user,
139 * FreeRTOS specifies the function name and parameters: void vApplicationTickHook(void )
140 * The tick interrupt can periodically call this function.
141 * The function must be very short and cannot use a lot of stack,
142 * and cannot call API functions ending with "FromISR" or "FROM_ISR"
143 */
144 /*xTaskIncrementTick function is called in the xPortSysTickHandler interrupt function. Therefore, the execution time of the vApplicationTickHook() function must be very short.*/
145#define configUSE_TICK_HOOK 0
146
147// Use memory allocation failure hook function
148#define configUSE_MALLOC_FAILED_HOOK 0
149
150/*
151 * Enable stack overflow detection function if greater than 0. If this function is used,
152 * the user must provide a stack overflow hook function if used.
153 * This value can be 1 or 2, as there are two methods for stack overflow detection. */
154#define configCHECK_FOR_STACK_OVERFLOW 0
155
/********************************************************************
159 FreeRTOS Runtime and Task State Collection Configuration Options
160**********************************************************************/
161// Enable runtime statistics function
162#define configGENERATE_RUN_TIME_STATS 0
163 // Enable visual trace debugging
164#define configUSE_TRACE_FACILITY 0
165/* When both macros configUSE_TRACE_FACILITY are 1, the following 3 functions will be compiled
166 * prvWriteNameToBuffer()
167 * vTaskList(),
168 * vTaskGetRunTimeStats()
169*/
170#define configUSE_STATS_FORMATTING_FUNCTIONS 1
171
172
173/********************************************************************
174 FreeRTOS Co-routine Configuration Options
175**********************************************************************/
176// Enable co-routines, after enabling co-routines, the file croutine.c must be added
177#define configUSE_CO_ROUTINES 0
178// Number of valid co-routine priorities
179#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
180
181
182/***********************************************************************
183 FreeRTOS Software Timer Configuration Options
184**********************************************************************/
185 // Enable software timers
186#define configUSE_TIMERS 1
187// Software timer priority
188#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
189// Software timer queue length
190#define configTIMER_QUEUE_LENGTH 10
191// Software timer task stack size
192#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
193
194/************************************************************
195 FreeRTOS Optional Function Configuration Options
196**************************************************************/
197#define INCLUDE_xTaskGetSchedulerState 1
198#define INCLUDE_vTaskPrioritySet 1
199#define INCLUDE_uxTaskPriorityGet 1
200#define INCLUDE_vTaskDelete 1
201#define INCLUDE_vTaskCleanUpResources 1
202#define INCLUDE_vTaskSuspend 1
203#define INCLUDE_vTaskDelayUntil 1
204#define INCLUDE_vTaskDelay 1
205#define INCLUDE_eTaskGetState 1
206#define INCLUDE_xTimerPendFunctionCall 1
207//#define INCLUDE_xTaskGetCurrentTaskHandle 1
208//#define INCLUDE_uxTaskGetStackHighWaterMark 0
209//#define INCLUDE_xTaskGetIdleTaskHandle 0
210
/******************************************************************
213 FreeRTOS Interrupt Configuration Options
214**********************************************************************/
215#ifdef __NVIC_PRIO_BITS
216 #define configPRIO_BITS __NVIC_PRIO_BITS
217#else
218 #define configPRIO_BITS 4
219#endif
220// Minimum interrupt priority
221#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
222
223// Maximum interrupt priority that the system can manage
224#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
225
226#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
227
228#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
229
/****************************************************************
232 FreeRTOS Interrupt Service Function Configuration Options
233**********************************************************************/
234#define xPortPendSVHandler PendSV_Handler
235#define vPortSVCHandler SVC_Handler
236
237
238/* The following are required for using Percepio Tracealyzer; set configUSE_TRACE_FACILITY to 0 if not needed */
239#if ( configUSE_TRACE_FACILITY == 1 )
240#include "trcRecorder.h"
241#define INCLUDE_xTaskGetCurrentTaskHandle 1 // Enable an optional function (this function is used by Trace source code, default value is 0 indicating not used)
242#endif
243
244
245#endif /* FREERTOS_CONFIG_H */
Modify stm32f10x_it.c
The SysTick interrupt service function is a very important function; all time-related tasks in FreeRTOS are handled within it. SysTick is the heartbeat clock of FreeRTOS, driving its operation. Just like a human heartbeat, if there is no heartbeat, we are essentially “dead”. Similarly, if FreeRTOS loses its heartbeat, it will get stuck somewhere, unable to perform task scheduling or run anything. Therefore, we need to implement a heartbeat clock for FreeRTOS. FreeRTOS has already configured the startup of SysTick for us: the function vPortSetupTimerInterrupt() has been implemented in the port.c file, and the general SysTick interrupt service function of FreeRTOS has also been implemented: the function xPortSysTickHandler() has been implemented in the port.c file. Thus, during the porting process, we only need to implement the SysTick_Handler() function for our corresponding (STM32) platform in the stm32f10x_it.c file. FreeRTOS has considered many things for developers; the two important functions PendSV_Handler() and SVC_Handler() have already been implemented in the port.c file as xPortPendSVHandler() and vPortSVCHandler(), preventing us from failing to implement them ourselves. Therefore, in stm32f10x_it.c, we need to comment out the PendSV_Handler() and SVC_Handler() functions.
1//void SVC_Handler(void)
2//{
3//}
4
5//void PendSV_Handler(void)
6//{
7//}
8
9extern void xPortSysTickHandler(void);
10
11// SysTick interrupt service function
12void SysTick_Handler(void)
13{
14 #if (INCLUDE_xTaskGetSchedulerState == 1 )
15 if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
16 {
17 #endif /* INCLUDE_xTaskGetSchedulerState */
18 xPortSysTickHandler();
19 #if (INCLUDE_xTaskGetSchedulerState == 1 )
20 }
21 #endif /* INCLUDE_xTaskGetSchedulerState */
22}
Create Task
Here, we create a single task. The stack and task control block used by the task are dynamically allocated by FreeRTOS when the task is created. A task must be in an infinite loop; otherwise, the task will return through LR, and if LR points to illegal memory, it will generate a HardFault_Handler. FreeRTOS points to an infinite loop, so when the task returns, it will execute in the infinite loop. Such tasks are unsafe, so we avoid this situation. Generally, tasks are infinite loops with no return value. Moreover, each task loop body should have blocking task functions; otherwise, it will starve tasks with lower priority than itself!!!
1/* FreeRTOS header files */
2#include "FreeRTOS.h"
3#include "task.h"
4/* Development board hardware bsp header files */
5#include "bsp_led.h"
6
7static void AppTaskCreate(void);/* AppTask task */
8
9 /* Create task handle */
10static TaskHandle_t AppTask_Handle = NULL;
11
12int main(void)
13{
14 BaseType_t xReturn = pdPASS;/* Define a return value for task creation information, default is pdPASS */
15
16 /* Development board hardware initialization */
17 BSP_Init();
18
19 /* Create AppTaskCreate task */
20 xReturn = xTaskCreate((TaskFunction_t )AppTask, /* Task entry function */
21 (const char* )"AppTask",/* Task name */
22 (uint16_t )512, /* Task stack size */
23 (void* )NULL,/* Task entry function parameter */
24 (UBaseType_t )1, /* Task priority */
25 (TaskHandle_t* )&AppTask_Handle);/* Task control block pointer */
26 /* Start task scheduling */
27 if(pdPASS == xReturn)
28 vTaskStartScheduler(); /* Start task, enable scheduling */
29 else
30 return -1;
31
32 while(1); /* Normally will not execute here */
33}
34
35static void AppTask(void* parameter)
36{
37 while (1)
38 {
39 LED1_ON;
40 vTaskDelay(500); /* Delay 500 ticks */
41 LED1_OFF;
42 vTaskDelay(500); /* Delay 500 ticks */
43 }
44}
Previous Exciting Articles
[Series] From Microcontroller to Operating System ③ – Entering FreeRTOS
[Series] From Microcontroller to Operating System ④ – Detailed Explanation of FreeRTOS Tasks
[Series] From Microcontroller to Operating System ⑤ – Source Code Interpretation of FreeRTOS Lists & List Items
[Series] From Microcontroller to Operating System ⑥ – Detailed Explanation of FreeRTOS Task Switching Mechanism
Mind Map of RT-Thread Kernel Implementation
Detailed Explanation of RTOS Critical Section Knowledge
Understand the Characteristics of RTOS Idle Threads in One Sentence!
STM32 Advanced Serial Port Circular Buffer Implementation
Jie Jie Takes You to Interpret the Source Code of [Smart Cloud] Circular Buffer
Porting kfifo Based on Linux to STM32 (Supporting Mutex Access)
STM32 Serial Port DMA Receiving Variable Length Data
Button_drive and rtpkgs Internal Testing
A Precise Delay Method in Cortex-M Kernel (ns Level)
[Book Sharing] Programmer’s Mathematics Series
[Book Sharing] Must-Read for C Language Advancement “C and Pointers”
Jie Jie:
Progress Begins with Communication
Harvest Comes from Sharing
Welcome to Follow Jie Jie