Searching \ for '[PIC]: Many channels of PWM' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/microchip/ios.htm?key=pwm
Search entire site for: 'Many channels of PWM'.

Exact match. Not showing close matches.
PICList Thread
'[PIC]: Many channels of PWM'
2002\11\14@162003 by Josh Koffman

flavicon
face
Hi all. I seem to be in a creative mood lately. I came up with an odd
idea for a festive holiday light type item. Problem is I want 15-30
channels of PWM. I really like the PIC's PWM module for its simplicity.
However, 15 16f628s or 8 16f870s seems a bit much. I have seen Scott
Dattalo's code for 8 concurrent streams. However it's all software, and
I'm worried that running a routine for dealing with serial comms will
throw off the timing. Is there a commercially available chip that does
multiple PWMs in one package? Or, has anyone else dealt with this?

Thanks,
Josh

--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\14@164526 by Olin Lathrop

face picon face
> Hi all. I seem to be in a creative mood lately. I came up with an odd
> idea for a festive holiday light type item. Problem is I want 15-30
> channels of PWM. I really like the PIC's PWM module for its simplicity.
> However, 15 16f628s or 8 16f870s seems a bit much. I have seen Scott
> Dattalo's code for 8 concurrent streams. However it's all software, and
> I'm worried that running a routine for dealing with serial comms will
> throw off the timing. Is there a commercially available chip that does
> multiple PWMs in one package? Or, has anyone else dealt with this?

What bandwidth do you need from the PWM average signals?  If you are
controlling some incandescent lighting, then software PWM will be plenty
fast enough at sufficient resolution.  A CCP module makes PWM easy, but
software PWM is not hard either.

You can set up a 16F877 to interrupt at 5KHz (every 1000 instructions at
20MHz) without taking a large fraction of the processor cycles.  If you
wanted 100 analog levels, the PWM frequency would be 50Hz.

The attached file BAT_INTR.ASPIC is an example of 5 software PWM outputs
from a 16F876.  There are other things going on too, but it shouldn't be
too hard to find the PWM code.  I've also attached BAT.INS.ASPIC so you
can see how some of the symbols used in the interrupt module are defined.


*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\14@164944 by Andy Kunz

flavicon
face
Hitachi makes some.  Look for at PCM R/C decoders.  They have up to 11 or
so channels, and do 1/2uS resolution usually.

Andy

At 04:47 PM 11/14/02 -0600, you wrote:
{Quote hidden}

-------------------------------------------------------------------
Race Boats  - spam_OUTandyTakeThisOuTspamRC-Hydros.com      http://www.RC-Hydros.com
Airplanes   - .....andyKILLspamspam@spam@FlyingHobbies.com  http://www.FlyingHobbies.com
Electronics - andyspamKILLspamMontanaDesign.com  http://www.MontanaDesign.com
-------------------------------------------------------------------

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\14@165149 by Olin Lathrop

face picon face
part 1 430 bytes content-type:text/plain; charset="iso-8859-1" (decoded 7bit)

> The attached file BAT_INTR.ASPIC is an example of 5 software PWM outputs
> from a 16F876.  There are other things going on too, but it shouldn't be
> too hard to find the PWM code.  I've also attached BAT.INS.ASPIC so you
> can see how some of the symbols used in the interrupt module are
defined.

Oops, hit SEND right before remebering to do the attaching.  Here are the
files.


part 2 13293 bytes content-type:application/octet-stream; name="bat_intr.aspic" (decode)

part 3 7955 bytes content-type:application/octet-stream; name="bat.ins.aspic" (decode)

part 4 320 bytes

*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\14@183647 by Josh Koffman

