Skip to main content
Associate
May 6, 2025
Question

How to Achieve 0.1 Hz Resolution from 0 to 10 kHz Using STM32 Timer?

 

Hi everyone,

I'm trying to generate output frequencies from 0 Hz to 10,000 Hz using STM32F407VET6 with Timer2 in Output Compare Toggle Mode. The timer clock is 84 MHz.

I need to achieve:

  • 0.1 Hz resolution up to 10 kHz

  • Ideally, also support 0.001 Hz resolution at lower frequencies

The problem is that at higher frequencies, the formula:

f_out = f_timer / (2 * ARR)
often results in a non-integer ARR. For example, to generate 9999.9 Hz, I get:

 

ARR = 84,000,000 / (2 * 9999.9) ≈ 4200.042

But since ARR must be an integer, I have to use 4200, which gives 10,000 Hz — a 0.1 Hz error.


i am using DDS (Direct Digital Synthesis) using a phase accumulator in software
With DDS, I can theoretically achieve very fine resolution, but when I measure the output using a CRO (oscilloscope), I notice jitter in the waveform.

Question:
How can I generate accurate frequencies with 0.1 Hz resolution across the full range (up to 10 kHz)?
Are there any techniques (e.g., fractional timers, clock tricks, advanced modes, combining timers) to overcome this limitation?

This topic has been closed for replies.

2 replies

TDK
May 6, 2025

No, you can't have fractional ticks here. On STM32H7, you could adjust the PLL clock fractionally to achieve this.

You could set up DMA to update the ticks so that the average frequency is as desired, but there will be jitter of up to 1 tick.

"If you feel a post has answered your question, please click ""Accept as Solution""."
unsigned_char_array
Lead III
May 6, 2025

Like TDK said if you cannot get the exact frequency you want using an integer period at your specific timer clock you can adjust your PLL and/or timer clock and/or clock source. Or live with jitter (switches between 9997.6Hz and 10000.0Hz to create 9999.9Hz on average).

9999.9Hz +- .1 Hz -> 10ppm
9999.9Hz +- .01 Hz -> 1ppm
so you would need very precise clock source to get an accuracy that is in the same ballpark as your resolution.

UPDATE:

I wrote a little python script to try some values for clock sources and PLLs

#HSE = 4-26MHZ
for hse in [10E6,16E6,25E6]:
 
 min_abs_error = 1
 best = {}
 for m in range(2, 63+1):
 m_out = hse / m 
 # M output >= 0.95MHz <= 2.1MHz 
 if m_out >=0.95E6 and m_out <= 2.1E6:
 for n in range(50, 432+1):
 n_out = m_out * n
 if n_out >= 100E6 and n_out <= 432E6:
 for p in [2,4,6,8]:
 p_out = n_out/p
 if p_out >= 24E6 and p_out <= 168E6:
 for ahb_prescaler in [1,2,4,8]: 
 if ahb_prescaler == 1:
 timer_mult = 1
 else: 
 timer_mult = 2

 ahb1 = p_out / ahb_prescaler
 if ahb1 <= 42E6: 
 timer_clock = ahb1 * timer_mult

 prescaler_desired = timer_clock / (f_desired*2)

 prescaler = round(prescaler_desired) 
 f_out = (timer_clock / prescaler)/2
 error = f_out - f_desired
 if abs(error) < min_abs_error: 
 error_ppm = 1E6 * error / f_desired 
 best = {"error": error, "error_ppm":error_ppm, "hse":hse,"m":m,"n":n,"p":p,"ahb_prescaler":ahb_prescaler, "prescaler":prescaler}
 min_abs_error = abs(error)

 print(best)

 

This is the output:

{'error': -0.006950727800358436, 'error_ppm': -0.695079730833152, 'hse': 10000000.0, 'm': 9, 'n': 374, 'p': 6, 'ahb_prescaler': 2, 'prescaler': 3463} 
{'error': -0.004601416303557926, 'error_ppm': -0.46014623181811076, 'hse': 16000000.0, 'm': 11, 'n': 239, 'p': 6, 'ahb_prescaler': 2, 'prescaler': 2897}
{'error': 0.0024399761969107203, 'error_ppm': 0.24400005969166896, 'hse': 25000000.0, 'm': 21, 'n': 164, 'p': 6, 'ahb_prescaler': 1, 'prescaler': 1627}

So if you use a 25MHz clock source you can get 9999.902Hz. The rounding error is much smaller then the frequency error of the clock source at 0.2ppm so it is neglectable (or even correctable if you can shift the frequency of the clock source).

 

"Kudo posts if you have the same problem and kudo replies if the solution works.Click ""Accept as Solution"" if a reply solved your problem. If no solution was posted please answer with your own."