CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PWM duty calculation

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PWM duty calculation
PostPosted: Wed Mar 13, 2013 4:37 am     Reply with quote

I use dspic30f6014a to generate pwm signal with variable duty cycle but I have a problem to calculate the maximum duty value. The code is:
Code:
#include <30f6014a.h>
#device adc=10
#FUSES WPSA8,WPSB1,XT_PLL8, NOCKSFSM, NOWDT, PUT4, NOBROWNOUT
#use delay(clock=58982400)  // 7.3728MHz xtal * 8.
#use rs232(baud=9600,parity=N,UART1,bits=8)
setup_compare( 1, COMPARE_PWM | COMPARE_TIMER2 );
setup_timer2( TMR_INTERNAL | TMR_DIV_BY_1, 294 ); // 50kHz PWM.

void pwm_duty_percent(int16 Duty, int16 Duty_max) {
 // this function to set duty in percent value.
set_pwm_duty( 1, (Duty_max/100)*Duty );
}


The function " pwm_duty_percent " will be called in the main code and the entered value of duty will be in percent.
The problem is how to calculate the value of Duty_max ?
By the way, I got the desired frequency but the duty was wrong.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 4:54 am     Reply with quote

I don't do the dsPICs, but I'll try to help.

On the 18F series the timer2 counts every machine cycle, which is once every 4 oscillator clock cycles.
The PWM resolution on the other hand is in clock cycles.
So with the timer2 period set to your 294, the max duty is 4*(294+1), that's 1180.

You will need to check on the data sheet for your PIC to find the frequency ratio between the oscillator clock and timer2 incrementing.
(I believe the ratio is 4 for some devices and 2 for others.)

Mike
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 5:06 am     Reply with quote

Two for dsPIC's.

Best Wishes
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 6:57 am     Reply with quote

Thank you for help
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 10:23 am     Reply with quote

Hey guys ! what's wrong ? I used the formula 2*(294+1) to find the maximum duty, but when I put duty=50% I get more than 80% duty in the oscilloscope. I randomly put 300 for maximum duty and I got abot 51% duty. I don't know what the problem is. Help please !
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 11:37 am     Reply with quote

Well, 300 is about right for 51% duty ratio.

So there must be something wrong with your conversion from 50%.

Post the shortest possible complete, compilable code which gives the 80% duty ratio.

It looks likely that you've got something in hex when you intended decimal (or the other way round).
That will give you an error of 16/10 which rather nicely converts your expected 50% to the observed 80%.

Mike
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 12:36 pm     Reply with quote

The following code is the shortest possible complete, compilable code which gives the 80% duty ratio. Some functions were removed to make it as short and clear as possible.
Code:
#include <30f6014a.h>
#device adc=10
#FUSES WPSA8,WPSB1,XT_PLL8, NOCKSFSM, NOWDT, PUT4, NOBROWNOUT
#use delay(clock=58982400)
#use rs232(baud=9600,parity=N,UART1,bits=8)

void pwm_duty_percent(int16 Duty, int16 Duty_max) {
// this function to set duty in percent value.

set_pwm_duty( 1, (Duty_max/100)*Duty );
}

void main() {   
 
int16  Dmax;
signed int16 D, dD; // D is duty cycle and dD is the stepsize (+/- 1% or 0.01).
Dmax=590; // 2*(294+1) 
D=50;  // 50% duty cycle.

 setup_compare( 1, COMPARE_PWM | COMPARE_TIMER2 );
 setup_timer2( TMR_INTERNAL | TMR_DIV_BY_1, 294 ); // 50kHz frequency.
 
  pwm_duty_percent(D,Dmax); 
  delay_ms(1000); 
 
  while (TRUE);
}
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 5:41 pm     Reply with quote

I don't know. As I said before I don't do the dsPICs.

I'd be doing some tests.

1) Examine the registers.
2) Try pre-calculating the duty, rather than have a formula in the set_pwm_duty(x,y) function.
3) Force the multiply before divide. As it stands, the compiler may do the divide by hundred first, giving larger rounding off errors.
4) Examine the asm listing.
5) Step through the code.

Just for starters.

