In embedded systems development, precise timing and the ability to execute code after a specified delay or at periodic intervals are crucial for many applications. FreeRTOS, a popular real-time operating system (RTOS) for embedded devices, provides built-in support for timers. These timers are essential tools for managing time-sensitive tasks, such as periodic sensor reads, timed communication, and system heartbeat signals.
In this article, we’ll explore the concept of timers in FreeRTOS, how they work, and how you can implement them on the ESP32 platform. We will include a practical example to help you get started.
What are Timers in FreeRTOS?
Timers in FreeRTOS are software-based timers that are used to execute a function after a specified period, either once or repeatedly. FreeRTOS provides two types of timers:
- One-Shot Timer: This timer triggers once after the specified delay and does not repeat unless reinitialized.
- Periodic Timer: This timer triggers repeatedly at a fixed interval after the initial delay
Timers in FreeRTOS operate asynchronously, meaning they don’t block other tasks from running. They are particularly useful for offloading time-based operations from tasks that may be performing more complex logic or I/O operations.
Timer API in FreeRTOS
FreeRTOS provides a set of API functions to create, start, stop, and delete timers. These include:
- xTimerCreate(): Creates a timer with a specific period and a callback function.
- xTimerStart(): Starts the created timer.
- xTimerStop(): Stops the running timer.
- xTimerChangePeriod(): Changes the period of an existing timer.
- xTimerDelete(): Deletes a timer.
- xTimerIsTimerActive(): Checks whether a timer is currently active.
How Timers Work in FreeRTOS
Timers are managed by the FreeRTOS kernel and are generally executed in the timer service task (a background task that FreeRTOS manages). When a timer expires, the associated callback function is executed. If the timer is periodic, it is reset automatically to the original period. If it is one-shot, it will not repeat until manually started again.
The timer callback functions execute in an interrupt context, which means they must be kept short and non-blocking to avoid disrupting the real-time performance of the system.
ESP32 Timer Implementation
One shot timer
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
// Timer callback function that gets called when the timer expires
void timer_callback(TimerHandle_t xTimer)
{
printf("Timer expired and callback executed.\n");
// Check if the timer is still active (it shouldn't be because it's a one-shot)
if (xTimerIsTimerActive(xTimer)) {
printf("Timer is still active.\n");
} else {
printf("Timer is not active.\n");
}
}
void app_main()
{
// Create a one-shot timer that will trigger after 2 seconds
TimerHandle_t timer = xTimerCreate(
"OneShotTimer", // Name of the timer
pdMS_TO_TICKS(2000), // Timer period in ticks (2000 ms = 2 seconds)
pdFALSE, // One-shot timer (won't restart automatically)
(void *)0, // Timer ID (not used here)
timer_callback // Callback function to be executed when timer expires
);
if (timer == NULL)
{
printf("Failed to create the timer.\n");
return;
}
// Start the timer
if (xTimerStart(timer, 0) != pdPASS)
{
printf("Failed to start the timer.\n");
return;
}
printf("Timer started. Waiting for expiration...\n");
// Check if the timer is active (should be active right after starting)
if (xTimerIsTimerActive(timer)) {
printf("Timer is active.\n");
} else {
printf("Timer is not active.\n");
}
// Wait 2 seconds for the timer to expire
vTaskDelay(pdMS_TO_TICKS(3000));
// Change the timer period to 1 second (1000 ms)
printf("Changing the timer period to 1000ms.\n");
if (xTimerChangePeriod(timer, pdMS_TO_TICKS(1000), 0) != pdPASS) {
printf("Failed to change the timer period.\n");
}
// Stop the timer after changing the period (before it expires again)
if (xTimerStop(timer, 0) != pdPASS) {
printf("Failed to stop the timer.\n");
}
printf("Timer stopped before next expiration.\n");
// Check if the timer is still active after stopping
if (xTimerIsTimerActive(timer)) {
printf("Timer is still active.\n");
} else {
printf("Timer is not active.\n");
}
// Delete the timer
if (xTimerDelete(timer, 0) != pdPASS) {
printf("Failed to delete the timer.\n");
}
printf("Timer deleted.\n");
}
Periodic timer
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
// Timer callback function that gets called when the timer expires
void timer_callback(TimerHandle_t xTimer)
{
printf("Timer expired and callback executed.\n");
// Check if the timer is still active
if (xTimerIsTimerActive(xTimer)) {
printf("Timer is still active.\n");
} else {
printf("Timer is not active.\n");
}
}
void app_main()
{
// Create a periodic timer that will trigger every 2 seconds
TimerHandle_t timer = xTimerCreate(
"PeriodicTimer", // Name of the timer
pdMS_TO_TICKS(2000), // Timer period in ticks (2000 ms = 2 seconds)
pdTRUE, // Periodic timer (will restart automatically after each expiration)
(void *)0, // Timer ID (not used here)
timer_callback // Callback function to be executed when timer expires
);
if (timer == NULL)
{
printf("Failed to create the timer.\n");
return;
}
// Start the timer
if (xTimerStart(timer, 0) != pdPASS)
{
printf("Failed to start the timer.\n");
return;
}
printf("Periodic timer started. Waiting for expirations...\n");
// Check if the timer is active (should be active right after starting)
if (xTimerIsTimerActive(timer)) {
printf("Timer is active.\n");
} else {
printf("Timer is not active.\n");
}
// Wait for 10 seconds, during which the timer will expire multiple times
vTaskDelay(pdMS_TO_TICKS(10000));
// Change the timer period to 1 second (1000 ms)
printf("Changing the timer period to 1000ms.\n");
if (xTimerChangePeriod(timer, pdMS_TO_TICKS(1000), 0) != pdPASS) {
printf("Failed to change the timer period.\n");
}
// Wait for another 6 seconds, during which the timer will continue to expire at the new period
vTaskDelay(pdMS_TO_TICKS(6000));
// Stop the timer
if (xTimerStop(timer, 0) != pdPASS) {
printf("Failed to stop the timer.\n");
}
printf("Timer stopped.\n");
// Check if the timer is still active after stopping
if (xTimerIsTimerActive(timer)) {
printf("Timer is still active.\n");
} else {
printf("Timer is not active.\n");
}
// Delete the timer
if (xTimerDelete(timer, 0) != pdPASS) {
printf("Failed to delete the timer.\n");
}
printf("Timer deleted.\n");
}
Conclusion
Timers in FreeRTOS are a powerful tool for implementing time-based events without blocking the main execution flow of your program. They are ideal for tasks such as periodic sensor readings, timeouts, or managing periodic events like blinking an LED.