flavicon
face
Well, It doesn't really matter what the exact frequency is, but I was
worried that anything under 20KHz might cause audible interference. Is
this even possible? I haven't had a chance to look at your code, but
thank you for sending it. Ideally if I could squeeze 8 PWM outputs with
256 levels each on one f87x chip, I'd be happy. I do also have the
option of moving up to the 18f452 which would allow double the clock
speed. As long as the device doesn't cause any sort of interference,
isn't visibly flickering (shouldn't be much of an issue), and has a bit
of processor time to do some serial stuff, I'd be satisfied. Any
ideas/comments?

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Olin Lathrop wrote:
> You can set up a 16F877 to interrupt at 5KHz (every 1000 instructions at
> 20MHz) without taking a large fraction of the processor cycles.  If you
> wanted 100 analog levels, the PWM frequency would be 50Hz.

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\14@185933 by Brent Brown

picon face
It may not be quite what you want, but for interest sake if nothing
else....

I have 12 LEDs (well actually 12 banks of LEDs), each switched by an
N-channel MOSFET. Each gate connects to the single PWM output of a
PIC16F627 through a resistor. Each gate also connects directly to an
individual port line on the PIC16F627.

To turn an LED off, output a '0' on the port.
To turn an LED on (100%), ouput a '1' on the port.
To turn an LED on  (xxx%), set PWM, set the port to input.

Doesn't give you the ability to select any PWM value for any LED, but
does give you three different intensities to choose from. Can be
applied to almost any number of LEDs. Happy pulsing!


--
Brent Brown, Electronic Design Solutions
16 English Street, Hamilton, New Zealand
Ph/fax: +64 7 849 0069
Mobile/txt: 025 334 069
eMail:  .....brent.brownKILLspamspam.....clear.net.nz

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics




2002\11\15@145823 by Wouter van Ooijen

face picon face
> Hi all. I seem to be in a creative mood lately. I came up with an odd
> idea for a festive holiday light type item. Problem is I want 15-30
> channels of PWM. I really like the PIC's PWM module for its
> simplicity.

Does the DA need to be quick? A kid on my club wanted to make an
18-channel DMX-to- 0..10 Volt, for which I suggested one DA (chip or
R2R), one opamp, analog 1-to-18 switch, 18 caps, 10 opamps. The details
are left as an exercise...

Wouter van Ooijen

-- -------------------------------------------
Van Ooijen Technische Informatica: http://www.voti.nl
consultancy, development, PICmicro products

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.




2002\11\15@184158 by Josh Koffman

flavicon
face
Well, it's controlling lights, so it needs to be quick enough so there
is no interference, and no flicker. I am not looking for an analog
output though, I am looking for actual PWM. I don't think this idea
would work...am I wrong?

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Wouter van Ooijen wrote:
> Does the DA need to be quick? A kid on my club wanted to make an
> 18-channel DMX-to- 0..10 Volt, for which I suggested one DA (chip or
> R2R), one opamp, analog 1-to-18 switch, 18 caps, 10 opamps. The details
> are left as an exercise...

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.




2002\11\15@190532 by Scott Dattalo

face
flavicon
face
On Fri, 15 Nov 2002, Josh Koffman wrote:

> Well, it's controlling lights, so it needs to be quick enough so there
> is no interference, and no flicker. I am not looking for an analog
> output though, I am looking for actual PWM. I don't think this idea
> would work...am I wrong?

Well, as Olin suggested, the PWM routine can be placed in a Timer
interrupt routine. You could run

http://www.dattalo.com/technical/software/pic/pwm8.asm

For 23 cycles / 8 PWM's. Or 46 cycles for 16 PWMs. For the last two PWMs
you could shrink the routine to just this:

       CLRW                    ;Build the bit mask for turning
                               ;off the PWM outputs. Assume that
                               ;all of the outputs will be turned
                               ;off.
                               ;
       DECFSZ  pwm0,F          ;If the first counter has not reached 0
        IORLW  00000001b       ;then we don't want to turn it off.
                               ;
       DECFSZ  pwm1,F          ;Same for the second one
        IORLW  00000010b       ;

       ANDWF   pwm_state,W     ; Clear all of those pwm outputs
                               ;that have reached zero.
                               ;
       XORLW   00000011b       ;Toggle the current state.
       INCFSZ  rising_edge,F   ;If the rising edge counter has not
        XORLW  00000011b       ;rolled over then toggle them again.
                               ;Double toggle == no effect. However,
                               ;if the rising edge counter does roll
                               ;over then a single toggle will turn
                               ;the pwm bits on, unless of course the
                               ;pwm counter has just rolled over too.
                               ;
       MOVWF   pwm_state       ;Save the state
       MOVWF   PWM_PORT        ;update the outputs



