Fundamentals 10 min read

How Software Timers Work on STM32: Architecture, Implementation, and Sample Code

This article explains the concept of software timers, their implementation principles in operating systems, and provides a complete STM32 example with data structures, state management, and functions for initializing, starting, updating, stopping, and querying timers.

Liangxu Linux
Liangxu Linux
Liangxu Linux
How Software Timers Work on STM32: Architecture, Implementation, and Sample Code

Software Timer Overview

Software timers simulate hardware timers in software, allowing thousands of logical timers to share a single hardware tick source. Each tick increments a global counter; a timer expires when its stored match value is less than or equal to the counter. Periodic timers recompute the next match by adding the period.

STM32 Implementation

Clock Tick

A 32‑bit volatile variable tickCnt holds the tick count and is incremented in the hardware timer ISR:

static volatile uint32_t tickCnt = 0; // tick counter
void tickCnt_Update(void) { tickCnt++; }

Data Structures

Timers are stored in a static array timer[TIMER_NUM]. Each element is defined as:

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 pointer
    void    *argv;    // argument pointer
    uint16_t argc;    // argument count
} softTimer;

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

typedef enum { MODE_ONE_SHOT = 0, MODE_PERIODIC } tmrMode;

typedef void callback(void *argv, uint16_t argc);

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;
    timer[id].period = delay;
    timer[id].state  = SOFT_TIMER_RUNNING;
    timer[id].mode   = mode;
    timer[id].cb     = cb;
    timer[id].argv   = argv;
    timer[id].argc   = argc;
}

Update

The update routine scans all timers, transitions states, and invokes callbacks:

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 Example

The following main demonstrates three timers: a one‑shot print after 1 s, a periodic LED toggle every 0.5 s, and a delayed LED activation after 3 s using a no‑operation callback.

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();
        }
    }
}

This example validates correct handling of one‑shot and periodic timers, callback execution, and external state queries.

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.

C programmingSTM32real‑time operating systemsoftware timerperiodic timertimer callback
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.