|
|
View previous topic :: View next topic |
Author |
Message |
picer
Joined: 25 Jan 2005 Posts: 28 Location: Taxahoma
|
PWM Duty cycle problem |
Posted: Tue Feb 01, 2005 12:31 am |
|
|
Code first.....
Code: |
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include <16F628.h>
//#use fast_io (B) --- this makes the pic not work at all
SET_TRIS_B( 0b00000000 );
static long pwm1 = 0;
static long pwm2 = 0;
static long pwm3 = 0;
static long pwm4 = 0;
static long pwm5 = 0;
static long pwm6 = 0;
//timer interupt for pwm
#int_timer2
void int_pwm_out()
{
// PWM1
if (pwm1 == 385) // 50Hz
{
pwm1 = 0;
}
else
{
pwm1++;
}
if (pwm1 <= 192 ) // 50% duty
{
output_high(PIN_B7);
}
else
if (pwm1 > 192 ) // 50% duty
{
output_low(PIN_B7);
}
//PWM2
if (pwm2 == 192) // 100Hz
{
pwm2 = 0;
}
else
{
pwm2++;
}
if (pwm2 <= 96 ) // 50% duty
{
output_high(PIN_B6);
}
else
if (pwm2 > 96 ) // 50% duty
{
output_low(PIN_B6);
}
// all the way to PWM6
}
void main()
{
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
setup_timer_2(T2_DIV_BY_1, 127, 1);
//setup_timer_2(T2_DIV_BY_16, 204, 1);
while (true)
{
}
} |
Hope that's readable when it posts....
I want the clock to be as fast as possible, I need to have 0% - 100% duty cycle and need to be able to select the freq on each PWM from 50Hz - 650Hz. Problem looks to be that at 200Hz I'm already not able to control duty cycle in precise 1% increments. This is the only way I know to be able to get all the way down to 50Hz and still be able to get up to @600Hz. Even if I had a 40MHz clock that would still only get me to 200Hz before I ran out of resolution. Am I going about this completely wrong? Will actually be using the 16F819 but don't think that makes any difference at all. Am I looking at 1 pic for 50Hz - 100Hz, another 100Hz-200Hz, etc?? Lost on how to accomplish this task, HW PWM is out do to the low PWM frequency requirements. Any help would be great, thanks. |
|
|
rwyoung
Joined: 12 Nov 2003 Posts: 563 Location: Lawrence, KS USA
|
|
Posted: Tue Feb 01, 2005 1:02 am |
|
|
For better readability of code, use the "code" buttons. Otherwise it seems to loose the indentations and everything ends up jammed against the left margin. Also, the #use fast_io(B) should be fine but put the "set_tris_b(0);" line in your main().
I'd count down instead of up, each time the counter "zeros" use the "output_toggle" function to change the signal. You can also juggle between two different reload values to do something other than 50% duty cycle.
0% and 100% are special cases since they represent an always low or always high state for the output pin.
1% increments of what? 1% of 650Hz? In that case it is 6.5Hz "steps" or 1% of 50Hz for 0.5Hz "steps". Or do you mean you want to decrease (increase) the frequency 1% each time? 650Hz, 643.5Hz, 637.05Hz, 630.694Hz, etc.
Also, I'm not really following your logic behind using a 39.4kHz clock. But then I probably shouldn't be looking at this at 1am.
Take a look at the example program "EX_PATG.C" that should have come with your compiler. I think it will help sort out your issues for generating multiple PWM frequencies and dutycycles from the same master clock. It is set up for a 50% but several different output frequencies. A good start. _________________ Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month! |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Feb 01, 2005 7:54 am |
|
|
You could reduce the code a bit from
Code: |
// PWM1
if (pwm1 == 385) // 50Hz
{
pwm1 = 0;
}
else
{
pwm1++;
}
if (pwm1 <= 192 ) // 50% duty
{
output_high(PIN_B7);
}
else
if (pwm1 > 192 ) // 50% duty
{
output_low(PIN_B7);
}
|
To:
Code: |
// PWM1
pwm1++;
if (pwm1 > 384) // 50Hz
{
output_high(PIN_B7);
pwm1 = 0;
}
else if (pwm1 > 192 ) // 50% duty
{
output_low(PIN_B7);
}
|
Also, any pwm values less than 256 should be declared as int8 which will reduce the code even further. |
|
|
picer
Joined: 25 Jan 2005 Posts: 28 Location: Taxahoma
|
|
Posted: Tue Feb 01, 2005 8:25 am |
|
|
I didn't optimize anything yet, thanks. For instance, I want to set PWM1 to 50Hz and need to control duty cycle from 0%-100% in 1% increments and set PWM2 to 200Hz duty 0%-100% 1% increments and PWM3 to 650Hz, same thing for duty cycle. Once I get above 200Hz I cannot do 1% increments anymore. The loop count came out with 20MHz clock, 385 for 50Hz, 192 for 100Hz, 96 for 200Hz. Doing calcs for 400Hz:
Code: |
100.00% 48
99.00% 48
98.00% 47
97.00% 47
96.00% 46
95.00% 46
94.00% 45
93.00% 45
92.00% 44
91.00% 44
90.00% 43
..
..
7.00% 3
6.00% 3
5.00% 2
4.00% 2
3.00% 1
2.00% 1
1.00% 0
0.00% 0
|
As you can see I can no longer do 1% increments of duty cycle. Hope this was clearer as to what I want to do, it was late for me too last night
Hoping there is a better way, fastest at 20MHz is what 39KHz. Seems the fastest I can do with the interupt and software loop is around 13KHz. Just hoping I'm missing something obvious.... |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Feb 01, 2005 8:34 am |
|
|
First thing you need to do is to decide what frequencies you need. Are they constant or can they change as well? |
|
|
picer
Joined: 25 Jan 2005 Posts: 28 Location: Taxahoma
|
|
Posted: Tue Feb 01, 2005 9:02 am |
|
|
There are a set of constant freqs I want to use, 50Hz is one, 300, 600 but want to be able to select different ones as well, 650Hz would be the highest hense the added PWM outputs. ex_patg.c seems pretty slick if I can figure out how to do the duty cycle. Havn't done C in over a year so brushing up.
Thanks. |
|
|
picer
Joined: 25 Jan 2005 Posts: 28 Location: Taxahoma
|
|
Posted: Tue Feb 01, 2005 5:24 pm |
|
|
I've searched all day for something and came up with squat on how to fix my problem. Is duty cycle not as controlable as I thought? Keep looking.... |
|
|
Bill Boucher
Joined: 04 Feb 2005 Posts: 34 Location: Chatham,ON,CA
|
|
Posted: Fri Feb 04, 2005 12:52 pm |
|
|
i'm a newbie to CCS C but have been programming PICs daily in assembler for all sorts of PWM projects for about 14 years. You could try to generate the lower freqs in software using a fast loop and at the same time generate the higher freqs in hardware CCP. To get freqs as low as 300 or 600Hz in hardware, you'd have to drop the osc. freq and that will be the opposite of what you'd want to do with regards to the software based PWM outputs.
Anyway, using a 20MHz pic (i.e. 16C711), I have built true PID loop position controllers for linear solenoid EEGR valves. The controller reads an analog position sensor, a pot position reference, coil current, 4 mode switches, controls 4 LEDs, reads the dutycycle of an input reference PWM and generates a fixed freq output PWM to drive the solenoid coil. All this runs continuously at up to 256Hz PWM with 8-bit (0.39%) resolution. Granted, this was done in assembler and pushed the 711 to the limit.
Here's what you do. First off, forget the timer interupt. It wastes time saving and restoring context every time it overflows and at this speed, you don't want that. Create a main loop that runs at a multiple of your fastest output PWM freq. The rate for 1% resolution would be 100x the PWM freq of 600Hz, so 60,000 Hz. That leaves you 85mc per loop with 5 MIPS. Not a lot, but if I can make a box that does all of what I quoted above in that much time, then we should be able to manage 3 PWM outputs. To do this, simply enter the loop, exec your PWM code, and wait at the bottom until the T0IF bit is set. Then fall through, clear the flag, move a constant into TMR0, and jump back to the top of the loop. If you want to, use an interupt to run code that changes the PWM freq and %DC. That will cause some PWM output flutter, but otherwise you'd have to interleve code into your fast loop to monitor switches or something to control the freq & %DC.
Inside your fast loop, create two counters for each PWM output. Use the "register" type of var so it accesses afap. One is OnTime, one is OffTime. Load these with values to set freq & %DC. Then when running, if Ontime>0 then output = high and dec OnTime. If OnTime=0 and OffTime>0 then output=low and dec OffTime. When both=0 then reload both.
To figure out OnTime and OffTime from Freq & %DC, well, with just one PWM output, I'd make the fast loop 256x the PWM frame rate. Using an 8b ref value for OnTime, then OffTime is the compliment of OnTime. With multiple PWM output freqs, I'd probably want to do the "reference conversion" in a separate PIC, then load them into the PWM-gen PIC using its hardware PSP or SPI or I2C. Something fast enough and int-driven so as not to flutter the PWM outputs too much when new values are loaded. Also, if you had 2 PICs doing this job, you could make a software generated 4-wire communication between them to synchronously shift the OnTime1/OffTime1/OnTime2/OffTime2/OnTime3/OffTime3 values for each channel from the PWM master to the Ref-converter slave. The master could produce an enable output & clock output. The slave presents the data and an ack output. This gets a little complicated but basically the PWM PIC will sample 1 bit of data each time it sees the ack line go high. Then it would toggle the clock state, wait for ack to go away, toggle clock, wait for ack, then sample. The trick is to write this code to execute 1 stage per main loop pass. This requires a few counters and bit flags to keep track of where it is in the communications. I've done exactly this sort of thing several times in assembler. The main advantage of this is totally glitch-free PWM while loading in the PWM reference values.
Sorry I didn't present any C code here, just ideas, but this is basically how I've scanned & generated fixed-freq 8b & 10b PWM in software for years with great success. |
|
|
|
|
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
|