Timers, PWM and Timing

Prescaler and compare math, periodic interrupts, PWM frequency and resolution, servo pulses and timing without blocking.

What a hardware timer is

A timer is a counter that the chip’s clock increments on its own, whatever the program is doing. It can raise an interrupt when it overflows or reaches a compare value, toggle a pin automatically, capture the count when an input changes, and generate PWM. Because it runs in hardware, a timer keeps exact time without any CPU load, which is why periodic work, PWM and precise measurement are built on timers, not on loops and delay().

The prescaler trades resolution for range (16-bit timer, 16 MHz)1 ms10 ms100 ms1000 ms10000 ms÷1tick 0.0625 µs; max 4.1 ms÷8tick 0.5 µs; max 32.8 ms÷64tick 4 µs; max 262.1 ms÷256tick 16 µs; max 1.05 s÷1024tick 64 µs; max 4.19 sBar length is the longest period before the 16-bit counter overflows (log scale).
A 16-bit timer on a 16 MHz clock: a larger prescaler gives a longer range but coarser steps.

Prescaler and compare math

The timer’s clock is the system clock divided by a prescaler (1, 8, 64, 256, 1024 on an AVR). The time of one count is the tick, and the period of an interrupt is the compare value plus one, times the tick.

tick = prescaler ÷ fclk    period = (compare + 1) × tick    compare = period ÷ tick − 1

Worked example: a 1 Hz interrupt on a 16-bit timer

At 16 MHz we need 1 s. Try each prescaler: counts needed = 1 s ÷ tick.

PrescalerTickCounts for 1 sFits in 16 bits?
10.0625 µs16 000 000no (over 65 536)
80.5000 µs2 000 000no (over 65 536)
644.0000 µs250 000no (over 65 536)
25616.0000 µs62 500yes
102464.0000 µs15 625yes

The smallest prescaler that fits is 256 (the best resolution), giving a compare value of 62 499 counting from 0 (62 500 − 1 = 62 499). With a prescaler of 1 024 the tick of 64 µs would also work but with coarser steps.

// ATmega328P Timer1: interrupt once per second (CTC mode, prescaler 256)
cli();
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS12);   // CTC mode, prescaler 256
OCR1A  = 62499;                        // 16e6 / 256 - 1
TIMSK1 = (1 << OCIE1A);                // enable the compare-match interrupt
sei();

ISR(TIMER1_COMPA_vect) { seconds++; }  // keep it short

Worked example: an exact 1 kHz tick, and a note that is a little flat

  • 1 kHz tick. With the 8-bit Timer0 and a prescaler of 64: tick = 64 ÷ 16 MHz = 4 µs, and 1 ms = 250 ticks, so the compare value is 250 − 1 = 249, an exact division (16 000 000 ÷ 64 000 = 250). This is the interrupt that drives millis() and the debounce routine of GPIO, Interrupts and Debouncing.
  • A tone. To toggle a speaker pin at 440 Hz (the note A4) with a prescaler of 8, the pin must toggle every half period: count = 16 000 000 ÷ (2 × 8 × 440) = 2272.73, which rounds to 2 273, giving 439.95 Hz: an error of -0.21 cents (a hundred cents make a semitone; most people notice about 5 to 10 cents). Rounding error is largest for high frequencies where the count is small.

Timer modes: overflow, CTC, capture, PWM

ModeWhat it doesUse
Normal / overflowCounts to the maximum (255 or 65 535), overflows to 0 and raises an interruptA simple periodic tick; a free-running time base
CTC (clear on compare match)Counts to the compare value, then restartsExact periods and frequencies
Input captureCopies the counter to a register when an input pin changesMeasuring pulse width, frequency and time between events with tick resolution; no ISR jitter
Output compareToggles, sets or clears a pin when the counter matchesGenerating exact waveforms and delays by hardware
PWM (fast, phase-correct)Counts and sets the pin high and low at compare valuesPWM for LEDs, motors, servos and DACs
Quadrature decoder / external clockCounts edges from an encoder or pinEncoders and pulse counting (see GPIO, Interrupts and Debouncing)

