Detailed Explanation of FreeRTOS Multitasking Execution Principles – Part Two

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

Leave a Comment