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