STM32 Timer Tutorial Using Interrupt with HAL Code Example

Timers are one of the most fundamental peripherals in embedded systems. They are like the “timekeepers” inside your system. You can use them to blink an LED at regular intervals, run a motor at a fixed speed, or simply keep track of how much time has passed. In real projects, timers are used everywhere — for generating delays, creating periodic events, producing PWM signals, measuring input signals, or scheduling tasks.

Apart from these common uses, timers also help in more advanced jobs like calibrating the clock source, testing flash memory, capturing input signals for analysis, or triggering an ADC at precise moments.

In this tutorial, we will start from the basics and learn how to configure an STM32 timer using the HAL library. We will also see how to use timer interrupts, so that your program can perform actions at exact time intervals without getting stuck in delays.

By the end of this post, you will know how to set up a timer to generate interrupts and use it for simple tasks like blinking an LED, as well as understand how timers can be applied to more advanced tasks like real-time scheduling or syncing peripherals in STM32.

 

Why STM32 Timers Are Important?

In every embedded project, timing is everywhere. You might want to blink an LED every second, measure how long a button is pressed, or run a motor at a fixed speed. A quick way to do this is by using HAL_Delay(), but the problem is simple: while the delay is running, your CPU is just sitting and waiting, it can’t do anything else.

Timers are the perfect solution to this problem; they quietly keep track of time in the background while your CPU stays free for real work

You can think of a timer as a separate little stopwatch inside your microcontroller. You can set it, forget it, and it will keep ticking in the background without disturbing your main program. When the timer reaches the time you set, it rings a kind of “alarm” (an interrupt), and your code can take action right away.

With timers, you get:

  • Non-blocking events: Your CPU is free while the timer works in the background.
  • Accurate timing: Timer’s tick based on hardware clocks, not CPU load.
  • Parallel tasks: Multiple timers can handle different jobs at the same time.
  • Extra features: PWM generation, input capture, and synchronization with other peripherals.

It is the reason timers are often called the heartbeat of real-time embedded systems — they keep everything running on schedule in the background while your CPU stays free for other work.

 

Understanding Timer Basics in Microcontrollers:

Let’s explore the fundamental concepts of timers, which will help you understand how they work and how they are used in microcontroller applications.

What is a Timer?

A timer is a hardware peripheral built into microcontrollers that measures time or counts external/internal events. It operates by incrementing (or decrementing) a counter at a fixed rate, which can then trigger specific actions once certain conditions are met.

Timers are fundamental in embedded systems for tasks such as:

  • Generating precise delays.
  • Measuring the duration of events.
  • Producing periodic signals (PWM, square waves).
  • Scheduling time-critical tasks using interrupts.

 

How Does a Timer Work?

A timer in a microcontroller is essentially composed of two fundamental components:

  • Clock Source – This provides the precise timing reference, which can originate from the system clock, an external oscillator, or another timer.
  • Counter – This register increments (or decrements) with each clock tick, after accounting for any prescaler division, effectively tracking the passage of time or events.

The operation of a timer is governed by several control registers, which allow fine-grained configuration of its behavior. Key parameters include:

  • Prescaler – Divides the input clock to adjust the counting speed.
  • Auto-Reload Value (ARR) – Defines the maximum counter value, effectively setting the timer period.
  • Interrupts or Output Compare – Specifies actions to execute when the counter reaches certain values, enabling precise event handling.

Note: We will discuss each of these parameters in detail in later sections of this blog post.

When the counter reaches the predefined threshold, a timer can perform one or more of the following actions:

  • Reset and Restart – The counter returns to zero and begins a new cycle.
  • Trigger an Interrupt – Initiates an Interrupt Service Routine (ISR) for time-critical tasks.
  • Update Output Signals – Toggles or modifies output pins, a feature widely used for generating PWM signals or precise square waves.

This combination of high-resolution counting and configurable actions makes timers indispensable for precise control in embedded systems.

 

Fundamental Parameters of STM32 Timers:

STM32 timers are highly configurable hardware modules. Understanding their fundamental parameters is crucial for precise timing, event generation, and signal control. The key parameters include:

1. Prescaler (PSC):

The prescaler divides the timer’s input clock frequency to slow down the counter. It allows adjustment of the timer’s resolution and range.

f_timer = f_clock / (PSC + 1)

Where,

  • PSC = 16-bit prescaler register.
  • f_clock = clock source (internal, external, or trigger clocks for timer operation)

 

High PSC → Slower counting → Longer timer periods → Coarser resolution

Low PSC → Faster counting → Shorter timer periods → Finer resolution

 

Example:

