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+0 ALL 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
;--------------------------------------------------------------------------------------------------------------------------------------