1. First FreeRTOS project

Submitted by admin on Thu, 07/15/2021 - 22:34

FreeRTOS tutorials have been tested and illustrated using STM32CubeIDE. You may refer to above tutorials for more details on installing and using this IDE.



1. The foundation : A clean bare-metal working project

Our starting point is a simple, clean and basic STM32 project. Basically we need CMSIS files, a clock configuration function for a 48MHz operation, and a minimal set of BSP functions for working with LED, Push-Button, and debug console (printf). We neither need interrupts nor DMA initializations here.

In order to setup such a starting project, you may either :

  • Highly recommended: Start a new project named 'nucleo_64_F072_FreeRTOS'
  • Moderately recommended: Start from a copy of the 'blink3' project (if you already have it from previous 1.7. Hello World #4 and 1.8. Clock settings)
  • Not quite recommended: Start from a copy of the 'my-project' project from the previous tutorial program
    • Refer to the 1.9. Importing & Cloning projects tutorial to make a copy of the 'my_project' project into a new project named 'nucleo_64_F072_FreeRTOS'
    • Then carefully clean (i.e. delete code) this copy from everything (including interrupt handlers) but some basic BSP functions for LED, Push-Button, UART and printf() control.

In any case, make sure that all references to previous Git repositories (either local or remote) are removed. If necessary, use the disconnect  function and delete .git local folder. You'll have to start a fresh local repository and to configure a new remote for these tutorials.

Your project structure should be similar to this:


From the STM32 tutorials section, the BSP functions we keep as a start are simply:

 * bsp.h
 *  Created on: 24 mai 2021
 *      Author: Laurent

#ifndef INC_BSP_H_
#define INC_BSP_H_

#include "stm32f0xx.h"

 * LED driver functions
void        BSP_LED_Init       (void);
void        BSP_LED_On         (void);
void        BSP_LED_Off        (void);
void        BSP_LED_Toggle     (void);

 * Push-Button driver functions
void        BSP_PB_Init        (void);
uint8_t     BSP_PB_GetState    (void);

 * Debug Console init
void        BSP_Console_Init   (void);    // Simple, with no DMA on RX channel

#endif /* INC_BSP_H_ */


As a test, use the following main() function:

int main()
	uint32_t	i;

	// Configure clock for 48MHz operation

	// Initialize LED and User button

	// Initialize Console

	my_printf("Console Ready!\r\n");
	my_printf("SYSCLK = %d\r\n", SystemCoreClock);

		// LED toggle

		// User-Button test
		if(BSP_PB_GetState() == 1)

		// Wait
		for(i=0; i<1000000; i++);


Review the build configuration to make sure that everything is correctly set. You may for instance check for include paths:

And the linker script path:


Then build the project and repair any error or warning you'll have first time (unless you're very good). What we need is a perfectly clean base for further FreeRTOS integration:


You may also take a look at the memory footprint:


Next, if not done yet, create a debug configuration:


Now you should be ready to launch the debug session. Using the Expressions view, make sure that SystemCoreClock global variable switches from 8MHz to 48MHz when stepping over  the SystemClock_Config() function.

Open a console application (e.g. Putty) with correct COM port settings and then run  the program. You should get the famous blinking LED, with some '#' being printed when the user button is held down. Well done!


Repeating steps from tutorial 1.10. Git and GitLab, create a local repository for this tutorial, and configure the remote repository according to teacher instructions.

gitlab commit  Commit name "Bare-metal base project"
push  Push onto Gitlab


2. Getting FreeRTOS into the project

2.1. Adding FreeRTOS source code

