PWM Timer / Prescaler Calculator

Calculate PSC and ARR register values for a target PWM frequency on STM32, nRF52, RP2040, and ESP32. Shows duty cycle resolution and register code snippet.

Parameters

Result

PSC

2

÷3

ARR

55999

56000 counts

Actual freq

1 kHz

< 0.01% error

Resolution

15.8 bits

56,000 steps

Min duty step

1.786 m%

CCR = 28000 / 56000 (50% duty)

Register code

TIM3->PSC  = 2;   // prescaler (÷3)
TIM3->ARR  = 55999;  // period (56000 counts)
TIM3->CCR1 = 28000;  // 50% duty cycle

Common PWM applications

ApplicationTarget freqNote
RC Servo50 Hz1–2 ms pulse, 20 ms period
DC Motor (PWM)1–20 kHzAbove audible range: > 20 kHz
BLDC / FOC20 kHzMatches current control loop
LED dimming1 kHzAvoid 50/100 Hz (flicker visible)
Audio DAC44.1 kHzHigh-res needed (16+ bits)

How PWM timers work

A PWM peripheral counts from 0 to ARR (Auto-Reload Register), then resets. The output pin is high while the counter is below CCR (Capture/Compare Register), giving you duty cycle control. The core formula on STM32:

f_PWM = f_TIM / ((PSC + 1) × (ARR + 1))

PSC divides the timer clock before the counter sees it. ARR sets the period. CCR sets the duty cycle threshold. That’s the whole model.

The APB×2 timer clock trap

The most common source of miscalculation: STM32 timers run at a different clock than PCLK.

On STM32F4 at 168 MHz, the default CubeMX clock tree sets APB1 = 42 MHz (HCLK ÷ 4). But timers on APB1 run at 84 MHz, not 42 MHz — the hardware automatically doubles the peripheral clock when the APB prescaler is not 1. This is documented in the RCC chapter of the reference manual under “Timer clock frequencies.”

The rule: if APB prescaler = 1, timer clock = APBCLK. If APB prescaler > 1, timer clock = 2 × APBCLK.

Verify in firmware before trusting your calculations:

uint32_t pclk1 = HAL_RCC_GetPCLK1Freq();
// On STM32F4 @ 168 MHz: pclk1 = 42000000
// Timer clock = 2 × 42000000 = 84000000 — NOT 168 MHz

This calculator uses the actual timer clock, not PCLK. On STM32F4, TIM3/4 = 84 MHz and TIM2/5 = 84 MHz. Enter the correct value in the override field if your clock tree differs.

ARR sets both period and resolution

Maximize ARR to maximize duty cycle resolution. Every step in CCR represents 1 / (ARR + 1) of the full duty range. With ARR = 999: 1000 steps, 0.1% minimum duty. With ARR = 65535: 65536 steps, 0.0015% minimum duty.

The algorithm here finds the PSC value that keeps ARR as high as possible for the target frequency. For 1 kHz on STM32F4 at 84 MHz:

PSC = 0: ARR = 84,000,000 / (1 × 1,000) − 1 = 83,999  → fits in 16-bit (65535 max)? No.
PSC = 1: ARR = 84,000,000 / (2 × 1,000) − 1 = 41,999  → fits. ~15.4 bits.
PSC = 2: ARR = 84,000,000 / (3 × 1,000) − 1 = 27,999  → fits. ~14.8 bits. Worse.

PSC = 1, ARR = 41999 wins because it gives the most resolution while staying within the 16-bit register limit.

For TIM2 (32-bit ARR), PSC = 0 and ARR = 83999 is achievable — 16+ bits of resolution at 1 kHz.

nRF52 COUNTERTOP register

nRF52 PWM is simpler: no separate PSC register in the classic sense. Instead, PRESCALER[2:0] selects a power-of-two clock divider (1, 2, 4, 8, 16, 32, 64, or 128), and COUNTERTOP directly sets the number of counts in one period.

f_PWM = 16_000_000 / (prescaler_divider × COUNTERTOP)
resolution_bits = log2(COUNTERTOP)

COUNTERTOP must be 3 to 32767. It sets both period and maximum duty cycle count in one register — the duty value in your sequence buffer must be ≤ COUNTERTOP. A common mistake is setting duty to a value larger than COUNTERTOP, which clamps the output to 100% permanently.

To hit 1 kHz with maximum resolution: PRESCALER = 0 (÷1), COUNTERTOP = 16000 → exactly 1 kHz with log2(16000) ≈ 13.97 bits of resolution.

RP2040 PWM fractional divider

