Hello! Welcome back to our journey into Real-Time Operating Systems.
In our last lesson, we covered the fundamental theory behind an RTOS. We explored the concepts of tasks, the scheduler's role in creating the illusion of concurrency, pre-emptive scheduling, and how a context switch allows the CPU to move seamlessly between different threads of execution.
Today, we transition from theory to practice. Our goal is to take the official FreeRTOS source code and integrate it into a "bare-metal" STM32 project from scratch. This is a skill that separates a professional firmware developer from a hobbyist. While IDE wizards can automate this process, understanding how to do it manually gives you complete control and a much deeper understanding of how the RTOS interacts with your hardware.
Your learning outcome for this lesson is to manually integrate FreeRTOS source files into an STM32 project. By the end, you will have a clean, working foundation ready for building our first multi-tasking application.
1. Preparing the Project Environment
Before we can add FreeRTOS, we need a correctly configured base project. One of the most common mistakes when manually integrating FreeRTOS is a conflict over the SysTick timer.
- The STM32 HAL (Hardware Abstraction Layer) uses the SysTick timer by default to implement its
HAL_Delay()function. - FreeRTOS requires exclusive control of the SysTick timer to generate its periodic "kernel tick," which drives the scheduler.
If both systems try to configure and use SysTick, it will lead to unpredictable behavior. To prevent this, we must tell the HAL to use a different timer for its time base.
Action: Create a New Project
- Open STM32CubeIDE and create a new project for your
STM32F103C8T6board. - In the CubeMX perspective (
.iocfile), go to the Pinout & Configuration tab. - Under the
System Corecategory, selectSYS. - In the
SYS Mode and Configurationpanel, locate the Timebase Source. Change it fromSysTickto one of the basic timers, likeTIM1orTIM2. - Save the
.iocfile to generate the code. When prompted, answerYesto open the C/C++ perspective.
This simple change is crucial for a stable FreeRTOS integration.
2. Obtaining and Structuring the FreeRTOS Source Code
Now, let's get the FreeRTOS kernel itself.
- Download: Go to the official FreeRTOS website and download the latest kernel version as a .zip file.
- Extract: Unzip the archive to a known location on your computer. You will see a directory named something like
FreeRTOSv202212.01.
Inside this folder, you'll find a Source directory. This contains the generic kernel files and the "portable" layers for dozens of different microcontrollers and compilers. Our task is to pick only the files relevant to our setup: an ARM Cortex-M3 processor using the GCC compiler.
First FreeRTOS project - PoMAD
The following article from PoMAD provides a clear, step-by-step guide on how to copy the FreeRTOS source files and clean up the unnecessary ones. We will follow its methodology.
Please read Section 2.1, 'Adding FreeRTOS source code'. Follow the instructions for copying the Source folder into your project directory and renaming it to FreeRTOS. Then, perform the 'housekeeping' steps to remove unneeded files. Crucially, when you get to the step for the ARM_CM0 folder, you must adapt it for our processor. Instead of keeping ARM_CM0, you will keep the ARM_CM3 folder inside FreeRTOS/portable/GCC/.
To summarize and adapt the process for our specific board:
- In your STM32CubeIDE project explorer, right-click on the project root and select New -> Folder. Name it
FreeRTOS. - From the unzipped FreeRTOS download, copy the entire
Sourcedirectory into the newFreeRTOSfolder in your project. - Prune the portable layer: Navigate to
FreeRTOS/Source/portable/.- Delete all folders except
GCCandMemMang.
- Delete all folders except
- Select the correct core: Navigate into
FreeRTOS/Source/portable/GCC/.- Delete all folders except
ARM_CM3. This is the porting layer for our Cortex-M3 processor.
- Delete all folders except
- Select a memory management scheme: Navigate into
FreeRTOS/Source/portable/MemMang/.- Delete all files except
heap_4.c. FreeRTOS offers several ways to manage memory for creating tasks and other objects.heap_4.cis a robust choice that helps prevent memory fragmentation.
- Delete all files except
Your final file structure should look similar to this, with the core kernel files at the top level and the specific port files in the portable directory.

