Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

STM32 Timer Essentials with HAL: Modes, PWM, and Input Capture

Tech 2

Timer capabilities at a glance

  • 16-bit counter with up, down, and center-aligned (up/down) counting modes and auto-reload
  • 16-bit prescaler
  • Multiple independent channels per timer supporting:
    • Input capture
    • Output compare
    • PWM generation
    • One-pulse mode
  • Advanced-control timers can generate complementary outputs
  • Interrupt/DMA events include:
    • Update events (counter overflow/underflow, re-initialization)
    • Trigger events (start/stop/initialize via internal/external triggers)
    • Capture/Compare events (input capture, output compare)

Note: The timer’s input clock is the APB timer clock. On many STM32 families, if the APB prescaler is greater than 1, the timer clock is 2× APB frequency; otherwise it equals the APB frequency.


1. Counter modes and base timing

Counting modes

  • Up-counting: CNT increments from 0 to ARR, generates an update event, then rolls over to 0
  • Down-counting: CNT decrements from ARR to 0, generates an update event, then reloads ARR
  • Center-aligned (up/down): CNT counts 0 → ARR−1 (event) then ARR−1 → 1 (event) then repeats

Overflow period calculation

Time per update event:

T = (ARR + 1) × (PSC + 1) / Ftim

  • ARR: Auto-reload value
  • PSC: Prescaler value
  • Ftim: Timer input clock (after APB and x2 rules)

Example: Ftim = 84 MHz, PSC = 8399, ARR = 999

T = (999+1) × (8399+1) / 84,000,000 = 0.1 s (100 ms)

CubeMX pointers

  • Ensure the timer is on APB1 or APB2 as intended and set the corresponding clock tree
  • Clock source: Internal Clock
  • Base configuraton fields:
    • Prescaler (PSC, 16-bit)
    • Counter Mode: Up, Down, or Center Aligned
    • Auto-Reload (ARR, 16-bit)
    • Auto-reload preload: enable if you want buffered ARR updates
    • Enable Update interrupt and set NVIC priority (lower number = higher priority)

Minimal HAL example (base timer interrupt)

Start the base timer interrupt and toggle a GPIO in the update callback.

// Start timer update interrupt
auto status = HAL_TIM_Base_Start_IT(&htim3);

// Update event (overflow/underflow) callback
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    }
}

2. PWM generation

PWM (Pulse-Width Modulation) modulates the duty cycle of a periodic waveform. LEDs and motors smooth out the rapid on/off transitions, yielding perceived analog behavior.

Frequency basics

  • Frequency f (Hz) = 1 / period (s)
  • 50 Hz → 20 ms period (e.g., common servo frame rate)
  • 1 kHz → 1 ms period; 10 kHz → 100 µs period

PWM timing math

  • Timer tick frequency Ftick = Ftim / (PSC + 1)
  • PWM period = (ARR + 1) / Ftick
  • PWM frequency Fpwm = Ftick / (ARR + 1) = Ftim / ((PSC + 1) × (ARR + 1))
  • Duty cycle for a channel is set by CCRx in [0, ARR]
  • PWM modes:
    • Mode 1: output active while CNT < CCRx
    • Mode 2: output inactive while CNT < CCRx

Example: Ftim = 72 MHz, PSC = 71, ARR = 999 → Ftick = 1 MHz, Fpwm = 1 MHz / 1000 = 1 kHz

CubeMX configuration

  • Clock source: Internal
  • Enable a PWM channel (e.g., TIM3 CH2)
  • Base:
    • Prescaler: 71 (for 1 MHz tick at 72 MHz Ftim)
    • Counter Period: 999 (for 1 kHz PWM)
  • Channel:
    • PWM Mode 1 or 2
    • Polarity: High or Low

HAL code example

Start PWM and ramp the duty cycle.

// Enable PWM output on TIM3_CH2
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);

// Set initial duty (e.g., 10%)
uint32_t period = __HAL_TIM_GET_AUTORELOAD(&htim3); // ARR
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (period + 1) / 10);

// Simple breathing effect
static uint32_t duty = 0;
static int32_t step = 1; // increase/decrease

for (;;)
{
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, duty);

    if ((step > 0 && duty >= period) || (step < 0 && duty == 0))
    {
        step = -step;
    }
    duty += step;

    HAL_Delay(5);
}

3. Input capture

Input capture latches the counter value on an input edge. It is commonly used to measure pulse widths (high or low duration) and periods (time between successive edges).

Key options:

  • Polarity: rising, falling, or both (edge selection)
  • Filter: digital filtering to suppress spurious edges; samples at fDTS and requires consecutive qualified samples
  • Channel mappinng: cofnigure which TIx feeds ICy (direct/indirect/combined)
  • Prescaler: capture every 1st, 2nd, 4th, or 8th qualifying edge

CubeMX configuration example

  • TIM4 Channel 1 as Input Capture (PB6 on many STM32F1 parts)
  • Internal Clock
  • IC selection: Direct
  • Pull-up/down according to your circuitry
  • Polarity: start with Falling (to measure low pulse width)
  • Enable Update interrupt and IC interrupt
  • Base timing: set PSC for 1 µs resolution
    • Example: Ftim = 72 MHz → PSC = 71 → Ftick = 1 MHz (1 tick = 1 µs)
    • ARR = 0xFFFF (max window ≈ 65,536 µs per wrap)

HAL code example: measure low-level duration (falling→rising)

This example resets the counter on the falling edge, counts with 1 µs resolution, and captures the elapsed time on the next rising edge, accounting for counter overflows.

typedef struct {
    volatile uint8_t  measuring; // 0 = waiting for falling, 1 = waiting for rising
    volatile uint8_t  ready;     // 1 when a measurement is ready
    volatile uint32_t wraps;     // number of CNT overflows during measurement
    volatile uint32_t ccr;       // captured CCR value at rising edge
} PulseMeter;

static PulseMeter g_pulse = {0};

// Start base timer and input capture with interrupts
void IC_Start(void)
{
    HAL_TIM_Base_Start_IT(&htim4);

    // Begin by detecting a falling edge (start of low pulse)
    __HAL_TIM_SET_CAPTUREPOLARITY(&htim4, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
    HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM4 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
        if (!g_pulse.measuring)
        {
            // Falling edge: reset counter and prepare to latch on rising edge
            __HAL_TIM_SET_COUNTER(htim, 0);
            g_pulse.wraps = 0;
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
            g_pulse.measuring = 1;
        }
        else
        {
            // Rising edge: capture CCR and mark ready
            g_pulse.ccr = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            g_pulse.ready = 1;

            // Re-arm for next measurement (falling edge)
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
            g_pulse.measuring = 0;
        }
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM4 && g_pulse.measuring)
    {
        // Count timer wraps that occur while waiting for the rising edge
        g_pulse.wraps++;
    }
}

// Poll the result in your main loop when ready
void PollPulseWidthUs(void)
{
    if (g_pulse.ready)
    {
        uint32_t ticksPerWrap = __HAL_TIM_GET_AUTORELOAD(&htim4) + 1; // ARR + 1
        uint32_t totalTicks   = g_pulse.wraps * ticksPerWrap + g_pulse.ccr;
        // With 1 MHz timer tick, totalTicks equals microseconds directly
        printf("Low pulse width: %lu us\n", (unsigned long)totalTicks);
        g_pulse.ready = 0;
    }
}
Tags: STM32

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.