Scan to FollowLearn Embedded Together, learn and grow together

This is a series of introductory articles on FreeRTOS. While organizing my knowledge, I hope to help beginners quickly get started and master the basic principles and usage of FreeRTOS.
Quick Start with FreeRTOS – Exploring the System
The official Chinese version of the FreeRTOS website is now online!
FreeRTOS Coding Standards and Data Types
Quick Start with FreeRTOS – Task Management
Learning FreeRTOS: Detailed Explanation of Message Queues
Learning FreeRTOS: Counting Semaphores
Learning FreeRTOS: Mutex Semaphores
Learning FreeRTOS: Event Groups
Learning FreeRTOS: Task Notifications
This article introduces the timer functionality of FreeRTOS.
FreeRTOS provides two types of timer functionalities, offering flexible time management solutions for embedded real-time systems:
- Software Timers: Timers implemented purely in software managed by the FreeRTOS kernel
- Task Delays: Task-level delay functionality based on system ticks
Timers play a critical role in embedded systems and are commonly used for:
- Periodic task scheduling
- Timeout detection
- Delayed execution
- Watchdog functionality
- Timed event handling in protocol stacks
Working Principle
Basic Concepts
FreeRTOS software timers are a high-level abstraction built on the basis of system ticks, with the following characteristics:
- Pure Software Implementation: Does not rely on hardware timer peripherals
- Callback Mechanism: Calls a predefined callback function when expired
- Dynamic Creation: Can be dynamically created and deleted at runtime
- Two Modes:
- One-shot Timer: Triggers only once
- Auto-reload Timer: Repeatedly triggers periodically
Implementation Mechanism
FreeRTOS implements software timers in the following way:
- Timer Daemon Task: A dedicated FreeRTOS task that handles timer callbacks
- Timer Command Queue: Used to send commands to the daemon task in a thread-safe manner
- Timer List: A list of created timers maintained by the kernel
When a system tick interrupt occurs, the FreeRTOS kernel checks the timer list, sends expired timer commands to the timer command queue, and the daemon task processes these commands and executes the corresponding callback functions.
Configuration and Usage
Configuring FreeRTOS to Use Software Timers
Add the following configuration in FreeRTOSConfig.h:
#define configUSE_TIMERS 1 // Enable software timer functionality
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) // Daemon task priority
#define configTIMER_QUEUE_LENGTH 10 // Timer command queue length
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) // Daemon task stack size
Creating a Timer
#include "FreeRTOS.h"
#include "timers.h"
TimerHandle_t xTimerCreate(
const char * const pcTimerName, // Timer name (for debugging)
const TickType_t xTimerPeriodInTicks,// Timer period (in ticks)
const UBaseType_t uxAutoReload, // Auto-reload flag (pdTRUE for auto-reload)
void * const pvTimerID, // Timer ID
TimerCallbackFunction_t pxCallbackFunction // Callback function
);
Example:
// Define callback function
void vTimerCallback(TimerHandle_t xTimer)
{
uint32_t *pulParameter = (uint32_t *)pvTimerGetTimerID(xTimer);
// Handle timer event
}
// Create an auto-reload timer (period 1000 ticks)
uint32_t ulParameter = 123;
TimerHandle_t xAutoReloadTimer = xTimerCreate(
"AutoReloadTimer",
pdMS_TO_TICKS(1000), // 1000ms
pdTRUE, // Auto-reload
(void *)&ulParameter, // Parameter passed to callback function
vTimerCallback // Callback function
);
Starting a Timer
BaseType_t xTimerStart(TimerHandle_t xTimer, TickType_t xTicksToWait);
Parameters:
<span>xTimer</span>: The timer handle to start<span>xTicksToWait</span>: Maximum ticks to wait if the timer command queue is full
Example:
if(xTimerStart(xAutoReloadTimer, 0) != pdPASS)
{
// Handle timer start failure
}
Stopping a Timer
BaseType_t xTimerStop(TimerHandle_t xTimer, TickType_t xTicksToWait);
Changing Timer Period
BaseType_t xTimerChangePeriod(
TimerHandle_t xTimer,
TickType_t xNewPeriod,
TickType_t xTicksToWait
);
Deleting a Timer
BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xTicksToWait);
Task Delay Functionality
In addition to software timers, FreeRTOS also provides task-based delay functionality:
Simple Delay
void vTaskDelay(const TickType_t xTicksToDelay);
Example: Delay 500ms
vTaskDelay(pdMS_TO_TICKS(500));
Absolute Time Delay
void vTaskDelayUntil(TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement);
Example: Precise periodic task
void vPeriodicTask(void *pvParameters)
{
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(100); // 100ms period
for(;;)
{
// Perform periodic work
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
Usage Recommendations
- Callback Function Design Principles:
- Keep it short, avoid long blocking
- Do not call APIs that may cause blocking (e.g., vTaskDelay)
- Consider using queues or event groups for task communication instead of directly handling complex logic in the callback
- The priority of the timer daemon task should be higher than that of the tasks using the timer
- However, it should not be set too high to avoid affecting higher-priority real-time tasks
- Delete timers that are no longer in use in a timely manner
- Pay attention to the configuration of the timer command queue length
- The precision of software timers is affected by system ticks
- For high precision requirements, consider using hardware timers
- Assign meaningful names to timers
- Use uxTimerGetTimerNumber() to assist in debugging
Common Issues and Solutions
- Timer Callback Not Executing:
- Check if xTimerStart() was called
- Ensure the daemon task has enough stack space
- Verify if the timer command queue is overflowing
- Consider whether the system tick frequency is appropriate
- Check if higher-priority tasks are occupying the CPU for extended periods
- Check heap space when timer creation fails
- Consider statically allocating timer memory
- Use appropriate synchronization mechanisms
- Consider using timer IDs to distinguish different contexts
Advanced Examples
Hardware Watchdog with Software Timer
TimerHandle_t xWatchdogTimer;
void vWatchdogCallback(TimerHandle_t xTimer)
{
// Timer expired, system may hang
LOG_ERROR("Watchdog timeout!");
NVIC_SystemReset();
}
void vInitWatchdog(void)
{
// Create watchdog timer (one-shot mode, 5 seconds timeout)
xWatchdogTimer = xTimerCreate(
"Watchdog",
pdMS_TO_TICKS(5000),
pdFALSE,
NULL,
vWatchdogCallback
);
xTimerStart(xWatchdogTimer, 0);
}
void vPetWatchdog(void)
{
// Reset watchdog timer
xTimerReset(xWatchdogTimer, 0);
}
Managing Multiple Timers
typedef enum
{
TIMER_ID_SENSOR_READ,
TIMER_ID_NETWORK_UPDATE,
TIMER_ID_UI_REFRESH
} timer_id_t;
void vTimerCallback(TimerHandle_t xTimer)
{
timer_id_t id = (timer_id_t)pvTimerGetTimerID(xTimer);
switch(id)
{
case TIMER_ID_SENSOR_READ:
// Handle sensor reading
break;
case TIMER_ID_NETWORK_UPDATE:
// Handle network update
break;
case TIMER_ID_UI_REFRESH:
// Handle UI refresh
break;
}
}
void vInitApplicationTimers(void)
{
// Create multiple timers, using IDs to distinguish
TimerHandle_t xSensorTimer = xTimerCreate("Sensor", pdMS_TO_TICKS(100), pdTRUE,
(void *)TIMER_ID_SENSOR_READ, vTimerCallback);
TimerHandle_t xNetworkTimer = xTimerCreate("Network", pdMS_TO_TICKS(1000), pdTRUE,
(void *)TIMER_ID_NETWORK_UPDATE, vTimerCallback);
TimerHandle_t xUiTimer = xTimerCreate("UI", pdMS_TO_TICKS(50), pdTRUE,
(void *)TIMER_ID_UI_REFRESH, vTimerCallback);
xTimerStart(xSensorTimer, 0);
xTimerStart(xNetworkTimer, 0);
xTimerStart(xUiTimer, 0);
}
Performance Considerations and Optimization
-
System Tick Frequency Selection:
- A higher frequency provides finer timing resolution
- But increases CPU overhead (there is processing overhead for each tick interrupt)
- Typical values: 100Hz (10ms) to 1000Hz (1ms)
Timer Count Limitations:
- Each timer requires about 40 bytes of memory (depending on architecture)
- Active timers increase the processing burden on the daemon task
Low Power Design:
- Allow the CPU to enter low power mode when idle
- Consider using tickless mode (configure configUSE_TICKLESS_IDLE)
Static Memory Allocation:
StaticTimer_t xTimerBuffer;
TimerHandle_t xTimer = xTimerCreateStatic(..., &xTimerBuffer);
- For systems with high determinism requirements, static allocated timers can be used:
In Conclusion
By properly using software timers and task delay functionalities, developers can build efficient and reliable real-time applications.
The key is to choose the appropriate timing strategy based on specific application scenarios and follow best practices to ensure system stability and responsiveness.
In actual projects, it is recommended to:
- Thoroughly test the behavior of timers under various load conditions
- Monitor the stack usage of the timer daemon task
- For time-critical operations, consider using hardware timers in conjunction
- Document the design and expected behavior of timers for easier maintenance

Follow 【Learn Embedded Together】 to become better together..
If you find this article helpful, click “Share”, “Like”, or “Recommend”!