That'd be 58 cycles for 18 outputs. If TMR0 is the interrupt source and
you don't select the prescaler, then you'd have ~75% of the CPU time
available for other things. Those "other things" would be updating all
those PWMs! (BTW, there's a way to save a few cycles by combining the
checks for "rising_edge").

Scott

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.




2002\11\16@112847 by Wouter van Ooijen

face picon face
No, this was meant as DA, not PWM. My mistake.

> Well, it's controlling lights, so it needs to be quick enough so there
> is no interference, and no flicker. I am not looking for an analog
> output though, I am looking for actual PWM. I don't think this idea
> would work...am I wrong?

> Wouter van Ooijen wrote:
> > Does the DA need to be quick? A kid on my club wanted to make an
> > 18-channel DMX-to- 0..10 Volt, for which I suggested one DA (chip or
> > R2R), one opamp, analog 1-to-18 switch, 18 caps, 10 opamps.
> The details
> > are left as an exercise...

--
http://www.piclist.com#nomail Going offline? Don't AutoReply us!
email EraseMElistservspam_OUTspamTakeThisOuTmitvma.mit.edu with SET PICList DIGEST in the body


2002\11\16@113452 by Al Williams

flavicon
face
Have a look at http://www.al-williams.com/pak5.htm -- a lot of these are
used in commercial lighting displays.

Other than that, you might consider writing your own PWM in an interrupt
timer routine. However, 30 might be a bit much depending on the
frequency you want.

Regards,

Al Williams
AWC
* Easy RS-232 Prototyping
http://www.al-williams.com/awce/rs1.htm

> {Original Message removed}

2002\11\16@114117 by Mike Harrison

flavicon
face
I'd suggest using a single, fast timer interrupt to do  the PWM, and
use a PIC with a hardware uart in polled mode for comms.

On Sat, 16 Nov 2002 10:34:18 -0600, you wrote:

{Quote hidden}

>> {Original Message removed}

2002\11\17@014824 by Josh Koffman

flavicon
face
Do you by any chance have a part number or reference or something? I
have been looking at Hitachi's site, and I can't find anything.

Thanks,

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Andy Kunz wrote:
>
> Hitachi makes some.  Look for at PCM R/C decoders.  They have up to 11 or
> so channels, and do 1/2uS resolution usually.

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestspamspam_OUTmitvma.mit.edu


2002\11\19@153522 by Josh Koffman

flavicon
face
Ok, for awhile I had some real trouble with your code, but I think I
have figured it out. I guess I have trouble doing 8 bit binary logic in
my head. Plus IOR and XOR sometimes confuse me. Please check my comments
within to see if I've got it.

I will likely be converting this to a timer based system, based on a 28
or 40 pin PIC. This would give me enough pins for one or two 8 channel
PWMs, and an 8 bit + control lines parallel port for input. The
controller will likely be a 28 pin PIC that receives data via UART, and
has an 8 bit data port common with all the slaves, and individiual
control (address) lines. We'll see, I haven't figured out the control
end quite yet. I will have to build in a failsafe so if one of the
slaves freezes or dies, the controller doesn't sit in a loop waiting for
a response. I figured on one line for a "Data Ready" and another one for
"Request to Send". DR would be output from controller, and RTS would be
input to controller. The controller would assert DR when it wanted to
update the slave, then wait for the slave to assert RTS. Then the
controller would put data on the bus, and either drop and reassert DR,
or perhaps assert another pin. Then the slave would aknowledge
transmission via RTS, and wait for the next value. The problem I see is
that if the slave dies, the controller may sit in an endless loop
waiting for a RTS from the slave. I hope that isn't too confusing.

