FreeRTOS FAQ

FreeRTOS configuration

Which configuration files are essential for FreeRTOS on STM32?

  • FreeRTOSConfig.h: Configuration of the kernel: priorities, stack size, timers, etc.

  • Main FreeRTOS source files:

    • tasks.c, queue.c, list.c, timers.c, event_groups.c

    • Cortex-M port files: port.c and portmacro.h

  • STM32Cube-level files:

    • Startup file: startup_stm32xxxx.s (handles PendSV, SysTick, SVC)

    • system_stm32xxxx.c (system clock configuration)

How to initialize FreeRTOS with STM32CubeMX?

In STM32CubeMX:

  • Go to Middleware > FreeRTOS and activate FreeRTOS.

Configure:

  • Heap implementation (e.g. heap_4.c or heap_5.c).

  • Minimum stack size for tasks.

  • Number of priorities: configMAX_PRIORITIES.

Generate the code:

  • STM32CubeMX typically generates: - mx_freertos_app.c, mx_freertos_app.h and FreeRTOSConfig.h - FreeRTOS sources in misc/STMicroelectronics.freertos.X.Y.Z

In main.c:

  • Call app_synctasks_init(); to create tasks and objects.

  • Start the kernel with vTaskStartScheduler();

Which FreeRTOS heap implementation should I use on STM32?

Commonly used on STM32:

  • heap_1.c: - Only static allocation (no vPortFree). - Very simple but not flexible.

  • heap_2.c: - Dynamic allocation + free. - More prone to fragmentation.

  • heap_4.c: - Most common choice. - Dynamic allocation + free with coalescing of free blocks. - More robust and widely recommended for STM32 projects.

  • heap_5.c: - Similar behavior to heap_4 but supports multiple memory regions : useful if you want to use different SRAM banks, TCM, SRAM2, etc.

In practice, STM32 community examples very often use heap_4.c.

How to size ``configTOTAL_HEAP_SIZE`` for FreeRTOS on STM32?

Basic approach:

  • Estimate the total memory required for:

    • Sum of all task stacks.

    • Queues, semaphores, mutexes, timers.

    • Plus a safety margin (30–50% at the beginning).

  • Example values (rough guidelines):

    • STM32F4 with 128 KB RAM: between 20*1024 and 50*1024 bytes, depending on number of tasks and features.

    • Low-RAM devices (STM32G0/L0): start around 4*1024 or 8*1024 bytes and adjust.

  • Monitor usage:

    • Use functions like uxTaskGetStackHighWaterMark() for each task.

    • Use uxTaskGetSystemState() and/or RTOS-aware debug tools to refine heap and stack sizes.

Interrupts and priorities

How should NVIC interrupt priorities be set with FreeRTOS on STM32?

Key macros in FreeRTOSConfig.h:

  • configLIBRARY_LOWEST_INTERRUPT_PRIORITY

  • configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

  • configKERNEL_INTERRUPT_PRIORITY

Cortex-M rules:

  • Any interrupt that calls a FreeRTOS FromISR API (e.g. xQueueSendFromISR, xSemaphoreGiveFromISR) must have a numerical priority greater or equal (i.e. less urgent) than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY.

  • Interrupts that are more urgent than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY must not call any FreeRTOS API.

Also ensure:

  • configPRIO_BITS matches the MCU (from CMSIS define __NVIC_PRIO_BITS).

  • Priority grouping is consistent if used (e.g. no conflicting NVIC_PriorityGroupConfig on older devices).

Why does my system freeze after calling ``vTaskStartScheduler()``?

Typical root causes:

Incorrect NVIC configuration :

  • Wrong priorities for PendSV, SysTick or SVCall.

  • They must match the configuration expected by the FreeRTOS port.

Main stack (MSP) too small :

  • The scheduler startup initially uses the main stack pointer.

  • Increase stack size in the linker script or startup file.

Insufficient heap :

  • Task creation may fail before the scheduler starts.

  • Check task handles for NULL and verify the malloc failed hook.

Malloc or stack overflow hooks triggered :

  • vApplicationMallocFailedHook or vApplicationStackOverflowHook may be called and loop indefinitely.

  • Ensure configASSERT is enabled and check for assertion failures.

When should I use the *FromISR* versions of FreeRTOS functions?

In any ISR (hardware interrupt):

  • Use the FromISR variants:

    • xQueueSendFromISR, xQueueReceiveFromISR, xSemaphoreGiveFromISR, xTaskNotifyFromISR, etc.

  • Never call the “normal” API functions (e.g. xQueueSend, xSemaphoreTake) from an ISR.

  • Always consider the pxHigherPriorityTaskWoken argument and call portYIELD_FROM_ISR() (or equivalent macro) if it indicates that a context switch should occur.

Tasks, stack and performance

How do I choose the task stack size?

Guidelines:

  • Start with a “large” stack value, e.g. 256 or 512 words (depending on port, usually 4-byte words).

  • During testing, use:

    • uxTaskGetStackHighWaterMark() to check remaining stack for each task.

    • Increase or decrease stack sizes based on the minimum observed headroom.

  • Beware of heavy stack users:

    • printf / sprintf and floating-point operations can consume a lot of stack.

    • Large local arrays should be made static or allocated from the heap instead of the stack.

Is there a significant CPU cost to using FreeRTOS on STM32?

For most STM32 families:

  • Context switch overhead is typically small (a few microseconds) at common clock speeds.

  • The impact depends on:

    • Interrupt rate.

    • Frequency of task wake-ups.

    • Number of ready tasks at any time.

  • For many real-time applications, the scheduling overhead is negligible compared to I/O operations or complex computations.

