FreeRTOS Deep Dive · Part 1 of 8

Scheduler, Priorities & Context Switch

FreeRTOS STM32 Beginner Source Code
FreeRTOS Part 1
FreeRTOS Deep Dive — Part 1
Scheduler, Priorities & Context Switch
55:30
Part 1 / 8
55:30
Beginner
FreeRTOS v10 · STM32F4
01

Overview

This is the foundation of the entire FreeRTOS series. Before you can use queues, mutexes, or timers you need to understand how the scheduler works, how tasks are created, and exactly what happens during a context switch on ARM Cortex-M. Everything else builds on this.

  • Create and delete FreeRTOS tasks using xTaskCreate()
  • Understand priorities, the ready list, and how the preemptive scheduler selects tasks
  • Measure context switch overhead on real hardware with an oscilloscope
  • Know when to use vTaskDelay() vs vTaskDelayUntil() for jitter-free periodic tasks
02

How the Scheduler Works

FreeRTOS uses a fixed-priority preemptive scheduler. At every tick interrupt (typically every 1ms), it checks whether a higher-priority task has become ready. If one has, it immediately context-switches to it. Equal-priority tasks share the CPU in round-robin.

🏃
Running
The one task executing on the CPU right now. Only one runs at a time on single-core.
eRunning
Ready
Able to run, waiting for the CPU. Scheduler picks the highest-priority ready task.
eReady
💤
Blocked
Waiting for a delay, queue item, or semaphore. Zero CPU usage while blocked.
eBlocked
03

Code

01
Creating tasks with priorities
main.c
C
void vHighTask(void *pv), vLowTask(void *pv);

int main(void) {
    HAL_Init(); SystemClock_Config();
    // xTaskCreate(func, name, stack_words, param, priority, handle)
    xTaskCreate(vHighTask, "High", 256, NULL, 3, NULL);
    xTaskCreate(vLowTask,  "Low",  256, NULL, 1, NULL);
    vTaskStartScheduler();
    while(1);
}

void vHighTask(void *pv) {
    TickType_t xLast = xTaskGetTickCount();
    while(1) {
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
        // Exact 100ms period — compensates for execution time
        vTaskDelayUntil(&xLast, pdMS_TO_TICKS(100));
    }
}

void vLowTask(void *pv) {
    while(1) {
        DoBackgroundWork();
        // 500ms AFTER execution — period drifts
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
💡
vTaskDelayUntil vs vTaskDelay
Use vTaskDelayUntil() for periodic tasks — it absorbs execution time to maintain a fixed period. vTaskDelay() adds delay after the current run, so period drifts under load.

Continue the Series

Work through all 8 parts of the FreeRTOS Deep Dive to master real-time embedded systems programming.