Capture is the accurate way to measure. To measure the period of a signal, capture the timer at two successive edges and subtract: the unsigned subtraction survives one counter overflow. The resolution is one tick, so a 4 µs tick measures a 1 kHz signal’s period (1 000 µs) to ±0.4 %.

PWM: frequency, duty and resolution

Pulse-width modulation switches a pin on and off at a fixed frequency and varies the fraction of time it is on, the duty cycle. A load that responds slowly (an LED to the eye, a motor to its inertia, an RC filter) sees the average voltage.

Vaverage = duty × Vsupply    fPWM = fclk ÷ (prescaler × counts per period)

25 %average 1.25 V50 %average 2.50 V75 %average 3.75 Vone periodLogic high is 5 V, low is 0 V. The load sees the average if the frequency is high enough.
The same frequency at three duty cycles. The orange dashed line is the average value seen by a slow load.

Worked example: frequency against resolution

  • The Arduino default. 8-bit fast PWM (256 counts), prescaler 64 at 16 MHz: f = 16 000 000 ÷ (64 × 256) = 976.6 Hz (the familiar “about 980 Hz” of pins 5 and 6).
  • A higher frequency for a motor. A 20 kHz PWM, above hearing, needs 16 000 000 ÷ 20 000 = 800 counts per period at prescaler 1: that is only log2(800) = 9.6 bits, so about 800 duty steps (0.125 % each).
  • More resolution, lower frequency. 10 bits (1 024 counts) with a prescaler of 8 gives 1 953 Hz; a full 16 bits at prescaler 1 gives 244.1 Hz. Frequency and resolution trade against each other for a given clock: bits = log2(fclk ÷ fPWM).
  • Phase-correct PWM counts up and down, halving the frequency (16 MHz ÷ (64 × 510) = 490 Hz here) but placing the pulses symmetrically, which is better for motors and for two PWMs that must not switch together.
  • Pick the frequency for the load: well above 100 Hz for LEDs (to avoid flicker, and 1 kHz or more for cameras); 1 to 20 kHz for small motors (20 kHz is silent); tens to hundreds of kHz for switching power stages.
  • Change the duty cycle by writing the compare register, not by stopping and restarting the timer, so there are no glitches. Most timers latch the new value at the end of the period.
  • Dead time between the on-times of two switches in a half-bridge prevents shoot-through; use a timer with a dead-time generator for that.
  • PWM is not an analog output. For a smooth voltage add an RC filter (below) or use a DAC.
  • Motors and heaters need a driver stage, and a flyback path for inductive loads (see Driving Motors and Actuators).

Servo and ESC pulses

A hobby servo is not controlled by duty cycle but by pulse width: a pulse of 1 to 2 ms repeated every 20 ms (50 Hz), with 1.5 ms at the center. The same signal drives most ESCs (motor controllers).

Worked example: servo resolution

  • A 16-bit timer with a prescaler of 8 at 16 MHz ticks every 0.5 µs. The 20 ms period is 40 000 counts, and the 1 ms and 2 ms pulses are 2 000 and 4 000 counts.
  • Across 1 000 µs of pulse the servo travels about 90° (a typical servo; many go further), so one count is 90 ÷ 2 000 = 0.045°, far finer than the servo can resolve (its dead-band is about 1 to 5 µs, 0.1° to 0.5°).
  • The Arduino Servo library uses the same idea on a timer; with 8-bit timers the resolution would be coarser, so use a 16-bit timer or a PWM peripheral for precision.
  • Power: a servo draws hundreds of milliamps to amps when it moves; power it from its own supply with the grounds joined, not from the MCU’s regulator.

From PWM to an analog voltage

A simple RC low-pass turns a PWM into a slowly varying voltage. The cut-off frequency fc = 1 ÷ (2π R C) must be well below the PWM frequency to give a small ripple, and well above the rate at which you need the output to change.

Worked example: a PWM digital-to-analog converter

