| name | rtos-patterns |
| description | This skill should be used when the user asks about "FreeRTOS on STM32", "RTOS task creation", "task priorities", "stack sizing", "mutex", "semaphore", "queue", "event groups", "task notifications", "ISR safe API", "deferred interrupt processing", "FreeRTOSConfig.h", "heap management", "CMSIS-RTOS", "scheduling", "real-time application", or needs RTOS integration patterns for STM32 firmware. |
| version | 1.0.0 |
RTOS Patterns for STM32
Provide RTOS integration guidance for STM32 using FreeRTOS (native API and CMSIS-RTOS v2 wrapper).
FreeRTOS + STM32 Integration
HAL Timebase Conflict
STM32 HAL uses SysTick as its timebase by default. FreeRTOS also needs SysTick for its scheduler tick. This creates a conflict.
Solution: Configure HAL to use a different timer (e.g., TIM6 or TIM7) for its timebase:
- In STM32CubeMX: Pinout & Configuration -> SYS -> Timebase Source -> TIM6
- Manually: Implement
HAL_InitTick() and HAL_GetTick() using a basic timer
FreeRTOS keeps SysTick for its scheduler tick.
FreeRTOSConfig.h Key Parameters
Extract from documentation or configure based on the target MCU:
#define configCPU_CLOCK_HZ (SystemCoreClock)
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES (7)
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)15360)
#define configUSE_PREEMPTION 1
#define configUSE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_QUEUE_SETS 0
#define configPRIO_BITS 4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
Critical: Interrupt Priority Boundary
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY defines the boundary:
- Interrupts with priority >= this value (lower urgency) CAN use FreeRTOS
*FromISR() APIs
- Interrupts with priority < this value (higher urgency) MUST NOT call any FreeRTOS API
- This is the most common source of hard faults in FreeRTOS + STM32 projects
Check the reference manual NVIC section for the number of priority bits (__NVIC_PRIO_BITS).
Task Patterns
Task Creation
StaticTask_t xTaskBuffer;
StackType_t xStack[256];
xTaskCreateStatic(vTaskFunction, "Name", 256, NULL, 2, xStack, &xTaskBuffer);
xTaskCreate(vTaskFunction, "Name", 256, NULL, 2, &xHandle);
Stack Sizing Guidelines
- Minimal task (no printf, no float): 128 words (512 bytes)
- Task with printf/sprintf: 256+ words (uses significant stack)
- Task with floating-point: 256+ words (FPU context saving)
- Task with deep call chains: analyze worst-case depth
- Use
uxTaskGetStackHighWaterMark() to check actual usage
Priority Assignment
- Priority 0: Idle task (reserved by FreeRTOS)
- Priority 1: Low-priority background tasks
- Priority 2-3: Normal application tasks
- Priority 4-5: Time-sensitive tasks
- Priority 6 (configMAX_PRIORITIES-1): Highest priority, use sparingly
Synchronization Primitives
Binary Semaphore (Event Signaling)
Use for ISR-to-task signaling (deferred interrupt processing):
SemaphoreHandle_t xSem = xSemaphoreCreateBinary();
Mutex (Resource Protection)
Use for mutual exclusion with priority inheritance:
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
xSemaphoreTake(xMutex, portMAX_DELAY);
xSemaphoreGive(xMutex);
Never use a mutex from an ISR.
Queue (Data Transfer)
Use for passing data between tasks or from ISR to task:
QueueHandle_t xQueue = xQueueCreate(10, sizeof(uint32_t));
Task Notifications (Lightweight)
Faster than semaphores/queues for simple signaling:
ISR Safety Rules
- Only use
*FromISR() API variants in interrupt handlers
- Never use blocking calls in ISRs (no
xSemaphoreTake, no xQueueReceive, no vTaskDelay)
- Always check and yield: After
*FromISR() calls, check xHigherPriorityTaskWoken and call portYIELD_FROM_ISR() if set
- Respect priority boundary: Only ISRs with priority >=
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY may call FreeRTOS APIs
Deferred Interrupt Processing Pattern
void USART2_IRQHandler(void) {
BaseType_t xHigherPrioTaskWoken = pdFALSE;
uint8_t data = USART2->DR;
xQueueSendFromISR(xRxQueue, &data, &xHigherPrioTaskWoken);
portYIELD_FROM_ISR(xHigherPrioTaskWoken);
}
void vRxTask(void *pvParameters) {
uint8_t data;
for (;;) {
xQueueReceive(xRxQueue, &data, portMAX_DELAY);
}
}
Heap Management
| Scheme | Description | Use Case |
|---|
| heap_1 | Allocate only, no free | Static systems, tasks never deleted |
| heap_2 | Best fit, no coalescing | Fixed-size allocations |
| heap_3 | Wraps malloc/free | When libc malloc is available |
| heap_4 | First fit, coalescing | Recommended for most projects |
| heap_5 | Like heap_4, multiple regions | Spanning multiple SRAM regions |
heap_5 for Multi-Region SRAM
HeapRegion_t xHeapRegions[] = {
{ (uint8_t *)0x20000000, 0x10000 },
{ (uint8_t *)0x2001C000, 0x4000 },
{ NULL, 0 }
};
vPortDefineHeapRegions(xHeapRegions);
Where to Find RTOS-Related Info in Docs
- RM NVIC section:
__NVIC_PRIO_BITS value for configPRIO_BITS
- RM SysTick section: SysTick configuration (FreeRTOS manages this)
- AN5365: ST application note on FreeRTOS with STM32 (check
docs/application-notes/)
- Datasheet: Total SRAM size for
configTOTAL_HEAP_SIZE calculation
Additional Resources
references/rtos-integration-guide.md - Complete FreeRTOS+STM32 setup walkthrough, HAL timebase workaround details, ISR-to-task communication patterns