>
> 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
>
> >
> > 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?
>
> 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
>