Date: febrero 24, 2025
Author: Guillermo Garcia
Categories: RTOS Tags: FreeRTOS
In this article we will see how to configure the FreeRTOS kernel in our compilation environment and development board. For this series we will be working with the STM32F407VG Discovery kit development board, we will build the project where we will add the FreeRTOS kernel step by step.
Table of Contents
Let’s start by obtaining a base project that includes the necessary board drivers, as well as all the files required to compile the project using CMake in Visual Studio Code.
If you’re not familiar with CMake, don’t worry! You can check out our series of articles where we show you how to work with CMake and Visual Studio Code on any development board. Take a look [here].
Now, open STM32CubeMX and use the board selector to choose your board.
We search for our board using STM32F4, then select STM32F407G-DISC1 and click the Start Project button.
It asks if we want to start the project with the default settings. In this case, we choose No.
Once the project is created, we are in the configuration window. Many may already know that FreeRTOS can be added and managed directly from STM32CubeMX, but to demonstrate the process, we will add it manually. This approach gives us more control and allows us to explore its configurations throughout this series of articles.
Finally, we go to the Code Generator settings to configure the file generation process. Here, we specify that we only want the necessary libraries and drivers and ensure that the peripheral configuration files are generated separately for better organization and control.
We go to Project and select the option to generate the project for CMake. This way, we won’t need to manually write the CMakeLists.txt files or other configurations, making it easier to compile the project directly from Visual Studio Code.
Generate the Code and Prepare the Base Project for FreeRTOS.
Once we generate the code, we will have a base project ready to integrate FreeRTOS.
Before adding FreeRTOS, we need to ensure that our base project compiles correctly. To do this, we will add a new folder to the project, which will contain the necessary files to initiate the compilation using Visual Studio Code tasks. If you’re unfamiliar with this process, you can check out [this article].
.bin
ExecutableBy default, CMake generates a debuggable executable in .elf
format. However, we need to modify the configuration so that it also generates a binary (.bin
) file, which is essential for flashing the firmware onto the microcontroller.
To do this, we open the main CMakeLists.txt file and add the necessary command.
At the end of the CMakeLists.txt file, add the following command:
# Convert to bin file -> add conditional check? add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.bin )
This command ensures that after the project is compiled, the .elf
file is automatically converted into a .bin
file, making it easier to flash onto the microcontroller.
We create a folder named build and start the configuration using Visual Studio Code tasks.
To do this, open Visual Studio Code, press Ctrl + Shift + P, and search for «Tasks: Run Task». Then, select the CMake: Configure option to generate the necessary build files.
This step prepares the project for compilation and ensures that all dependencies are properly configured.
Now, we compile the project.
To do this, open Visual Studio Code, press Ctrl + Shift + P, and search for «Tasks: Run Task». Then, select CMake: Build to start the compilation process.
If everything is set up correctly, the project should compile successfully, generating both the .elf and .bin files.
We need to download the FreeRTOS kernel and add the necessary files to our project. To do this, we go to the FreeRTOS website and download the latest available version.
Once downloaded, we will extract the required files and integrate them into our project structure.
Inside the downloaded files, we find the FreeRTOS kernel at:
FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel
Now, we create a folder named RTOS in our project and copy the necessary files to compile the kernel for our board. This will ensure that FreeRTOS is properly integrated into our project.
In this folder, we will place the header files that contain the kernel declarations.
Go to the downloaded directory:
FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\include
Copy all the header files from this folder and paste them into the include folder of our project.
This ensures that our project has access to the necessary FreeRTOS API definitions.
In this folder, we will place the source files for the heap management algorithms used by the kernel.
Go to the downloaded directory:
FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\portable\MemMang
Copy all the files from this folder and paste them into the MemMang folder inside our project.
These files contain different memory management strategies, such as heap_1.c
, heap_2.c
, etc. We will later select the one that best suits our needs.
In this folder, we place the port that is compatible with our processor core and platform. Since we are using an ARM Cortex-M4 microcontroller, we need to select the correct port.
Go to the downloaded directory:
FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\portable\GCC\ARM_CM4F
Copy all the files from this folder and paste them into the portable folder in our project.
These files contain the architecture-specific implementation required for FreeRTOS to run on our microcontroller.
In this folder, we will place the main source files that contain the FreeRTOS kernel APIs.
Go to the downloaded directory:
FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel
Copy the source files (e.g., tasks.c
, queue.c
, timers.c
, etc.) and paste them into the source folder in our project.
These files implement the core functionalities of FreeRTOS, including task management, scheduling, and inter-process communication.
We need to add a header file that centralizes the FreeRTOS kernel configurations.
To do this, create a new file named FreeRTOSConfig.h inside the RTOS folder in our project.
This file will define important settings such as task priorities, stack sizes, and memory management options, allowing us to customize FreeRTOS for our microcontroller.
Add the following code to the FreeRTOSConfig.h file to set up a basic configuration for FreeRTOS. We will review the configurations in detail later.
/* * FreeRTOS V202112.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://www.FreeRTOS.org * http://aws.amazon.com/freertos * * 1 tab == 4 spaces! */ #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H /*----------------------------------------------------------- * Application specific definitions. * * These definitions should be adjusted for your particular hardware and * application requirements. * * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. * * See http://www.freertos.org/a00110.html *----------------------------------------------------------*/ /* Ensure stdint is only used by the compiler, and not the assembler. */ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include <stdint.h> extern uint32_t SystemCoreClock; #endif #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( ( uint16_t ) 128 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 10 ) #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 8 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_COUNTING_SEMAPHORES 1 #define configGENERATE_RUN_TIME_STATS 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 0 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* Software timer definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY ( 2 ) #define configTIMER_QUEUE_LENGTH 10 #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_pxTaskGetStackStart 1 /* Cortex-M specific definitions. */ #ifdef __NVIC_PRIO_BITS /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ #define configPRIO_BITS __NVIC_PRIO_BITS #else #define configPRIO_BITS 4 /* 15 priority levels */ #endif /* The lowest interrupt priority that can be used in a call to a "set priority" function. */ #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf /* The highest interrupt priority that can be used by any interrupt service routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER PRIORITY THAN THIS! (higher priorities are lower numeric values. */ #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* Interrupt priorities used by the kernel port layer itself. These are generic to all Cortex-M ports, and do not rely on any particular library functions. */ #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* Normal assert() semantics without relying on the provision of an assert.h header file. */ #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS standard names. */ #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler // #if defined(USE_SYSVIEW) // #include "SEGGER_SYSVIEW_FreeRTOS.h" // #endif #endif /* FREERTOS_CONFIG_H */
This configuration provides a basic setup to get FreeRTOS running. It defines task priorities, heap size, and core settings like preemption and tick rate. Later, we will adjust these settings to better fit our project. 🚀.
Now that we have the FreeRTOS kernel files in our project, we need to add them to the CMake build system so they are compiled as part of our project.
To do this, open the main CMakeLists.txt file and add the following lines:
So far, we have added and compiled the FreeRTOS kernel files with our project, but the kernel is not yet connected to our system.
At this moment, we have a time base provided by the SysTick peripheral, which executes every millisecond as the tick source for the HAL drivers of our board. However, FreeRTOS also requires a tick interrupt to manage task scheduling.
To connect FreeRTOS to our system, we need to:
void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ }
However, it is necessary to have a Tick system for the FreeRTOS kernel and another Tick for the system of our board, what we will do next is configure a Timer peripheral as a Tick source for the system and HAL libraries, with this we leave the SysTick peripheral free to be used as the main tick for the FreeRTOS Kernel.
We will do this from STM32CubeMX, open our configuration file and change the Tick source for an available generic Timer.
We change the time base source to TIMER2 and generate the code.
Now the SysTick driver is free and the system tick is now performed from the TIMER2 driver every millisecond.
Now let’s call the FreeRTOS kernel using the SysTick every millisecond we add the following code in the SysTick_Handler(void) handler as long as the kernel is started.
We need to redirect the PendSV and SVC exception handlers so that the function handlers declared in the kernel are used. To do this, we add two statements in the FreeRTOSConfig.h configuration file.
The handlers for these exceptions are already described in the stm32f4xx_it.c file, if we try to compile it will generate an error due to the double declaration, therefore we must remove the declarations of the functions present in stm32f4xx_it.c.
To prevent them from being generated again we are going to disable them from the STM32CubeMX, we will open our configuration file and disable these function declarations.
We uncheck the ISR function generation and generate the code again. We compile and see that there are no problems.
Finally, let’s start the kernel and set a task to make sure it’s working properly on our board.
Before starting, we must enable and configure the SysTick peripheral. By changing it as the time base, the peripheral is not enabled.
HAL_SYSTICK_Config(SystemCoreClock / (1000U / (uint32_t)uwTickFreq)); HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0U); HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
This ensures that the peripheral starts and generates an interrupt that will call the FreeRTOS kernel every period.
Now we can create a task called TaskTest to ensure that FreeRTOS is working.
void TaskTest(void* NotUsed) { while(1) { HAL_GPIO_TogglePin(LD3_GPIO_Port,LD3_Pin); vTaskDelay(500); } } xTaskCreate(TaskTest,"TaskTest",configMINIMAL_STACK_SIZE,NULL,tskIDLE_PRIORITY+1,NULL); vTaskStartScheduler();
We create the task and start the kernel. Now FreeRTOS has control, we see it in the debug.
Deja una respuesta