Detailed Explanation of FreeRTOS Multitasking Switching Underlying Principles
6. Task State Transitions
6.1 State Transition Diagram
Create Ready Queue CPU Execution
[NEW] -----> [READY] -----> [RUNNING]
↑ ↓ ↓
↑ ↓ [Time Slice Expired]
↑ ↓ ↓
[Waiting for Event] [BLOCKED]
[Delay Waiting] [SUSPENDED]
6.2 State Transition Conditions
| Transition | Trigger Condition | Description |
|---|---|---|
| NEW → READY | Task creation completed | Task enters the ready queue |
| READY → RUNNING | Scheduler selection | Becomes the currently running task |
| RUNNING → READY | Time slice expired/high-priority task ready | Preempted or voluntarily yielded |
| RUNNING → BLOCKED | Waiting for event/delay | Actively blocked waiting |
| BLOCKED → READY | Event occurs/delay expires | Waiting condition satisfied |
| RUNNING → SUSPENDED | Suspended | Explicit suspension operation |
| SUSPENDED → READY | Restored | Explicit restoration operation |
| Any State → DELETED | Task deletion | Task lifecycle ends |
6.3 State Transition Implementation
/**
* @brief Key function for task state transitions
*/
/* Add task to ready list */
static void prvAddTaskToReadyList( TCB_t * const pxTCB )
{
taskRECORD_READY_PRIORITY( pxTCB->uxPriority );
vListInsertEnd( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) );
}
/* Remove task from ready list */
static UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = pxItemToRemove->pxContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
pxItemToRemove->pxContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
/* Task delay implementation */
void vTaskDelay( const TickType_t xTicksToDelay )
{
if( xTicksToDelay > ( TickType_t ) 0U )
{
taskENTER_CRITICAL();
{
/* Add task to delay list */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
taskEXIT_CRITICAL();
/* Force a task switch */
portYIELD_WITHIN_API();
}
}
7. Practical Execution Example
7.1 Example Scenario Setup
Assume a simple embedded system with the following tasks:
/**
* @brief Example task definitions
*/
/* Task priority definitions */
#define TASK_PRIORITY_HIGH 3 /* LED control task */
#define TASK_PRIORITY_MEDIUM 2 /* Button detection task */
#define TASK_PRIORITY_LOW 1 /* UART communication task */
#define TASK_PRIORITY_IDLE 0 /* Idle task */
/* Task handles */
TaskHandle_t xTaskLED = NULL;
TaskHandle_t xTaskButton = NULL;
TaskHandle_t xTaskUART = NULL;
/* Task creation */
void CreateTasks(void)
{
xTaskCreate(TaskLED, "LED", 128, NULL, TASK_PRIORITY_HIGH, &xTaskLED);
xTaskCreate(TaskButton, "Button", 128, NULL, TASK_PRIORITY_MEDIUM, &xTaskButton);
xTaskCreate(TaskUART, "UART", 256, NULL, TASK_PRIORITY_LOW, &xTaskUART);
}
7.2 Task Implementation Code
/**
* @brief LED control task
*/
void TaskLED(void *pvParameters)
{
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(500); /* 500ms period */
for (;;)
{
/* Toggle LED state */
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
/* Delay until the next period */
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
/**
* @brief Button detection task
*/
void TaskButton(void *pvParameters)
{
for (;;)
{
/* Check button state */
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET)
{
/* Button pressed, send event */
xEventGroupSetBits(xEventGroup, BUTTON_PRESSED_BIT);
}
/* 10ms detection interval */
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/**
* @brief UART communication task
*/
void TaskUART(void *pvParameters)
{
char rxBuffer[64];
for (;;)
{
/* Wait for UART data (infinite wait) */
if (xQueueReceive(xUARTQueue, rxBuffer, portMAX_DELAY) == pdPASS)
{
/* Process received data */
ProcessUARTData(rxBuffer);
/* Send response */
HAL_UART_Transmit(&huart1, (uint8_t*)"OK\r\n", 4, HAL_MAX_DELAY);
}
}
}
7.3 Execution Timing Analysis
Timeline: 0ms 1ms 2ms 3ms 4ms 5ms 6ms 7ms 8ms 9ms 10ms
Task Execution: [LED] [BTN] [LED] [UART][LED] [BTN] [LED] [BTN] [LED] [UART][LED]
Events: ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
Sys Sys Sys Que Sys Sys Sys Sys Sys Que Sys
Tick Tick Tick Recv Tick Tick Tick Tick Tick Recv Tick
Description:
- The LED task has the highest priority and checks for execution on every SysTick.
- The button task executes every 10ms for detection.
- The UART task executes only when data is received (event-driven).
- The idle task executes when all other tasks are blocked.
7.4 Memory Usage Analysis
/**
* @brief Memory usage analysis
*/
/* Task stack size calculation */
#define LED_TASK_STACK_SIZE 128 /* 128 * 4 = 512 bytes */
#define BUTTON_TASK_STACK_SIZE 128 /* 128 * 4 = 512 bytes */
#define UART_TASK_STACK_SIZE 256 /* 256 * 4 = 1024 bytes */
#define IDLE_TASK_STACK_SIZE 64 /* 64 * 4 = 256 bytes */
/* TCB size (approximately 100-200 bytes per task) */
#define TCB_SIZE 150 /* Approximately 150 bytes per TCB */
/* Total memory usage estimation */
#define TOTAL_STACK_MEMORY (512 + 512 + 1024 + 256) /* 2304 bytes */
#define TOTAL_TCB_MEMORY (4 * 150) /* 600 bytes */
#define TOTAL_TASK_MEMORY (TOTAL_STACK_MEMORY + TOTAL_TCB_MEMORY) /* 2904 bytes */
8. Performance Analysis and Optimization
8.1 Task Switching Performance Analysis
/**
* @brief Task switching performance data (ARM Cortex-M4 @ 100MHz)
*/
/* Task switching time composition */
#define SYSTICK_ISR_TIME 5 /* SysTick interrupt handling: approximately 5 microseconds */
#define PENDSV_CONTEXT_SAVE 15 /* Context save: approximately 15 microseconds */
#define SCHEDULER_TIME 8 /* Scheduler selection: approximately 8 microseconds */
#define PENDSV_CONTEXT_RESTORE 15 /* Context restore: approximately 15 microseconds */
#define TOTAL_SWITCH_TIME 43 /* Total switching time: approximately 43 microseconds */
/* Time slice utilization */
#define TIME_SLICE_LENGTH 1000 /* Time slice length: 1000 microseconds (1ms) */
#define SWITCH_OVERHEAD_PERCENT 4.3 /* Switching overhead: 4.3% */
#define USEFUL_CPU_PERCENT 95.7 /* Effective CPU utilization: 95.7% */
8.2 Performance Optimization Suggestions
8.2.1 Scheduler Optimization
/**
* @brief Enable hardware-optimized task selection
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* Enable CLZ instruction optimization */
#define configMAX_PRIORITIES 8 /* Reduce the number of priorities */
/**
* @brief Reduce unnecessary functional overhead
*/
#define configGENERATE_RUN_TIME_STATS 0 /* Disable runtime statistics */
#define configUSE_TRACE_FACILITY 0 /* Disable tracing functionality */
#define configCHECK_FOR_STACK_OVERFLOW 1 /* Keep stack overflow detection */
8.2.2 Memory Optimization
/**
* @brief Memory optimization configuration
*/
#define configSUPPORT_STATIC_ALLOCATION 1 /* Enable static allocation */
#define configSUPPORT_DYNAMIC_ALLOCATION 0 /* Disable dynamic allocation */
#define configTOTAL_HEAP_SIZE 0 /* No heap memory used */
/**
* @brief Task stack size optimization
*/
#define configMINIMAL_STACK_SIZE 64 /* Minimum stack size */
#define configIDLE_TASK_STACK_SIZE 64 /* Idle task stack */
8.2.3 Interrupt Optimization
/**
* @brief Interrupt priority configuration
*/
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* Lowest interrupt priority */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* Highest syscall priority */
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4)
8.3 Performance Measurement Tools
/**
* @brief Performance measurement function
*/
/* Task runtime statistics */
void GetTaskStats(void)
{
TaskStatus_t *pxTaskStatusArray;
volatile UBaseType_t uxArraySize, x;
configRUN_TIME_COUNTER_TYPE ulTotalTime, ulStatsAsPercentage;
/* Get the number of tasks */
uxArraySize = uxTaskGetNumberOfTasks();
/* Allocate memory */
pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
if( pxTaskStatusArray != NULL )
{
/* Get task information */
uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime );
/* Calculate CPU usage for each task */
for( x = 0; x < uxArraySize; x++ )
{
ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / (ulTotalTime / 100UL);
printf("Task: %s, CPU: %lu%%\r\n",
pxTaskStatusArray[ x ].pcTaskName,
ulStatsAsPercentage);
}
vPortFree( pxTaskStatusArray );
}
}
/* Stack usage check */
void CheckStackUsage(void)
{
printf("LED Task Stack High Water Mark: %u\r\n",
uxTaskGetStackHighWaterMark(xTaskLED));
printf("Button Task Stack High Water Mark: %u\r\n",
uxTaskGetStackHighWaterMark(xTaskButton));
printf("UART Task Stack High Water Mark: %u\r\n",
uxTaskGetStackHighWaterMark(xTaskUART));
}
9. Configuration Key Points
9.1 Basic Configuration
/**
* @brief Key configuration items for FreeRTOSConfig.h
*/
/* Basic system configuration */
#define configUSE_PREEMPTION 1 /* Enable preemptive scheduling */
#define configUSE_TIME_SLICING 1 /* Enable time slicing */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* Enable hardware optimization */
#define configUSE_TICKLESS_IDLE 0 /* Disable tickless idle mode */
#define configCPU_CLOCK_HZ 100000000 /* CPU clock frequency */
#define configTICK_RATE_HZ 1000 /* System tick frequency */
#define configMAX_PRIORITIES 8 /* Maximum number of priorities */
#define configMINIMAL_STACK_SIZE 64 /* Minimum stack size */
#define configMAX_TASK_NAME_LEN 16 /* Task name length */
9.2 Memory Management Configuration
/**
* @brief Memory management strategy selection
*/
/* Option 1: Static allocation (recommended for resource-constrained systems) */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configTOTAL_HEAP_SIZE 0
/* Option 2: Dynamic allocation (recommended for resource-rich systems) */
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTOTAL_HEAP_SIZE 4096 /* 4KB heap size */
/* Option 3: Mixed allocation (highest flexibility) */
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTOTAL_HEAP_SIZE 2048 /* 2KB heap size */
9.3 Security Configuration
/**
* @brief Security-related configuration
*/
/* Stack overflow detection */
#define configCHECK_FOR_STACK_OVERFLOW 2 /* Method 2: Check bottom stack marker */
/* MPU support */
#define configENABLE_MPU 1 /* Enable MPU */
#define configENABLE_FPU 1 /* Enable FPU */
#define configENABLE_TRUSTZONE 0 /* TrustZone support */
/* Priority inheritance */
#define configUSE_MUTEXES 1 /* Enable mutexes */
#define configUSE_RECURSIVE_MUTEXES 1 /* Recursive mutexes */
9.4 Debugging Configuration
/**
* @brief Debug-related configuration
*/
/* Runtime statistics */
#define configGENERATE_RUN_TIME_STATS 1 /* Enable runtime statistics */
#define configUSE_TRACE_FACILITY 1 /* Enable tracing functionality */
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* Format output */
/* Assertion support */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Hook functions */
#define configUSE_IDLE_HOOK 1 /* Idle hook */
#define configUSE_TICK_HOOK 1 /* Tick hook */
#define configUSE_MALLOC_FAILED_HOOK 1 /* Memory allocation failure hook */
10. Conclusion
10.1 Core Points of FreeRTOS Multitasking Execution
Time Slicing Essence: Single-core MCU achieves “pseudo-parallelism” through rapid task switching
Hardware Dependency: Relies on SysTick timer and PendSV exception for task scheduling
Scheduling Algorithm: Priority-based preemptive scheduling, supports hardware optimization
State Management: Manages task state transitions through TCB and multiple linked lists
10.2 Why Do Users Feel It Is “Parallel”?
Firstly, the switching speed is extremely fast, with task switching taking only a few dozen CPU cycles (microsecond level)
Time slices are very small: usually 1ms, imperceptible to the human eye
Continuity is good: each task continuously receives CPU time; timely response allows high-priority tasks to immediately preempt low-priority tasks
10.3 Applicable Scenarios
| Scenario | Recommended Configuration | Description |
|---|---|---|
| Resource-Constrained Devices | Static Allocation + heap_1 | Memory usage is predictable |
| General Applications | Mixed Allocation + heap_4 | Balance performance and flexibility |
| Safety-Critical Applications | Enable MPU + Stack Detection | Highest safety level |
| High-Performance Applications | Hardware Optimized Scheduling | Minimum scheduling delay |