FreeRTOS tutorials have been tested and illustrated using Atollic TrueStudio® for STM32 IDE. You may refer to above tutorials for more details on installing and using Atollic IDE.
Start a new empty C Project and copy a minimal set of source files in order to have a basic (e.g. 'blink') application. Basically you may want a minimal set of BSP functions for working with LED, Push-button, and debug console (printf). Let us start with no interrupts, no DMA, ... You may refer to STM32 tutorials above to setup such a starting project.
The above operation may ask you to create a new linker script file. You can accept this, and save the linker script in the project directory. Then make sure that the linker script is correctly set the linker settings.
Use the above buttons to add /remove
or edit
include paths.
Apply all this settings, and then return to the main window.
/*
* main.c
*
* Created on: 24/02/2018
* Author: Laurent
*/
#include "stm32f0xx.h"
#include "bsp.h"
#include "main.h"
// Static functions
static void SystemClock_Config (void);
// Main program
int main()
{
uint32_t i;
// Configure System Clock
SystemClock_Config();
// Initialize LED pin
BSP_LED_Init();
// Main loop
while(1)
{
BSP_LED_Toggle();
for(i=0; i<1000000; i++);
}
}
/*
* Clock configuration for the Nucleo STM32F072RB board
* HSE input Bypass Mode -> 8MHz
* SYSCLK, AHB, APB1 -> 48MHz
*/
static void SystemClock_Config()
{
uint32_t HSE_Status;
uint32_t PLL_Status;
uint32_t SW_Status;
uint32_t timeout = 0;
timeout = 1000000;
// Start HSE in Bypass Mode
RCC->CR |= RCC_CR_HSEBYP;
RCC->CR |= RCC_CR_HSEON;
// Wait until HSE is ready
do
{
HSE_Status = RCC->CR & RCC_CR_HSERDY_Msk;
timeout--;
} while ((HSE_Status == 0) && (timeout > 0));
// Select HSE as PLL input source
RCC->CFGR &= ~RCC_CFGR_PLLSRC_Msk;
RCC->CFGR |= (0x02 <<RCC_CFGR_PLLSRC_Pos);
// Set PLL PREDIV to /1
RCC->CFGR2 = 0x00000000;
// Set PLL MUL to x6
RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk;
RCC->CFGR |= (0x04 <<RCC_CFGR_PLLMUL_Pos);
// Enable the main PLL
RCC-> CR |= RCC_CR_PLLON;
// Wait until PLL is ready
do
{
PLL_Status = RCC->CR & RCC_CR_PLLRDY_Msk;
timeout--;
} while ((PLL_Status == 0) && (timeout > 0));
// Set AHB prescaler to /1
RCC->CFGR &= ~RCC_CFGR_HPRE_Msk;
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
//Set APB1 prescaler to /1
RCC->CFGR &= ~RCC_CFGR_PPRE_Msk;
RCC->CFGR |= RCC_CFGR_PPRE_DIV1;
// Enable FLASH Prefetch Buffer and set Flash Latency (required for high speed)
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
/* --- Until this point, MCU was still clocked by HSI at 8MHz ---*/
/* --- Switching to PLL at 48MHz Now! Fasten your seat belt! ---*/
// Select the main PLL as system clock source
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
// Wait until PLL becomes main switch input
do
{
SW_Status = (RCC->CFGR & RCC_CFGR_SWS_Msk);
timeout--;
} while ((SW_Status != RCC_CFGR_SWS_PLL) && (timeout > 0));
/* --- Here we go ! ---*/
/*--- Use PA8 as MCO output at 48/16 = 3MHz ---*/
// Set MCO source as SYSCLK (48MHz)
RCC->CFGR &= ~RCC_CFGR_MCO_Msk;
RCC->CFGR |= RCC_CFGR_MCOSEL_SYSCLK;
// Set MCO prescaler to /16 -> 3MHz
RCC->CFGR &= ~RCC_CFGR_MCOPRE_Msk;
RCC->CFGR |= RCC_CFGR_MCOPRE_DIV16;
// Enable GPIOA clock
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
// Configure PA8 as Alternate function
GPIOA->MODER &= ~GPIO_MODER_MODER8_Msk;
GPIOA->MODER |= (0x02 <<GPIO_MODER_MODER8_Pos);
// Set to AF0 (MCO output)
GPIOA->AFR[1] &= ~(0x0000000F);
GPIOA->AFR[1] |= (0x00000000);
// Update SystemCoreClock global variable
SystemCoreClockUpdate();
}
Clean project from any previous build then rebuild all
. Repair any error or warning, what we want is a perfectly clean base for further FreeRTOS integration:
![]() |
![]() ![]() |
At the time of writing this tutorial, latest FreeRTOS release is version 10.2.1. Get the FreeRTOS source archive from the web (https://www.freertos.org), or school repository and copy it somewhere safe on your computer.
Take some time to browse the FreeRTOS archive and locate the \Source folder:
Copy\Paste the \Source folder into your project directory, beside \app, \bsp and \cmis folders. Then rename it '\FreeRTOS'.
In the \FreeRTOS\portable\MemMang directory, delete all files except:
Finally, browse the FreeRTOS archive, and try to find in the \Demo folder something that merely corresponds to your target device (STM32F072) and toolchain (Atollic). Then Copy/Paste the FreeRTOSConfig.h file from there into your app\inc folder.
For now, a good candidate is for instance in \FreeRTOSv10.2.1\FreeRTOS\Demo\CORTEX_STM32F100_Atollic\Simple_Demo_Source.
Once you're done with all this cleaning, you project explorer should look like this:
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.
/*-----------------------------------------------------------
* 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.
*----------------------------------------------------------*/
#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:
/**
* @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:
Generate build reports...
Print size information
text data bss dec hex filename
20628 20 7652 28300 6e8c nucleo_64_F072_FreeRTOS.elf
Print size information done
Generate listing file
Output sent to: nucleo_64_F072_FreeRTOS.list
Generate listing file done
Generate build reports done
12:49:45 Build Finished (took 3s.503ms)
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: 24/02/2018
* Author: Laurent
*/
#ifndef APP_INC_MAIN_H_
#define APP_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 /* APP_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 FreeRTOS vocabulary:
// FreeRTOS tasks
void vTask1 (void *pvParameters);
void vTask2 (void *pvParameters);
And now, let us write the main() function which basically:
// Main program
int main()
{
// Configure System Clock
SystemClock_Config();
// Initialize LED pin
BSP_LED_Init();
// Initialize Debug Console
BSP_Console_Init();
// Create Tasks
xTaskCreate(vTask1, "Task_1", 256, NULL, 1, NULL);
xTaskCreate(vTask2, "Task_2", 256, NULL, 2, NULL);
// Start the Scheduler
vTaskStartScheduler();
while(1)
{
// 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...
And finally, the implementation of 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)
{
while(1)
{
BSP_LED_Toggle();
vTaskDelay(300);
}
}
/*
* Task2 sends a message to console every 1s
*/
void vTask2 (void *pvParameters)
{
uint16_t count;
count = 0;
while(1)
{
my_printf("Hello %2d from task2\r\n", count);
count++;
vTaskDelay(1000);
}
}
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 start the debugger
and run
the program.
You should see both the LED blinking, and the console displaying:
vTask1() | vTask2() |
![]() |
![]() |
![]() |
![]() ![]() |
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.