Scaler in STM32 Timer
If the system clock is 80 MHz and PSC = 79, the timer frequency becomes:

//formula
Timer Frequency: f_timer = f_clock / (PSC + 1)


f_timer = 80,000,000 / (79 + 1) = 1,000,000 Hz

Tick period = 1 / f_timer = 1 µs per tick

This means the counter increments every 1μs.

 

2. Auto-Reload Register (ARR)

The Auto-Reload Register (ARR) is a key component of STM32 timers. It defines when the timer counter should reset, which directly controls the timer period.

What ARR Does?

  • In up-counting mode: The counter starts from 0 and increments with each timer tick. When it reaches the value stored in ARR, it resets back to 0.
  • In down-counting mode: The counter starts from ARR and decrements with each tick until it reaches 0, then reloads.

In short, ARR sets the maximum count of the timer, which determines how long one timer period lasts.

 

Formula to Calculate Timer Period:

The timer period is calculated as:

1. Using the Timer Clock Frequency:

Timer period = (ARR + 1) / f_timer;

Where,

  • ARR = Auto-Reload Register value.
  • f_timer = Timer clock frequency after the prescaler.
2. Using the System Clock Frequency:

Timer period = (ARR+1)×(PSC+1)/SystemCoreClock

Where,

  • ARR = Auto-Reload Register value.
  • SystemCoreClock → Base frequency of the MCU core (set by RCC/PLL).
  • PSC (Prescaler) → A divider that slows down the timer input clock.

Note: We add 1 because the counter counts from 0 to ARR, so there are actually (ARR + 1) ticks in one complete period.

STM32h5 Timer period Calculation

Example Calculation:

Suppose:

  • Timer clock frequency (after prescaler) f_timer = 1Mhz
  • Auto-Reload Register ARR = 999

Then,

Timer period = (999+ 1) / 1000000 = 0.001 s=1 ms

This means the timer will reset every 1 millisecond.

 

👉 Before we move on, it is important to highlight a few key points about the Auto-Reload Register (ARR) in STM32 timers. Understanding these details will help you avoid unexpected behavior when updating the timer period.

When you write a new value to the ARR, it does not directly overwrite the active register (also called the shadow register) used by the timer. Instead, the value is first stored in a preload register. The timer then transfers this value to the active register only during an Update Event (UEV). An update event occurs when the counter overflows or when it is triggered by software.

The behavior of ARR updates is controlled by the ARPE (Auto-Reload Preload Enable) bit in TIMx_CR1:

  • ARPE = 1 → The new ARR value is applied at the next update event. This ensures safe and glitch-free updates.
  • ARPE = 0 → The new ARR value is applied immediately, which may lead to timing glitches.

Note: Using ARR preload ensures smooth updates to the timer period by applying changes only at safe update events, preventing glitches in timer operation.

 

3.Timer Clock Sources

STM32 timers can use various clock sources:

  • Internal Clock: Derived from the system clock (typically APB1 or APB2).
  • External Clock Mode 1: External input pin (TIx).
  • External Clock Mode 2: External trigger input (ETR).
  • Internal Trigger: Output from another timer.

Clock Source of Stm32 timer

⚠️ It is very important to know that in STM32H5, the timer clock frequency is determined automatically by the hardware.

There are two possible scenarios, depending on the APB prescaler:

  • APB prescaler = 1 → The timer clock frequency equals the APB bus frequency.
  • APB prescaler > 1 → The timer clock frequency is twice the APB bus frequency (×2).

 

4.Counter Mode:

STM32 timers support different counting modes that determine how the counter progresses:

  • Upcounting – The counter increments from 0 to ARR. This is the most commonly used mode.
  • Downcounting – The counter decrements from ARR down to 0.
  • Center-Aligned (Up/Down) – The counter counts up to ARR and then down to 0. This mode is especially useful for PWM generation with symmetrical waveforms, ensuring balanced rising and falling edges.

 

5. Clock Division (CKD)

The clock division (CKD) feature controls how the timer samples its internal clock. This is particularly useful for advanced timer features, such as dead-time insertion in PWM signals.

Clock Division Stm32

The available options are:

  • CKD = 1 → No division (timer uses the full internal clock frequency)
  • CKD = 2 → Clock is divided by 2
  • CKD = 4 → Clock is divided by 4

Note: Adjusting CKD allows fine-tuning of timer operation and ensures proper timing when generating complex PWM signals or other time-critical outputs.

 

6. Repetition Counter (RCR)

Available only in advanced timers (TIM1, TIM8). It allows the timer to repeat a set number of cycles before generating an update event. Useful in complex PWM patterns.

 

7. Capture/Compare Registers (CCR)

