In-Depth Understanding of FreeRTOS Configuration

In-Depth Understanding of FreeRTOS Configuration

<span>FreeRTOS</span> configuration is implemented through macro definitions to control the behavior of the FreeRTOS kernel, with parameters designed in <span>FreeRTOSConfig.h</span>. Below is a detailed explanation of commonly used configurations.

Scheduling Method

  • Whether to enable preemptive scheduling
  • <span>#define configUSE_PREEMPTION 1</span> : Enables preemptive scheduling (<span>preemptive scheduling</span>), allowing higher priority tasks to interrupt lower priority tasks.
  • <span>#define configUSE_PREEMPTION 0</span> : Disables preemption, using cooperative scheduling (<span>cooperative scheduling</span>), where tasks must explicitly yield the CPU (e.g., by calling <span>taskYIELD()</span>).
  • Typically set to 1 for real-time responsiveness.

Idle Task

  • Whether to enable the idle task hook function
  • <span>#define configUSE_IDLE_HOOK 1</span> : Enables, periodically calling <span>vApplicationIdleHook()</span>
  • <span>#define configUSE_IDLE_HOOK 0</span> : Disables
  • The idle hook is suitable for entering low power modes or background tasks.

Tick Hook Function

  • Whether to enable the tick hook function
  • <span>#define configUSE_TICK_HOOK 1</span> : Enables, called on each system tick interrupt by <span>vApplicationTickHook()</span>.
  • <span>#define configUSE_TICK_HOOK 0</span> : Disables
  • Can be used for periodic processing, such as software timers, LED blinking, etc.

CPU Clock

  • Used to set the CPU clock frequency, in Hz
  • <span>#define configCPU_CLOCK_HZ ( ( unsigned long ) system_core_clock )</span>
    • <span>system_core_clock</span> is a user-defined variable representing the <span>MCU</span> main frequency.
    • For example, for a <span>72 MHz</span> <span>STM32</span>, <span>system_core_clock = 72000000</span>

Number of Task Priorities

  • Used to set the number of available task priorities
  • <span>#define configMAX_PRIORITIES ( 5 )</span>
    • Indicates priority levels from 0 to 4, totaling 5 levels.
    • The higher the priority, the sooner the task runs.
  • If there are too many tasks and not enough priorities, this can be increased appropriately.

Minimum Stack Size

  • <span>#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )</span>
  • Used to set the default minimum stack size for idle tasks and other small tasks (in words).
    • If <span>uint32_t</span> is 4 bytes, then 128 means the stack is 512 bytes.
    • Should be adjusted based on the MCU architecture and function call depth.

Dynamic Memory

  • Whether to support dynamic memory allocation (e.g., <span>pvPortMalloc</span>)
    • <span>#define configSUPPORT_DYNAMIC_ALLOCATION 1</span> : Supports
    • <span>#define configSUPPORT_DYNAMIC_ALLOCATION 0</span> : Does not support (only static allocation is allowed)
  • If tasks, queues, semaphores, etc., are created at runtime, this option must be enabled.

Total Heap Size

  • <span>#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 8 * 1024 ) )</span>
    • Used to set the total heap size available to FreeRTOS (in bytes).
    • Used for dynamically allocating task stacks, control blocks, queues, and other resources.
  • Insufficient size may lead to <span>malloc failed</span>.

Task Name Length

  • <span>#define configMAX_TASK_NAME_LEN ( 16 )</span>
    • Maximum length of task names (including the null terminator <span>\0</span>).
    • 16 means the longest task name can be 15 characters.
  • Used for task debugging, status monitoring, etc.

Tick Counter

  • Whether to use a 16-bit Tick counter
    • <span>#define configUSE_16_BIT_TICKS 0</span> : Uses 32-bit Tick (recommended, supports longer delays)
    • <span>#define configUSE_16_BIT_TICKS 1</span> : Uses 16-bit Tick (saves memory, but maximum delay is 65535 ticks)
  • For high-frequency systems, 32-bit Tick should be used.

Same Priority Tasks

  • Whether the idle task yields the <span>CPU</span> when there are other tasks of the same priority
    • <span>#define configIDLE_SHOULD_YIELD 1</span> : The idle task will call <span>taskYIELD()</span>, allowing other ready tasks to run.
    • <span>#define configIDLE_SHOULD_YIELD 0</span> : The idle task will not actively yield the CPU.
  • Suitable for running certain low-priority but real-time demanding tasks.

Co-routine Enable

  • Whether to enable <span>Co-routines</span> (coroutines)
    • <span>#define configUSE_CO_ROUTINES 1</span> : Enables coroutines (uses fewer resources but has limited functionality).
    • <span>#define configUSE_CO_ROUTINES 0</span> : Disables coroutine functionality (generally not used in modern projects).
  • Coroutines are suitable for use on extremely resource-constrained 8-bit microcontrollers. However, compared to tasks, they do not support parallel stacks and have weaker execution capabilities.