Mike
jeremiah



Joined: 20 Jul 2010
Posts: 1353

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 5:52 pm     Reply with quote

Your problem is in the calculation. Your timer 2 setup uses the period value to 294 to cap the timer module. So your timer 2 will count up to 294 and reset to 0.

Then, in your duty setting function, you take the value 590 and divide by 100 leaving 5 (integer division result). Then you multiply 5*50 and get 250.

250/294 is around 85%

If you want your duty to be 50%, your calculation needs to come out to (294+1)*0.5 = 147

As a note, Dmax should not be higher than your period of the timer.

EDIT: As a comment, this information was found in the manual entry for set_pwm_duty(). You should really spend some time reading the manual entries for the functions you choose. It can save you a lot of time and help you in the long run.
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Thu Mar 14, 2013 2:03 am     Reply with quote

and (of course), implicit in one part of this, is the general comment about integer arithmetic. Always do the division as the _last_ thing.

You have to be a little careful about 'Dmax'. Think about a standard PIC18. Timer set to 255, yet the PWM count can go from 0 to 1023. The timer counts in 'peripheral cycles' (Fosc/2 or Fosc/4 according to the PIC), but for these the PWM, operates directly off the Fosc source. So for these chips, Dmax can be higher than the timer period value. However this is on the PIC16/18, where the PWM, counts from a different clock to the timer. On the DsPIC's, the two peripherals use the same clock, and Jeremiah's calculation is the way to go for these.

There is also another big difference, that the timers are all 16bit on the DsPIC's, when on the older PIC's the timers used for the PWM are 8bit. Using the two clocks, allows 10bit resolution for the PWM, despite only having an 8bit timer. However with the 16bit timers, this 'cheat' is not needed.

All timer counts, PWM cycles etc., are based upon 'Fcy' on the DsPIC's.

Code:

//Duty_max=295
void pwm_duty_percent(int16 Duty, int16 Duty_max) {
   // this function to set duty in percent value.
   set_pwm_duty( 1, (Duty_max*Duty)/100 );
}


Best Wishes
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Fri Mar 15, 2013 4:05 am     Reply with quote

Thanks alot guys !
I really appreciate your help.
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Sat Mar 16, 2013 3:05 am     Reply with quote

It works well now.
Thank you all again !
I have the following function to read 50 samples of ADC channels and find the average, but I want to make sure whether the delays I'm using in the function is proper or not.

Code:
float average(byte CH) {         // this function to read from the A/D channel
                                 // 50 samples and then find the average.
int count;
float sum = 0.0;
set_adc_channel(CH);
delay_us(500);
for (count=1; count<=50; ++count) {
sum+= read_adc();
delay_us(100);
}
return(sum/50);
}                             // end of 'average' function.


Thanks !
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sat Mar 16, 2013 3:41 am     Reply with quote

Do you need it to be this slow?....

Read the data sheet. Tacq for this chip, assuming the supply is used as Vref, is only just 2.7uSec, even assuming the worst case on temperature etc..

Second comment, don't use float for the sum. Pointless, wastes time, and _degrades_ the accuracy. You are adding up integers. Add them in an integer sum. Only use float for the final result.

Code:

float average(byte CH)          // this function to read from the A/D channel
{                                 // 50 samples and then find the average.
   int count;
   int32 sum = 0;
   set_adc_channel(CH);
   delay_us(3);
   for (count=1; count<=50; ++count)
   {
      sum+= read_adc();
      delay_us(3);
   }
   return(sum/50.0); //Using the 50.0 here will make the result float
}                             // end of 'average' function.

At least 30* faster, and smaller.

Best Wishes
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Sat Mar 16, 2013 4:13 am     Reply with quote

Thank you very very much !
AbuSaleh



Joined: 09 Dec 2012
Posts: 14

View user's profile Send private message

PostPosted: Sat Mar 16, 2013 4:36 am     Reply with quote

I have another question Mr. Ttelmah !
What is the better mode to use with the function
Code:
setup_adc (mode)

Code:
ADC_CLOCK_INTERNAL
or
Code:
 ADC_CLOCK_DIV_32
?

Thanks.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group