4
\$\begingroup\$

I'm trying to write a function that takes a desired frequency of PWM and sets the timer based on this value:

void initTimer(unsigned int freq ){

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    TIM_TimeBaseInitTypeDef timerInitStructure;
    timerInitStructure.TIM_Prescaler = 1000;
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    timerInitStructure.TIM_Period = 1000;
    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    timerInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM3, &timerInitStructure);
    TIM_Cmd(TIM3, ENABLE);

    TIM_OCInitTypeDef outputChannelInit;
    outputChannelInit.TIM_OCMode = TIM_OCMode_PWM1;
    outputChannelInit.TIM_Pulse = 500;
    outputChannelInit.TIM_OutputState = TIM_OutputState_Enable;
    outputChannelInit.TIM_OCPolarity = TIM_OCPolarity_High;
    // the first channel
    TIM_OC1Init(TIM3, &outputChannelInit);
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);

}

The code works fine and generates the expected PWM. My Problem is that I don't know how to calculate the value of the prescaler based on the given frequency. In the example above the values are fixed.

Any idea how may I do this? Thanks in advance!

\$\endgroup\$
1
  • \$\begingroup\$ Read the datasheet/programming reference guide for your chip. Specifically the section about PWM. It has it all. If you can't find it, look for some application notes (yes, I know, STMs docs are somewhat difficult to navigate..). \$\endgroup\$
    – Eugene Sh.
    Commented Jun 23, 2015 at 14:18

2 Answers 2

11
\$\begingroup\$

In short, you want to satisfy the equation:

Prescaler * Overflow = Period-in-Cycles = Clock-cycles-per-Second / Frequency

A good way to calculate the prescaler and overflow values for PWM is to chose the smallest possible prescaler, then trim down the overflow to the desired total frequency. This keeps the overflow value as large as possible, which then gives the most precision if you later want to modulate the duty cycle of the waveform.

For example, if the clock frequency is 72 MHz and you want a 250 Hz output square wave (50% duty), then the total output waveform period in clock cycles (period_cycles) would be 72 MHz / 250 Hz = 288000 cycles. The timer's overflow value is usually 16-bits at most (0xFFFF, or 65535), the prescaler must be such that prescaler * overflow >= period_cycles, and 288000.0 / 0xFFFF => 4.39459..., so the prescaler should be 5. Finally, 288000 / 5 => 57600, which is a nice round number for the overflow (you would want to round this to the nearest integer if it wasn't).

Here's some code implementing the above algorithm, including appropriate rounding using C integer arithmetic:

#define CLOCK_CYCLES_PER_SECOND  72000000
#define MAX_RELOAD               0xFFFF

uint32_t period_cycles = CLOCK_CYCLES_PER_SECOND / freq;
uint16_t prescaler = (uint16)(period_cycles / MAX_RELOAD + 1);
uint16_t overflow = (uint16)((period_cycles + (prescaler / 2)) / prescaler);
uint16_t duty = (uint16)(overflow / 2);

Of course, in your actual code you'll want to check for edge cases like freq == 0 or freq > CLOCK_CYCLES_PER_SECOND/2.

\$\endgroup\$
2
  • \$\begingroup\$ I always did this way from head, but it is good to have this nicely written. \$\endgroup\$
    – Darko
    Commented Apr 18, 2019 at 20:44
  • 2
    \$\begingroup\$ If you do not want 50% duty, it can be calculated like duty = overflow * percent_duty / 100. \$\endgroup\$
    – Darko
    Commented Apr 18, 2019 at 20:50
1
\$\begingroup\$

To expand on Eugene's comment ... in the datasheet the relevant sections are about clocks (there's a nice diagram how they're derived) and PWM. And there's possibly also a document describing the library that you use (FWLib or what is it?).

Your init code shows that timer/PWM clock is derived from PCLK1 (APB1), which is normally cpu clock divided by something. There are functions to get clocks (RCC_xxx), so you can get the number. After that you basically have two dividers, Prescaler and Period, so you get PCLK1/1000000.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.