Need more speed when changing PWM duty cycle.

Post here first, or if you can't find a relevant section!
Post Reply
Eldi4
Posts: 22
Joined: Fri Dec 20, 2019 8:10 am
Answers: 1
Location: Kediri, Indonesia

Need more speed when changing PWM duty cycle.

Post by Eldi4 »

Hello there,
Is there any way to sped up the speed of HardwareTimer::setCaptureCompare()?. I need it to be pretty fast, to be used on 100kHz timer interrupt, because i use it for my PWM-DAC on my digital power supply. I have tested the speed of setCaptureCompare on my BluePill and it was around 12us~15us and sometime it jump to whopping 120us, which is a big problem, because i call that function on a 100kHz timer interrupt. And because it can't accomodate the speed of my 100kHz timer, the chip actually goes freeze, and once i reduce it to like 40kHz the cpu run buttt realllly sloooowww, if i run it on 10kHz the cpu seems to working fine, but it was out of my target frequency which is 100kHz, or probably faster because it was dual channel.
I use BluePill board and STM32Core.

Here is my code:

Code: Select all


void Update_IT_callback(void){
  int ch1AV = ch1Voltage.getAverage();
  int ch1AA = ch1Current.getAverage();
  int ch2AV = ch2Voltage.getAverage();
  int ch2AA = ch2Current.getAverage();
  setPwmValue(1,bufferDutyCh1);
  bufferDutyCh1 = pwmDuty1;
  if (raw_ch1Current > pre_ch1PresetA + pre_ch1PresetA / 2) {
    bufferDutyCh1 = bufferDutyCh1/2;
  }
  else if (ch1AA > pre_ch1PresetA) {
    bufferDutyCh1 = bufferDutyCh1-1;
  }
  else if (ch1AV > pre_ch1PresetV) {
    bufferDutyCh1 = bufferDutyCh1-1;
  }
  else if (ch1AV < pre_ch1PresetV){ 
    bufferDutyCh1 = bufferDutyCh1+1;
  }
}

