FreeRTOS for Beginners: Building Real-Time Embedded Applications
FreeRTOS for Beginners: Building Real-Time Embedded Applications
Real-time operating systems are the backbone of reliable embedded products. FreeRTOS is the most popular choice — it's free, lightweight, and runs on everything from tiny Cortex-M0 chips to powerful Cortex-M7 processors.
Why Use an RTOS?
Bare-metal programming works for simple applications. But as complexity grows, you need:
- Deterministic timing — Guaranteed response within deadlines
- Task isolation — Independent execution contexts
- Inter-task communication — Queues, semaphores, and event groups
- Power management — Idle hooks for sleep modes
Creating Your First Task
#include "FreeRTOS.h"
#include "task.h"
void vLedTask(void *pvParameters) {
for (;;) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
xTaskCreate(vLedTask, "LED", 128, NULL, 1, NULL);
vTaskStartScheduler();
for (;;); // Should never reach here
}
Task Priorities
FreeRTOS uses preemptive scheduling. Higher priority tasks always run first:
| Priority | Task | Purpose | |----------|------|---------| | 3 (High) | Sensor Read | Time-critical sampling | | 2 (Medium) | Data Process | Signal filtering | | 1 (Low) | Display Update | UI refresh | | 0 (Idle) | Power Save | Enter sleep mode |
Inter-Task Communication
Queues
Pass data between tasks safely:
QueueHandle_t xSensorQueue = xQueueCreate(10, sizeof(float));
// Producer task
float temperature = readSensor();
xQueueSend(xSensorQueue, &temperature, portMAX_DELAY);
// Consumer task
float received;
xQueueReceive(xSensorQueue, &received, portMAX_DELAY);
Binary Semaphores
Synchronize tasks with events:
SemaphoreHandle_t xButtonSem = xSemaphoreCreateBinary();
// ISR gives the semaphore
void EXTI0_IRQHandler(void) {
xSemaphoreGiveFromISR(xButtonSem, NULL);
}
// Task waits for the event
xSemaphoreTake(xButtonSem, portMAX_DELAY);
handleButtonPress();
Best Practices
- Keep stack sizes minimal — Profile with
uxTaskGetStackHighWaterMark() - Avoid floating point in ISRs — Use integer math or deferred processing
- Never block in an ISR — Use
FromISRvariants of FreeRTOS APIs - Use static allocation — Avoid heap fragmentation in long-running systems
- Monitor idle time — Track CPU utilization with runtime stats
Master FreeRTOS hands-on in our Embedded Systems Engineering track.