Real-time does not mean fast. It means predictable. Today you'll understand what RTOS guarantees, when you need one, and what FreeRTOS provides.
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.
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 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.
// 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.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?