Code:
TITLE 'SUPER PWM FOR 12-BIT PIC' ;-------------------------------------------------------------------------------------------------------------------------------------- ; PROGRAM DESCRIPTION ;-------------------------------------------------------------------------------------------------------------------------------------- ; ; THIS EXAMPLE SHOWS HOW TO CREATE A HIGH RESOLUTION VARIABLE PWM IN A 12-BIT PIC ; ; FEATURES: ; 16-BIT BIT-BANG PWM ; VARIABLE PERIOD ; VARIABLE DUTY CYCLE ; INSTRUCTION CYCLE RESOLUTION ; NO INTERRUPTS ; ; THIS PROGRAM EXECUTES THE MAIN PROGRAM CODE IN SMALL CHUNKS, BETWEEN THESE CHUNKS THE PROGRAM LOOKS AT THE TIMER VALUE AND ; UPDATES THE OUTPUT WHEN NEEDED ; ; NOTE: DUE TO THE TIMER OVERHEAD THERE IS A LIMIT TO THE MAXIMUM FREQUENCY (VERY SHORT PERIODS ARE NOT POSSIBLE WITH THIS CODE) ; ;-------------------------------------------------------------------------------------------------------------------------------------- ; CONSTANTS DEFINITIONS ;-------------------------------------------------------------------------------------------------------------------------------------- ; GPIO BITS PWM EQU 0 ; PWM OUTPUT (ACTIVE HIGH) ;-------------------------------------------------------------------------------------------------------------------------------------- ; VARIABLE DEFINITIONS ;-------------------------------------------------------------------------------------------------------------------------------------- CBLOCK H'10' TIMER_LOW ; ENABLE PROBLEM FREE (ROLLOVER FREE) TESTING AND COPYING OF THE TIMER TIMER_HIGH ; USED TO EXTEND THE TIMER0 RESOLUTION TO A 16-BIT TOTAL CALCPOINTER ; USED TO SEQUENTIALLY PERFORM ALL CALCULATIONS A0 ; PWM HIGH TIME (INSTRUCTION CYCLES) A1 ; B0 ; PWM LOW TIME (INSTRUCTION CYCLES) TO BE COPIED TO Y B1 ; Y0 ; PWM LOW TIME (INSTRUCTION CYCLES) Y1 ; ENDC ;-------------------------------------------------------------------------------------------------------------------------------------- ; START OF PROGRAM ;-------------------------------------------------------------------------------------------------------------------------------------- ORG H'000' ; TELL THE ASSEMBLER TO PLACE FOLLOWING PROGRAM CODE AT BOTTOM OF THE PROGRAM MEMORY START MOVLW B'00000000' ; MAKE GP0(PWM) LOW: PWM OFF MOVWF GPIO ; MOVLW B'11111110' ; MAKE GP0(PWM) OUTPUT TRIS GPIO ; CLRF ADCON0 ; AD-CONVERTER OFF CLRWDT ; WE NEED TO CLEAR THE WATCHDOG TIMER FIRST OR ELSE WE MAY GET A RESET, ALSO CLEARS PRESCALER CLRF TMR0 ; CLEAR TIMER0 AND THE PRESCALER MOVLW B'11001111' ; VALUE FOR NO WAKE-UP ON PIN CHANGE, NO PULL-UPS, TMR0 FROM INSTRUCTION CLOCK 1:1, ASSIGN THE OPTION ; PRESCALER TO THE WATCHDOG 1:128 (2.3 SECOND WATCHDOG TIMEOUT) CLRF A0 ; PWM FREQUENCY IS 100 HZ SO PWM PERIOD IS 10000 INSTRUCTIONS (FOSC = 4 MHZ) CLRF A1 ; A1:A0 = PWM HIGH TIME = 0 MOVLW D'16' ; B1:B0 = PWM LOW TIME = 10000 = 39:16 MOVWF B0 ; MOVLW D'39' ; MOVWF B1 ; GOTO PWM_DOWNFLANK_4 ; NOW GO RUN PWM LOOP ;-------------------------------------------------------------------------------------------------------------------------------------- ; PWM CODE ;-------------------------------------------------------------------------------------------------------------------------------------- JUMP_TO_CALC MOVF CALCPOINTER,W ; WE PERFORM ALL CALCULATION PARTS SEQUENTIALLY SINCE WE MUST ALLOW TIME FOR THE MAIN LOOP MOVWF PCL ; JUMP TO CALCULATION ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; BECAUSE THE 8TH BIT WILL ALWAYS BE RESET TO ZERO ;-------------------------------------------------------------------------------------------------------------------------------------- WAIT4DOWNFLANK CALL JUMP_TO_CALC ; GO DO MAIN STUFF AND PWM CALCULATIONS (THIS LINE CAN TAKE UP TO 36 INSTRUCTION CYCLES) MOVWF CALCPOINTER ; USE RETURN VALUE TO KEEP TRACK OF CALCULATION PROGRESS MOVF TMR0,W ; WE WANT A COPY FOR PROBLEM FREE (ROLLOVER FREE) TESTING AND COPYING OF THE TIMER VALUE SUBWF TIMER_LOW,F ; TEST FOR ROLLOVER, WE NEED TO BE HERE AT LEAST EVERY 256 INSTRUCTIONS OR WE'LL MISS ROLLOVER MOVWF TIMER_LOW ; SKPNC ; HAS TMR0 ROLLED OVER ? INCF TIMER_HIGH,F ; YES, INCREMENT TIMER HIGH BYTE MOVLW D'105' ; SEE IF IS TIME TO UPDATE THE PWM OUTPUT OR IF WE CAN DO THE OTHER STUFF ADDWF TIMER_LOW,W ; CHECK FOR ROLLOVER: T+36+25+44 = T + 105 = 256 = ROLLOVER, SO WE NEED TO ADD 105 SKPNC ; ROLLOVER ? INCFSZ TIMER_HIGH,W ; IS THERE TIME LEFT FOR THE OTHER TASKS ? GOTO WAIT4DOWNFLANK ; YES, GO DO THESE OTHER TASKS ;--------------- ---------------------------------------------------------------------------------------------- MOVLW D'44' ; CORRECT FOR OVERHEAD, WE WANT TO CHANGE THE PWM OUTPUT AT EXACT MOMENT OF TIMER ROLLOVER ADDWF TMR0,F ; RESULT OF THIS ADD MUST BE 255 MAXIMUM OR WE GET ILLEGAL JUMP ; REMEMBER TIMER KEEPS THE NEW VALUE FOR THREE FOLLOWING CYCLES BEFORE INCREMENTING ; BUT WE ALREADY START TESTING TIMER VALUE LOOP_1 BTFSC TMR0,7 ; HAS TIMER 0 ROLLED OVER ? GOTO LOOP_1 ; NO, LOOP AGAIN DECF TMR0,W ; W=1,2,3 ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT OF THE PROGRAM COUNTER WILL ALWAYS BE RESET TO ZERO C0 RETLW LOW(C0) ; PCL+0ALL CALCULATIONS DONE, WAITING FOR START OF NEW PWM PERIOD NOP ; PCL+1 NOP ; PCL+2 COMF Y0,W ; PCL+3, SET NEW TIME SETPOINT: MOVWF TMR0 ; T = 65535 - Y = 65535 - PWM LOW TIME MOVWF TIMER_LOW ; COMF Y1,W ; MOVWF TIMER_HIGH ; ;--------------- ---------------------------------------------------------------------------------------------- CHOOSE_DELAY_1 MOVLW D'123' ; PCL+3, SHORTEST TIME FOR LONG DELAY IS 123? INSTRUCTIONS (INCLUDING MEAS/CALC) SUBWF Y0,W ; CARRY IS CLEAR WHEN LESS MOVF Y1,W ; TEST HIGH BYTE OF DELAY TIME FOR ZERO SKPNZ ; IS THE HIGH BYTE ZERO ? SKPNC ; YES, IS THE LOW BYTE LESS THAN 123 ? GOTO PWMLONGDELAY2 ; NO, HIGH BYTE IS NOT ZERO/NO, LOW BYTE IS MORE, GO USE THE LONG DELAY LOOP MOVLW D'16' ; VARIABLE DELAY FROM 0..255 INSTRUCTIONS SUBWF Y0,W ; CHECK THE DELAY TIME SKPC ; IS THE DESIRED DELAY MORE THAN 16 ? GOTO LOW_VERYSHORT ; NO, SO THE PWM LOW TIME IS VERY SMALL: 0..15 CYCLES, GO USE THE VERY SHORT DELAY ;--------------- ---------------------------------------------------------------------------------------------- PWM_LOW_SHORT MOVWF TIMER_HIGH ; SET DELAY GOTO $+1 ; THREE INSTRUCTION CYCLE DELAY NOP ; MOVLW D'4' ; NEEDED FOR ADJUSTABLE DELAY PWM_DOWNFLANK_1 BCF GPIO,PWM ; MAKE THE PWM OUTPUT LOW LOOP_2 SUBWF TIMER_HIGH,F ; WE USE AN ADJUSTABLE DELAY SKPNC ; GOTO LOOP_2 ; COMF TIMER_HIGH,W ; IORLW B'00001000' ; ADD 8 TO W TO JUMP OVER VERY SHORT DELAY ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT WILL ALWAYS BE RESET TO ZERO ;--------------- ---------------------------------------------------------------------------------------------- LOW_VERYSHORT ADDWF Y0,W ; INVERT W AND THEN INCREMENT W SUBWF Y0,W ; ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT WILL ALWAYS BE RESET TO ZERO NOP ; PCL+0 PWM_DOWNFLANK_2 BCF GPIO,PWM ; 15, PCL+1, MAKE THE PWM OUTPUT LOW BCF GPIO,PWM ; 14, PCL+2 BCF GPIO,PWM ; 13 BCF GPIO,PWM ; 12 BCF GPIO,PWM ; 11 BCF GPIO,PWM ; 10 BCF GPIO,PWM ; 9 BCF GPIO,PWM ; 8 BCF GPIO,PWM ; 7 BCF GPIO,PWM ; 6 BCF GPIO,PWM ; 5 BCF GPIO,PWM ; 4 BCF GPIO,PWM ; 3 BCF GPIO,PWM ; 2 BCF GPIO,PWM ; 1 ;--------------- ---------------------------------------------------------------------------------------------- PWM_UPFLANK_1 BSF GPIO,PWM ; 0: MAKE THE PWM OUTPUT HIGH MOVF B0,W ; COPY NEW PWM TIMING VALUES: MOVWF Y0 ; Y = B = PWM LOW TIME MOVF B1,W ; MOVWF Y1 ; MOVLW LOW(C1) ; START NEW CALCULATIONS MOVWF CALCPOINTER ; COMF A1,W ; SET NEW TIME SETPOINT: MOVWF TIMER_HIGH ; T = 65535 - A = 65535 - PWM HIGH TIME COMF A0,W ; MOVWF TMR0 ; THE EXACT MOMENT OF WRITING THE NEW VALUE TO TMR0 IS CRITICAL MOVWF TIMER_LOW ; GOTO WAIT4DOWNFLANK ; ;-------------------------------------------------------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------------------------------------------------------- PWMLONGDELAY2 CALL C0 ; EIGHT CYCLE DELAY CALL C0 ; PWM_DOWNFLANK_3 BCF GPIO,PWM ; MAKE THE PWM OUTPUT LOW ;--------------- ---------------------------------------------------------------------------------------------- WAIT4UPFLANK CALL JUMP_TO_CALC ; GO DO MAIN STUFF AND PWM CALCULATIONS (THIS LINE CAN TAKE UP TO 36 INSTRUCTION CYCLES) MOVWF CALCPOINTER ; USE RETURN VALUE TO KEEP TRACK OF CALCULATION PROGRESS MOVF TMR0,W ; WE WANT A COPY FOR PROBLEM FREE (ROLLOVER FREE) TESTING AND COPYING OF THE TIMER VALUE SUBWF TIMER_LOW,F ; TEST FOR ROLLOVER, WE NEED TO BE HERE AT LEAST EVERY 256 INSTRUCTIONS OR WE'LL MISS ROLLOVER MOVWF TIMER_LOW ; SKPNC ; HAS TMR0 ROLLED OVER ? INCF TIMER_HIGH,F ; YES, INCREMENT TIMER HIGH BYTE MOVLW D'72' ; SEE IF IS TIME TO UPDATE THE PWM OUTPUT OR IF WE CAN DO THE OTHER STUFF ADDWF TIMER_LOW,W ; CHECK FOR ROLLOVER: T+36+25+11 = T + 72 = 256 = ROLLOVER, SO WE NEED TO ADD 72 SKPNC ; ROLLOVER ? INCFSZ TIMER_HIGH,W ; IS THERE TIME LEFT FOR THE OTHER TASKS ? GOTO WAIT4UPFLANK ; YES, GO DO THESE OTHER TASKS ;--------------- ---------------------------------------------------------------------------------------------- MOVLW D'11' ; CORRECT FOR OVERHEAD, WE WANT TO CHANGE THE PWM OUTPUT AT EXACT MOMENT OF TIMER ROLLOVER ADDWF TMR0,F ; RESULT OF THIS ADD MUST BE 255 MAXIMUM OR WE GET ILLEGAL JUMP ; REMEMBER TIMER KEEPS THE NEW VALUE FOR THREE FOLLOWING CYCLES BEFORE INCREMENTING ; BUT WE ALREADY START TESTING TIMER VALUE LOOP_3 BTFSC TMR0,7 ; HAS TIMER 0 ROLLED OVER ? GOTO LOOP_3 ; NO, LOOP AGAIN DECF TMR0,W ; W=1,2,3 ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT OF THE PROGRAM COUNTER WILL ALWAYS BE RESET TO ZERO NOP ; PCL+0 NOP ; PCL+1 NOP ; PCL+2 COMF A0,W ; PCL+3, SET NEW TIME SETPOINT: MOVWF TMR0 ; T = 65535 - A = 65535 - NEW PWM HIGH TIME MOVWF TIMER_LOW ; THE EXACT MOMENT OF WRITING THE NEW VALUE TO TMR0 IS CRITICAL COMF A1,W ; MOVWF TIMER_HIGH ; ;--------------- ---------------------------------------------------------------------------------------------- CHOOSE_DELAY_2 MOVLW D'123' ; PCL+3, SHORTEST TIME FOR LONG DELAY IS 123? INSTRUCTIONS (INCLUDING MEAS/CALC) SUBWF A0,W ; CARRY IS CLEAR WHEN LESS MOVF A1,W ; TEST HIGH BYTE OF DELAY TIME FOR ZERO SKPNZ ; IS THE HIGH BYTE ZERO ? SKPNC ; YES, IS THE LOW BYTE LESS THAN 123 ? GOTO DELAYED_JUMP ; NO, HIGH BYTE IS NOT ZERO/NO, LOW BYTE IS MORE, GO USE THE LONG DELAY LOOP MOVLW D'16' ; VARIABLE DELAY FROM 0..255 INSTRUCTIONS SUBWF A0,W ; CHECK THE DELAY TIME SKPC ; IS THE DESIRED DELAY MORE THAN 16 ? GOTO HIGH_VERYSHORT ; NO, SO THE PWM LOW TIME IS VERY SMALL: 0..15 CYCLES, GO USE THE VERY SHORT DELAY ;--------------- ---------------------------------------------------------------------------------------------- PWM_HIGH_SHORT MOVWF TIMER_HIGH ; SET DELAY GOTO $+1 ; THREE INSTRUCTION CYCLE DELAY NOP ; MOVLW D'4' ; NEEDED FOR ADJUSTABLE DELAY PWM_UPFLANK_2 BSF GPIO,PWM ; MAKE THE PWM OUTPUT HIGH LOOP_4 SUBWF TIMER_HIGH,F ; WE USE AN ADJUSTABLE DELAY SKPNC ; GOTO LOOP_4 ; COMF TIMER_HIGH,W ; IORLW B'00001000' ; ADD 8 TO W TO JUMP OVER VERY SHORT DELAY ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT WILL ALWAYS BE RESET TO ZERO ;--------------- ---------------------------------------------------------------------------------------------- HIGH_VERYSHORT ADDWF A0,W ; INVERT W AND THEN INCREMENT W SUBWF A0,W ; ADDWF PCL,F ; NOTE: FOR THIS 12-BIT PROCESSOR WE CAN ONLY JUMP THIS WAY IN THE LOWER 256 MEMORY LOCATIONS, ; REMEMBER THAT THE 8TH BIT WILL ALWAYS BE RESET TO ZERO NOP ; PCL+0 PWM_UPFLANK_3 BSF GPIO,PWM ; 15, PCL+1, MAKE THE PWM OUTPUT HIGH BSF GPIO,PWM ; 14, PCL+2 BSF GPIO,PWM ; 13 BSF GPIO,PWM ; 12 BSF GPIO,PWM ; 11 BSF GPIO,PWM ; 10 BSF GPIO,PWM ; 9 BSF GPIO,PWM ; 8 BSF GPIO,PWM ; 7 BSF GPIO,PWM ; 6 BSF GPIO,PWM ; 5 BSF GPIO,PWM ; 4 BSF GPIO,PWM ; 3 BSF GPIO,PWM ; 2 BSF GPIO,PWM ; 1 ;--------------- ---------------------------------------------------------------------------------------------- IF $ > H'FF' ; LET THE ASSEMBLER WARN US IF WE PUT THE FOLLOWING INSTRUCTION AT AN ADDRESS > 255 ERROR 'WE CANNOT JUMP TO THIS LOCATION !' ENDIF ; ; REMEMBER THAT ALL SUBROUTINE CALLS OR COMPUTED JUMPS ARE LIMITED TO THE FIRST 256 PROGRAM ; MEMORY LOCATIONS ! WE CAN'T USE "ADDWF PCL,F","CALL","MOVWF PCL,F" OR "BSF PCL,X" ; INSTRUCTIONS TO JUMP TO AN ADDRESS > 255, BUT WE CAN STILL USE 'GOTO' AND 'RETLW'. ;--------------- ---------------------------------------------------------------------------------------------- PWM_DOWNFLANK_4 BCF GPIO,PWM ; 0: MAKE THE PWM OUTPUT LOW MOVLW D'29' ; MAKE CORRECTION SUBWF B0,F ; SKPC ; DECF B1,F ; COMF B0,W ; SET NEW TIME SETPOINT: MOVWF TMR0 ; T = 65535 - B = 65535 - PWM HIGH TIME MOVWF TIMER_LOW ; THE EXACT MOMENT OF WRITING THE NEW VALUE TO TMR0 IS CRITICAL COMF B1,W ; MOVWF TIMER_HIGH ; MOVLW LOW(C1) ; START NEW CALCULATIONS MOVWF CALCPOINTER ; GOTO WAIT4UPFLANK ; ;-------------------------------------------------------------------------------------------------------------------------------------- DELAYED_JUMP GOTO $+1 ; GOTO PWM_UPFLANK_1 ; ;-------------------------------------------------------------------------------------------------------------------------------------- ; MAIN PROGRAM CODE ;-------------------------------------------------------------------------------------------------------------------------------------- C1 ; MAIN PROGRAM CODE GOES HERE (NOT MORE THAN 32 INSTRUCTION CYCLES) RETLW LOW(C2) ; THIS PART OF CALCULATIONS DONE ;-------------------------------------------------------------------------------------------------------------------------------------- C2 ; ..MORE PROGRAM CODE GOES HERE (NOT MORE THAN 32 INSTRUCTION CYCLES) RETLW LOW(C3) ; THIS PART OF CALCULATIONS DONE ;-------------------------------------------------------------------------------------------------------------------------------------- C3 ; ..MORE PROGRAM CODE GOES HERE (NOT MORE THAN 32 INSTRUCTION CYCLES) RETLW LOW(C4) ; THIS PART OF CALCULATIONS DONE ;-------------------------------------------------------------------------------------------------------------------------------------- C4 ; ..MORE PROGRAM CODE GOES HERE (NOT MORE THAN 32 INSTRUCTION CYCLES) RETLW LOW(C5) ; THIS PART OF CALCULATIONS DONE ;-------------------------------------------------------------------------------------------------------------------------------------- C5 ; NOTE: DO NOT CHANGE THE DUTY CYCLE FROM MINIMUM TO MAXIMUM (OR VICE VERSA) INSTANTLY, ; PUT SOME AVERAGING CODE HERE (OR THE PULSE WIDTH MAY BE AFFECTED TEMPORARILY) MOVLW D'208' ; SET A1:A0 = PWM HIGH TIME = 2000 = 7:208 MOVWF A0 ; MOVLW D'7' ; MOVWF A1 ; ; NOW A IS NEW PWM HIGH TIME (TO BE USED IN PWM CODE) MOVLW D'16' ; SET B1:B0 = PWM PERIOD = 10000 = 39:16 MOVWF B0 ; MOVLW D'39' ; MOVWF B1 ; ; NOW CALCULATE B, THE NUMBER OF INSTRUCTIONS THAT PWM OUTPUT IS LOW PER PWM PERIOD MOVF A0,W ; B = B - A SUBWF B0,F ; MOVF A1,W ; SKPC ; INCFSZ A1,W ; SUBWF B1,F ; ; NOW B IS NEW PWM LOW TIME (TO BE USED IN PWM CODE) RETLW LOW(C0) ; ALL CALCULATIONS DONE ! ;-------------------------------------------------------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------------------------------------------------------- ORG H'100' ; ADDRESS BEYOND THE JUMP RANGE OF A MODIFY PCL INSTRUCTION C6 ; WE CANNOT MODIFY THE PCL REGISTER TO JUMP HERE DIRECTLY, BUT WE CAN PLACE A GOTO POINTING TO ; HERE IN THE LOWER PART OF THE MEMORY AND JUMP TO THE GOTO INSTEAD ; ..MORE PROGRAM CODE GOES HERE (NOT MORE THAN 32 INSTRUCTION CYCLES) RETLW LOW(C0) ; THIS PART OF CALCULATIONS DONE ;-------------------------------------------------------------------------------------------------------------------------------------- END ; TELL THE ASSEMBLER TO STOP ;--------------------------------------------------------------------------------------------------------------------------------------