Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 47

Integrating FreeRTOS into STM32 Projects

Recommended resources

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

  1. Open STM32CubeIDE and create a new project for your STM32F103C8T6 board.
  2. In the CubeMX perspective (.ioc file), go to the Pinout & Configuration tab.
  3. Under the System Core category, select SYS.
  4. In the SYS Mode and Configuration panel, locate the Timebase Source. Change it from SysTick to one of the basic timers, like TIM1 or TIM2.
  5. Save the .ioc file to generate the code. When prompted, answer Yes to 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.

  1. Download: Go to the official FreeRTOS website and download the latest kernel version as a .zip file.
  2. 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:

  1. In your STM32CubeIDE project explorer, right-click on the project root and select New -> Folder. Name it FreeRTOS.
  2. From the unzipped FreeRTOS download, copy the entire Source directory into the new FreeRTOS folder in your project.
  3. Prune the portable layer: Navigate to FreeRTOS/Source/portable/.
    • Delete all folders except GCC and MemMang.
  4. 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.
  5. 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.c is a robust choice that helps prevent memory fragmentation.

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.

FreeRTOS File Structure for Manual Integration
This image shows a typical FreeRTOS file structure within an embedded project. Note the core files (`tasks.c`, `queue.c`, etc.) and the specific `port.c` and `heap_4.c` files within the portable directories.

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 by heap_4.c to dynamically allocate memory for tasks, queues, etc.

  • Interrupt Redirection Macros:

    
    
    
    #define vPortSVCHandler     SVC_Handler
    #define xPortPendSVHandler  PendSV_Handler
    #define xPortSysTickHandler SysTick_Handler
    

    These 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:

  1. Add Source Location:

    • Right-click your project -> Properties.
    • Go to C/C++ General -> Paths and Symbols -> Source Location.
    • Click Add Folder... and check the FreeRTOS folder you created. Click OK, then Apply and Close. You should see a small 'C' icon appear on the FreeRTOS folder in the Project Explorer.
  2. 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.

  1. Open Core/Src/main.c.
  2. Under the private include section, add #include "FreeRTOS.h".
  3. 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 portable directory to include only the files for your specific MCU core (ARM_CM3), compiler (GCC), and desired memory manager (heap_4.c).
  • FreeRTOSConfig.h is 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.h and removing the default implementations from stm32f1xx_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.