How can I avoid priority inversion and starvation?

Best practices:

  • Do not assign too many tasks to the maximum priority.

  • Use FreeRTOS mutexes (with priority inheritance) to protect shared resources.

  • Avoid high-priority tasks that loop without blocking:

    • They should call vTaskDelay(), xQueueReceive(), etc., so that lower-priority tasks can run.

  • Prefer a small number of well-designed tasks with internal state machines over many micro-tasks that complicate scheduling.

Inter-task communication

Queue vs Semaphore for notifying a task?

  • Queue:

    • Use to transfer data (structures, messages, values).

    • Allows buffering and decoupling producers and consumers.

  • Semaphore:

    • Binary semaphore: signal that an event occurred (often from an ISR).

    • Counting semaphore: count occurrences of an event, such as repeated interrupts or jobs.

  • Task Notification:

    • Very lightweight and efficient mechanism for one-to-one signaling.

    • Often recommended instead of a simple semaphore when only one task is involved.

When should I use Event Groups?

Event groups are useful when:

  • A task must wait on multiple conditions simultaneously.

    • Example: waiting for both ETH_READY and LINK_UP bits.

Advantages:

  • More readable than managing multiple semaphores and combining them manually.

Limitations:

  • Not meant for data transfer, only for signaling status bits/events.

SysTick and HAL_Delay

Why does ``HAL_Delay()`` behave incorrectly after starting FreeRTOS?

On STM32:

  • HAL_Delay() uses SysTick by default.

  • FreeRTOS also uses SysTick for the OS tick.

After vTaskStartScheduler():

  • HAL_Delay() is often re-implemented to rely on osDelay() or vTaskDelay().

  • If this is not done, HAL_Delay() can become inaccurate or block the system.

Recommendations:

  • In FreeRTOS-based projects, replace application calls to HAL_Delay() with osDelay() / vTaskDelay() as soon as possible.

  • Before the scheduler starts (initialization phase), short calls to HAL_Delay() are acceptable but should be minimized.

How to configure the FreeRTOS tick rate (``configTICK_RATE_HZ``)?

Typical values:

  • 1000 Hz (1 ms tick) is common for many applications.

For low-power applications (STM32L0/L4/U5, etc.):

  • You may reduce it to 100 Hz or even lower to save energy.

Trade-offs:

  • Higher tick frequencies mean more SysTick interrupts and more context switches.

  • Lower tick frequencies reduce timing resolution for vTaskDelay, vTaskDelayUntil, and timeouts.

Debugging and common issues

How do I detect a task stack overflow with FreeRTOS?

In FreeRTOSConfig.h:

#define configCHECK_FOR_STACK_OVERFLOW 2

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    /* Breakpoint, logging, reset, etc. */
    for (;;);
}

Notes:

  • Mode 2 enables a more comprehensive check.

  • Place a breakpoint inside the hook to identify the offending task.

My program ends up in HardFault after some time: how do I debug this?

Common causes on STM32 + FreeRTOS:

  1. Task or main stack overflow.

  2. Null pointer dereference in callbacks or ISRs.

  3. Incorrect NVIC/FreeRTOS configuration (wrong priority setup).

Suggested actions:

  • Enable configASSERT and the stack overflow hook.

  • Implement a detailed HardFault handler that dumps registers and stack frame.

  • Verify that all tasks have adequate stack size.

  • Double-check configPRIO_BITS and interrupt priority configuration.

Integration with HAL/LL

How to use FreeRTOS with HAL callbacks (DMA, UART, SPI, etc.)?

Typical pattern:

In the HAL callback (executed in interrupt context):

  • Notify a task using xSemaphoreGiveFromISR(), xQueueSendFromISR(), or vTaskNotifyGiveFromISR().

In the task:

  • Wait for the event using xSemaphoreTake(), xQueueReceive(), ulTaskNotifyTake(), etc.

Best practice:

  • Keep ISR code small and fast.

  • Perform heavy processing in tasks, not in ISRs.

Should access to HAL drivers be limited per task?

Often recommended architecture:

  • One peripheral = one “owner” task, responsible for:

    • Initializing the peripheral.

    • Handling all I/O.

    • Managing errors.

  • Other tasks communicate with this owner task using queues or messages.

  • For shared peripherals accessed by multiple tasks:

    • Use FreeRTOS mutexes around HAL calls (if a single owner task is not possible).

    • Ensure that blocking calls in HAL are understood and compatible with the RTOS scheduling model.

Low-Power and FreeRTOS

How to use STM32 low-power modes with FreeRTOS?

Typical approach:

  • Enable tickless idle:

    • Set configUSE_TICKLESS_IDLE in FreeRTOSConfig.h.

    • Provide or use the MCU-specific implementation of vPortSuppressTicksAndSleep().

  • Use a low-power timer (RTC, LPTIM) to wake up after several OS ticks.

  • Enter a low-power mode (e.g. STOPx or SLEEP) when no tasks are ready.

Key points:

  • Ensure that interrupt sources that should wake the MCU are configured properly.

  • Take care with clock configuration:

    • Some clocks need to be maintained or restored on wake-up.

    • Check STM32 ANs for RTOS + low-power examples specific to your family.

What about ``HAL_Delay()`` and low-power with FreeRTOS?

Problems:

  • HAL_Delay() based on SysTick or busy-waiting can prevent entry into deep sleep and introduce unnecessary active time.

Recommendations:

  • In FreeRTOS low-power designs, prefer:

    • vTaskDelay() or xTaskDelayUntil().

    • FreeRTOS timers.

    • RTC-based wake-ups when long delays are needed.

  • Avoid long blocking delays in ISRs or before the kernel starts, if low power is critical.