How to port FreeRTOS to ARM Cortex-M4

This is a step-by-step guide for getting FreeRTOS up and running on an ARM Cortex-M4 microcontroller using Keil MDK and standard ARM debugging tools.

Step 1 — Download FreeRTOS source

Grab the latest source bundle and reference docs from the official site: freertos.org/a00104.html.

Step 2 — Prepare your debugger

Two debuggers cover almost every Cortex-M4 board you'll meet:

Install the matching flash utility for your specific MCU.

Step 3 — Lay out the Keil project

Organize the project into three logical groups:

Project/
├── cmsis_core/        ← ARM Cortex-M4 core headers from chip vendor SDK
├── FREERTOS/          ← FreeRTOS source code
└── portable/
    ├── ARM_CM4F/      ← Cortex-M4 + FPU port files
    └── MemMang/       ← heap_1.c / heap_2.c / heap_4.c — pick one

Pick the port files specific to ARM Cortex-M4 (with FPU if your chip has one). Mismatching the port is the most common source of weird hard faults.

Step 4 — Configure FreeRTOS

The single most important file is FreeRTOSConfig.h. At minimum, edit:

A minimal Cortex-M4 FreeRTOSConfig.h looks roughly like this:

#define configUSE_PREEMPTION                    1
#define configCPU_CLOCK_HZ                      ((unsigned long) 168000000)
#define configTICK_RATE_HZ                      ((TickType_t) 1000)
#define configMAX_PRIORITIES                    5
#define configMINIMAL_STACK_SIZE                ((uint16_t) 128)
#define configTOTAL_HEAP_SIZE                   ((size_t) (32 * 1024))
#define configUSE_IDLE_HOOK                     0
#define configUSE_TICK_HOOK                     0
#define configCHECK_FOR_STACK_OVERFLOW          2
#define configUSE_MALLOC_FAILED_HOOK            1

/* Cortex-M4 specific — interrupt priorities */
#define configPRIO_BITS                         4   /* STM32F4 has 4 priority bits */
#define configKERNEL_INTERRUPT_PRIORITY         (15 << (8 - configPRIO_BITS))
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( 5 << (8 - configPRIO_BITS))

/* Map the kernel handlers to the CMSIS names */
#define vPortSVCHandler         SVC_Handler
#define xPortPendSVHandler      PendSV_Handler
#define xPortSysTickHandler     SysTick_Handler

Step 5 — Pick a heap implementation

FreeRTOS ships five heap allocators in portable/MemMang/. For a Cortex-M4 starter project, pick one:

Step 6 — Run the demo

Start with the bundled main_blinky example. A minimal main.c looks like this:

#include "FreeRTOS.h"
#include "task.h"

static void vBlinkTask(void *pvParameters)
{
    for (;;) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART2_UART_Init();

    xTaskCreate(vBlinkTask, "Blink", 256, NULL, 1, NULL);

    vTaskStartScheduler();   // never returns
    for (;;) { /* heap exhausted */ }
}

Once your LED blinks and a second task prints on schedule, you have a working port and can start building on top.

Common pitfalls

Tip: The MCU clock must be configured correctly with your MCU before starting the RTOS. Initializing UART early makes debugging dramatically easier.
← Back to all posts