Used for output compare, input capture, and PWM generation. Each CCR defines the value at which the timer triggers an action (like toggling an output pin or generating an interrupt).

Example: In PWM mode, CCR determines the duty cycle:

Duty Cycle= CCR / (ARR+1)​×100 %

 

8. Interrupt and DMA Requests

Timers can generate interrupts or DMA requests on various events:

  • Update Event – When counter overflows or underflows
  • Compare Match – When counter equals CCR value
  • Trigger Event – For synchronization with other peripherals.

 

Timer Types in STM32H5xxx:

STM32 microcontrollers provide several categories of timers. Each type is optimized for a specific role in embedded systems design.

1. Basic Timers:

Purpose: Provide a simple time base for generating delays or periodic events.

Key Features:

  • No input capture or PWM capability
  • Can trigger interrupts or other peripherals

Typical Uses:

  • Generating a periodic system tick
  • Scheduling tasks at regular intervals
  • Acting as triggers for ADC conversions

2. General-Purpose Timers:

Purpose: The most versatile and commonly used timers.

Key Features:

  • Multiple channels (usually 2–4)
  • Support for Input Capture (measure signal period/duty cycle)
  • Output Compare (generate toggles/waveforms at precise times)
  • PWM generation (variable duty cycle control)

Typical Uses:

  • LED dimming via PWM
  • Servo motor control
  • Measuring external pulse width or frequency
  • Generating time-accurate waveforms

3. Advanced Timers:

Purpose: Built for motor control, power electronics, and safety-critical systems.

Key Features:

  • Complementary PWM outputs (for half-bridge/full-bridge control)
  • Dead-time insertion to prevent transistor shoot-through
  • Break inputs and fault protection mechanisms
  • Synchronization with other peripherals

Typical Uses:

  • Driving BLDC motors or AC motors
  • Power inverters, SMPS (switch-mode power supplies)
  • High-precision, fault-tolerant PWM generation

4. Low-Power Timers

Purpose: Provide accurate timing while consuming very little power.

Key Features:

  • Operate even in low-power modes.
  • Can run from low-speed clocks (e.g., LSE or LSI).
  • Minimal current consumption.

Typical Uses:

  • Wake-up timers in sleep/stop modes.
  • Periodic sensor polling in battery-powered devices.
  • Timekeeping in ultra-low-power IoT applications.

A quick visual summary of STM32H5

Timer Type Timer Instances Bit Features
Advanced-Control Timers TIM1, TIM8 16-bit PWM, complementary outputs, motor control
General-Purpose Timers TIM2, TIM3, TIM4, TIM5 TIM2/TIM5: 32-bit TIM3/TIM4: 16-bit Input capture, PWM, flexible timing
Basic Timers TIM6, TIM7 16-bit Time base generation, DAC triggering
1-Channel Timers TIM13, TIM14 16-bit Simple PWM or input capture
2-Channel Timers TIM9, TIM12 16-bit Moderate PWM or input capture
Complementary Output Timers TIM15, TIM16, TIM17 16-bit PWM with complementary outputs, motor control
Low-Power Timers (LPTIM) LPTIM1, LPTIM2, LPTIM3 16-bit Ultra-low power operation, wake-up from stop mode
High-Resolution Timer (HRTIM) HRTIM1 16-bit (multi-unit) Precise PWM generation, ideal for digital power conversion and motor control
Note: The number of timer and channel may vary depending on the product. Check the datasheet or reference manual for details.

 

STM32 Timer Modes:

STM32 timers can operate in multiple modes depending on the application. These modes define how the timer counts and what actions it performs.

1. Time Base Mode

Function: Timer works as a simple counter.

Details:

  • Counts clock ticks until it reaches the value in ARR (Auto-Reload Register).
  • Generates an update event (UEV) when it overflows.

Use Cases:

  • Delays
  • Periodic interrupts (like system tick)

2. Input Capture Mode

Function: Measures external signal characteristics.

Details:

  • Captures the timer’s counter value when an external edge (rising/falling) occurs on a capture pin.
  • Allows measurement of signal period, frequency, or pulse width.

Use Cases:

  • Frequency measurement of an external clock
  • Pulse width measurement in communication protocols
  • Capturing events (button press, encoder signals)

3. Output Compare Mode

Function: Generates an output event when the counter matches a given value.

Details:

  • Compare registers (CCR) hold threshold values.
  • When counter = CCR, an action can occur (toggle pin, set/reset pin, generate interrupt).

Use Cases:

  • Generating square waves
  • Precise event triggering
  • Controlling digital outputs

4. PWM (Pulse Width Modulation) Mode

Function: Produces periodic signals with variable duty cycle.

