Skip to main content
Visitor II
May 6, 2025
Question

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

  • May 6, 2025
  • 2 replies
  • 585 views

 

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

    Super User
    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.

    Graduate II
    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).