Your tasks are in Ready; the scheduler decides who runs. Here’s the practical path to start it, pick a policy (pre-emptive vs co-operative), and understand the heap/TCB story behind xTaskCreate().
✅ Start the Scheduler
vTaskStartScheduler() launches the kernel; it never returns unless heap is insufficient to create the Idle (and timer) task.
⚙️ Scheduling Policies
-
Pre-emptive (
configUSE_PREEMPTION 1): the running task can be replaced on ticks or when a higher-priority task becomes ready. -
Co-operative (
configUSE_PREEMPTION 0): tasks voluntarily give up the CPU withtaskYIELD()(no forced pre-emption).
🔁 Round-Robin & ⏫ Priority-Based
-
Round-robin: equal-priority ready tasks share the CPU in time slices tied to the RTOS tick.
-
Priority-based: the highest priority ready task runs; when it blocks, the next highest takes over; if a higher priority task unblocks, it pre-empts immediately.
🤝 Co-operative Example (clean prints)
If your output looks garbled under pre-emption, protect the shared sink (UART/ITM) with a mutex.
🧠 Behind the Scenes: Heap, TCB, Stacks
-
xTaskCreate()dynamically allocates the TCB and the task’s private stack from the RTOS heap and then links the TCB into the Ready list. -
Heap size is set by
configTOTAL_HEAP_SIZE; many projects use heap4.c (malloc/free over a single heap array). -
On Cortex-M, task stacks are tracked with PSP (process stack pointer); the TCB stores TopOfStack.
⚡ Pro Tips
-
Keep
for(;;){ … vTaskDelay(...) }or block on queues; avoid busy loops. -
Few priority levels → fewer context switches (less RAM/time overhead).
-
Always check
xTaskCreate()and enable overflow/malloc-fail hooks. -
Put app code inside USER CODE BEGIN/END in Cube projects to survive regen.
🎯 Conclusion
Choose pre-emptive for responsiveness or co-operative for deterministic prints, start the scheduler, and size the heap/stack sanely, your tasks will play nicely and your logs will, too.
Written By: Musaab Taha
This article was improved with the assistance of AI.
No comments:
Post a Comment