When configuring hardware timers on microcontrollers like the ATmega328P, ESP32, or STM32, you cannot pass floating-point numbers to the configuration registers. Hardware registers demand strict integers. To find the exact configuration for a target frequency, you need an integer calculator with steps to derive the Prescaler ($N$) and Output Compare Register ($OCR$) values without triggering overflow or truncation bugs.

The master formula governing almost all basic microcontroller hardware timers is:

$f_{target} = \frac{f_{clk}}{N \times (OCR + 1)}$

If you are building a standard 50Hz servo controller on a 16MHz Arduino Uno using the 16-bit Timer1, the concrete default pick is Prescaler $N = 8$ and $OCR = 39999$. Below is the complete derivation, unit tracking, and decision framework to calculate this for any clock speed and target frequency.

The Core Timer Frequency Formula & Symbol Definitions

This formula applies to standard up-counting or phase-correct hardware timers where the counter resets to zero upon matching the compare register. It assumes a discrete clock tick architecture and 0-indexed counting (which is why the $+1$ is mathematically mandatory).

Symbol Parameter Unit Hardware Constraint
$f_{target}$ Target Interrupt or PWM Frequency Hertz (Hz) Must be $> 0$
$f_{clk}$ Source Clock Frequency Hertz (Hz) Typically 16MHz, 80MHz, or 72MHz
$N$ Prescaler Divisor Unitless Restricted to hardware steps (e.g., 1, 8, 64, 256, 1024)
$OCR$ Output Compare Register Value Unitless (Ticks) Must be $\le$ Max Register Width (e.g., 255 for 8-bit, 65535 for 16-bit)
⚠️ Critical Safety & Stability Caveat: If your integer math results in an interrupt frequency exceeding 100kHz on an ESP32 or AVR, the CPU will spend 100% of its cycles servicing the interrupt (interrupt thrashing). This will starve the Wi-Fi stack or RTOS watchdog, resulting in a brownout reset or a bricked boot loop. Always verify your $f_{target}$ is realistic for your CPU overhead.

Rearranged Forms for Embedded Integer Math

In firmware, you rarely solve for $f_{target}$; you already know the frequency you want. You need to solve for the register values. Because C/C++ integer division truncates decimals silently, we use the floor function $\lfloor x \rfloor$ to represent the hardware reality.

  • Solve for OCR (Most Common):
    $OCR = \lfloor \frac{f_{clk}}{N \times f_{target}} \rfloor - 1$
  • Solve for Prescaler N (Validation):
    $N = \frac{f_{clk}}{f_{target} \times (OCR + 1)}$
  • Solve for Source Clock (Diagnostics):
    $f_{clk} = f_{target} \times N \times (OCR + 1)$
💡 Pro-Tip: The Integer Truncation Trap
Never write OCR = (f_clk / N / f_target) - 1; in C without ensuring the intermediate division doesn't truncate prematurely. Always group the denominator: OCR = (f_clk / (N * f_target)) - 1; and use 32-bit unsigned integers (uint32_t) to prevent overflow during the multiplication of $N \times f_{target}$.

Worked Problem 1: ATmega328P (Arduino Uno) 50Hz Servo PWM

Scenario: You need a precise 50Hz PWM signal to drive a standard SG90 servo using Timer1 (16-bit).
Knowns: $f_{clk} = 16,000,000 \text{ Hz}$, $f_{target} = 50 \text{ Hz}$, Max $OCR = 65535$.
Available Prescalers ($N$): 1, 8, 64, 256, 1024.

Step 1: Test Prescaler $N = 1$

  • $OCR = \lfloor \frac{16,000,000 \text{ Hz}}{1 \times 50 \text{ Hz}} \rfloor - 1$
  • $OCR = \lfloor 320,000 \rfloor - 1 = 319,999$
  • Check constraint: $319,999 > 65,535$. Overflow. Reject $N=1$.

Step 2: Test Prescaler $N = 8$

  • $OCR = \lfloor \frac{16,000,000 \text{ Hz}}{8 \times 50 \text{ Hz}} \rfloor - 1$
  • $OCR = \lfloor \frac{16,000,000}{400} \rfloor - 1$
  • $OCR = 40,000 - 1 = 39,999$
  • Check constraint: $39,999 \le 65,535$. Fits perfectly.

