Fundamentals 10 min read

Mastering Software Timers on STM32: From Theory to Practical Implementation

This article explains what software timers are, their implementation principles in operating systems, and provides a detailed STM32-based example—including clock tick handling, data structures, state management, and full C code for initializing, starting, updating, stopping, and testing timers.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering Software Timers on STM32: From Theory to Practical Implementation

What is a Software Timer

A software timer is a timer implemented in program code. It allows many logical timers to be created from a single hardware timer, removing the hardware quantity limit. The trade‑off is higher CPU usage and lower precision compared with a hardware timer.

Implementation Principle

In operating systems such as Linux, uC/OS, and FreeRTOS a hardware timer generates periodic ticks. Each tick increments a global counter. Every software timer stores an expiration tick value. The system periodically scans all active timers, compares each timer’s expiration tick with the global counter, and invokes the associated callback when the timer expires. For periodic timers the next expiration tick is set by adding the period to the current tick count after the callback.

STM32 Software Timer Example

Clock Tick

A 32‑bit global variable tickCnt records the tick count.

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

The hardware‑timer interrupt increments this counter:

void tickCnt_Update(void) {
    tickCnt++;
}

Data Structures

This example stores timers in a static array.

static softTimer timer[TIMER_NUM]; // timer array
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 mode
    callback *cb;     // callback function pointer
    void *argv;       // argument pointer for callback
    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

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

Testing the Timer

Three timers are created for demonstration: TMR_STRING_PRINT: one‑shot, prints a string after 1 s. TMR_TWINKLING: periodic, toggles LED0 every 0.5 s. TMR_DELAY_ON: one‑shot, lights LED1 after 3 s; its callback is a no‑op and the LED is turned on by checking the timer state in the main loop.

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

The example shows how to create, control, and monitor software timers on an STM32 platform, illustrating the flexibility of software timers and the associated CPU overhead.

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.