Blinking LEDs with Interrupts
Hot on the heels of my success with BLINKY, we do iBLINKY, the interrupt driven version! The cool thing for me was that by the time I got this running I have Visual SlickEdit configured to execute MPASM when I tell it to compile, and the EPICC program to program the PIC when I "execute." Now I never have to leave the IDE to compile and test, woo-hoo!
; ; IBLINKY.ASM ; ; Ok, so if you followed BLINKY.ASM this is just the ; version that uses TMR0 interrupts to do the blinking. ; TITLE "IBLINKY - Blink an LED on 628" LIST P=PIC16F628, R=HEX, C=120, N=50 include "P16f628.inc" __FUSES _CP_OFF&_LP_OSC&_WDT_OFF&_LVP_OFF&_MCLRE_OFF ; ; Data bits ; CBLOCK H'20' COUNT ENDC ; ; Initialization ; ORG H'0000' ; Start VECTOR BCF STATUS, RP0 ; Set to BCF STATUS, RP1 ; .. bank 0 GOTO MAIN ; Reset the part ; ; Interrupt service routines ; ; Interrupts on the PIC vector to 0x0004 and then you have ; to figure out why you were interrupted and deal with it. ; The preamble saves W and STATUS the postamble Restores W ; and Status. Note that the SWAPF instruction is the only ; way to move data around without changing the actual ; status bits. ; ; Data Section ; ; These two variables, TMP_W and TMP_STATUS, are located ; at 0x70 because this memory appears in all banks of RAM. ; Thus the ISR does not have to mess with the bank number ; to work correctly. ; CBLOCK H'70' TMP_W ; Holder for W TMP_STATUS ; Holder for Status ENDC LED1 EQU 4 LED2 EQU 5 I_CNT EQU H'10' ; ; Code Section ; ; The code section of the ISR lives at 0x0004. Its possible ; to put a jump here however doing so adds two clocks of ; latency to servicing any interrupt. ; ORG H'0004' ; Interrupt service routine INTR_SVC: MOVWF TMP_W ; Copy W to a temp SWAPF STATUS,W ; Preserve Status MOVWF TMP_STATUS ; Copy STATUS to a temp BCF STATUS, RP0 ; Force Bank 0 BCF STATUS, RP1 ; ; ; State is saved, and we've expended 3 Tcy plus the 3 Tcy ;(4 worst case) of interrupt latency for a total of ;6(7) Tcy. ; ; Now loop through until we've satisfied all the ; pending interrupts. ; INTR_POLL: ; ... test bit to see if it is set BTFSS INTCON,T0IF ; Did Timeer0 Overflow? GOTO ISR_1 ; Nope, so check next thing. ; ; Process Timer 0 Overflow Interrupt ; BCF INTCON, T0IF ; Clear Timer 0 interrupt DECF COUNT,F ; Decrement interrupt counter INCFSZ COUNT,W ; check for overflow GOTO ISR_1 ; Nope, keep counting ; ; Count underflows when we've hit this interrupt "n" ; times, where n is the number in COUNT. ; MOVLW I_CNT ; Restore counter value MOVWF COUNT CALL TOGGLE ; Toggle the LEDs ; ; Process next interrupt. ; ISR_1: ; ; Exit the interrupt service routine. ; This involves recovering W and STATUS and then ; returning. Note that putting STATUS back ; automatically pops the bank back as well. ; This takes 6 Tcy for a total overhead of ; 12 Tcy for sync interrupts and 13 Tcy for ; async interrupts. ; INTR_EXIT: SWAPF TMP_STATUS,W ; Pull Status back into W MOVWF STATUS ; Store in status SWAPF TMP_W,F ; Prepare W to be restored SWAPF TMP_W,W ; Restore it RETFIE ; ; * * * * * * * * * * * * ; * M A I N * ; * * * * * * * * * * * * ; ; Main Body with initialization Etc. ; MAIN: BSF STATUS, RP0 ; Select Bank 1 MOVLW B'0000011' ; Set TMR0 prescaler to 256 MOVWF OPTION_REG ; Store it in OPTION BCF PORTB, LED1 ; Enable LEDs to be outputs BCF PORTB, LED2 ; BCF STATUS, RP0 ; Back to page 0 BSF INTCON, T0IE ; Enable Timer 0 to interrupt BCF INTCON,T0IF ; Reset interrupt flag BSF INTCON, GIE ; Enable interrupts BCF PORTB, LED1 BCF PORTB, LED2 ; ; At this point we've got nothing to do except wait. ; In the input capture program we'll be printing out ; to the LCD what the current pulse width is. ; WAIT: GOTO WAIT ; ; Toggle the two LEDs. ; TOGGLE: BTFSS PORTB, LED1 ; If LED2 is on turn it off GOTO $+4 BCF PORTB, LED1 ; Turn LED2 off BSF PORTB, LED2 RETURN BSF PORTB, LED1 ; Turn LED2 on BCF PORTB, LED2 RETURN END
|Listing 1: LED Blinking Program|