STM32 FreeRTOS Tutorial: Introduction to Real-Time Operating Systems
FreeRTOS is a powerful real-time operating system (RTOS) for STM32 microcontrollers, enabling multitasking with precise control over task scheduling. In this tutorial, you’ll learn how to set up STM32 FreeRTOS using STM32CubeMX, create tasks, assign priorities, and understand why using FreeRTOS in STM32 is better than bare-metal code for complex embedded projects.

This tutorial is the first installment in a comprehensive series and will focus on the following key topics:
- Setting up FreeRTOS using CubeMX – A step-by-step guide to configuring FreeRTOS in your project.
- Benefits of using an RTOS – Understanding why a real-time operating system is advantageous for embedded applications.
- Creating tasks with and without CubeMX – Exploring different approaches to task creation and management.
- Using priorities to address common issues – Leveraging task priorities to resolve scheduling and resource conflicts effectively.
VIDEO TUTORIAL
You can check the video to see the complete explanation and working of this project.
Check out the Video Below
What is FreeRTOS? (Real-Time OS Overview)
FreeRTOS (Free Real-Time Operating System) is an open-source, lightweight RTOS designed for microcontrollers like STM32. It enables task scheduling, inter-task communication, and real-time responsiveness. Developers use FreeRTOS in STM32 for multitasking, running sensors and comms stacks concurrently, and simplifying timing challenges in embedded systems.
This tutorial introduces RTOS (Real-Time Operating System) for STM32 microcontrollers. You will learn how to set up FreeRTOS using CubeMX, create tasks, and understand how using an RTOS can make your programs run more smoothly and efficiently. This is a great starting point for anyone looking to manage multiple tasks in STM32 projects.
Important Features of RTOS:
- Multitasking – Run multiple tasks at the same time without conflicts.
- Task Priorities – Make sure important tasks run first.
- Predictable Timing – Tasks respond quickly and reliably when needed.
- Communication Between Tasks – Tasks can share information safely using queues, semaphores, and mutexes.
STM32CubeMX FreeRTOS Configuration
The image below shows the configuration for the FreeRTOS.
I am choosing version 1, because it is supported by majority of STM32 devices.
Open the ‘Tasks and Queues‘ tab. Here you will see a predefined default task. Open it to see more information about this task.
The Task details are explained below:
- Task Name:
defaultTask
– This is just the name of the task. You use this name in your code to work with this task. - Priority:
osPriorityNormal
– This decides how important the task is. Normal priority means it runs after high-priority tasks but before low-priority ones. It helps the CPU know which task to run first. - Stack Size (Words):
128
– This is the memory the task can use for its variables and function calls. Think of it as the workspace for the task. - Entry Function:
StartDefaultTask
– This is the function that will run when the task starts. You put the task’s code inside this function. - Code Generation Option:
Default
– CubeMX will generate the task using its standard method. - Parameter:
NULL
– If you wanted to give some information to the task when it starts, you could put it here. NULL means no information is passed. - Allocation:
Dynamic
– Memory for the task is given at runtime. The other option is static, where memory is reserved before the program runs.
We will create another task for the test. Below is the image showing the task properties.
I am calling it Task2, with normal priority, 128 Stack Size and the entry function is defined as Task2_init. We will write this entry function in our code.
When using FreeRTOS, you cannot use the default SysTick timer for the system time base because FreeRTOS uses it internally to manage task switching and delays.
So, you need to choose a different timer as the time base for your system (like TIMx) in STM32CubeMX. This ensures the system clock keeps running correctly while FreeRTOS manages tasks.
Why Use FreeRTOS in STM32 Projects
Imagine you want to toggle two pins at the same time, each with its own delay. With simple programming, this is hard because the microcontroller can only do one instruction at a time. So, the first pin has to finish toggling before the second one can start, even if both are inside a while
loop.
This is where RTOS helps. By creating two separate tasks, each task can handle one pin independently. FreeRTOS will switch between the tasks quickly, making it appear as if both pins are toggling at the same time.

