cancel
Showing results for 
Search instead for 
Did you mean: 

Increase PWM Resolution

RWazlawick
Associate II

Hi!

Recently, I've been trying to develop a PWM generation module, in a frequency range of 1Hz to 100kHz with a duty cycle step of 1%. I'm using STM32F103C8 Bluepill to do the job.

I can implement this just fine. However, in this range I obtain errors of 6kHz at the top (higher frequencies) and I wondered if I could improve my sofwate in some way. My initial configuration was using TIM1 (up to 72MHz), a period of 100 to generate de 1% step. The prescaler varies according to the selected frequency.

So, I made some research and found the Dithering Technique, but reading the application note of ST, I could not understand very well how it works. I even tested the given example and worked just fine. In some moment I've found someone in this very community who also had doubts about dithering and a certain held my attention

"Imagine the PWM has 100 step (100%) with granularity of 1%.

If over 4 pulses period you generate say 45%, 45%,45%,45% 4 pulses, you get 45%

If over 4 pulses period you generate say 45%, 45%, 45%, 46% 4 pulses, you get 45.25%"

So, what I did next:

  • Configured a second timer (TIM4) with a frequency x10 my first frequency adjusted in TIM1;
  • Enabled the TIM4 global interrupt;
  • Adjusted my duty cycle step to 5% in TIM1 (Period = 20) so I could improve frequency resolution;
  • From a given duty cycle (for instance 42%), came up with a simple algorithm using 10 values with 5% step to achieve the 42%. Example: (45% + 45% + 45% +45% + 40% + 40% + 40% + 40% + 40% + 40% = 420% / 10 = 42%;

Thus, for 10 interrupts of TIM4 I would have 1 period in TIM1. For each interrupt in TIM4, I adjust one of the ten values calculated, I expected the amount would be the 42% and my wave form would have the 42% of duty cycle. However, what it is actually happening is that the last vector from the ten values is adjusted as dutycycle.

The interrupts are occuring correctly and timers seem to be synchronized; Below it is the interupt from TIM4 part.

 if(htim->Instance == TIM4)

 {

if(htim1.Instance->CNT == 0)

{

flag  = 1;

}

if (flag == 1)

{

     HAL_GPIO_TogglePin(SAIDA_*****_GPIO_Port, TEST_O);

   uint32_t dc = dc_calc[period_TIM1]*(htim1.Instance->ARR+1)/100;

/* Converts dutycyle (0 - 100 )to a (0 - 20) step*/

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dc ); /*update*/

   if(period_TIM1 >= 9)

 {

period_TIM1 = 0;

flag    = 0;

 }

 else{

  period_TIM1++;

 }

  }

 }

}

Is it my approach wrong? Is there a better way to do it?

Maybe someone could clarify dithering in STM32 implementation for me?

Thanks in advance.

12 REPLIES 12

TDK,

In average yes, I would obtain the 42%, However I'd like to see from my adjustmen with 45 45 45 45 40 40 40 40, this 42% when I measure with the oscilloscoppe.

I have an IHM where I can adjust my duty cycle and the frequency is informed. When I set for duty cycle 42% and the frequency is 100kHz, for example, this is the waveform I expect to see when I measure. If I just put all this duty cycles, what I'll be seeing is my waveform changing duty cycles in 100kHz, correct? Or maybe I did not understand what you meant.

> I want to develop a PWM module, with frequencies of 1Hz - 100kHz (step of 1Hz) and duty cycles of 1% step.

Well, you can't, not with a fixed source frequency (here, 72MHz system frequency of the mcu) and dividers (that's what the timer is). You can achieve only integer subdivision of the principal frequency, i.e. 72MHz/N; and the the duty resolution is given by N. So, if you'd insist on 1% duty step, you'd need N=K*100, and you'll be constrained to 72kHz, 36kHz etc.

Dithering does not actually change the duty cycle. It is just a method how to reduce the impact of limited duty cycle resolution for the typical application of PWM, which is replacement for DAC. It means, only *after analog filtering* the resulting DC voltage is, with dithered PWM, as if the duty step was smaller than it naturally is.

While you most probably won't be able to achieve exactly what you described above, there's one more degree of freedom - you can change the primary frequency (using the two cascaded PLLs, again resulting in quite severe constraints as there are only integer dividers and "multipliers" available (okay there's the 6.5x setting in PLL1, but that's just 13/5 anyway). This, provided you don't need a fixed primary frequency for some other reasons - UART, USB, whatever depending on timing.

JW

PS Yes that's the same as mine, I have the Polish variant

dbeta.1
Associate

for more pwm resolution you must to use HSE oscillator in cpuclock settings