View previous topic :: View next topic |
Author |
Message |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
400 hz (2ms) hardware pwm on 50 mhz clock speed |
Posted: Thu Aug 01, 2013 3:03 pm |
|
|
hi friends.
I want to generate 400 hz (2ms) hardware pwm on pic18f87j11 with 50 mhz clock speed for input of esc, but there is one problem.
Hardware pwm is work with timer 2 and timer 2 in prescaler=16 overflow in 327 us.
My compiler v is 4.130
Can anyone help me? |
|
![](templates/subSilver/images/spacer.gif) |
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Thu Aug 01, 2013 3:13 pm |
|
|
400 Hz is 2.5 ms |
|
![](templates/subSilver/images/spacer.gif) |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
|
Posted: Thu Aug 01, 2013 3:27 pm |
|
|
oxo wrote: | 400 Hz is 2.5 ms |
Right. you are right.
500 hz 2.5 ms
do you have any way to solve this problem? |
|
![](templates/subSilver/images/spacer.gif) |
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Fri Aug 02, 2013 12:07 am |
|
|
Despite of the numerical confusion, 327 µs maximal PWM period for 50 MHz clock is right.
This means, software PWM is the only option for 400 Hz. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Aug 02, 2013 12:24 am |
|
|
The normal solution, is to use a 'half hardware' PWM. Use the CCP, with the modes to automatically set/clear it's output pin, and in it's interrupt change the time period wanted for the next half cycle. Because the CCP supports 16bit timing, rather than 8bit, the slower period is no problem, and given the slower period, the overhead of the interrupt calls is not normally a problem.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
|
Posted: Fri Aug 02, 2013 1:54 am |
|
|
FvM wrote: | Despite of the numerical confusion, 327 µs maximal PWM period for 50 MHz clock is right.
This means, software PWM is the only option for 400 Hz. |
Right, but software pwm is kill processor speed. Thanks.
Ttelmah wrote: | The normal solution, is to use a 'half hardware' PWM. Use the CCP, with the modes to automatically set/clear it's output pin, and in it's interrupt change the time period wanted for the next half cycle. Because the CCP supports 16bit timing, rather than 8bit, the slower period is no problem, and given the slower period, the overhead of the interrupt calls is not normally a problem.
Best Wishes |
Thanks Ttelmah. Do you have any example code? |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Fri Aug 02, 2013 5:29 am |
|
|
You might want to consider another PIC? In your original post you mention 'input to esc' as well as 400Hz( 2.5 ms).
That sounds like an RC servo system (e)lectronic (s)peed (c)ontrol unit.
If so I can think of several options.
1) slow down your current PIC.Depends on what else it's doing..
2) choose another PIC.Again, depends on other functions it needs to do.
3) add a 2nd smaller PIC.It'd only be the esc 'controller'.
4) use the hard/soft ccp pwm as suggested.
5) rethink/design of the esc software.
If it's really an RC based 'esc' then timing isn't super 'critical'.There's a fair amount of RC servo code here,have a look,see what's useful to you.
How you proceed is up to you, but you really don't need a 50MHz PIC to run RC servo systems.My first 'PC' ran at 2Mhz.
hth
jay |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Aug 02, 2013 8:14 am |
|
|
Many of the later PIC's also allow the timer2 output to be used to feed the PWM, giving wider frequency choices. However:
Code: |
#define PERCENT 312
int16 on_time;
#define PERIOD 31250 //400Hz at 50Mhz
#define OP_PIN PIN_C2 //whatever you want
void set_duty(int8 in_percent)
{
//This sets the duty cycle from 0 to 100 in percent (integer).
//Locks output full off/on for 0 & 100%.
int16 temp;
temp=((int16)in_percent*PERCENT)+(in_percent/2);
temp=0-temp; //This gives the correct positive unsigned value for timer
if (in_percent<1)
{
disable_interrupts(INT_CCP1);
output_low(OP_PIN);
return;
}
if (in_percent>99)
{
disable_interrupts(INT_CCP1);
output_high(OP_PIN);
return;
}
disable_interrupts(INT_CCP1);
on_time=temp;
enable_interrupts(INT_CCP1);
}
#INT_TIMER3
void falling(void)
{
//Here timer3 has triggered so need to set the output pin low
output_low(OP_PIN);
disable_interrupts(INT_TIMER3);
}
#INT_CCP1
void rising(void)
{
output_high(OP_PIN);
//just need to load the 'on' time, and enable the
//timer interrupt
set_timer3(on_time);
clear_interrupt(INT_TIMER3);
enable_interrupts(INT_TIMER3);
}
void main()
{
setup_timer_1(T1_DIV_BY_1 | T1_INTERNAL);
CCP_1=PERIOD; //Now gives 400Hz
setup_timer_3(T3_DIV_BY_1 | T3_INTERNAL);
setup_ccp1(CCP_COMPARE_RESET_TIMER);
//Now CCP1, is going to interrupt at the end of the period
//while timer3 is going to handle the 'off'.
setup_comparator(NC_NC_NC_NC);
set_duty(10); //10% duty
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(TRUE)
{
//do what you want.
}
}
|
About the minimum 'overhead' to do this.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
|
Posted: Fri Aug 02, 2013 11:16 am |
|
|
temtronic wrote: |
How you proceed is up to you, but you really don't need a 50MHz PIC to run RC servo systems.My first 'PC' ran at 2Mhz.
|
50 mhz speed is low let alone 2 mhz.
my Quadcopter has 5000 line of codes and a sensor that has 3 axis accel and 3 axis gyro with i2c interface and a pid loop.that this element need to high clock speed.my sensor has 6 axis for reading.450*6=2.7 ms each cycle of read from i2c is 400us with 2 mhz clock speed.and i add the math library and use the atan() , sqrt() , pow() function.this function need high clock speed.when add the math lib with 2 mhz clock you have a 20 ms delay on CPU. |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Fri Aug 02, 2013 11:45 am |
|
|
You'll find out that PICs are not great for 'math' especially transcendentals and floating point operations.
If you settle for integer math operations you can easily speed up by a factor of 10-30 times or more.
I've done several 3DOF helis with PID using 16F877s at 20MHz without any problems.
jay |
|
![](templates/subSilver/images/spacer.gif) |
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Aug 02, 2013 12:15 pm |
|
|
I hope you are not using floating point math in a quadcopter! All that trig can be done with scaled ints so much faster. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
![](templates/subSilver/images/spacer.gif) |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
|
Posted: Fri Aug 02, 2013 2:26 pm |
|
|
temtronic wrote: | You'll find out that PICs are not great for 'math' especially transcendentals and floating point operations.
If you settle for integer math operations you can easily speed up by a factor of 10-30 times or more.
I've done several 3DOF helis with PID using 16F877s at 20MHz without any problems.
jay |
float pointing is very accurate for Quadcopter.0.5 to 1 degree accurate.
thanks alot.My opinion use dspic33fj256mc710.40 MIPS clock speed.great for math and float pointing
Ttelmah wrote: | Many of the later PIC's also allow the timer2 output to be used to feed the PWM, giving wider frequency choices. However:
Code: |
#define PERCENT 312
int16 on_time;
#define PERIOD 31250 //400Hz at 50Mhz
#define OP_PIN PIN_C2 //whatever you want
void set_duty(int8 in_percent)
{
//This sets the duty cycle from 0 to 100 in percent (integer).
//Locks output full off/on for 0 & 100%.
int16 temp;
temp=((int16)in_percent*PERCENT)+(in_percent/2);
temp=0-temp; //This gives the correct positive unsigned value for timer
if (in_percent<1)
{
disable_interrupts(INT_CCP1);
output_low(OP_PIN);
return;
}
if (in_percent>99)
{
disable_interrupts(INT_CCP1);
output_high(OP_PIN);
return;
}
disable_interrupts(INT_CCP1);
on_time=temp;
enable_interrupts(INT_CCP1);
}
#INT_TIMER3
void falling(void)
{
//Here timer3 has triggered so need to set the output pin low
output_low(OP_PIN);
disable_interrupts(INT_TIMER3);
}
#INT_CCP1
void rising(void)
{
output_high(OP_PIN);
//just need to load the 'on' time, and enable the
//timer interrupt
set_timer3(on_time);
clear_interrupt(INT_TIMER3);
enable_interrupts(INT_TIMER3);
}
void main()
{
setup_timer_1(T1_DIV_BY_1 | T1_INTERNAL);
CCP_1=PERIOD; //Now gives 400Hz
setup_timer_3(T3_DIV_BY_1 | T3_INTERNAL);
setup_ccp1(CCP_COMPARE_RESET_TIMER);
//Now CCP1, is going to interrupt at the end of the period
//while timer3 is going to handle the 'off'.
setup_comparator(NC_NC_NC_NC);
set_duty(10); //10% duty
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(TRUE)
{
//do what you want.
}
}
|
About the minimum 'overhead' to do this.
Best Wishes |
thanks alot Ttelmah for your code.this work perfect.but can you give a source code to 4 channel pwm?
thanks alot |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Sat Aug 03, 2013 1:40 am |
|
|
I'm not going to write your code for your. However some comments:
Remember that the 'period' will be the same for all, so one timer can handle the start of all four pulses.
I must admit I'd probably just use a basic PIC12 to actually generate the servo pulse train, and leave the main processor handling the calculation work.
However don't get hooked on 400Hz.
If you try out one of the Quad controllers based on faster processors, you will find that even with ESC's flashed to support 400Hz, changing to this rate has little if any effect on the performance. Problem is that the actual update rate is dependant on the timing of the main algorithm loop, and even on a much faster processor, people tend to waste time in this loop, and so it updates it's output at several tens of mSec intervals - if you are lucky....
Don't use float. Use scaled integers, and some real thought. The servo algorithm should be written so that the PID terms can be handled in something like an int32, and divided by 256 for the fractional part. A single floating point division, even at 50MHz, takes 116uSec. Solve a few PID calculations, and you are into hundreds of uSec. Using scaled integers, with the scaling chosen so that divisions are binary values, and you should be able to get _dozens_ of times faster updates. This is the vital part, not the servo output rate. You need to be designing your algorithm _from scratch_ with a real understanding of what takes time in the processor, and what can be done quickly. Temtronic's comment is spot on here.
Even if you switch to much faster processors, this still applies. The Arducopter has run into significant problems recently with this. Lot's of new 'frilly bits' added, and in certain states, the update rate plummets (as does the copter....).
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Mahdi
Joined: 21 Aug 2012 Posts: 47
|
|
Posted: Sat Aug 03, 2013 12:14 pm |
|
|
Ttelmah wrote: | I'm not going to write your code for your. However some comments:
Remember that the 'period' will be the same for all, so one timer can handle the start of all four pulses.
|
Sorry but i just have problem with 4 channel software pwm.
I understand with your code style that any timer run a PWM channel. Now how can i run 4 channel PWM with same freq and different duty with One timer?
My quadcopter is flying but it fly with a little delay. Now i want to remove this delay with increase the clock speed. If i increase the clock speed i cant use hardware PWM, so i must use software pwm. Now I would ask you to help me to build a 4 channel software pwm
thanks alot buddies |
|
![](templates/subSilver/images/spacer.gif) |
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Sat Aug 03, 2013 7:49 pm |
|
|
It may be easy to run 4 (or any number) of PWM's on one timer, or it may not. What makes the difference is how much resolution you need in the outgoing pulse, and I don't believe you've told us that.
The way to do this is to learn to use the timer so that you can make it generate successive time intervals that add together to make exactly 2.5msec. Assuming you do want 4 PWM outputs, you make up 4 bytes (or perhaps fewer, as I'll explain) to write to an output port when each PWM bit goes low. So at time t=0, you set all the PWM outputs high. Then after the right interval, the first one goes low, then after some other interval the next one goes low (and the first one stays low) and so forth until they've all gone low at the correct times. You might have some of the PWM's with the same duty cycle, and that's the situation where you don't need all 5 interrupts: in fact you might need as few as 2, one each to start and stop the waveforms.
Anyway, when all 4 outputs are low, the last time interval you use is something that makes the total end up at 2.5msec, and that's when you set them all high and start again.
But I said it "may be easy". I claim it's simple if you don't need a very high resolution in time intervals. If that's not the case, you're going to have problems if there are PWM duty cycles which are almost, but not exactly, the same. The limit is how long it takes to get out of, and then back into, an interrupt. I can imagine solving that by staying in the interrupt and somehow measuring off the right time between transitions, but that seems like a real mess, and I'd hate to have to do it.
Anyway, that's how to do it. Tell us how it comes out. |
|
![](templates/subSilver/images/spacer.gif) |
|