;;;;;;; P1 PROGRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Toggle the top three LEDs every half second in sequence: bottom, middle, top. ; Use 4 MHz crystal for 1 microsecond internal clock period. ; Use Timer2 to obtain ten millisecond loop time. ; Toggle bit 5 of PORTA each time around the mainline loop (to test loop time). ; Echo RPG "encoder emulator" outputs to the bottom two LEDs in response to ; presses of the "INC" and "DEC" pushbuttons. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Mainline ; Initial ; Blink ; BlinkTable ; LoopTime ; ;IntService ; RPG ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC16C74A, F=INHX8M, C=160, N=77, ST=OFF, MM=OFF, R=DEC, X=OFF #include P16C74A.inc __config(_CP_OFF & _PWRTE_ON & _XT_OSC & _WDT_OFF & _BODEN_OFF) ;;;;;;; Equates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Bank0RAM equ H'20' ;Start of Bank 0 RAM area MaxCount equ 50 ;Number of loops in half a second ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock Bank0RAM W_TEMP ;Temporary storage for W during interrupts STATUS_TEMP ;Temporary storage for STATUS during interrupts BLNKCNT ;Loop counter for blinking LEDs every half sec. TEMP ;Temporary variable for BlinkTable subroutine endc ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest movlw literal movwf dest endm MOVFF macro source,dest movf source,W movwf dest endm ;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org H'000' ;Reset vector goto Mainline ;Branch past tables org H'004' ;Interrupt vector goto IntService ;Branch to interrupt service routine ;;;;;;; BlinkTable subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine reads PORTD and retains only the upper three LED bits. It ; uses them to access table. It returns in W the bits of PORTD to be toggled. BlinkTable MOVFF PORTD,TEMP ;Copy present state of LEDs to TEMP swapf TEMP,F ;Move bits 7,6,5 to bits 3,2,1 rrf TEMP,W ; and on to bits 2,1,0 of W andlw B'00000111' ;Keep only bits to be shifted addwf PCL,F ;Change PC with PCLATH and offset in W retlw B'00100000' ;(000 -> 001) reinitialize to bottom retlw B'01100000' ; 001 -> 010 bottom to middle retlw B'11000000' ; 010 -> 100 middle to top retlw B'01000000' ;(011 -> 001) reinitialize to bottom retlw B'10100000' ; 100 -> 001 top to bottom retlw B'10000000' ;(101 -> 001) reinitialize to bottom retlw B'11100000' ;(110 -> 001) reinitialize to bottom retlw B'11000000' ;(111 -> 001) reinitialize to bottom ;;;;;;; End of Tables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline call Initial ;Initialize everything MainLoop call Blink ;Blink upper three LEDs call LoopTime ;Force loop time to be ten milliseconds goto MainLoop ;;;;;;; Initial subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine performs all initializations of variables and registers. Initial bsf STATUS,RP0 ;Set register access to bank 1 MOVLF B'00000100',ADCON1 ;Select PORTA pins for ADC or digital I/O MOVLF B'00001011',TRISA ;Set I/O for PORTA MOVLF B'00001111',TRISB ;Set I/O for PORTB MOVLF B'11010111',TRISC ;Set I/O for PORTC clrf TRISD ;Set I/O for PORTD MOVLF B'00000100',TRISE ;Set I/O for PORTE MOVLF 249,PR2 ;Set up Timer2 for a looptime of 10 ms bcf STATUS,RP0 ;Set register access back to bank 0 MOVLF B'01001101',T2CON ;Finish set up of Timer2 (see page 62) clrf PORTD ;Turn off LEDs MOVLF B'11010000',INTCON ;Enable RB0/INT interrupts (see page 98) return ;;;;;;; Blink subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine blinks a new LED every 0.5 second. Blink decfsz BLNKCNT,F ;Decrement loop counter and return if not zero goto BlinkEnd movlw MaxCount ;Reinitialize BLNKCNT movwf BLNKCNT call BlinkTable ;Get bits to change into W xorwf PORTD,F ; and toggle them into PORTD BlinkEnd return ;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for Timer2 to complete its ten millisecond count ; sequence. LoopTime btfss PIR1,TMR2IF ;Check whether ten milliseconds are up goto LoopTime bcf PIR1,TMR2IF ;Clear flag movlw B'00100000' ;Toggle PORTA, bit 5 xorwf PORTA,F ; for testing loop time return ;;;;;;; IntService interrupt service routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This interrupt service routine fields all interrupts. It first sets aside W ; and STATUS. It assumes that direct addressing will not be used in the ; mainline code to access Bank 1 addresses (once the Initial subroutine has ; been executed and interrupts enabled). It polls each possible interrupt ; source to determine whether it needs service. IntService ; Set aside W and STATUS movwf W_TEMP ;Copy W to RAM swapf STATUS,W ;Move STATUS to W without affecting Z bit movwf STATUS_TEMP ;Copy to RAM (with nibbles swapped) ; Execute polling routine Poll btfsc INTCON,INTF ;Test PB0/INT flag goto RPG ;Echo RPG outputs to lower two LEDs ; Restore STATUS and W and return from interrupt swapf STATUS_TEMP,W ;Restore STATUS bits (unswapping nibbles) movwf STATUS ; without affecting Z bit swapf W_TEMP,F ;Swap W_TEMP swapf W_TEMP,W ; and swap again into W without affecting Z bit retfie ;Return from mainline code; reenable interrupts ;;;;;;; RPG handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Whenever the simulated rotary pulse generator (RPG) generates interrupts ; via a press of either one of the "Inc" or "Dec" keyswitches, this code ; echoes the two outputs of the encoder emulator. See page 99. RPG bcf INTCON,INTF ;Clear RBO/INT flag MOVLF OPTION_REG,FSR ;Use indirect addressing for bank 1 register movlw B'01000000' ;Toggle INTEDG to catch opposite edge xorwf INDF,F btfss PORTC,0 ;Copy Inc/Dec "direction" to PORTD,1 bsf PORTD,1 btfsc PORTC,0 bcf PORTD,1 btfsc PORTB,0 ;Copy interrupt signal to PORTD,0 bsf PORTD,0 btfss PORTB,0 bcf PORTD,0 goto Poll end