From: Scott Dattalo
;------------------------------------------------------------ ;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. ; ; ;RAM: ; pwm0-pwm7 - pwm counters ; rising_edge - rising edge counter ; pwm_state - current state of the pwm outputs. ;ROM ; 23 instructions ;Execution time ; 23 cycles pwm_multiple 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 ; ; DECFSZ pwm2,F ;and so on... IORLW 00000100b ; ; DECFSZ pwm3,F ; IORLW 00001000b ; ; DECFSZ pwm4,F ; IORLW 00010000b ; ; DECFSZ pwm5,F ; IORLW 00100000b ; ; DECFSZ pwm6,F ; IORLW 01000000b ; ; DECFSZ pwm7,F ; IORLW 10000000b ; ; ; ANDWF pwm_state,W ; Clear all of those pwm outputs ;that have reached zero. ; 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. ; MOVWF pwm_state ;Save the state MOVWF PWM_PORT ;update the outputs Now this routine has been optimized for speed. It would be difficult (but not impossible) to modify it to: 1) change the duty cycle on the fly and 2) have different frequencies for each pwm. One technique is to maintain two counters for each pwm. Call them say, rising_edge_counter and falling_edge_counter. Make the counters the same size and do something like this: if(--rising_edge_counter == 0) { turn_PWM_on(); rising_edge_counter = PWM_PERIOD; } if(--falling_edge_counter == 0) { turn_PWM_off(); falling_edge_counter = PWM_PERIOD; } In other words, every time the rising edge counter rolls over, you make the PWM output high and when the falling edge counter rolls over you make it low. Now, to get duty cycle, you simply adjust the phase between the counters: set_duty_cycle(int new_duty_cyle) { falling_edge_counter = rising_edge_counter + new_duty_cyle; if(falling_edge_counter > PWM_PERIOD) falling_edge_counter -= PWM_PERIOD; } Now this routine assumes that the new_duty_cycle is less than the PWM_PERIOD. Scott
See also:
See:
Questions: