Increase PWM Resolution
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.