With R = 10 kΩ and C = 1.0 µF, τ = R C = 10 ms and fc = 15.9 Hz. A 20 kHz PWM at 50 % duty (5 V supply) is 1 257 times higher than fc; the filtered output is 2.5 V with a peak-to-peak ripple of about V × D (1 − D) ÷ (fPWM R C) = 5 × 0.5 × 0.5 ÷ (20 000 × 0.01) = 6.3 mV. The output takes about 5 τ = 50 ms to settle after a change of duty. A second stage reduces ripple more; a buffer amplifier stops the load from changing the divider.

Timing in software without blocking

delay() stops the program, so nothing else runs while it waits. Instead of waiting, compare a running millisecond counter against the time of the last action. Unsigned subtraction handles the counter roll-over automatically: millis() − last ≥ interval stays correct when the 32-bit counter wraps, which happens after 232 ms = 49.7 days.

const unsigned long BLINK_MS = 500, READ_MS = 100;
unsigned long lastBlink = 0, lastRead = 0;

void loop() {
  unsigned long now = millis();
  if (now - lastBlink >= BLINK_MS) { lastBlink += BLINK_MS; toggle_led(); }  // += keeps long-term accuracy
  if (now - lastRead  >= READ_MS)  { lastRead  += READ_MS;  read_sensor(); }
}
  • Add the interval, do not reset to now: last += interval keeps the average period exact even when a pass is late; last = now accumulates the lateness.
  • Time stamps: micros() overflows every 71.6 minutes (232 µs): use it only for short intervals.
  • Short delays of a few microseconds (waiting for a sensor, a bit-banged protocol) are fine as busy waits in a driver function; keep them under about 100 µs.
  • Jitter: a loop-based schedule runs a task late when the loop is busy. If timing must be exact, use a timer interrupt or a hardware timer output, or an RTOS with priorities (see Firmware Architecture, State Machines and RTOS).

Clock accuracy and drift

Clock sourceTypical accuracyError per dayNotes
Internal RC oscillator±1 to ±5 % (factory trimmed ±1 %)14 to 72 minutesDrifts with temperature and voltage; fine for timing loops, not for UART at high speed or time-keeping
Ceramic resonator±0.5 %7 minutesCheap and good enough for UART
Quartz crystal (20 to 50 ppm)about 0.002 to 0.005 %4.3 s at 50 ppmThe normal choice; temperature affects it
32.768 kHz watch crystal (RTC)10 to 20 ppm at 25 °C1 to 2 sLow power, used for a real-time clock; the error is largest at temperature extremes
TCXO / GPS-disciplined0.5 to 2 ppm, or betterunder 0.2 sTime and frequency standards

A 50 ppm clock is off by 50 × 10-6 × 86 400 s = 4.3 s per day, about 26.3 minutes a year. A device that must keep the right time over months needs a time source (GPS, network time) to correct it, or a better oscillator.

Related

FAQ

Why is my PWM at a different frequency than I expected?

The library may use a different timer mode or prescaler than you assumed (Arduino sets different defaults on different pins). Read the timer registers, check the library source and measure with an oscilloscope or logic analyzer.

What is the difference between millis() and a timer interrupt?

millis() is itself driven by a timer interrupt (about every millisecond); it gives a time you can compare. A timer interrupt of your own runs code at exact intervals regardless of what the main loop is doing.

How do I get more PWM channels than there are timers?

Each timer provides several compare channels at the same frequency. For more, use a dedicated PWM driver chip over I²C (such as a PCA9685) or a bit-banged software PWM at a low frequency.

Why does my servo jitter?

Noise on the signal, an unstable supply, or pulses generated by software with variable timing. Use hardware timer pulses, a separate servo supply with the grounds joined, and a decoupling capacitor.

References

  • Microcontroller datasheets (timer/counter chapters; prescaler, CTC, PWM modes) for the ATmega328P, RP2040 and STM32.
  • Hobby servo and ESC manufacturers’ data (pulse widths and rates); Horowitz and Hill, The Art of Electronics (RC filters).
  • White, E., Making Embedded Systems, O’Reilly (timers and time-keeping).

Last reviewed: October 2026. Register names and counts are for the ATmega328P at 16 MHz: check your own chip’s datasheet.