View previous topic :: View next topic |
Author |
Message |
AbuSaleh
Joined: 09 Dec 2012 Posts: 14
|
PWM duty calculation |
Posted: Wed Mar 13, 2013 4:37 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 4:54 am |
|
|
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: 19504
|
|
Posted: Wed Mar 13, 2013 5:06 am |
|
|
Two for dsPIC's.
Best Wishes |
|
|
AbuSaleh
Joined: 09 Dec 2012 Posts: 14
|
|
Posted: Wed Mar 13, 2013 6:57 am |
|
|
Thank you for help |
|
|
AbuSaleh
Joined: 09 Dec 2012 Posts: 14
|
|
Posted: Wed Mar 13, 2013 10:23 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 11:37 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 12:36 pm |
|
|
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
|
|
Posted: Wed Mar 13, 2013 5:41 pm |
|
|
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: 1345
|
|
Posted: Wed Mar 13, 2013 5:52 pm |
|
|
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: 19504
|
|
Posted: Thu Mar 14, 2013 2:03 am |
|
|
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
|
|
Posted: Fri Mar 15, 2013 4:05 am |
|
|
Thanks alot guys !
I really appreciate your help. |
|
|
AbuSaleh
Joined: 09 Dec 2012 Posts: 14
|
|
Posted: Sat Mar 16, 2013 3:05 am |
|
|
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: 19504
|
|
Posted: Sat Mar 16, 2013 3:41 am |
|
|
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
|
|
Posted: Sat Mar 16, 2013 4:13 am |
|
|
Thank you very very much ! |
|
|
AbuSaleh
Joined: 09 Dec 2012 Posts: 14
|
|
Posted: Sat Mar 16, 2013 4:36 am |
|
|
I have another question Mr. Ttelmah !
What is the better mode to use with the function
or ?
Thanks. |
|
|
|