The other thing I was thinking is that if I switch to a 16f628, and a
hacked up parallel comms between slave and master, I might be able to do
some interesting stuff. If I don't care about PWM frequency, could I
just run the PIC on the internal clock? Since PWM is more about a ratio
between on and off, if I don't care about the total period, I should be
able to get away with internal oscillator, and let it run however it
wishes, right? As long as I plan the parallel comms with enough leeway
for both extremes of course.

Anyway, see comments in the code.


Scott Dattalo wrote:
(I'll use the abbreviated version of the code here to save space)

{Quote hidden}

Create a mask of all the bits on the PWM port that have had their
counters expire. A 0 indicates an expired timer.

>
>         ANDWF   pwm_state,W     ; Clear all of those pwm outputs
>                                 ;that have reached zero.

AND the mask with the current state. If an output was 1 and the timer
expired, you get 0, if it's 1 and the timer hasn't expired, you get a 1.
So you end up with what the new state should be, assuming the main timer
hasn't expired.

>                                 ;
>         XORLW   00000011b       ;Toggle the current state.

Basically invert the mask, 1 becomes 0, 0 becomes 1.

>         INCFSZ  rising_edge,F   ;If the rising edge counter has not
>          XORLW  00000011b       ;rolled over then toggle them again.
>                                 ;Double toggle == no effect. However,
>                                 ;if the rising edge counter does roll
>                                 ;over then a single toggle will turn
>                                 ;the pwm bits on, unless of course the
>                                 ;pwm counter has just rolled over too.

If the main timer has expired, then you accept the inverted state, if it
has expired, you invert again, which ends up righting the output, and
then you can copy it right to the port.

*** What happens if you have a timer that expires while the mask was
originally a 0? When you invert it the first time, it becomes a 1, which
then would go and turn on the port. If it was a 1 though, then it
becomes a 0, and the port is turned off. That should only occur if the
value in the pwmX register was equal and in sync to the rising_edge main
timer register. Does that mean that to have a PWM port off, you then
need to reload the pwmX register with a 0x01 so it will time out
immediately? Also, should there be a subroutine that runs after the main
timer expires that will reload all the individual pwmX registers?

>         MOVWF   pwm_state       ;Save the state
>         MOVWF   PWM_PORT        ;update the outputs

Ok, super long post, but comments truly appreciated. I am just trying to
get my head around a lot of this.

Thanks,

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics



'[PIC]: Many channels of PWM'
2002\12\15@190158 by Josh Koffman
flavicon
face
Ok, sorry to dig up this old thread, but now that I actually go to
integrate it into a project, I seem to have forgotten that perfect view
of the solution I had a month ago :)

I am looking to use the code at
www.dattalo.com/technical/software/pic/pwm8.asm
to do multiple PWMs on one chip. My basic question is...where and when
do I update the PWM counters? I plan on putting the timer checking and
updating in an ISR, and triggering it with a TMR0 interrupt. This will
work on the first iteration, but once a counter has hit zero, doesn't it
just start again at 255? Therefore, I would need to reload the value I
want in the counter so that it will start to count down again. What if
after:
        DECFSZ  pwm0,F          ;If the first counter has not reached 0
        IORLW  00000001b       ;then we don't want to turn it off.
I add something to the effect of:
       check pwm0 for zero
       if zero, reload pwm0 with proper value
This would still be in the ISR. How does this sound? Plausible? Am I on
the right track? I'm using an 18f252 chip, so I was thinking about using
the BZ (Branch if Zero) operand. I guess I would have to do a movf
pwm0,f in order to trigger the zero bit. If only there was an
instruction similar to TSTFSZ (Test f, skip if 0), but skip if not zero.
Can having the branch operand in an ISR cause a stack overflow if I run
too many of them?

Anyway, as per usual, any and all help/advice/assistance is appreciated.

Thanks,

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams


Scott Dattalo wrote:
{Quote hidden}

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.

2002\12\15@224656 by Patrick J

