Skip to content

Software Timers

A software timer lets FreeRTOS call a function later or at a fixed interval without forcing you to create a dedicated polling task just to wait.

This is useful when the problem is:

  • “do something every 500 ms”
  • “run this timeout once after 2 s”
  • “generate a periodic event, then wake a worker task”

The callback runs in the context of the FreeRTOS timer service task, not in an interrupt and not in your application task.

Use a software timer when:

  • you need a periodic trigger but not a full task loop
  • the periodic work is short
  • you want to wake another task at regular intervals

Do not use a software timer callback for:

  • long computations
  • blocking waits
  • drawing to the screen for a long time
  • anything that would stall other timer callbacks

Before using software timers, enable them in FreeRTOSConfig.h:

#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 2
#define configTIMER_QUEUE_LENGTH 8
#define configTIMER_TASK_STACK_DEPTH 256

What these settings mean:

  • configUSE_TIMERS: turns the feature on
  • configTIMER_TASK_PRIORITY: priority of the internal timer service task
  • configTIMER_QUEUE_LENGTH: how many pending timer commands FreeRTOS can queue
  • configTIMER_TASK_STACK_DEPTH: stack size for the timer service task

The most common timer functions are:

  • xTimerCreate() - create a timer object
  • xTimerStart() - start it
  • xTimerStop() - stop it
  • xTimerReset() - restart its countdown from now
  • xTimerChangePeriod() - change the interval

The callback type is:

void MyTimerCallback(TimerHandle_t xTimer);

This is the basic pattern you will use most often:

#include "FreeRTOS.h"
#include "timers.h"
static void HeartbeatCb(TimerHandle_t xTimer)
{
(void)xTimer;
// short, non-blocking work here
}
static TimerHandle_t xHeartbeatTimer;
void App_Init(void)
{
xHeartbeatTimer = xTimerCreate(
"heartbeat",
pdMS_TO_TICKS(500),
pdTRUE,
NULL,
HeartbeatCb);
xTimerStart(xHeartbeatTimer, 0);
}

What each argument means:

  • "heartbeat": debug name
  • pdMS_TO_TICKS(500): timer period
  • pdTRUE: auto-reload, so it repeats
  • NULL: optional user data
  • HeartbeatCb: callback to run

Use a one-shot timer when something should happen once after a delay:

static void TimeoutCb(TimerHandle_t xTimer)
{
(void)xTimer;
// timeout action
}
static TimerHandle_t xTimeoutTimer;
void InitTimeout(void)
{
xTimeoutTimer = xTimerCreate(
"timeout",
pdMS_TO_TICKS(2000),
pdFALSE,
NULL,
TimeoutCb);
}

pdFALSE means the timer does not reload automatically.

In Lab 2, the timer is not doing the full microphone processing. Instead, it is used as a regular sample trigger.

The pattern is:

software timer callback
-> read one sample
-> store it in a buffer
-> when buffer is full, signal MicTask
MicTask
-> waits for the signal
-> computes RMS
-> updates shared display values

That is a very typical FreeRTOS design:

  • the timer callback does the small time-critical step
  • the task does the heavier work
#include "FreeRTOS.h"
#include "timers.h"
#include "semphr.h"
#define WINDOW_SIZE 128
static TimerHandle_t xMicTimer;
static SemaphoreHandle_t xMicReadySem;
static uint16_t gMicSamples[WINDOW_SIZE];
static uint16_t gMicIndex = 0;
static void MicSampleCb(TimerHandle_t xTimer)
{
(void)xTimer;
gMicSamples[gMicIndex++] = Mic_Read();
if (gMicIndex >= WINDOW_SIZE) {
gMicIndex = 0;
xSemaphoreGive(xMicReadySem);
}
}
void Mic_InitTasking(void)
{
xMicReadySem = xSemaphoreCreateBinary();
xMicTimer = xTimerCreate(
"mic",
pdMS_TO_TICKS(1),
pdTRUE,
NULL,
MicSampleCb);
xTimerStart(xMicTimer, 0);
}
void MicTask(void *pvParams)
{
for (;;) {
xSemaphoreTake(xMicReadySem, portMAX_DELAY);
// process the completed sample window here
}
}

Inside a timer callback:

  • keep the code short
  • do not call vTaskDelay()
  • do not block on queues, semaphores, or mutexes
  • avoid heavy math if a task can do it instead

Why? Because all timer callbacks share the same timer service task. If one callback takes too long, every other software timer is delayed too.

xTimerReset(xTimeoutTimer, 0);
xTimerChangePeriod(xHeartbeatTimer, pdMS_TO_TICKS(1000), 0);
xTimerStop(xHeartbeatTimer, 0);

These are useful for:

  • inactivity timeouts
  • game delays
  • changing update rates at runtime

Choosing Between A Task And A Software Timer

Section titled “Choosing Between A Task And A Software Timer”

Use a task when:

  • the code is long-running
  • the code may block
  • the work has its own complex state machine

Use a software timer when:

  • you need a short callback at a known interval
  • you just want to trigger or notify another task
  • the timing relationship matters more than the amount of work
  • Forgetting to enable configUSE_TIMERS
  • Doing too much work inside the callback
  • Assuming the callback runs in interrupt context
  • Reading and processing the same buffer concurrently without a handoff strategy
  • Using a timer when a regular task with vTaskDelay() would be simpler and sufficient

Software timers are best when you need a small, regular trigger. In Lab 2, the timer is not the whole microphone subsystem; it is the piece that creates a consistent sampling rhythm and wakes the real processing task when enough data has been collected.