At the time of writing this tutorial, latest FreeRTOS kernel version is 10.4.3 (release 202104.00). Get the FreeRTOS source archive from the web (https://www.freertos.org), or school repository and copy it somewhere safe on your computer.

Unzip the downloaded archive and locate the Source\ folder:

Using Windows file navigator, Copy and Paste the Source\ folder into your project directory, beside app\, bsp\ and cmis\ folders. Then rename it 'FreeRTOS\'.

At this step, you need to understand that FreeRTOS source files are provided with support for a lot of different MCUs and toolchains. Therefore, the first thing to do is some housekeeping to only retain relevant source files in our context: STM32F072 (ARM Cortex-M0 CPU) with gcc toolchain.

  • Delete readme.txt from FreeRTOS\portable\ folder.
  • Delete stdint.readme from FreeRTOS\include\ folder.
  • In the FreeRTOS\portable\ directory delete all the folders, except:
    • GCC\
    • MemMang\
  • In the FreeRTOS\portable\GCC\ directory, delete all the folders except:
    • ARM_CM0\
  • In the FreeRTOS\portable\MemMang\ directory, delete all files except:
    • heap_1.c


Finally, browse the FreeRTOS archive, and find in the Demo\ folder something that merely corresponds to your target device (STM32F072) and toolchain (gcc). Then Copy/Paste the FreeRTOSConfig.h file from there into your app\inc\ folder. For now, a good candidate is for instance in FreeRTOS\Demo\CORTEX_STM32F100_Atollic\Simple_Demo_Source\

Once you're done with all this work, your project explorer should look like this:

You may also notice that FreeRTOS\ folder does not appear as a source folder. This is because it was copied using Windows file explorer. That's a problem because if we don't repair this, all the source files below that folder are not involved into the build process.

Open the project properties, and select the C/C++ General→Path and Symbols category, then select the Source Location tab. Using the Add Folder... button, add the FreeRTOS folder to the folder source list, then Apply and Close.

Make sure that the FreeRTOS folder is now tagged as a source folder:

Add the two new header paths into your build settings and apply changes.


2.2. FreeRTOS configuration

Then open the FreeRTOSConfig.h file and edit as follows :

  • First, delete (or comment) these lines:
/* The following #error directive is to remind users that a batch file must be
 * executed prior to this project being built.  The batch file *cannot* be 
 * executed from within CCS4!  Once it has been executed, re-open or refresh 
 * the CCS4 project and remove the #error line below.
#error Ensure CreateProjectDirectoryStructure.bat has been executed before building.  See comment immediately above.
  • Second, tune the FreeRTOS configuration as follows:
 * Application specific definitions.
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 * See http://www.freertos.org/a00110.html.

#define configUSE_PREEMPTION			1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( 48000000UL )	
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES			( 5 )
#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 70 )
#define configTOTAL_HEAP_SIZE			( ( size_t ) ( 7 * 1024 ) )
#define configMAX_TASK_NAME_LEN			( 10 )
#define configUSE_TRACE_FACILITY		0
#define configUSE_16_BIT_TICKS			0
#define configIDLE_SHOULD_YIELD			1
#define configUSE_MUTEXES			1
#define configQUEUE_REGISTRY_SIZE		0
#define configGENERATE_RUN_TIME_STATS	        0
#define configCHECK_FOR_STACK_OVERFLOW	        0
#define configUSE_RECURSIVE_MUTEXES		0
#define configUSE_MALLOC_FAILED_HOOK	        0
#define configUSE_APPLICATION_TASK_TAG	        0
#define configUSE_COUNTING_SEMAPHORES	        0


  • Finally, open stm32f0xx_it.c and comment the 3 interrupt handlers implementations, as FreeRTOS needs its own definition of these:
    • SVC_Handler()
    • PendSV_Handler()
    • SysTick_Handler()
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
//void SVC_Handler(void)

  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
//void PendSV_Handler(void)

  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
//void SysTick_Handler(void)
//  // HAL_IncTick();


Save all the project files , clean the project and rebuild all . Hopefully, you'll get a clean build report with no error or warnings:

21:42:56 **** Build of configuration Debug for project nucleo_64_F072_FreeRTOS ****
make all 
arm-none-eabi-gcc -mcpu=cortex-m0 -g3 -c -x assembler-with-cpp -MMD -MP -MF"cmsis/device/src/startup_stm32f072xb.d" -MT"cmsis/device/src/startup_stm32f072xb.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "cmsis/device/src/startup_stm32f072xb.o" "../cmsis/device/src/startup_stm32f072xb.s"
arm-none-eabi-gcc "../cmsis/device/src/system_stm32f0xx.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"cmsis/device/src/system_stm32f0xx.d" -MT"cmsis/device/src/system_stm32f0xx.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "cmsis/device/src/system_stm32f0xx.o"
arm-none-eabi-gcc "../bsp/src/bsp.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"bsp/src/bsp.d" -MT"bsp/src/bsp.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "bsp/src/bsp.o"
arm-none-eabi-gcc "../app/src/main.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"app/src/main.d" -MT"app/src/main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "app/src/main.o"
arm-none-eabi-gcc "../app/src/printf-stdarg.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"app/src/printf-stdarg.d" -MT"app/src/printf-stdarg.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "app/src/printf-stdarg.o"
arm-none-eabi-gcc "../app/src/stm32f0xx_it.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"app/src/stm32f0xx_it.d" -MT"app/src/stm32f0xx_it.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "app/src/stm32f0xx_it.o"
arm-none-eabi-gcc "../FreeRTOS/portable/MemMang/heap_1.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/portable/MemMang/heap_1.d" -MT"FreeRTOS/portable/MemMang/heap_1.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/portable/MemMang/heap_1.o"
arm-none-eabi-gcc "../FreeRTOS/portable/GCC/ARM_CM0/port.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/portable/GCC/ARM_CM0/port.d" -MT"FreeRTOS/portable/GCC/ARM_CM0/port.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/portable/GCC/ARM_CM0/port.o"
arm-none-eabi-gcc "../FreeRTOS/croutine.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/croutine.d" -MT"FreeRTOS/croutine.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/croutine.o"
arm-none-eabi-gcc "../FreeRTOS/event_groups.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/event_groups.d" -MT"FreeRTOS/event_groups.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/event_groups.o"
arm-none-eabi-gcc "../FreeRTOS/list.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/list.d" -MT"FreeRTOS/list.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/list.o"
arm-none-eabi-gcc "../FreeRTOS/queue.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/queue.d" -MT"FreeRTOS/queue.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/queue.o"
arm-none-eabi-gcc "../FreeRTOS/stream_buffer.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/stream_buffer.d" -MT"FreeRTOS/stream_buffer.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/stream_buffer.o"
arm-none-eabi-gcc "../FreeRTOS/tasks.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/tasks.d" -MT"FreeRTOS/tasks.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/tasks.o"
arm-none-eabi-gcc "../FreeRTOS/timers.c" -mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F072xB -c -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/app/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/bsp/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/core" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/cmsis/device/inc" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/include" -I"C:/STM/workspace_tuto/nucleo_64_F072_FreeRTOS/FreeRTOS/portable/GCC/ARM_CM0" -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"FreeRTOS/timers.d" -MT"FreeRTOS/timers.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "FreeRTOS/timers.o"
arm-none-eabi-gcc -o "nucleo_64_F072_FreeRTOS.elf" @"objects.list"   -mcpu=cortex-m0 -T"C:\STM\workspace_tuto\nucleo_64_F072_FreeRTOS\STM32F072RBTx_FLASH.ld" --specs=nosys.specs -Wl,-Map="nucleo_64_F072_FreeRTOS.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
Finished building target: nucleo_64_F072_FreeRTOS.elf
arm-none-eabi-size   nucleo_64_F072_FreeRTOS.elf 
   text	   data	    bss	    dec	    hex	filename
   4096	     12	   1708	   5816	   16b8	nucleo_64_F072_FreeRTOS.elf
Finished building: default.size.stdout
arm-none-eabi-objdump -h -S  nucleo_64_F072_FreeRTOS.elf  > "nucleo_64_F072_FreeRTOS.list"
Finished building: nucleo_64_F072_FreeRTOS.list
arm-none-eabi-objcopy  -O binary  nucleo_64_F072_FreeRTOS.elf  "nucleo_64_F072_FreeRTOS.bin"
Finished building: nucleo_64_F072_FreeRTOS.bin

21:43:00 Build Finished. 0 errors, 0 warnings. (took 3s.711ms)


3. Your first FreeRTOS application

Let-us first include FreeRTOS headers into the project. We will take this opportunity to regroup all the headers in main.h as follows:

 * main.h
 *  Created on: 15 mai 2021
 *      Author: Laurent

#ifndef INC_MAIN_H_
#define INC_MAIN_H_

// Device header
#include "stm32f0xx.h"

// BSP functions
#include "bsp.h"

// FreeRTOS headers
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"
#include "queue.h"
#include "event_groups.h"
#include "stream_buffer.h"

// Global functions
int my_printf	(const char *format, ...);
int my_sprintf	(char *out, const char *format, ...);

#endif /* INC_MAIN_H_ */


Doing so, you just have to include main.h in main.c and all the required functions will be available:

 * main.c
 *  Created on: 24/02/2018
 *      Author: Laurent

#include "main.h"

// Static functions
static void SystemClock_Config	(void);


Next, still in main.c, let us prototype two global functions that we will use for the implementation of two so-called tasks in the RTOS vocabulary:

// FreeRTOS tasks
void vTask1 	(void *pvParameters);
void vTask2 	(void *pvParameters);


And now, let us write the main() function which basically:

  1. Performs MCU init, including clock settings and some low-level peripheral configurations
  2. Create tasks as OS kernel objects, providing a name, a given amount of RAM and a priority level
  3. Start the OS scheduler
// Main program
int main()
	// Configure System Clock

	// Initialize LED pin

	// Initialize Debug Console

	// Create Tasks
	xTaskCreate(vTask1, "Task_1", 256, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task_2", 256, NULL, 2, NULL);

	// Start the Scheduler

		// The program should never be here...


In a common FreeRTOS application, the vTaskStartScheduler() function never returns. It starts the OS scheduler that manages tasks execution. Any code below this function call will never execute if everything goes well. The while(1) loop is placed here to catch abnormal behavior in the debugger. If you get there, there's something wrong...

Last thing, we need an implementation code for each task. In this very first example, vTask1() simply toggles the LED every 300ms, while vTask2() outputs a console message every 1s. For simplicity, you can put the following implementation right below the main() function within main.c:

 *	Task1 toggles LED every 300ms
void vTask1 (void *pvParameters)

 *	Task2 sends a message to console every 1s
void vTask2 (void *pvParameters)
	uint16_t count;

	count = 0;

		my_printf("Hello %2d from task2\r\n", count);


Save all  and rebuild  the project. You should have no error or warning. Open a Putty serial session using correct COM number and baudrate. Then run the program.

You should see both the LED blinking, and the console displaying:

vTask1() vTask2()


Congratulations, you've got your first FreeRTOS multi-tasking application up and running!

Note that vTask1() and vTask2() have two things in common, that you may consider as primary rules, working in most cases, for ANY task placed under RTOS control:

  1. Both are implemented just like a small main() function with :
    • local variables declaration
    • variables and peripherals initializations
    • a while(1) never ending loop, that actually performs the desired task operations  
  2. Inside of the never ending loop, there MUST BE a waiting mechanism. This is something telling the scheduler that the task is not busy for a while. If it's not there, other tasks of lower priority level will never run. That's the purpose of the vTaskDelay() function. We will see later that many other waiting mechanisms may be involved.


gitlab commit  Commit name "FreeRTOS base project"
push  Push onto Gitlab

4. Summary

In this tutorial, you have learned how to create a FreeRTOS template project, and wrote two very simple tasks to verify that everything is actually working as expected.