Day 5 of 5
⏱ ~60 minutes
RTOS in 5 Days — Day 5

Timers and Profiling

FreeRTOS software timers run callbacks without creating full tasks. Today you'll use them, then profile your system's timing behavior.

Software Timers

FreeRTOS software timers call a callback function after a period (one-shot) or repeatedly (auto-reload). They run in the timer service task (priority configTIMER_TASK_PRIORITY). xTimerCreate(name, period, autoReload, timerID, callback). xTimerStart(timer, timeout). Callbacks must be short and non-blocking — they run in the timer task, blocking it affects all other timers. For longer work, post to a queue from the callback and process in a dedicated task.

FreeRTOS+Trace

FreeRTOS+Trace (Percepio Tracealyzer) records every task switch, ISR, queue operation, and semaphore event. The recorder runs in RAM (circular buffer, configurable size). On demand, dump the buffer via UART, USB, or Segger RTT. Visualize in Tracealyzer: see exactly which tasks ran, for how long, what blocked them. Free version available with limited features. Alternative: SystemView (Segger, free) with similar capabilities and real-time streaming.

Stack Overflow and Heap Monitoring

Configure configCHECK_FOR_STACK_OVERFLOW=2 and implement vApplicationStackOverflowHook(). Configure configUSE_MALLOC_FAILED_HOOK=1 and implement vApplicationMallocFailedHook(). In production: log the faulting task name to flash or send over UART before resetting. Use vTaskList() to dump a table of all tasks, their state, priority, and stack HWM — invaluable for debugging.

c
// Software timers + system profiling
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"

TimerHandle_t xHeartbeatTimer;
TimerHandle_t xWatchdogTimer;
volatile uint32_t watchdog_fed = 0;

// Heartbeat: toggle LED every 500ms (auto-reload)
void vHeartbeatCb(TimerHandle_t xTimer) {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}

// Watchdog: if not fed within 3 seconds, reset
void vWatchdogCb(TimerHandle_t xTimer) {
    if (!watchdog_fed) {
        printf("WATCHDOG: system not responding, resetting!\n");
        NVIC_SystemReset();
    }
    watchdog_fed = 0;
    // Restart the timer
    xTimerReset(xTimer, 0);
}

// Print task list and heap stats every 10 seconds
void vProfileTask(void *pv) {
    static char taskListBuf[512];
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(10000));

        // Dump task table: name, state, priority, HWM, task number
        vTaskList(taskListBuf);
        printf("\n=== Task List ===\n%s\n", taskListBuf);
        printf("Free heap: %u / %u bytes\n",
               (unsigned)xPortGetFreeHeapSize(),
               (unsigned)configTOTAL_HEAP_SIZE);
    }
}

// Main application task — feeds watchdog
void vAppTask(void *pv) {
    while (1) {
        // Do application work...
        watchdog_fed = 1;  // feed the watchdog
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

int main(void) {
    // One-shot watchdog (restarted in callback)
    xWatchdogTimer = xTimerCreate("WDG", pdMS_TO_TICKS(3000),
                                   pdFALSE, 0, vWatchdogCb);

    // Auto-reload heartbeat
    xHeartbeatTimer = xTimerCreate("HB", pdMS_TO_TICKS(500),
                                    pdTRUE, 0, vHeartbeatCb);

    xTaskCreate(vAppTask,     "APP",  256, NULL, 2, NULL);
    xTaskCreate(vProfileTask, "PROF", 512, NULL, 1, NULL);

    xTimerStart(xHeartbeatTimer, 0);
    xTimerStart(xWatchdogTimer, 0);
    vTaskStartScheduler();
    while (1);
}
💡
A software watchdog is essential in production firmware. If any task gets stuck in an infinite loop or deadlock, the watchdog timer fires and resets the system. The watchdog token (watchdog_fed) should only be set in the task that represents the system's heartbeat — not by every task independently.
📝 Day 5 Exercise
Profile Your RTOS Application
  1. Add the profile task to your application. Run it for 5 minutes. What are the HWM values for each task?
  2. Enable configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS in FreeRTOSConfig.h. Use vTaskGetRunTimeStats() to see CPU utilization per task.
  3. Create a software watchdog with a 5-second timeout. Verify it fires if you add an infinite loop to a task.
  4. Time a critical section: use DWT_CYCCNT (ARM cycle counter) before and after a mutex-protected operation. How many cycles does it take?
  5. Set up FreeRTOS+Tracealyzer or Segger SystemView. Record 10 seconds of execution. Find the longest gap between task activations.

Day 5 Summary

  • Software timer callbacks run in the timer task — keep them short; post to queue for longer work
  • Software watchdog: if application doesn't feed it within timeout, reset — essential for production
  • vTaskList() + vTaskGetRunTimeStats() give a complete picture of task state and CPU utilization
  • Profile with DWT cycle counter or Tracealyzer before optimizing — measure, don't guess
Challenge

Implement a FreeRTOS task notification system as a lightweight alternative to binary semaphores. Use xTaskNotifyGive() from an ISR and ulTaskNotifyTake() in the task. Benchmark: signal 100,000 times and compare latency to binary semaphore. Task notifications skip the semaphore overhead (no handle lookup, direct to task TCB) — on Cortex-M, they're typically 30–50% faster than semaphores.

Finished this lesson?