Co-routine Priority Count

  • Sets the number of priorities for coroutines
    • <span>#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )</span>
    • Only effective when <span>configUSE_CO_ROUTINES == 1</span>.
    • Sets the available levels of coroutine priority, here it is 2, i.e., levels 0 and 1.

API Function Enable Switch

<span>FreeRTOS</span> allows enabling/disabling certain <span>API</span> functions as needed to reduce code size. Each macro starting with <span>INCLUDE_</span> controls whether a function is available.

  • Dynamic Task Priority Modification

    • Control macro:<span>#define INCLUDE_vTaskPrioritySet 1</span> : Enables
    • Function:<span>vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)</span>
  • Get Current Priority of Specified Task

    • Control macro:<span>#define INCLUDE_uxTaskPriorityGet 1</span> : Enables
    • Function:<span>uxTaskPriorityGet(TaskHandle_t xTask)</span>
  • Task Deletion

    • Control macro:<span>#define INCLUDE_vTaskDelete 1</span> : Enables
    • Function:<span>vTaskDelete(TaskHandle_t xTask)</span>
    • This function is used to delete itself or other tasks.
  • Resource Cleanup

    • Control macro:<span>#define INCLUDE_vTaskCleanUpResources 0</span> : Disables
    • Function:<span>vTaskCleanUpResources()</span>
    • Deprecated or only used for some special applications to manually clean up resources, generally not needed.
  • Task Suspension and Resumption

    • Control macro:<span>#define INCLUDE_vTaskSuspend 1</span> : Enables
    • Task suspension:<span>vTaskSuspend(TaskHandle_t xTask)</span>
    • Task resumption:<span>vTaskResume()</span>
  • Task Delay

    • Control macro:<span>#define INCLUDE_vTaskDelay 1</span> : Enables
    • Function:<span>vTaskDelay(const TickType_t xTicksToDelay)</span>
    • Used to delay the current task for a period of time, it is the most commonly used delay function.
  • Periodic Task Delay

    • Control macro:<span>#define INCLUDE_vTaskDelayUntil 1</span> : Enables
    • Function:<span>vTaskDelayUntil(TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement)</span>
    • Used for precise timing delays of periodic tasks, it can prevent periodic drift (more suitable for periodic tasks than <span>vTaskDelay()</span>).
  • Get Task Handle

    • Control macro:<span>#define INCLUDE_xTaskGetCurrentTaskHandle 1</span> : Enables
    • Function:<span>xTaskGetCurrentTaskHandle()</span>
    • Used to get the handle of the currently running task, very useful for implementing some task management logic, such as recording the current task status.

Interrupt Priority

  • Control macro:<span>#define configPRIO_BITS 4</span>
  • Configuration method:
#ifdef __NVIC_PRIO_BITS
  #define configPRIO_BITS           __NVIC_PRIO_BITS
#else
  #define configPRIO_BITS           4        /* 15 priority levels */
#endif
  • If <span>__NVIC_PRIO_BITS</span> is not defined, it defaults to using 4-bit priority, i.e., 16 priority levels.
  • If using the <span>CMSIS</span> standard header file (i.e., the chip support package provided by <span>ARM</span>), it usually defines a macro <span>__NVIC_PRIO_BITS</span>
  • This macro defines the number of interrupt priority bits supported by the current <span>MCU</span>, common values include:
    • <span>__NVIC_PRIO_BITS = 2</span> : 4 available interrupt priorities
    • <span>__NVIC_PRIO_BITS = 3</span> : 8 available interrupt priorities
    • <span>__NVIC_PRIO_BITS = 4</span> : 16 available interrupt priorities
  • Functionality:
    • <span>5 << (8 - configPRIO_BITS) // i.e., 5 << 4 = 0x50</span>
    • <span>FreeRTOS</span> needs to know the number of interrupt priority bits when using interrupt nesting control (e.g., <span>configMAX_SYSCALL_INTERRUPT_PRIORITY</span>).
    • <span>#define configMAX_SYSCALL_INTERRUPT_PRIORITY 5</span> : This value is actually placed in the high bits of the <span>NVIC</span> priority register, for example, in a <span>4-bit</span> priority system, the value 5 is actually represented in hardware as
    • FreeRTOS kernel needs to use this information to correctly mask or allow interrupts to enter, to prevent being interrupted in critical sections.

Lowest Priority

Background Knowledge

In the <span>ARM Cortex-M</span> architecture (especially common embedded chips using <span>FreeRTOS</span>), interrupt priority is a numerical value.

  • The smaller the number → the higher the priority (faster response)
  • The larger the number → the lower the priority (slower response)

However, these numbers are not directly written in <span>FreeRTOS</span> macros like <span>configMAX_SYSCALL_INTERRUPT_PRIORITY</span>, but need to be left-shifted to align with the <span>NVIC</span> 8-bit register (because the actual register is <span>8-bit</span>, but you can only use a few high bits, with the low bits fixed at 0).