flavicon
face
Hi Josh,
You can put the counterupdates in the interrupt routine if u want
to control the PWM freq. independently from your mainloop.

First of all I'd like to present a way to think about how it works:
Think of it as 2 cam-wheels (like in a washing machine)
One wheel sets the PWM output to 0 once each turn.
The other one sets the PWM output to 1 once each turn.

Now if these two wheels were on the same axle, and u start
turning the axle around... the axle rpm decides the PWM-freq.
And the phase-difference between the wheels decides the
pulsefactor 0-100%. So it is perfectly okay that the counters
roll over, they are supposed to !

You do not need to reload the counters unless u want to change
the pulsefactor. And then all you do is add say 1 to the PWM-
counter and the phase will shift slightly.

Above method is dead simple and works like a charm, the routine
below is more complicated and doesnt offer anything extra (i think)
*ducks*

I prolly could dig up some code I wrote using above techinqe
for a recent project if ure interested... for the 877 tho.
I figured id put it on piclist.com but no time to mess w that now :-)



{Original Message removed}

2002\12\15@231806 by Josh Koffman

flavicon
face
Hi Patrick, thanks for the reply. I think you might have kick started my
mind again. While I could reload the PWM counters, there really isn't
any need. After I load them the first time, they are locked to the main
counter. Because both counters are counting to 0xFF, there really isn't
any need to reload the value. The PWM counter will roll over at the
exact same time in relation to the main counter every time if I don't
touch it. Essentially, the initial value offsets the PWM counter from
the main counter, and because they count to the same value, the offset
stays constant.

Of course, this brings up the problem of when in the cycle to change the
PWM counter values. I guess the time to do this is when the main counter
rolls over. I think what I will do is test the desired values in the
mainline, and if one has changed, set a flag. When the main counter
rolls over, I will test that flag in the ISR. If it is set, I will
reload all the values. I figure I might as well just reload them all
rather than trying to test them and see if one has changed.

Actually, looking again at the TSTFSZ operand, I might change my plan. I
think I will have a byte wide register, and if one of the PWM values
changes, I will set the corresponding bit in that register. Then testing
it for zero is a single instruction, and if one of them is set, I can
reload only that counter.

I would love to see any code you have, I am most familiar with the 16f
series anyways, so that shouldn't be a problem. Porting it to 18f should
be trivial as well.

Hope to hear from you soon!

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Patrick J wrote:
{Quote hidden}

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.

2002\12\16@023944 by Scott Dattalo

face
flavicon
face
On Sun, 15 Dec 2002, Josh Koffman wrote:

{Quote hidden}

If you're using the 18F series then  you can save an instruction:

Instead of doing this (at the end of the routine)

       XORLW   11111111b       ;Toggle the current state.
       INCFSZ  rising_edge,F   ;If the rising edge counter has not
        XORLW  11111111b       ;rolled over then toggle them again.
                               ;Double toggle == no effect. However,
                               ;if the rising edge counter does roll
                               ;over then a single toggle will turn
                               ;the pwm bits on, unless of course the
                               ;pwm counter has just rolled over too.

You save the instruction by:

      INFSNZ   rising_edge,F
       BRA     UpdatePWMs      ;When the rising edge counter rolls over
                               ; then update everything.

      MOVWF   pwm_state       ;Save the state
      MOVWF   PWM_PORT        ;update the outputs

      RETURN


Then:

UpdatePWMs:

 ; (note the reversed order from above - this ensures the
 ; pwm_port is written isochronously).

      MOVWF   PWM_PORT        ;update the outputs
      MOVWF   pwm_state       ;Save the state

 ; copy the current duty cycles into counters

      MOVFF    pwm0DC,pmw0
      ...


      RETURN

Everytime the "rising_edge" counter rolls over, 17 extra cycles are spent
updating the PWM counters. However, the counters don't need to be updated
if nothing has changed (they just roll over). So you could save 15
instructions by adding a flag:

