Home Wiki Electricity & Electrons Timers and PWM: Controlling Time, Speed, and Brightness
Electricity & Electrons

Timers and PWM: Controlling Time, Speed, and Brightness

Microcontroller Timers: More Than Just Counting

A timer is a hardware peripheral that counts clock cycles independently of the CPU. While the processor executes firmware, timers run in the background — counting up, counting down, or capturing external events with zero CPU overhead.

On the STM32F407, there are 14 timers for delay generation, PWM, input capture, and watchdog monitoring. The timer counts to a configurable auto-reload value (ARR) at a rate set by the prescaler:

Timer frequency = System clock / (Prescaler + 1)
Period = (ARR + 1) / Timer frequency

For a 168 MHz clock, prescaler 167 gives 1 MHz (1 us per tick). With ARR 999, the timer overflows every 1 ms.

Delay Timer: Creating Precise Time Intervals

The simplest timer application generates precise delays. Dedicated timers offer higher precision than HAL_Delay() and can trigger interrupts instead of blocking the CPU.

// TIM2: 1 ms overflow interrupt (168 MHz system clock)
void timer_init(void) {
    __HAL_RCC_TIM2_CLK_ENABLE();
    TIM_HandleTypeDef htim2 = {0};
    htim2.Instance           = TIM2;
    htim2.Init.Prescaler     = 83;     // 168 MHz / 84 = 2 MHz
    htim2.Init.CounterMode   = TIM_COUNTERMODE_UP;
    htim2.Init.Period        = 1999;   // 2 MHz / 2000 = 1 kHz
    HAL_TIM_Base_Init(&htim2);
    HAL_TIM_Base_Start_IT(&htim2);
}

volatile uint32_t system_ms = 0;
void TIM2_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
        system_ms++;
    }
}

PWM: Pulse Width Modulation

PWM generates a digital signal alternating between HIGH and LOW at a fixed frequency. The duty cycle — the ratio of HIGH time to total period — controls average power delivered to a load.

The timer's capture/compare register (CCR) sets the pulse width. When the counter is below CCR, output is HIGH; above it, output is LOW.

// TIM3 Channel 1: 20 kHz PWM on PA6
void pwm_init(void) {
    __HAL_RCC_TIM3_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_6;
    gpio.Mode = GPIO_MODE_AF_PP;
    gpio.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOA, &gpio);

    TIM_HandleTypeDef htim3 = {0};
    htim3.Instance       = TIM3;
    htim3.Init.Prescaler = 0;
    htim3.Init.Period    = 8399;  // 168 MHz / 8400 = 20 kHz
    HAL_TIM_PWM_Init(&htim3);

    TIM_OC_InitTypeDef oc = {0};
    oc.OCMode = TIM_OCMODE_PWM1;
    oc.Pulse  = 0;
    HAL_TIM_PWM_ConfigChannel(&htim3, &oc, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

void set_duty_cycle(TIM_HandleTypeDef *htim, uint16_t percent) {
    uint32_t pulse = (htim->Init.Period + 1) * percent / 100;
    __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, pulse);
}

Controlling DC Motor Speed With PWM

A DC motor speed is proportional to average voltage. PWM controls this through a MOSFET gate driver or H-bridge (e.g., L298N, BTS7960) for bidirectional speed control. Two PWM channels handle forward and reverse:

typedef struct {
    TIM_HandleTypeDef *htim;
    uint32_t ch_fwd, ch_rev;
} MotorDriver;

void motor_set_speed(MotorDriver *m, int16_t speed) {
    uint32_t pulse = (m->htim->Init.Period + 1) * abs(speed) / 100;
    __HAL_TIM_SET_COMPARE(m->htim, m->ch_fwd, speed >= 0 ? pulse : 0);
    __HAL_TIM_SET_COMPARE(m->htim, m->ch_rev, speed <  0 ? pulse : 0);
}

Controlling Servo Motor Angle

Industrial servos use 50 Hz PWM (20 ms period) where pulse width sets the angle: 1 ms = 0 degrees, 1.5 ms = 90 degrees, 2 ms = 180 degrees.

#define SERVO_MIN  1000   // 1.0 ms
#define SERVO_MAX  2000   // 2.0 ms

void servo_set_angle(TIM_HandleTypeDef *htim, uint32_t ch, float angle) {
    if (angle < 0.0f) angle = 0.0f;
    if (angle > 180.0f) angle = 180.0f;
    uint32_t pulse = SERVO_MIN +
        (uint32_t)((angle / 180.0f) * (SERVO_MAX - SERVO_MIN));
    __HAL_TIM_SET_COMPARE(htim, ch, pulse);
}

Servos are used for valve positioning, camera pan/tilt on inspection systems, and robotic arm joints.

Practical Example: Simple PID Fan Speed Control

A PID controller maintains target temperature by adjusting fan speed via PWM.

typedef struct {
    float kp, ki, kd, integral, prev_error, out_min, out_max;
} PidController;

float pid_compute(PidController *p, float setpoint, float meas, float dt) {
    float err = setpoint - meas;
    p->integral += err * dt;
    if (p->integral > p->out_max) p->integral = p->out_max;
    if (p->integral < p->out_min) p->integral = p->out_min;
    float deriv = (err - p->prev_error) / dt;
    p->prev_error = err;
    float out = p->kp * err + p->ki * p->integral + p->kd * deriv;
    if (out > p->out_max) return p->out_max;
    if (out < p->out_min) return p->out_min;
    return out;
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    pwm_init(); adc_init();
    PidController pid = {2.0f, 0.5f, 0.1f, 0, 0, 0, 100};
    while (1) {
        float duty = pid_compute(&pid, 45.0f, read_temperature(&hadc1), 0.1f);
        set_duty_cycle(&htim3, (uint16_t)duty);
        HAL_Delay(100);
    }
}

Summary

Timers are versatile hardware peripherals providing precise timing, PWM generation, and event capture independently of the CPU. PWM controls motor speed and servo angle by varying duty cycle. DC motors use H-bridge drivers for bidirectional speed control, while servos use a 50 Hz protocol for angle positioning. PID control is the standard algorithm for closed-loop systems like temperature-regulated fans. The next lesson covers interrupts and RTOS for handling multiple tasks and responding instantly to real-time events.

timers PWM duty-cycle motor-speed LED-dimming frequency المؤقتات تعديل عرض النبضة دورة العمل سرعة المحرك السطوع التردد