>> skills/rtos-patterns
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()andHAL_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) // From RCC config
#define configTICK_RATE_HZ ((TickType_t)1000) // 1ms tick
#define configMAX_PRIORITIES (7) // 0-6 priority levels
#define configMINIMAL_STACK_SIZE ((uint16_t)128) // Words (512 bytes)
#define configTOTAL_HEAP_SIZE ((size_t)15360) // Bytes, adjust to SRAM
#define configUSE_PREEMPTION 1
#define configUSE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_QUEUE_SETS 0
/* Cortex-M interrupt priority configuration - CRITICAL */
#define configPRIO_BITS 4 // Check RM for __NVIC_PRIO_BITS
#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
// Static allocation (recommended - deterministic)
StaticTask_t xTaskBuffer;
StackType_t xStack[256];
xTaskCreateStatic(vTaskFunction, "Name", 256, NULL, 2, xStack, &xTaskBuffer);
// Dynamic allocation
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();
// In ISR: xSemaphoreGiveFromISR(xSem, &xHigherPrioTaskWoken);
// In task: xSemaphoreTake(xSem, portMAX_DELAY);
Mutex (Resource Protection)
Use for mutual exclusion with priority inheritance:
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
xSemaphoreTake(xMutex, portMAX_DELAY);
// ... access shared resource ...
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));
// Producer: xQueueSend(xQueue, &data, portMAX_DELAY);
// Consumer: xQueueReceive(xQueue, &data, portMAX_DELAY);
// From ISR: xQueueSendFromISR(xQueue, &data, &xHigherPrioTaskWoken);
Task Notifications (Lightweight)
Faster than semaphores/queues for simple signaling:
// Signal: xTaskNotifyGive(xTaskHandle);
// Wait: ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// From ISR: vTaskNotifyGiveFromISR(xTaskHandle, &xHigherPrioTaskWoken);
ISR Safety Rules
- Only use
*FromISR()API variants in interrupt handlers - Never use blocking calls in ISRs (no
xSemaphoreTake, noxQueueReceive, novTaskDelay) - Always check and yield: After
*FromISR()calls, checkxHigherPriorityTaskWokenand callportYIELD_FROM_ISR()if set - Respect priority boundary: Only ISRs with priority >=
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITYmay call FreeRTOS APIs
Deferred Interrupt Processing Pattern
void USART2_IRQHandler(void) {
BaseType_t xHigherPrioTaskWoken = pdFALSE;
// Minimal ISR work: clear flag, grab data
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);
// Process data here (heavy work in task context)
}
}
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 }, // SRAM1: 64KB
{ (uint8_t *)0x2001C000, 0x4000 }, // SRAM2: 16KB
{ NULL, 0 } // Terminator
};
vPortDefineHeapRegions(xHeapRegions);
Where to Find RTOS-Related Info in Docs
- RM NVIC section:
__NVIC_PRIO_BITSvalue forconfigPRIO_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_SIZEcalculation
Additional Resources
references/rtos-integration-guide.md- Complete FreeRTOS+STM32 setup walkthrough, HAL timebase workaround details, ISR-to-task communication patterns