3. Configuring the Kernel with FreeRTOSConfig.h
FreeRTOS is highly configurable. You control its features and behavior through a header file named FreeRTOSConfig.h. This file is not part of the standard source code; you must create it for your specific project.
First FreeRTOS project - PoMAD
Now, let's create the configuration file. The same PoMAD article provides an excellent template and explains the next critical steps.
Please read Section 2.2, 'FreeRTOS configuration'. Follow the steps to: Create a new header file named FreeRTOSConfig.h inside a suitable inc folder (e.g., your project's Core/Inc folder). Copy the entire provided template into your FreeRTOSConfig.h file. We will dissect some of these settings shortly. Crucially, follow the instruction to open stm32f1xx_it.c and comment out the function bodies for SVC_Handler, PendSV_Handler, and SysTick_Handler.
Why Comment Out the Handlers?
The FreeRTOS porting layer (port.c) provides its own implementations of these core system exception handlers to manage context switching and the kernel tick. The default project provides weak-aliased, empty versions. By commenting them out, you ensure the linker uses the FreeRTOS versions, avoiding a "multiple definition" error.
Key Configuration Options
Let's look at a few vital settings in your new FreeRTOSConfig.h:
-
#define configUSE_PREEMPTION 1: Enables pre-emptive scheduling, which we discussed in the last lesson. -
#define configCPU_CLOCK_HZ ( SystemCoreClock ): Tells FreeRTOS your main system clock frequency. -
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ): Sets the kernel tick frequency to 1000 Hz, meaning the scheduler will run every 1 millisecond. -
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 6 * 1024 ) ): Allocates 6KB of RAM for the FreeRTOS heap, which is used byheap_4.cto dynamically allocate memory for tasks, queues, etc. -
Interrupt Redirection Macros:
#define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_HandlerThese macros are the final piece of the puzzle. They map the generic FreeRTOS handler names (e.g.,
vPortSVCHandler) to the specific CMSIS-compliant names used in the STM32 startup file (e.g.,SVC_Handler). This is how FreeRTOS "hooks into" the core ARM exceptions.
4. Finalizing the IDE Setup and Verification
We're almost there. We've added the files and the configuration, but now we need to tell the IDE's compiler where to find everything.
How to integrate FreeRTOS and SystemView inside a project | Part 1 | Learn with George
This video from 'Learn embedded systems with George' provides a clear visual walkthrough of the final IDE configuration steps. We'll watch the relevant parts that cover adding include paths and setting source folders.
Please watch from 04:58 to 05:55 and from 15:00 to 15:40. These clips show: (04:58) How to add the FreeRTOS folder as a 'Source Location' in the project properties. This tells the IDE to compile the .c files within it. (15:00) How to add the necessary 'Include Paths' to the compiler settings. You will need to add the paths to FreeRTOS/Source/include and FreeRTOS/Source/portable/GCC/ARM_CM3.
Your Steps in STM32CubeIDE:
-
Add Source Location:
- Right-click your project -> Properties.
- Go to C/C++ General -> Paths and Symbols -> Source Location.
- Click Add Folder... and check the
FreeRTOSfolder you created. Click OK, then Apply and Close. You should see a small 'C' icon appear on theFreeRTOSfolder in the Project Explorer.
-
Add Include Paths:
- Right-click your project -> Properties.
- Go to C/C++ Build -> Settings -> Tool Settings -> MCU GCC Compiler -> Include paths.
- Click the Add... icon. In the dialog, click Workspace....
- Navigate to and select
Your_Project/FreeRTOS/Source/include. - Click Add... again and add the path to the portable layer:
Your_Project/FreeRTOS/Source/portable/GCC/ARM_CM3. - Click Apply and Close.
Verification
The moment of truth. Let's include the main FreeRTOS header in main.c and try to compile.
- Open
Core/Src/main.c. - Under the private include section, add
#include "FreeRTOS.h". - Clean and build the project (Project -> Clean..., then Project -> Build All).
If the build completes with 0 errors, you have successfully integrated FreeRTOS into your project!
Conclusion
In this lesson, you performed the essential steps of a manual FreeRTOS integration. This is a significant milestone that provides a robust foundation for all our future RTOS development.
Key Takeaways:
- SysTick Conflict: Always re-route the HAL time base away from SysTick to avoid conflicts with the FreeRTOS kernel.
- File Organization: A clean integration involves copying the core kernel source and then pruning the
portabledirectory to include only the files for your specific MCU core (ARM_CM3), compiler (GCC), and desired memory manager (heap_4.c). FreeRTOSConfig.his Key: This file is your control panel for the RTOS. It must be created by you and configured for your hardware.- Interrupt Handling is Critical: You must ensure the FreeRTOS exception handlers are used by the linker. This involves both redirecting the handler names in
FreeRTOSConfig.hand removing the default implementations fromstm32f1xx_it.c. - IDE Configuration: The compiler needs to know where to find the source files (
Source Location) and header files (Include Paths).
Now that we have the RTOS kernel integrated, our next step is to bring it to life. In the next lesson, you will learn how to create and manage multiple tasks, finally seeing the power of pre-emptive multitasking in action on your own hardware.