UpdatePWMs:

 ; (note the reversed order from above - this ensures the
 ; pwm_port is written isochronously).

      MOVWF   PWM_PORT        ;update the outputs
      MOVWF   pwm_state       ;Save the state

      BTFSS   ShouldUpdate,bitn
       return

      BCF     ShouldUpdate,bitn

; copy the current duty cycles into counters

      MOVFF    pwm0DC,pmw0
      ...

----

You can also dynamically adjust the PWM. However, this runs the risk of
creating an abrupt discontinuity. See the comments in the code (on my web
page) on how this can be done.


----

BTW, If you're using the 18F there's a way to save many instructions:


      DCFSNZ    pwm0,F           ;When the pwm counter reaches 0
       BTG      PWM_LATCH,0      ;toggle it's output

     ....

      DCFSNZ    pwm7,F
       BTG      PWM_LATCH,7

      DCFSNZ    rising_edge,F
       NEGF     PWM_LATCH,F      ; tricky - drive all ouput highs.

18 cycles versus 23.

The NEGF works because all of the outputs are cleared by executing a
single BTG (bit toggle) instruction every 256 iterations. At the end of
256 cycles, all 8 outputs are cleared and the NEGF will turn them all back
on. Of course, this assumes that when the PWM engine was started that the
output latch was initialized to all ones.

A slightly better ending:

      DECFSZ    rising_edge,F
       RETURN

      NEGF      PWM_LATCH,F

      ; update the PWM's:

      BTFSS    ShouldUpdate,bitn
       return

      BCF      ShouldUpdate,bitn

      MOVFF    pwm0DC, pwm0
      ...

      RETURN

Scott

--
http://www.piclist.com#nomail Going offline? Don't AutoReply us!
email @spam@listservKILLspamspammitvma.mit.edu with SET PICList DIGEST in the body

2002\12\17@174022 by Josh Koffman

flavicon
face
Scott, if I use the INFSNZ/BRA method you showed, do I still need to AND
right before it? I've confused myself sufficiently to not be able to
figure it out. Help! I like the NEGF version...I will investigate that
once I get the problem I just mentioned clear in my head.

Help!

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Scott Dattalo wrote:
{Quote hidden}

--
http://www.piclist.com hint: To leave the PICList
KILLspampiclist-unsubscribe-requestKILLspamspammitvma.mit.edu>

2002\12\20@005159 by Josh Koffman

flavicon
face
Well, I've hit yet another problem. Everything was going great...until I
tried to run all PWM channels at the same value. Apparently, that wasn't
such a great idea :)

I'm running the older version of the code, what's posted on the web, not
the new suggestions Scott has made below. I'm sorry the post is so long,
but I felt it was somewhat important to leave Scott's comments attached.

Basically what I think is happening is that when all the counters roll
over at the same time, each bit in the mask needs to be set, and it adds
too many cycles to the ISR. So when I set all PWMs to 0xFF, instead of
looking like almost a perfect 100% duty cycle, instead I see about a 50%
duty cycle. And yes, it happens with 0xFE too, so it's not just a wacky
end of range thing.

So...the question is, what do I do now? I was thinking of perhaps
splitting the channels up. Instead of having them all on 1 main counter,
I could have 4 counters. They'd all increment in the ISR, but I'd
preload them at the start with different values, and offset them. This
would make my ISR much longer, but it would be a uniform longness, not
based (as much) on concurrent roll overs. I guess another option would
be to try Scott's updated 18F code below, but I think I'll run into
similar problems as long as there are 8 roll overs occuring at once.

I can't quite figure out exactly why this is all happening, my ideas
above are educated guesses at best. The problem doesn't seem to occur as
much with lower PWM values, but once I get significantly above 0x80
(halfway) things start to go odd. Below that, I'm getting the right
pulse widths on most channels, but have some values that cause wierd
things to happen with some channels and not others. That's why I leaning
towards the seperate, offset main counters.

Any comments? Am I even on the right track? Or am I so horribly wrong
that it doesn't even make sense?

Thanks :)

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Scott Dattalo wrote:
{Quote hidden}

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.

2002\12\20@120558 by Scott Dattalo