If you scroll down in the main.c
file, you will see the entry functions for the tasks we defined in the cubeMX. These functions contain the code that runs for each task—one for each pin.
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
osDelay(1);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_Task2_init */
/**
* @brief Function implementing the Task2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task2_init */
void Task2_init(void const * argument)
{
/* USER CODE BEGIN Task2_init */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
osDelay(1);
}
/* USER CODE END Task2_init */
}
In this example, we toggle pin PA0 within the default task and pin PA1 within Task2. The FreeRTOS scheduler manages the execution, ensuring that both tasks receive sufficient CPU time to run properly.
The oscilloscope reading below shows the output when the above code was executed.
How to Create Tasks in STM32 FreeRTOS
In order to create a new Task, we have to follow some set of steps, and they are as follows:-
1.) Define a ThreadID for the task. This variable will store the unique ID of the task, once created. Later, all the operations will require this ID.
osThreadId Task3Handle;
2.) Define the entry function for the task. This is the main function of the task. Your program will be written inside it. Remember that the tasks in the Free RTOS, are not designed to handle any return value. So, the entry function should always have an infinite loop, inside which, your whole program should be written.
void Task3_init (void const * argument)
{
while (1)
{
// do something
osDelay (1000); // 3 sec delay
}
}
3.) Inside our main function, we need to define the task first and than create it.
// define thread
osThreadDef(Task3, Task3_init, osPriorityBelowNormal, 0, 128);
//create thread
Task3Handle = osThreadCreate(osThread (Task3), NULL);
- osThreadDef takes the parameters as the name of the task, the entry function, the priority, instance, and the stack size.
- After the task is defined, we can create it using osThreadCreate, and assign the ID to the Task3Handle
Task Priorities in STM32 FreeRTOS (with Code)
So far, we have seen how to perform multitasking using FreeRTOS. However, using multiple tasks can sometimes lead to certain challenges. One common issue arises when multiple tasks try to access the same resource at the same time.
For example, consider sending data over UART from three different tasks simultaneously:
- When we write a program for this, the transmission will not happen exactly at the same time.
- Instead, each task will send its data one after another — the first task sends for 1 second, then the second task for another second, and so on.
- This occurs because the tasks are sharing the same resource (UART) and have the same priority.
- The second task has to wait for the first task to finish before it gets control, and similarly, the third task waits for the second.
To avoid these situations, we use different priorities for different tasks. That means we have to redefine our task priorities in the main function.
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
osThreadDef(Task2, Task2_init, osPriorityAboveNormal, 0, 128);
osThreadDef(Task3, Task3_init, osPriorityBelowNormal, 0, 128);
- Now the Task2 have the highest priority, than Default task, and Task3 have the lowest.
- When the program runs, Task2 will execute first, than default task and at last the Task3.
- All three tasks will send the data at the same time, with Task2 sending the data first, then default task and at last the Task3.
In this STM32 FreeRTOS tutorial, we’ve explored how to configure RTOS using STM32CubeMX, create multiple tasks, and assign priorities. Using FreeRTOS in STM32 not only simplifies multitasking but also improves timing accuracy and application structure. Stay tuned for future guides on advanced FreeRTOS topics like queues, semaphores, and task notifications.
PROJECT DOWNLOAD
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
Project FAQs
In FreeRTOS, each task can be assigned a priority, which determines the order in which the scheduler executes them. Higher-priority tasks will preempt lower-priority tasks, ensuring that critical operations run on time. It’s important to carefully assign priorities so that no task monopolizes the CPU, and lower-priority tasks still get a chance to execute. Task priorities can also be adjusted dynamically during runtime if needed.
FreeRTOS provides several memory allocation schemes that can be chosen based on the application’s needs. These schemes differ in how they allocate and free memory, handle fragmentation, and support multiple memory regions. For STM32 projects, Heap_4 is often preferred because it offers a good balance between efficiency and flexibility, allowing dynamic allocation while minimizing fragmentation.
When using FreeRTOS, hardware interrupts should be kept short and efficient. FreeRTOS provides special APIs that can be safely called from within an interrupt, such as sending data to queues or notifying tasks. This ensures that interrupts do not block the scheduler and tasks continue to run smoothly. Care must be taken to avoid calling standard FreeRTOS APIs from an ISR unless they are specifically designed for it.
Yes, FreeRTOS can help optimize power usage. By using low-power sleep modes when no tasks are ready to run, the MCU can save energy. Tasks can also be designed to block or delay themselves when idle, allowing the CPU to enter low-power states without affecting functionality. Proper use of the scheduler and task delays can significantly reduce overall power consumption.
Debugging multitasking issues often requires monitoring task execution and resource access. Tools such as trace analyzers and RTOS-aware debuggers can help visualize task states, switching, and CPU usage. Additionally, using semaphores, mutexes, or queues correctly helps prevent race conditions and ensures proper synchronization between tasks. Observing timing behavior with an oscilloscope or logic analyzer can also provide insight into real-time performance.
Support Us by Disabling Adblock
We rely on ad revenue to keep Controllerstech free and regularly updated. If you enjoy the content and find it helpful, please consider whitelisting our website in your ad blocker.
We promise to keep ads minimal and non-intrusive.
Thank you for your support! 💙
Hello Master I’m new comer in FreeRTOS so I have one question.How to calculate the “stack size” in each Task?
https://www.freertos.org/FreeRTOS_Support_Forum_Archive/April_2014/freertos_Exact_task_stack_size_8c443f40j.html#:~:text=The%20stack%20is%20managed%20by,n%27%20bytes%20of%20stack%20space.
very useful!
Very good knowledge tutorial STM32F4 with FreeRTOS
your website is the best tutorial material I ever found.
thank you for the nice tutorial!
thank you for job!
Please make single pdf file for this excellent tutorial series on RTOS
voting system lies.. counting and result not matching… good job!..
Removing it
where is the code download? I installed chrome just to download your code.
sorry, i didn’t upload any code for it. This was a simple case. just set everything from cube mx and run it.
Could you help me to use modul NFC modul v3 for stm32cubemx and Keilc v5 thanks