<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.