Day 1 of 5
⏱ ~60 minutes
RTOS in 5 Days — Day 1

Real-Time Concepts

Real-time does not mean fast. It means predictable. Today you'll understand what RTOS guarantees, when you need one, and what FreeRTOS provides.

What Real-Time Means

A real-time system must respond to events within a guaranteed time bound (deadline). Hard real-time: missing a deadline is catastrophic — airbag controller, pacemaker, brake-by-wire. Soft real-time: missing a deadline degrades quality but doesn't cause failure — video player, audio codec. Firm real-time: a missed deadline makes the result useless — stock trading, VoIP. An RTOS gives you the tools to meet deadlines: preemptive scheduling, known interrupt latency, and deterministic task switching.

RTOS vs Bare Metal vs Linux

Bare metal with super-loop: one infinite loop, no preemption. Fine for simple projects; breaks down when tasks have different timing requirements. RTOS (FreeRTOS): preemptive multi-tasking, deterministic 10–50µs context switch, 5–50KB footprint. Ideal for MCUs with real-time requirements. Linux (RTOS-patched): full OS with PREEMPT_RT patch gives 50–200µs worst-case latency. For complex systems (machine vision, network protocol) that need a real-time core.

FreeRTOS Architecture

FreeRTOS is an open-source RTOS kernel (MIT license). Key components: Scheduler: runs the highest-priority ready task. Tasks: independent execution contexts with own stack. Queues: thread-safe message passing. Semaphores/Mutexes: synchronization. Software Timers: callback-based timers. Event Groups: wait for multiple events. Ported to 40+ MCU families including STM32, ESP32, Arduino, and NXP.

c
// FreeRTOS: two tasks running concurrently
// On STM32 or ESP32 with FreeRTOS support

#include "FreeRTOS.h"
#include "task.h"

// Task 1: blink LED at 2Hz (every 250ms on, 250ms off)
void vBlinkTask(void *pvParameters) {
    // Each task runs forever in its own while(1)
    while (1) {
        // LED on
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // active low
        vTaskDelay(pdMS_TO_TICKS(250));  // yield for 250ms

        // LED off
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        vTaskDelay(pdMS_TO_TICKS(250));
    }
}

// Task 2: print "tick" every 1 second
void vPrintTask(void *pvParameters) {
    uint32_t count = 0;
    while (1) {
        printf("Tick %lu | FreeHeap: %lu bytes\n",
               count++, xPortGetFreeHeapSize());
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();

    // Create tasks: (function, name, stack, param, priority, handle)
    xTaskCreate(vBlinkTask, "BLINK", 128, NULL, 1, NULL);
    xTaskCreate(vPrintTask, "PRINT", 256, NULL, 1, NULL);

    // Start the scheduler — never returns
    vTaskStartScheduler();

    // If we reach here, heap was too small to start scheduler
    while (1);
}
💡
vTaskDelay(pdMS_TO_TICKS(ms)) is the correct way to delay in FreeRTOS — it yields the CPU to other tasks. Never use HAL_Delay() or a busy-wait loop in an RTOS task — it blocks the CPU and starves other tasks of the same or lower priority.
📝 Day 1 Exercise
Create Your First FreeRTOS Tasks
  1. Set up a FreeRTOS project on STM32 using STM32CubeIDE (it integrates FreeRTOS via CubeMX) or on ESP32 (FreeRTOS is built in).
  2. Create two tasks: one blinks an LED at 2Hz, one reads a button and prints to UART every 100ms.
  3. Open the FreeRTOS heap configuration in FreeRTOSConfig.h. Find configTOTAL_HEAP_SIZE. What happens if you set it too small?
  4. Add a third task that prints xPortGetFreeHeapSize() every second. Watch the value — it should be stable (no leak).
  5. Intentionally remove vTaskDelay from one task. What happens to the other tasks?

Day 1 Summary

  • Real-time = predictable, not fast: hard RT misses are catastrophic; soft RT misses degrade quality
  • FreeRTOS runs on MCUs with 5–50KB footprint; 10–50µs context switch latency
  • Tasks: independent execution with own stack, run forever in while(1), yield with vTaskDelay
  • Never busy-wait in a FreeRTOS task — vTaskDelay(pdMS_TO_TICKS(ms)) yields to other tasks
Challenge

Configure FreeRTOS tick rate at 1000Hz (1ms). Create 5 tasks with periods 1ms, 5ms, 10ms, 50ms, 100ms. Each task toggles a different GPIO pin. Connect a logic analyzer or oscilloscope to verify each pin toggles at its expected frequency. Calculate CPU utilization if each task takes 100µs to execute — is it feasible?

Finished this lesson?