Fundamentals 10 min read

Mastering Software Timers on STM32: Theory, Design, and Code

This guide explains what software timers are, their implementation principles in operating systems, and provides a complete STM32‑based example with data structures, state machines, and full C code for initialization, start, update, stop, and status queries.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering Software Timers on STM32: Theory, Design, and Code

What Is a Software Timer

A software timer is implemented entirely in code. It allows thousands of timers to be created from a single hardware tick, eliminating hardware quantity limits at the cost of CPU cycles and reduced precision compared with dedicated hardware timers.

Implementation Principle

In operating systems such as Linux, uC/OS, and FreeRTOS a hardware timer generates periodic ticks. An interrupt handler increments a global tick counter. Each software timer stores an expiration tick value. A periodic scan compares the stored expiration against the global counter; when they match the timer’s callback is invoked and the timer is either stopped (one‑shot) or rescheduled (periodic).

STM32 Software Timer Design

Clock Tick

A 32‑bit volatile variable tickCnt records the number of hardware ticks. The tick interrupt handler increments this counter.

static volatile uint32_t tickCnt = 0; // software timer tick counter

void tickCnt_Update(void) {
    tickCnt++;
}

Data Structures

The implementation uses a fixed‑size array of timer objects.

static softTimer timer[TIMER_NUM]; // timer array

The timer structure holds state, mode, expiration, period, callback pointer, and optional arguments.

typedef struct softTimer {
    uint8_t  state;   // STOPPED, RUNNING, TIMEOUT
    uint8_t  mode;    // ONE_SHOT or PERIODIC
    uint32_t match;   // expiration tick
    uint32_t period;  // period for periodic timers
    callback *cb;      // callback function
    void *argv;       // argument pointer
    uint16_t argc;    // argument count
} softTimer;

Enumerations define possible states and modes.

typedef enum { SOFT_TIMER_STOPPED = 0, SOFT_TIMER_RUNNING, SOFT_TIMER_TIMEOUT } tmrState;

typedef enum { MODE_ONE_SHOT = 0, MODE_PERIODIC } tmrMode;

Timer Operations

Initialization

void softTimer_Init(void) {
    for (uint16_t i = 0; i < TIMER_NUM; i++) {
        timer[i].state = SOFT_TIMER_STOPPED;
        timer[i].mode  = MODE_ONE_SHOT;
        timer[i].match = 0;
        timer[i].period= 0;
        timer[i].cb    = NULL;
        timer[i].argv  = NULL;
        timer[i].argc  = 0;
    }
}

Start

void softTimer_Start(uint16_t id, tmrMode mode, uint32_t delay, callback *cb, void *argv, uint16_t argc) {
    assert_param(id < TIMER_NUM);
    assert_param(mode == MODE_ONE_SHOT || mode == MODE_PERIODIC);
    timer[id].match  = tickCnt_Get() + delay; // expiration tick
    timer[id].period = delay;                 // period for periodic mode
    timer[id].state  = SOFT_TIMER_RUNNING;
    timer[id].mode   = mode;
    timer[id].cb     = cb;
    timer[id].argv   = argv;
    timer[id].argc   = argc;
}

Update

void softTimer_Update(void) {
    for (uint16_t i = 0; i < TIMER_NUM; i++) {
        switch (timer[i].state) {
            case SOFT_TIMER_STOPPED:
                break;
            case SOFT_TIMER_RUNNING:
                if (timer[i].match <= tickCnt_Get()) {
                    timer[i].state = SOFT_TIMER_TIMEOUT;
                    timer[i].cb(timer[i].argv, timer[i].argc);
                }
                break;
            case SOFT_TIMER_TIMEOUT:
                if (timer[i].mode == MODE_ONE_SHOT) {
                    timer[i].state = SOFT_TIMER_STOPPED;
                } else {
                    timer[i].match = tickCnt_Get() + timer[i].period;
                    timer[i].state = SOFT_TIMER_RUNNING;
                }
                break;
            default:
                printf("timer[%d] state error!
", i);
                break;
        }
    }
}

Stop

void softTimer_Stop(uint16_t id) {
    assert_param(id < TIMER_NUM);
    timer[id].state = SOFT_TIMER_STOPPED;
}

Read State

uint8_t softTimer_GetState(uint16_t id) {
    return timer[id].state;
}

Test Application

The example program creates three timers:

One‑shot timer (ID TMR_STRING_PRINT) prints a string after 1 s.

Periodic timer (ID TMR_TWINKLING) toggles LED0 every 0.5 s.

One‑shot timer with empty callback (ID TMR_DELAY_ON) turns on LED1 after 3 s; the main loop checks the timer state to perform the LED action.

static uint8_t data[] = {1,2,3,4,5,6,7,8,9,0};

int main(void) {
    USART1_Init(115200);
    TIM4_Init(TIME_BASE_MS);
    TIM4_NVIC_Config();
    LED_Init();
    printf("I just grabbed a spoon.
");

    softTimer_Start(TMR_STRING_PRINT, MODE_ONE_SHOT, 1000, stringPrint, data, 5);
    softTimer_Start(TMR_TWINKLING,    MODE_PERIODIC, 500, LED0_Twinkling, NULL, 0);
    softTimer_Start(TMR_DELAY_ON,    MODE_ONE_SHOT, 3000, nop, NULL, 0);

    while (1) {
        softTimer_Update();
        if (softTimer_GetState(TMR_DELAY_ON) == SOFT_TIMER_TIMEOUT) {
            LED1_On();
        }
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

callbackreal-time OStimer implementationSTM32Embedded Csoftware timer
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.