$npx -y skills add mohitmishra786/low-level-dev-skills --skill freertosFreeRTOS skill for embedded RTOS development. Use when creating tasks, managing priorities, using queues and mutexes, detecting stack overflows, configuring FreeRTOS via FreeRTOSConfig.h, or debugging FreeRTOS applications with OpenOCD and GDB. Activates on queries about FreeRTOS
| 1 | # FreeRTOS |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Guide agents through FreeRTOS application development: task creation and priorities, inter-task communication with queues and semaphores, stack overflow detection, configASSERT, and FreeRTOS-aware debugging with GDB and OpenOCD. |
| 6 | |
| 7 | ## Triggers |
| 8 | |
| 9 | - "How do I create a FreeRTOS task?" |
| 10 | - "How do I pass data between FreeRTOS tasks?" |
| 11 | - "My FreeRTOS task is crashing — how do I detect stack overflow?" |
| 12 | - "How do I use FreeRTOS mutexes?" |
| 13 | - "How do I debug FreeRTOS tasks with GDB?" |
| 14 | - "How do I configure FreeRTOSConfig.h?" |
| 15 | |
| 16 | ## Workflow |
| 17 | |
| 18 | ### 1. Task creation and priorities |
| 19 | |
| 20 | ```c |
| 21 | #include "FreeRTOS.h" |
| 22 | #include "task.h" |
| 23 | |
| 24 | // Task function signature |
| 25 | void vMyTask(void *pvParameters) { |
| 26 | const char *name = (const char *)pvParameters; |
| 27 | for (;;) { |
| 28 | // Task body — must never return |
| 29 | printf("Task %s running\n", name); |
| 30 | vTaskDelay(pdMS_TO_TICKS(500)); // yield for 500ms |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | int main(void) { |
| 35 | // xTaskCreate(function, name, stack_depth_words, param, priority, handle) |
| 36 | TaskHandle_t xHandle = NULL; |
| 37 | xTaskCreate(vMyTask, "MyTask", |
| 38 | configMINIMAL_STACK_SIZE + 128, // words, not bytes! |
| 39 | (void *)"sensor", |
| 40 | tskIDLE_PRIORITY + 2, // higher = more urgent |
| 41 | &xHandle); |
| 42 | |
| 43 | vTaskStartScheduler(); // never returns if heap is sufficient |
| 44 | for (;;); // should never reach here |
| 45 | } |
| 46 | ``` |
| 47 | |
| 48 | Priority guidelines: |
| 49 | - `tskIDLE_PRIORITY` (0) — idle task, never block here |
| 50 | - ISR-deferred tasks — highest priority to service interrupts quickly |
| 51 | - Avoid priorities above `configMAX_PRIORITIES - 1` |
| 52 | |
| 53 | ### 2. Queues — inter-task data passing |
| 54 | |
| 55 | ```c |
| 56 | #include "queue.h" |
| 57 | |
| 58 | typedef struct { uint32_t sensor_id; float value; } SensorReading_t; |
| 59 | |
| 60 | QueueHandle_t xSensorQueue; |
| 61 | |
| 62 | void vProducerTask(void *pvParam) { |
| 63 | SensorReading_t reading; |
| 64 | for (;;) { |
| 65 | reading.sensor_id = 1; |
| 66 | reading.value = read_adc(); |
| 67 | // Send; block max 10ms if queue full |
| 68 | xQueueSend(xSensorQueue, &reading, pdMS_TO_TICKS(10)); |
| 69 | vTaskDelay(pdMS_TO_TICKS(100)); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | void vConsumerTask(void *pvParam) { |
| 74 | SensorReading_t reading; |
| 75 | for (;;) { |
| 76 | // Block forever until item available |
| 77 | if (xQueueReceive(xSensorQueue, &reading, portMAX_DELAY) == pdTRUE) { |
| 78 | process(reading.value); |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // Create before starting scheduler |
| 84 | xSensorQueue = xQueueCreate(10, sizeof(SensorReading_t)); |
| 85 | ``` |
| 86 | |
| 87 | From ISR: use `xQueueSendFromISR()` and pass `&xHigherPriorityTaskWoken`. |
| 88 | |
| 89 | ### 3. Semaphores and mutexes |
| 90 | |
| 91 | ```c |
| 92 | #include "semphr.h" |
| 93 | |
| 94 | // Binary semaphore — signaling (ISR→task) |
| 95 | SemaphoreHandle_t xSem = xSemaphoreCreateBinary(); |
| 96 | |
| 97 | void UART_ISR(void) { |
| 98 | BaseType_t xWoken = pdFALSE; |
| 99 | xSemaphoreGiveFromISR(xSem, &xWoken); |
| 100 | portYIELD_FROM_ISR(xWoken); |
| 101 | } |
| 102 | |
| 103 | void vUartTask(void *p) { |
| 104 | for (;;) { |
| 105 | xSemaphoreTake(xSem, portMAX_DELAY); |
| 106 | // process received data |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // Mutex — mutual exclusion (NOT from ISR) |
| 111 | SemaphoreHandle_t xMutex = xSemaphoreCreateMutex(); |
| 112 | |
| 113 | void vCriticalSection(void) { |
| 114 | if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) { |
| 115 | // protected access |
| 116 | shared_resource++; |
| 117 | xSemaphoreGive(xMutex); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | // Recursive mutex (same task can take multiple times) |
| 122 | SemaphoreHandle_t xRecursive = xSemaphoreCreateRecursiveMutex(); |
| 123 | xSemaphoreTakeRecursive(xRecursive, portMAX_DELAY); |
| 124 | xSemaphoreGiveRecursive(xRecursive); |
| 125 | ``` |
| 126 | |
| 127 | Use mutex (not binary semaphore) for shared resources to get priority inheritance. |
| 128 | |
| 129 | ### 4. Stack overflow detection |
| 130 | |
| 131 | ```c |
| 132 | // FreeRTOSConfig.h |
| 133 | #define configCHECK_FOR_STACK_OVERFLOW 2 // Method 2 (pattern + watermark) |
| 134 | #define configUSE_MALLOC_FAILED_HOOK 1 |
| 135 | |
| 136 | // Implement the hook (called when overflow detected) |
| 137 | void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { |
| 138 | // Log the offending task name, then halt |
| 139 | configASSERT(0); // triggers assertion failure |
| 140 | } |
| 141 | |
| 142 | void vApplicationMallocFailedHook(void) { |
| 143 | configASSERT(0); |
| 144 | } |
| 145 | ``` |
| 146 | |
| 147 | Check watermarks at runtime: |
| 148 | |
| 149 | ```c |
| 150 | // Returns minimum ever free stack words |
| 151 | UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(xHandle); |
| 152 | printf("Stack headroom: %lu words\n", uxHighWaterMark); |
| 153 | // Rule of thumb: keep headroom > 20 words |
| 154 | ``` |
| 155 | |
| 156 | ### 5. Essential FreeRTOSConfig.h settings |
| 157 | |
| 158 | ```c |
| 159 | // FreeRTOSConfig.h — adapt to your MCU |
| 160 | #define configCPU_CLOCK_HZ (SystemCoreClock) |
| 161 | #define configTICK_RATE_HZ 1000 // 1ms tick |
| 162 | #define configMAX_PRIORITIES 8 |
| 163 | #define configMINIMAL_STACK_SIZE 128 // words |
| 164 | #define configTOTAL |