void pwmSetup(){
  pwmTimer->setMode(CH1_TC, TIMER_OUTPUT_COMPARE_PWM1, PB8);
  pwmTimer->setMode(CH2_TC, TIMER_OUTPUT_COMPARE_PWM1, PB7);
  pwmTimer->setMode(DL_TC, TIMER_OUTPUT_COMPARE_PWM1, PB9);
  pwmTimer->setOverflow(10, MICROSEC_FORMAT);
  pwmTimer->setCaptureCompare(CH1_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->setCaptureCompare(CH2_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->setCaptureCompare(DL_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->resume();
  
  cces->setOverflow(100, MICROSEC_FORMAT);
  cces->attachInterrupt(Update_IT_callback);
  cces->resume();
}

unsigned long mci1;
unsigned long mci2;
void setPwmValue(int channel,int val){
  mci1 = micros();
  val = constrain(val,0,8191);
  if(channel == 1){
    pwmDuty1 = val;
    val = map(val, 0, 8191, 0, 65535);
    pwmTimer->setCaptureCompare(CH1_TC,val,RESOLUTION_16B_COMPARE_FORMAT); // this guy takes 12~15us, and sometime 113us
  }else if(channel == 2){
    pwmDuty2 = val;
    val = map(val, 0, 8191, 0, 65535);
    pwmTimer->setCaptureCompare(CH2_TC,val,RESOLUTION_16B_COMPARE_FORMAT);
  }
  mci2 = micros()-mci1;
}
by Bakisha » Wed Jul 22, 2020 6:41 am
Eldi4 wrote: Wed Jul 22, 2020 1:49 am

Looking to some STM32 Reference Manual about timer after looking your comment. I figured out that my timer ARR is 720, so because of how stm32 timer works, actually the duty cycle only had 720 resolution, is that correct?
And it seems to can only be 720 if my pwm was 100kHz.. argh it was a bad news, probably i'll lower my pwm frequency to 10kHz to get 7200, so it actually had more resolution than the ADC. I'm fine with the trade off, my LPF still had some 0.02Vpp ripple at 10kHz which probably is fine, i'm not powering any RF equipment.
Yes, for 10uS interrupt, overflow is 720 ticks. But that is just interrupt. For pin pwm you can set other timer/values. For example, you can set overflow of pwm pin of 100 ticks. Nice round number, no need to calculate percentage, and you have 1% resolution of pulse that is 1.388uS long (720KHz). If that's enough for you aplication and harware. You are just changing it at 10uS timebase.

I once measured setCaptureCompare, and is around 0.7uS.

Also, you can read interrupts timer to get value of passed microseconds. Just use

Code: Select all

mci1=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
mci2=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
I think getCount is also around 0.7uS, but you'll read it twice, so you'll should get exact value
Go to full post
User avatar
Bakisha
Posts: 140
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Need more speed when changing PWM duty cycle.

Post by Bakisha »

You can use:

Code: Select all

TIM4->CCR1 =  value; //  faster version of PWM->setCaptureCompare(1, value, TICK_COMPARE_FORMAT);
My advice, use tick format, not percentage. It adds overhead. It's not that hard to calculate 72 ticks in 1 uS.
Also, i think you have delays due to map function.

Interrupt latency is around 1.7uS to enter, and around same to exit. 10uS is doable, but need to be fast in execution.
Eldi4
Posts: 22
Joined: Fri Dec 20, 2019 8:10 am
Answers: 1
Location: Kediri, Indonesia

Re: Need more speed when changing PWM duty cycle.

Post by Eldi4 »

I just realized that

Code: Select all

mci1 = micros();
mci2 = micros() - mci1;
Itself takes around 12us to execute, DOH!. so the timer compare actually is fast?, so i guess the overhead problem was actually not on the setCaptureCompare, probably it was the micros() itself, or my getAverage().
So i changed my code to :

Code: Select all

void Update_IT_callback(void){  
  /*
  int ch1AV = ch1Voltage.getAverage();
  int ch1AA = ch1Current.getAverage();
  int ch2AV = ch2Voltage.getAverage();
  int ch2AA = ch2Current.getAverage();*/
  
  setPwmValue(1,bufferDutyCh1);
  bufferDutyCh1 = pwmDuty1;
  if (raw_ch1Current > pre_ch1PresetA + pre_ch1PresetA / 2) {
    bufferDutyCh1 = bufferDutyCh1/2;
  }
  else if (raw_ch1Current > pre_ch1PresetA) {
    bufferDutyCh1 = bufferDutyCh1-1;
  }
  else if (raw_ch1Voltage > pre_ch1PresetV) {
    bufferDutyCh1 = bufferDutyCh1-1;
  }
  else if (raw_ch1Voltage < pre_ch1PresetV){ 
    bufferDutyCh1 = bufferDutyCh1+1;
  }
  if(bufferDutyCh1 <= 0 ) bufferDutyCh1 = 0;
  if(bufferDutyCh1 >= 8191 ) bufferDutyCh1 = 8191;
}

void pwmSetup(){
  pwmTimer->setMode(CH1_TC, TIMER_OUTPUT_COMPARE_PWM1, PB8);
  pwmTimer->setMode(CH2_TC, TIMER_OUTPUT_COMPARE_PWM1, PB7);
  pwmTimer->setMode(DL_TC, TIMER_OUTPUT_COMPARE_PWM1, PB9);
  pwmTimer->setOverflow(10,MICROSEC_FORMAT);
  pwmTimer->setCaptureCompare(CH1_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->setCaptureCompare(CH2_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->setCaptureCompare(DL_TC, 0, PERCENT_COMPARE_FORMAT);
  pwmTimer->resume();
  
  cces->setOverflow(10,MICROSEC_FORMAT);
  cces->attachInterrupt(Update_IT_callback);
  cces->resume();
}

void setPwmValue(int channel,int val){
  if(channel == 1){
    pwmDuty1 = val;
    pwmTimer->setCaptureCompare(CH1_TC,val,RESOLUTION_13B_COMPARE_FORMAT);
  }else if(channel == 2){
    pwmDuty2 = val;
    pwmTimer->setCaptureCompare(CH2_TC,val,RESOLUTION_13B_COMPARE_FORMAT);
  }
}
And sure enough, setCaptureCompare on 10uS interrupt is fine!.

Looking to some STM32 Reference Manual about timer after looking your comment. I figured out that my timer ARR is 720, so because of how stm32 timer works, actually the duty cycle only had 720 resolution, is that correct?
And it seems to can only be 720 if my pwm was 100kHz.. argh it was a bad news, probably i'll lower my pwm frequency to 10kHz to get 7200, so it actually had more resolution than the ADC. I'm fine with the trade off, my LPF still had some 0.02Vpp ripple at 10kHz which probably is fine, i'm not powering any RF equipment.
User avatar
Bakisha
Posts: 140
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Need more speed when changing PWM duty cycle.

Post by Bakisha »

Eldi4 wrote: Wed Jul 22, 2020 1:49 am

Looking to some STM32 Reference Manual about timer after looking your comment. I figured out that my timer ARR is 720, so because of how stm32 timer works, actually the duty cycle only had 720 resolution, is that correct?
And it seems to can only be 720 if my pwm was 100kHz.. argh it was a bad news, probably i'll lower my pwm frequency to 10kHz to get 7200, so it actually had more resolution than the ADC. I'm fine with the trade off, my LPF still had some 0.02Vpp ripple at 10kHz which probably is fine, i'm not powering any RF equipment.
Yes, for 10uS interrupt, overflow is 720 ticks. But that is just interrupt. For pin pwm you can set other timer/values. For example, you can set overflow of pwm pin of 100 ticks. Nice round number, no need to calculate percentage, and you have 1% resolution of pulse that is 1.388uS long (720KHz). If that's enough for you aplication and harware. You are just changing it at 10uS timebase.

I once measured setCaptureCompare, and is around 0.7uS.

Also, you can read interrupts timer to get value of passed microseconds. Just use

Code: Select all

mci1=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
mci2=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
I think getCount is also around 0.7uS, but you'll read it twice, so you'll should get exact value
Eldi4
Posts: 22
Joined: Fri Dec 20, 2019 8:10 am
Answers: 1
Location: Kediri, Indonesia

Re: Need more speed when changing PWM duty cycle.

Post by Eldi4 »

Bakisha wrote: Wed Jul 22, 2020 6:41 am Yes, for 10uS interrupt, overflow is 720 ticks. But that is just interrupt. For pin pwm you can set other timer/values. For example, you can set overflow of pwm pin of 100 ticks. Nice round number, no need to calculate percentage, and you have 1% resolution of pulse that is 1.388uS long (720KHz). If that's enough for you aplication and harware. You are just changing it at 10uS timebase.
I decided to get it down to 10kHz, to get 7200 ticks so that my DAC PWM can have more space to move or 0 to 7200 duty cycle, rather than 0 to 720 duty cycle with 100kHz frequency, i don't think 720 is enough. But it's just a guesswork, i can't really state which is the better one because i don't have any oscilloscope there.... It's pretty painful not to have an oscilloscope lol, might have a lot of problem solved if i have some.
Bakisha wrote: Wed Jul 22, 2020 6:41 am Also, you can read interrupts timer to get value of passed microseconds. Just use

Code: Select all

mci1=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
mci2=irq_timer->getCount(irq_channel,  TICK_FORMAT)/72;
I think getCount is also around 0.7uS, but you'll read it twice, so you'll should get exact value
Ah, i see, a lot faster than micros(). I will definitely going to need it in the future, Thanks a lot!
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Need more speed when changing PWM duty cycle.

Post by dannyf »

I need it to be pretty fast, to be used on 100kHz timer interrupt,
I think you will find out that slow speed here is your friend, as a fast loop is much easier to oscillate.

You should step back and think about the overall architecture of your design. you have a lot of stuff in the ISR - is that really necessary?

with that said, if you really want to use fast pwm for power supply, check out freescale's products (now NXP's). those guys are known for that.
Post Reply

Return to “General discussion”