face
flavicon
face
On Fri, 20 Dec 2002, Josh Koffman wrote:

> Well, I've hit yet another problem. Everything was going great...until I
> tried to run all PWM channels at the same value. Apparently, that wasn't
> such a great idea :)

It should work just fine!

> I'm running the older version of the code, what's posted on the web, not
> the new suggestions Scott has made below. I'm sorry the post is so long,
> but I felt it was somewhat important to leave Scott's comments attached.

There are a couple of enhancements to that code. Probably the biggest one
is that I suggested a "NEGF" instruction when I meant a "COMF"
instruction. I bet that is what's causing you the problems. Sorry about
that.

>
> Basically what I think is happening is that when all the counters roll
> over at the same time, each bit in the mask needs to be set, and it adds
> too many cycles to the ISR. So when I set all PWMs to 0xFF, instead of
> looking like almost a perfect 100% duty cycle, instead I see about a 50%
> duty cycle. And yes, it happens with 0xFE too, so it's not just a wacky
> end of range thing.

It sounds like these roll-over counters are throwing you through a
while(1) loop. You may wish to read a brief description of the pwm
algorithm here:

http://www.dattalo.com/technical/theory/pwm.html


{Quote hidden}

In the original routine there are 8 counters for the 8 pwms, 1 counter for
the rising edge and there's one variable for the current state of the pwm
outputs. None of these should be changed while the program is running! The
other day we discussed a method by which the 8 pwm counters could be
dynamically changed. Recall that this require 8 new variables that get
copied to the 8 pwm counters when the rising edge counter rolls over.

Let's look at the roll over issue in a little more detail. From my web
page I write:


RE  |---------|---------|---------|---------|---------
   01234567890123456789012345678901234567890123456789

FE  ----|---------|---------|---------|---------|-----
   67890123456789012345678901234567890123456789012345

PWM ----______----______----______----______----______



RE = Rising Edge counter

FE = Falling Edge counter

PWM = PWM output

In this example, the counters roll over after 10 counts. In the beginning,
the rising edge counter is cleared and the output is driven high. Also,
the duty cycle is 4 so the falling edge counter is initialized to (10 - 4)
= 6. When the rising edge counter counts from 9 to 10, it is "rolled over"
back to zero and the output is driven high. Similarly, the falling edge
counter drives the output low when it rolls over.

Perhaps one thing to notice is that the difference (modulo 10) between the
two counters is always equal to the duty cycle. That's no coincidence. In
fact that's why this technique is called the "Phase Shifted Counters".

--------------

You describe the boundary condition of the pwm counters = 0xff. Let's look
at that for one pwm. This table illustrates what happens:

rising   pwm
edge     counter   output
-------------------------
 0       ff        1
 1       fe        1
 2       fd        1
 ...
 fe      1         1
 ff      0         0
 0       ff        1

Can you try this in a simulator to verify that it's true?

Now for code. You've got the routine on my web page. Here's a version that
saves an instruction (and still works for the 14-bit core devices):

       MOVF    pwm_state,W     ;Get the current state of the pwms.
                               ;We assume that the PWMs are not changed
                               ;on this cycle
                               ;
                               ;
       DECFSZ  pwm0,F          ;If the first counter has not reached 0
        ANDLW  11111110b       ;then we don't want to turn it off.
                               ;
       DECFSZ  pwm1,F          ;Same for the second one
        ANDLW  11111101b       ;
                               ;
       DECFSZ  pwm2,F          ;and so on...
        ANDLW  11111011b       ;
                               ;
       DECFSZ  pwm3,F          ;
        ANDLW  11110111b       ;
                               ;
       DECFSZ  pwm4,F          ;
        ANDLW  11101111b       ;
                               ;
       DECFSZ  pwm5,F          ;
        ANDLW  11011111b       ;
                               ;
       DECFSZ  pwm6,F          ;
        ANDLW  10111111b       ;
                               ;
       DECFSZ  pwm7,F          ;
        ANDLW  01111111b       ;
                               ;At this point, W contains a bit
                               ;mask of all PWMs that need to be
                               ;cleared
                               ;
                               ;
       INCFSZ  rising_edge,F   ;If the rising edge counter has not
        GOTO   drive_pwms      ;reached zero then drive the next state
                               ;
       MOVLW   11111111b       ;Drive all pwms high
       MOVWF   pwm_state       ;Save the state
       MOVWF   PWM_PORT        ;update the outputs

       ; update the pwm counters here.

       RETURN
                               ;