Details:

  • Based on Output Compare.
  • The duty cycle is defined by CCR relative to ARR.
  • Can be edge-aligned or center-aligned.

Use Cases:

  • Motor speed control
  • LED brightness control
  • Audio tone generation

5. One-Pulse Mode (OPM)

Function: Timer generates a single pulse in response to a trigger event.

Details:

  • Can be configured for internal or external trigger.
  • Stops automatically after generating the pulse.

Use Cases:

  • Precise pulse generation
  • Trigger-based signal output

6. Encoder Interface Mode

Function: Timer acts as a quadrature encoder counter.

Details:

  • Uses two input channels connected to encoder signals (A/B).
  • Increments or decrements counter based on rotation direction.

Use Cases:

  • Measuring shaft position and speed.
  • Robotics, motor control, industrial encoders.

7. Slave Mode / Master Mode (Synchronization)

Function: Coordinate timers with each other or with external signals.

Details:

  • Slave mode: Timer runs based on trigger from another timer or external input.
  • Master mode: Timer generates trigger signals to control other timers or peripherals.

Use Cases:

  • Multi-phase PWM generation.
  • Synchronizing ADC sampling with timers.
  • Cascading timers for extended counting.

8. Hall Sensor Interface Mode:

  • Specifically designed for BLDC motor commutation.
  • Uses 3-phase Hall sensor signals to determine rotor position.

9. External Clock Mode:

  • Timer can be clocked from an external signal instead of the internal system clock.
  • Useful for frequency measurement and synchronization with external devices.

 

Basic Timer Control Registers in STM32

To operate STM32 timers, you mainly configure and monitor a set of control registers. These registers define how the timer runs, how it interacts with interrupts/DMA, and how events are generated.

  • TIMx_CR1 (Control Register 1): Configures fundamental timer behavior: enable/disable counter, counting direction (up/down), and alignment mode.
  • TIMx_DIER (DMA/Interrupt Enable Register): Enables interrupts and DMA requests for events like update, compare match, and capture.
  • TIMx_SR (Status Register): Shows pending events (e.g., update, compare, capture). Flags here indicate what happened, and software must clear them after handling.
  • TIMx_EGR (Event Generation Register): Allows software to generate events manually (e.g., force an update event) for testing or synchronization.
  • TIMx_CNT (Counter Register): Holds the current value of the timer counter. Reading it gives the live count, writing can reset or preload it.
  • TIMx_PSC (Prescaler Register): Defines the prescaler value, effectively dividing the timer clock before it drives the counter.
  • TIMx_ARR (Auto-Reload Register): Sets the counter’s maximum (or minimum, in down-counting). Defines the timer period.

 

Practical Example of STM2 Timer configuration:

In this part of the blog post, we will learn how to configure a Basic Timer in STM32 using the HAL API. We will also explore how to enable timer interrupts and properly handle the interrupt event in code.

“Here in this example, we will configure TIM6 to generate an interrupt every 1 second“.

The steps include:

Step 1: Timer 6 Initialization:

In STM32 microcontrollers, setting up a timer to generate a specific delay like 1 second; you need to configure two essential registers: the Prescaler (PSC) and the Auto-Reload Register (ARR). The PSC controls how fast the timer counts, while the ARR defines how many counts make up one complete timer cycle. Together, these two registers allow you to precisely control timing. You can learn more about PSC and ARR in the previous section of this article.

 

TIM_HandleTypeDef htim6;

void initTimer(void)
{
    __HAL_RCC_TIM6_CLK_ENABLE();   // Enable TIM6 clock

    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 7999;    // PSC
    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim6.Init.Period = 9999;       // ARR
    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
    {
        Error_Handler();
    }
}

With SystemCoreClock = 80 MHz:

  • PSC = 7999 → Timer clock = 80 MHz / 8000 = 10 kHz
  • ARR = 9999 → Overflow at 10,000 counts = 1 second

 

Step 2: Start Timer with Interrupt:

This function enables the timer and allows it to generate an interrupt every 1 second when it overflows.

void startTimer(void)
{
    HAL_TIM_Base_Start_IT(&htim6);
}

Step 3: NVIC Configuration

Enable and configure the TIM6 interrupt in the NVIC:

void timerMspInit(TIM_HandleTypeDef* tim_baseHandle)
{
    if(tim_baseHandle->Instance==TIM6)
    {
        HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
    }
}

Note: On STM32 devices, TIM6 shares the interrupt line with the DAC, so the IRQ name is TIM6_DAC_IRQn.

 

Step 4: Interrupt Handler and Callback:

The HAL_TIM_PeriodElapsedCallback() function is automatically called when the timer overflows.