Configuration Description
  • Control macro:<span>#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f</span>
    • <span>0x0f</span> in decimal is <span>15</span>, representing the lowest priority (maximum value).
    • This is not the actual value written to the <span>NVIC</span> register, but a logical “priority level”.
    • Generally used in conjunction with <span>configPRIO_BITS</span> (e.g., 4).
    • The actual value written to <span>NVIC</span> is <span>0x0f << (8 - configPRIO_BITS) // 0x0f << 4 = 0xf0</span>
Key Rules

Highest Priority

  • Control macro:<span>#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01</span> same principle as above.
  • All interrupt service routines (<span>ISR</span>) that call FreeRTOS interrupt-safe APIs must have interrupt priorities that are “lower than” this value (i.e., numerical values greater than or equal to this macro value).
Why Interrupts with Priority 0 Cannot Call FreeRTOS Interrupt-Safe APIs
  • In the <span>ARM Cortex-M</span> architecture (e.g., STM32, etc.), interrupt priority is a mechanism where the smaller the numerical value, the higher the priority.
  • The priority value is written into the <span>NVIC</span> (Nested Vectored Interrupt Controller), each interrupt has a priority value.
  • A priority value of 0 indicates the highest interrupt priority, meaning:
    • It can interrupt all other interrupts (non-maskable).
    • It may even interrupt critical sections of FreeRTOS kernel code.
  • FreeRTOS is a preemptive RTOS, internally protecting shared resources through the **critical section** mechanism.
    • These critical sections are implemented by disabling **”low priority interrupts”**.
    • But if an interrupt has a priority of 0 (highest), it cannot be masked in any way, including FreeRTOS critical sections.
    • Therefore, during the execution of such interrupts, FreeRTOS cannot prevent them from accessing queues, semaphores, and other shared resources, leading to race conditions, deadlocks, and memory corruption.
  • FreeRTOS controls the allowed interrupt priorities that can call APIs through these two macros.
    • <span>#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x01</span>
    • <span>#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY « (8 - configPRIO_BITS))</span>
    • Only interrupts with a priority of 1 (or lower, larger numerical value) can call interrupt-safe <span>FreeRTOS APIs</span>, while those with a priority of <span>0</span> cannot.
Summary
  • Do not call FreeRTOS APIs in ISRs.
  • In ISRs, only set a volatile flag.
  • Let lower priority tasks poll this flag or communicate through message queues.

Interrupt-Safe Priority

  • Control macro:<span>#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY « (8 - configPRIO_BITS) )</span>
  • Function: Defines the maximum priority (minimum numerical value) of interrupts allowed to call <span>FreeRTOS</span> interrupt-safe <span>APIs</span>.
  • Note: Cannot be 0!

Kernel Interrupt Priority

  • Control macro: <span>#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY « (8 - configPRIO_BITS) )</span>
  • Function: Sets the interrupt priority used by the FreeRTOS kernel itself (e.g., <span>SysTick</span>, <span>PendSV</span>).
  • Note: Generally uses the lowest priority, will not disturb other higher priority tasks.
Summary
  • <span>Cortex-M</span> interrupt priorities are in the <span>NVIC</span> register with an 8-bit width, but not all are used.
  • In the <span>ARM Cortex-M</span> series, the number of interrupt priority bits actually used by <span>NVIC</span> is defined by the macro provided by <span>CMSIS</span> as <span>#define __NVIC_PRIO_BITS</span>.
  • This macro is generally defined in the <span>core_cmx.h</span> file.
  • The value of this macro is defined by the chip manufacturer, commonly 3 or 4 bits.

Assert Macro Definition

  • Control macro:<span>#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }</span>
  • Function: Checks at runtime whether a certain condition holds; if not, interrupts program execution for debugging.
  • Note: When the assertion condition holds, all interrupts are disabled, and the program enters an infinite loop at the current position, allowing the use of <span>JTAG/SWD</span> debuggers to view the error location and stack.
  • Extension: You can add system logging, system reboot, etc., in this definition to enhance program adaptability.

Interrupt Functions

<span>FreeRTOS</span> uses three interrupt functions, namely:

  • <span>#define vPortSVCHandler SVC_Handler</span>
  • <span>#define xPortPendSVHandler PendSV_Handler</span>
  • <span>#define xPortSysTickHandler SysTick_Handler</span>

These three interrupt functions are implemented by <span>FreeRTOS</span> code. Specifically, we generally use <span>SysTick_Handler</span> to implement the function interface for obtaining system ticks; this interrupt function cannot be used externally.

SVC_Handler
  • Interrupt type: Instruction interrupt function <span>Supervisor Call</span>, SVC
  • Purpose: Used to perform privileged operations, such as task switching, first scheduling after task creation, etc.
PendSV_Handler
  • Interrupt type:<span>PendSV</span> (pending system service call) interrupt
  • Purpose: The main interrupt handling function for task switching. It is used every time a task is scheduled (context switch).
SysTick_Handler
  • Interrupt type:<span>SysTick</span> timer interrupt
  • Purpose: Used to generate system clock ticks, the scheduler decides whether to perform task switching based on it.

Leave a Comment