list P=pic16f84 #include p16f84.inc ;Variables cblock 0x0020 ; ; The duty cycle for each PWM is stored here: ; pwm0,pwm1,pwm2,pwm3 pwm4,pwm5,pwm6,pwm7 ; ; The counters are compared to the duty cycles ; to determine when the pulse should be turned off ; pwm0_cntr,pwm1_cntr,pwm2_cntr,pwm3_cntr pwm4_cntr,pwm5_cntr,pwm6_cntr,pwm7_cntr ; ; When rhe "rising_edge" counter rolls over, all ; pwms are driven high ; rising_edge pwm_state endc #define PWM_PORT PORTB org 0 goto Start org 4 ;------------------------------------------------------------ ;pwm_multiple ; ; The purpose of this routine is to generate 8 pulse width ;modulated waveforms. The algorithm consists of 9 counters ;that change the state of the pwm bits whenever they roll over. ;One of the counters, rising_edge, will drive the pwm bits ;high when it rolls over. The other 8 counters pwm0-pwm7 will ;drive their corresponding bits low when they roll over. ; ; The technique can be understood with an example for one ; counter. (This was taken from my web page: ; http://www.dattalo.com/technical/theory/pwm.html ) ; ; In Psuedo-code: ; ;// Initialize ; ;PWM_output = 1; ;rising_edge = 0; // Rising edge counter ;falling_edge = MAX_COUNT - duty_cycle; // Falling edge counter ; ;// Loop forever: ; ;while(1) { ; ; // If the rising_edge counter rolls over, make the output high ; ; if(rising_edge++ > MAX_COUNT) { ; rising_edge = 0; ; PWM_output = 1; ; } ; ; ; // If the falling_edge counter rolls over, make the output low ; ; if(falling_edge++ > MAX_COUNT) { ; falling_edge = 0; ; PWM_output = 0; ; } ; ;} ; ; ;In this un-optimized example, we start the PWM output in the high ;state and initialize the falling edge counter with the desired ;duty cycle (or actually MAX_COUNT - duty_cycle, since the ;algorithm increments the counters). When the counters reach their ;maximum count, they're cleared back to zero and the PWM output is ;updated. Perhaps a simple picture illustrates it better: ; ; ;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". ; ;The modulo arithmetic gets *much* simpler when MAX_COUNT = 2^N. ; ; ;RAM: ; pwm0-pwm7 - pwm counters ; rising_edge - rising edge counter ; pwm_state - current state of the pwm outputs. ;ROM ; 38 instructions ;Execution time ; 23 cycles pwm_multiple ; DECFSZ rising_edge,F ;If the rising edge counter has not rolled GOTO pwm_update ;over, then go update the PWM counters ; pwm_rising_edge: ; ; MOVF pwm0,W ;Update each counter with the latest MOVWF pwm0_cntr ;pwm value ; MOVF pwm1,W ; MOVWF pwm1_cntr ; ; MOVF pwm2,W ; MOVWF pwm2_cntr ; ; MOVF pwm3,W ; MOVWF pwm3_cntr ; ; MOVF pwm4,W ; MOVWF pwm4_cntr ; ; MOVF pwm5,W ; MOVWF pwm5_cntr ; ; MOVF pwm6,W ; MOVWF pwm6_cntr ; ; MOVF pwm7,W ; MOVWF pwm7_cntr ; ; MOVLW 0xff ;Turn on all of the PWM's. GOTO pwm_exit ; pwm_update: CLRW ;Build the bit mask for turning ;off the PWM outputs. Assume that ;all of the outputs will be turned ;off. ; DECFSZ pwm0_cntr,F ;If the first counter has not reached 0 IORLW b'00000001' ;then we don't want to turn it off. ; DECFSZ pwm1_cntr,F ;Same for the second one IORLW b'00000010' ; ; DECFSZ pwm2_cntr,F ;and so on... IORLW b'00000100' ; ; DECFSZ pwm3_cntr,F ; IORLW b'00001000' ; ; DECFSZ pwm4_cntr,F ; IORLW b'00010000' ; ; DECFSZ pwm5_cntr,F ; IORLW b'00100000' ; ; DECFSZ pwm6_cntr,F ; IORLW b'01000000' ; ; DECFSZ pwm7_cntr,F ; IORLW b'10000000' ; ; ; ; ; ANDWF pwm_state,W ; Clear all of those pwm outputs ;that have reached zero. pwm_exit: ; MOVWF PWM_PORT ;Update the outputs MOVWF pwm_state ;Save the state RETURN Start BSF STATUS,RP0 CLRF PWM_PORT BCF STATUS,RP0 MOVLW 0 MOVWF pwm0 MOVLW 0x10 MOVWF pwm1 MOVLW 0x20 MOVWF pwm2 MOVLW 0x30 MOVWF pwm3 MOVLW 0x40 MOVWF pwm4 MOVLW 0x50 MOVWF pwm5 MOVLW 0x60 MOVWF pwm6 MOVLW 0x70 MOVWF pwm7 loop CALL pwm_multiple goto loop end