drive_pwms:                     ;
       XORWF   pwm_state,W     ;Clear the outputs that have changed
       MOVWF   pwm_state       ;Save the state
       MOVWF   PWM_PORT        ;update the outputs

       RETURN

----------

The code I posted for the 18f instruction set last time directly modifies
the output latch. This is bad because it means that there's a skew in the
pulse widths of the PWMs. So here is a correction:

PWM_LATCH  EQU  LATB   ; assume port b is the output port.


       COMF    PWM_LATCH,W     ;Get the complement of the current state
                               ;of the pwms. We assume the pwms will
                               ;change this iteration.
                               ;
                               ;
       DECFSZ  pwm0,F          ;If the first counter has not reached 0
        ANDLW  11111110b       ;then don't turn it off.
                               ;
       DECFSZ  pwm1,F          ;Same for the second one
        ANDLW  11111101b       ;
                               ;
       DECFSZ  pwm2,F          ;and so on...
        ANDLW  11111011b       ;
                               ;
       DECFSZ  pwm3,F          ;
        ANDLW  11110111b       ;
                               ;
       DECFSZ  pwm4,F          ;
        ANDLW  11101111b       ;
                               ;
       DECFSZ  pwm5,F          ;
        ANDLW  11011111b       ;
                               ;
       DECFSZ  pwm6,F          ;
        ANDLW  10111111b       ;
                               ;
       DECFSZ  pwm7,F          ;
        ANDLW  01111111b       ;
                               ;At this point, W contains a bit
                               ;mask of all PWMs that need to be
                               ;cleared
                               ;
                               ;
       INFSNZ  rising_edge,F   ;If the rising edge counter has rolled
        COMF   PWM_LATCH,W     ;over, grab a complement of the current
                               ;state of the outputs
                               ;
       XORWF   PWM_LATCH       ;update the outputs

       TSTFSZ  rising_edge
        RETURN
                               ;

       ; update PWM counters here.

       RETURN

-----

Please note that the code has not been tested.

Scott

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.

2002\12\20@151242 by Josh Koffman

flavicon
face
Scott, again, thanks for your replies. I do understand the phase shifted
counters idea. Thank you for the pointer to your webpage. I think I
might be running into problems based on speed. I'm running an 18F252 at
10MHz, multiplied to 40MHz. I'm doing the PWM updating in the ISR,
triggered by TMR0, which is running with no prescaler (ie as fast as
possible). I though I had the problem narrowed down to the extra
instructions created by all the counters rolling over at once. But I
just tried using 0xC0 instead of 0xFF, and the same thing isn't
happening. I honestly don't understand exactly what is happening. If I
posted my adaptation of your code, would you be willing to look at it
and see if I've made any big mistakes? I am updating the PWM counters
only when the edge counter rolls over. In fact, I tried taking the
updating code out, and replacing it with NOPs (to maintain time), and
the problem still occurs. So I know it's in my counting/outputting, not
my updating. I'd appreciate any help you, or anyone else can give me. I
think my idea about having more than one edge counter is a bit flawed
too, because (although extremely unlikely), if one selected the right
values for each PWM channel, you could get them to roll over at the same
time, and could give me the same problem. I will likely try this to be
sure though.

Thanks,

Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
       -Douglas Adams

Scott Dattalo wrote:
{Quote hidden}

--
http://www.piclist.com hint: The PICList is archived three different
ways.  See http://www.piclist.com/#archives for details.

More... (looser matching)
- Last day of these posts
- In 2002 , 2003 only
- Today
- New search...