void TIM6_DAC_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim6);  // Pass control to HAL interrupt handler
}



void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *pHtim)
{
    if(pHtim->Instance == TIM6)
    {
        // Executes every 1 second
        
    }
}

 

🛠️ Common STM32 Timer issues and Debugging Tips:

In the following section, I will discuss some real-world issues that engineers often face during timer driver development along with their possible fix and best practices to help you avoid common pitfalls.

Basic issue and tips:

Below are some common timer-related issues along with possible tips to help resolve them.

1. Timer not running?

Ensure the RCC clock for your timer (TIMx) is enabled; without it, the timer simply won’t run. You can enable it using the HAL macro __HAL_RCC_TIMx_CLK_ENABLE().

Remember, no clock means no counting. The timer won’t increment, generate interrupts, or do anything until its clock source is active.

 

2. No interrupt firing?

Double-check your NVIC configuration. Even if your timer setup looks perfect, the interrupt won’t trigger unless you have correctly set its priority using HAL_NVIC_SetPriority() and enabled it with HAL_NVIC_EnableIRQ().

Also, verify that the NVIC interrupt line is actually enabled and the correct IRQ handler function (e.g., TIM6_DAC_IRQHandler()) is properly implemented in your code.

 

3. Wrong timing?

Recalculate PSC and ARR: Double-check your math. Even a small miscalculation can throw off timing.

 

4. ISR too slow?

Keep ISRs lean: Avoid heavy processing. Use flags, queues, or semaphores to defer work to the main loop or RTOS task.

 

Advanced STM32 Timer Debugging Issues:

Below are some advanced timer-related issues along with possible fix to help resolve them.

1. Timer Glitch Due to Clock Domain Crossing:

Symptom: Timer behaves erratically or misses interrupts.

Cause: Timer clock (e.g., APB1/APB2) is asynchronous to the system clock. If you reconfigure PSC/ARR while the timer is running, you may hit a race condition.

Fix: Always disable the timer before reconfiguring registers. Use __HAL_TIM_DISABLE() before changing PSC/ARR.

 

2. Interrupt Not Triggering Due to Missing Update Event:

Symptom: Timer interrupt never fires even though PSC/ARR are correct.

Cause: TIMx->DIER (DMA/Interrupt Enable Register) not set properly, or TIMx->SR (Status Register) has pending flags that block new events.

Fix: Ensure UIE bit is set in DIER, and clear SR flags manually if needed.

 

3. Shared IRQ Line Conflict (TIM6_DAC_IRQn):

Symptom: TIM6 interrupt handler never executes, or DAC misbehaves.

Cause: TIM6 and DAC share the same IRQ vector. If DAC is also enabled, it may override TIM6’s interrupt.

Fix: Carefully manage shared IRQs. Use conditional logic in the ISR to distinguish between TIM6 and DAC sources.

 

4. Timer Overflow Too Fast for ISR:

Symptom: ISR is overwhelmed, system becomes unresponsive.

Cause: ARR too small or PSC too low, causing frequent interrupts.

Fix: Increase ARR/PSC to reduce interrupt frequency. Offload heavy ISR tasks to main loop or RTOS.

 

5. PWM Output Not Working Despite Correct Configuration:

Symptom: No waveform on output pin.

Cause: GPIO not configured for alternate function, or AF mapping is incorrect.

Fix: Double-check HAL_TIM_PWM_Init() and HAL_TIM_PWM_Start(). Ensure GPIO is set to AF mode with correct AF number.

 

6. Timer Reset on Debug Stop:

Symptom: Timer resets or halts when debugger hits a breakpoint.

Cause: Debug configuration disables timer clock during halt.

Fix: In CubeMX or DBGMCU registers, enable timer clock during debug (DBG_TIMx_STOP = 0).

 

7. DMA Transfer Not Triggering from Timer

Symptom: DMA never starts despite timer update events.

Cause: DMA request not enabled in DIER, or DMA channel misconfigured.

Fix: Enable UDE in DIER, verify DMA channel and stream mapping, and ensure memory/peripheral addresses are correct.

 

8. Multiple Timers Interfering with Each Other

Symptom: One timer’s behavior affects another (e.g., missed interrupts, jitter).

Cause: Shared clock domain, overlapping IRQs, or incorrect NVIC priorities.

Fix: Isolate timers to different APB domains if possible. Assign distinct priorities and avoid shared IRQs unless necessary.

 

Related Articles You May Like:

 

Sources:

  • STM32 Reference Manual – Timer chapters
  • STM32CubeIDE examples for timer configuration
  • STM32 HAL documentation for timer functions