Skip to content
Home » Embedded Systems » RTOS » Timer Management in RTOS: Software Timers, Tick Hooks, and Timing

Timer Management in RTOS: Software Timers, Tick Hooks, and Timing

RTOS for Embedded Systems
Part 7 of 10View Full Path →

KEY TAKEAWAYS

  • RTOS software timers run callback functions at specified intervals without requiring a dedicated task for each timer — ideal for periodic checks, timeouts, and watchdogs.
  • The tick rate (configTICK_RATE_HZ) determines timing resolution and OS overhead — 1000 Hz gives 1 ms resolution but costs more CPU than 100 Hz (10 ms resolution).
  • For precise timing needs under 1 ms, use hardware timers directly — software timers and task delays are limited to tick resolution.

Part of the Complete Guide to RTOS for Embedded Systems series.

Time management is fundamental to real-time systems. RTOS provides multiple timing mechanisms — tick-based delays, software timers, tick hooks, and tickless idle mode. Understanding when to use each one and their limitations is essential for building firmware that meets timing requirements without wasting resources.

The RTOS Tick and Time Resolution

The RTOS tick is a periodic interrupt (usually driven by SysTick on ARM Cortex-M) that drives the scheduler. At each tick, the kernel checks if any blocked tasks should be unblocked (delay expired, timeout reached) and if a context switch is needed.

The tick rate is configured by configTICK_RATE_HZ:

  • 1000 Hz (1 ms tick) — 1 ms timing resolution, good for most applications. Context switch overhead: ~0.1-0.5% CPU on a Cortex-M4 at 100 MHz.
  • 100 Hz (10 ms tick) — lower overhead, suitable for systems where 10 ms resolution is acceptable. Common in battery-powered devices.
  • 10 Hz (100 ms tick) — minimal overhead, but very coarse timing. Only suitable for slow systems.

Important: all RTOS timing functions have a resolution of ±1 tick. A 10 ms delay with a 1 ms tick rate will delay between 9 and 10 ms, depending on where in the current tick period the call occurs. For applications needing precise timing, use vTaskDelayUntil() which compensates for this jitter.

// vTaskDelay — relative delay (may drift over time)
vTaskDelay(pdMS_TO_TICKS(100));  // Delay ~100 ms from now

// vTaskDelayUntil — absolute periodic timing (no drift) TickType_t xLastWake = xTaskGetTickCount(); for (;;) { do_periodic_work(); vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(100)); // Exactly every 100 ms } “`

Software Timers

Software timers execute a callback function after a specified period, either once (one-shot) or repeatedly (auto-reload). They run in the context of the timer service task (daemon task), not in a separate task per timer.

TimerHandle_t xHeartbeatTimer;
TimerHandle_t xTimeoutTimer;

void heartbeat_callback(TimerHandle_t xTimer) { toggle_led(); // Called every 500 ms }

void timeout_callback(TimerHandle_t xTimer) { // Communication timeout — no response received in 2 seconds report_comm_timeout(); }

void setup_timers(void) { // Auto-reload timer — repeats every 500 ms xHeartbeatTimer = xTimerCreate(“HB”, pdMS_TO_TICKS(500), pdTRUE, NULL, heartbeat_callback); xTimerStart(xHeartbeatTimer, 0);

// One-shot timer — fires once after 2000 ms xTimeoutTimer = xTimerCreate(“Timeout”, pdMS_TO_TICKS(2000), pdFALSE, NULL, timeout_callback); }

void on_packet_sent(void) { // Reset timeout timer each time we send a packet xTimerReset(xTimeoutTimer, 0); // Restart the 2-second countdown } “`

Timer callback rules:

  • Callbacks run in the timer daemon task context — they share its stack and priority
  • Keep callbacks short — a long callback delays all other timer callbacks
  • Do NOT call blocking RTOS functions from timer callbacks (no vTaskDelay, no xSemaphoreTake with timeout)
  • Timer commands (start, stop, reset) are sent through a queue to the daemon task — they don’t execute immediately

Configure the timer daemon task with:

  • configTIMER_TASK_PRIORITY — usually set high (just below your most critical tasks)
  • configTIMER_TASK_STACK_DEPTH — must be large enough for all timer callback stacks combined
  • configTIMER_QUEUE_LENGTH — number of pending timer commands (start/stop/reset) to buffer

Tickless Idle Mode for Low Power

In battery-powered applications, the periodic tick interrupt wastes power by waking the CPU every 1-10 ms even when no tasks need to run. Tickless idle mode solves this by stopping the tick interrupt during idle periods and compensating the tick count when the CPU wakes up.

// In FreeRTOSConfig.h
#define configUSE_TICKLESS_IDLE 1

// FreeRTOS automatically: // 1. Calculates the time until the next task needs to wake // 2. Programs a low-power timer for that duration // 3. Stops SysTick // 4. Enters sleep mode (WFI instruction) // 5. On wake, compensates the tick count for elapsed time “`

For custom low-power modes (stop mode, standby mode), implement the tickless idle hook:

// Custom tickless idle — enter deeper sleep modes
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) {
    uint32_t sleep_ms = xExpectedIdleTime * portTICK_PERIOD_MS;

if (sleep_ms > 10) { configure_rtc_wakeup(sleep_ms); enter_stop_mode(); // Deep sleep — only RTC running uint32_t actual_ms = read_rtc_elapsed(); vTaskStepTick(actual_ms / portTICK_PERIOD_MS); } else { // Short sleep — just WFI __WFI(); } } “`

Tickless idle can reduce idle power consumption from milliamps to microamps, extending battery life from days to months. The tradeoff is slightly more complex timer management and potential timing inaccuracy during sleep (depending on the RTC accuracy).

Hardware Timers vs Software Timers

Use hardware timers when you need:

  • Resolution finer than the tick period (sub-millisecond)
  • Precise periodic interrupts (jitter under 1 μs)
  • PWM generation, input capture, or pulse counting
  • Timing that continues during RTOS tickless sleep

Use software timers when you need:

  • Many independent timers (you’re limited by MCU hardware timer count, but can have hundreds of software timers)
  • Simple timeout or periodic callbacks where ±1 ms jitter is acceptable
  • Timers that interact with RTOS tasks and queues

Practical guideline: use hardware timers for control loops (PID, motor control, ADC sampling) and software timers for everything else (timeouts, heartbeats, periodic status checks, debouncing).

// Hardware timer for 10 kHz ADC sampling — 100 μs period, sub-μs jitter
void TIM2_IRQHandler(void) {
    TIM2->SR &= ~TIM_SR_UIF;
    uint16_t sample = ADC1->DR;
    xQueueSendFromISR(xAdcQueue, &sample, NULL);
}

// Software timer for 5-second inactivity timeout — ±1 ms jitter is fine void inactivity_callback(TimerHandle_t xTimer) { enter_low_power_mode(); } “`

Leave a Reply

Your email address will not be published. Required fields are marked *