RP2040 PWM has an integer + 4-bit fractional clock divider, plus a WRAP register (equivalent to ARR). The fractional divider allows non-integer division but introduces frequency dither (jitter at a rate equal to f_PWM × fractional_error). For motor control and audio applications, use integer division only.

f_PWM = f_SYS / (CLK_DIV_INT × (WRAP + 1))

This calculator uses integer CLK_DIV for predictable, jitter-free output. With 125 MHz system clock and CLK_DIV = 125, WRAP = 999: exactly 1 kHz.

ESP32 LEDC duty resolution

ESP32 LEDC maps differently: you choose the duty resolution in bits (1–20), and the hardware derives the divider. Higher bit count = more PWM steps but limits maximum frequency:

f_PWM = APB_CLK / (2^duty_bits × divider)

At 13-bit resolution (8192 steps) with 80 MHz APB: maximum clean frequency ≈ 80 MHz / 8192 ≈ 9.77 kHz per integer divider step. For 20 kHz, drop to 11-bit (2048 steps). For LED dimming at 1 kHz, 13-bit gives excellent smoothness.

Common mistakes

Setting CCR instead of ARR for period control. ARR sets when the counter resets. CCR sets the compare threshold. Writing to ARR changes your PWM period. Writing to CCR changes duty cycle. Getting these backwards produces a signal with the wrong frequency that appears correct on a scope if you only check duty cycle visually.

Forgetting TIM_CR1_ARPE for glitch-free duty updates. Without the Auto-Reload Preload Enable bit, writing a new ARR value takes effect immediately, mid-period. The counter may overshoot the new ARR and count for a very long (or very short) period until it wraps. Set TIM_CR1_ARPE to buffer ARR changes until the next update event:

TIMx->CR1 |= TIM_CR1_ARPE;

The same applies to CCR registers: enable OCxPE (output compare preload) in TIM_CCMRx to prevent glitchy duty updates:

TIM3->CCMR1 |= TIM_CCMR1_OC1PE;

PSC too high, resolution lost. Setting PSC = 999 and ARR = 83 for 1 kHz on a 84 MHz clock gives only 84 steps of duty cycle resolution — barely 6 bits. The rule is lowest PSC, highest ARR. The calculator enforces this automatically.

Forgetting to call HAL_TIM_PWM_Start() after configuring. The timer runs after HAL_TIM_Base_Start(), but the PWM output is not driven until the compare channel is started separately. On bare metal, ensure TIM_CCER_CC1E is set in TIM_CCER for channel 1, or equivalent for other channels.

nRF52: COUNTERTOP not updated before enabling the PWM peripheral. Write COUNTERTOP before calling nrfx_pwm_simple_playback(). Changing COUNTERTOP while PWM is running requires stopping and restarting the peripheral.

Frequently asked questions

Why does my STM32 timer run at double the APB clock? +

STM32 timers connected to APB1 or APB2 run at 2× the APB clock when the APB prescaler is not 1. For example, on STM32F4 with HCLK=168 MHz, APB1=42 MHz (÷4 prescaler), TIM2/3/4/5/6/7 actually run at 84 MHz (×2). Check the RCC section of your reference manual — the timer clock multiplier is often misunderstood. Use STM32CubeMX to verify the actual timer clock.

What PSC and ARR should I use for a 50 Hz RC servo PWM on STM32F4? +

For TIM3 at 84 MHz: PSC = 83 (÷84, giving 1 MHz TQ clock), ARR = 19999 (20 ms period). This gives exactly 50 Hz with 20000 steps of resolution — plenty for 1–2 ms pulse width servo control (1000–2000 steps out of 20000). Set CCR between 1000 (1 ms = 0°) and 2000 (2 ms = 180°).

How do I maximize duty cycle resolution at a given frequency? +

Maximize ARR. A higher ARR means more steps between 0% and 100% duty cycle. The algorithm here finds the PSC that keeps ARR as large as possible. For 20 kHz on STM32F4 at 84 MHz: ARR = 4199 with PSC = 0 gives ~12-bit equivalent (4200 steps). If you use PSC = 1, ARR = 2099 — only half the resolution. Always use the lowest PSC that keeps ARR within register range.

Why does my nRF52 PWM frequency drift? +

nRF52 PWM uses the 16 MHz HFCLK as source, which is the crystal oscillator when running — very accurate. If HFCLK is not started (e.g. in low-power mode relying on the RC oscillator HFRC), frequency accuracy drops to ±2%. Call nrfx_clock_hfclk_start() or ensure Zephyr's CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL is set if you need precise PWM.

Newsletter

The embedded engineer's weekly cheat sheet

Register tricks, timing gotchas, and tool updates. One email per week. No fluff.

No spam. Unsubscribe anytime.