Final Concrete Pick: Set TCCR1B prescaler bits to 8, and set OCR1A to 39999. For more on AVR timer registers, refer to the Microchip ATmega328P datasheet.

Worked Problem 2: STM32F103 10kHz Motor Control

Scenario: You are driving a DC motor via an H-bridge and need a 10kHz PWM to stay above the audible whine range using a 16-bit timer.
Knowns: $f_{clk} = 72,000,000 \text{ Hz}$ (APB1 Timer Clock), $f_{target} = 10,000 \text{ Hz}$, Max $OCR = 65535$.
Available Prescalers ($N$): Any integer from 1 to 65536 (STM32 uses a programmable prescaler register, not fixed steps like AVR).

Step 1: Calculate Ideal Total Division Factor

  • $Total\_Div = \frac{f_{clk}}{f_{target}} = \frac{72,000,000 \text{ Hz}}{10,000 \text{ Hz}} = 7,200$

Step 2: Factorize for Prescaler and OCR

We need $N \times (OCR + 1) = 7,200$. To maximize PWM duty-cycle resolution, we want $OCR$ to be as large as possible without exceeding 65535. Let's set $N$ to a small integer, like 1.

  • Let $N = 1$.
  • $OCR + 1 = \frac{7,200}{1} = 7,200$
  • $OCR = 7,199$
  • Check constraint: $7,199 \le 65,535$. Valid.

Final Concrete Pick: Set the Prescaler Register (PSC) to 0 (which equals $N=1$ in STM32 hardware, as it adds 1 internally) and the Auto-Reload Register (ARR) to 7199. See the STMicroelectronics STM32F103 reference for exact register mapping.

Decision Tree: Picking Your Prescaler and Register Value

Use this decision path to terminate your math and pick a concrete value. The most common unit mistakes that break this math are passing $f_{clk}$ in MHz (e.g., 16 instead of 16,000,000) and forgetting that hardware counts from $0$ to $OCR$, making the divisor $(OCR + 1)$, not just $OCR$.

Target Frequency ($f_{target}$) Recommended Prescaler ($N$) Expected OCR Magnitude (16MHz Clock) Concrete Action / Default Pick
$> 100 \text{ kHz}$ $N = 1$ $< 160$ Pick $N=1$. Calculate OCR directly. Warning: Low duty-cycle resolution.
$1 \text{ kHz} - 100 \text{ kHz}$ $N = 1 \text{ or } 8$ $160 - 16,000$ Pick $N=8$. Excellent balance of resolution and frequency for audio/motors.
$50 \text{ Hz} - 1 \text{ kHz}$ $N = 64 \text{ or } 256$ $1,000 - 65,000$ Pick $N=64$. Standard for servos (50Hz) and slow relays.
$< 50 \text{ Hz}$ $N = 1024$ $> 15,000$ Pick $N=1024$. Required for blink-without-delay style low-freq interrupts.

What a Realistic Answer Magnitude Looks Like

If your integer calculator outputs an $OCR$ value of 4,294,967,295 (which is $2^{32}-1$), you have experienced a 32-bit unsigned integer underflow/overflow. This happens when $N \times f_{target}$ evaluates to zero due to integer truncation before the division occurs, or when you divide by zero.

A realistic $OCR$ magnitude for a 16-bit timer must always fall between 1 and 65,535. If your calculated $OCR$ is 0, your target frequency is too high for the selected prescaler; drop to a lower $N$. If your $OCR$ is $> 65,535$, your target frequency is too low; step up to the next available $N$ in the hardware prescaler list.

🛑 Final Rule on Register Overflow: Never blindly cast a 32-bit calculation into a 16-bit register variable without a bounds check. If $OCR > 65535$, the microcontroller will truncate the upper 16 bits, resulting in a wildly incorrect frequency (e.g., 65536 becomes 0, causing the timer to fire on every single clock tick and instantly crashing your firmware). Always cap and assert your integer results before writing to the hardware register.