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) |
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)$
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.






