;;;;;;; P1 PROGRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Use the RPG to change which LEDs are turned on. ; ; Use the top potentiometer to adjust the brightness of the LEDs. ; ; PORTB,bit7 is a test point (TP11) for measuring looptime and duty cycle. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Mainline ; Initial ; Brightness ; Scale ; LoopTime ; ;IntService ; Timer0 ; RPG ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC16C74A, F=INHX8M, C=160, N=77, ST=OFF, MM=OFF, R=DEC, X=OFF #include P16C74A.inc errorlevel -302 ;Ignore "error" when storing to Bank1 __config(_CP_OFF & _PWRTE_ON & _XT_OSC & _WDT_OFF & _BODEN_OFF) ;;;;;;; Equates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Bank0RAM equ H'20' ;Start of Bank 0 RAM area Switch equ B'00000001' ;Flag in FLAGS which is used by Timer0 handler LoopDone equ B'00000010' ;Flag in FLAGS which indicates looptime is done ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock Bank0RAM W_TEMP ;Temporary storage for W during interrupts STATUS_TEMP ;Temporary storage for STATUS during interrupts FLAGS ;Various flags TEMP1 ;Temporary variable SCALED ;Scaled version of ADC output E_LEDS ;PORTE LEDs to turn on D_LEDS ;PORTD LEDs to turn on endc ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest movlw literal movwf dest endm MOVFF macro source,dest movf source,W movwf dest endm ;;;;;;; Program code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org H'000' ;Reset vector goto Mainline ;Branch past tables org H'004' ;Interrupt vector goto IntService ;Branch to interrupt service routine ;;;;;;; Table subroutines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; End of Tables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline call Initial ;Initialize everything MainLoop call Brightness ;Control brightness from top potentiometer 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'00011111',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 B'00000101',OPTION_REG ;Timer0 source is OSC/4, prescaler = 64 bcf STATUS,RP0 ;Set register access back to bank 0 clrf PORTE ;Turn off LEDs clrf PORTD MOVLF B'00000011',E_LEDS ;Initialize to turn on leftmost five LEDs MOVLF B'11100000',D_LEDS clrf TMR0 ;Initialize Timer0 clrf FLAGS ;Initialize flags used by Timer0 handler MOVLF B'10110000',INTCON ;Enable RB0/INT and TMR0 interrupts return ;;;;;;; Brightness subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine uses the topmost potentiometer and the analog-to-digital ; converter to obtain a voltage proportional to pot position. This is used to ; turn the selected LEDs on or off every ten milliseconds, with the on-time ; proportional to potentiometer position. Brightness MOVLF B'01000001',ADCON0 ;Select AN0 channel of ADC bsf ADCON0,GO_DONE ; and initiate a conversion Br_1 btfss ADCON0,GO_DONE ;Wait for conversion to complete goto Br_1 call Scale ;Scale the result between 0 and 158 return ;;;;;;; Scale subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine scales the value in ADRES, the analog-to-digital converter ; output, (ranging between 0 and 255) into SCALED (ranging between 0 and 153). ; It does so by multiplying by (128+16+8+4+2)/256 (i.e., by B'.10011110'). Scale bcf STATUS,C ;Clear carry before rotate rrf ADRES,W movwf SCALED ;SCALED=ADRES/2 movwf TEMP1 ;TEMP1=ADRES/2 bcf STATUS,C rrf TEMP1,F ;TEMP1=ADRES/4 bcf STATUS,C rrf TEMP1,F ;TEMP1=ADRES/8 bcf STATUS,C rrf TEMP1,F ;TEMP1=ADRES/16 movf TEMP1,W addwf SCALED,F ;Add ADRES/16 into SCALED bcf STATUS,C rrf TEMP1,F ;TEMP1=ADRES/32 movf TEMP1,W addwf SCALED,F ;Add ADRES/32 into SCALED bcf STATUS,C rrf TEMP1,F ;TEMP1=ADRES/64 movf TEMP1,W addwf SCALED,F ;Add ADRES/64 into SCALED bcf STATUS,C rrf TEMP1,W ;TEMP1=ADRES/128 addwf SCALED,F ;SCALED=ADRES x B'(.1001111)' return ;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine is signalled by the Timer0 interrupt handler when ten ; milliseconds have occurred since the last time that the LoopDone flag was ; set. LoopTime btfss FLAGS,LoopDone ;Check whether ten milliseconds are up goto LoopTime bcf FLAGS,LoopDone ;Clear flag 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,T0IF ;Test TMR0 overflow flag goto Timer0 ;Turn selected LEDs on or off btfsc INTCON,INTF ;Test PB0/INT flag goto RPG ;Change which LEDs to blink ; 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 ;;;;;;; Timer0 handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This code uses Timer0 to set the looptime to be roughly ten milliseconds. ; It does so by incrementing TMR0 every 64th clock cycle and then using TMR0 ; to count 156 counts (i.e., 156x64 = 9984 cycles or 9984 microseconds ; or 9.984 milliseconds) during each looptime. The counting of TMR0 occurs in ; two parts of the code below, First and Second. These parts are executed in ; response to alternate TMR0 overflow interrupts. The two parts together count ; TMR0 through the 156 counts (times 64 CPU cycles per count) to give a time ; interval of about ten milliseconds. ; ; This splitting of the Timer0 handler into two parts is done to provide a ; mechanism for pulse-width-modulating the bargraph LEDs which are to be turned ; on. The duty cycle is controlled by the SCALED parameter which is ; proportional to the potentiometer setting. By turning the LEDs on and off ; during each ten millisecond looptime, they appear as constantly lit but with ; variable brightness. The scale factor, implemented by the Scale subroutine, ; insures that the calculation of 156-SCALED, below, will produce a positive ; result. Timer0 btfsc FLAGS,Switch ;First or second part of looptime? goto Second First bsf FLAGS,LoopDone ;Signal mainline code that 10 millisec is up bsf FLAGS,Switch ;Go to Second next time MOVFF E_LEDS,PORTE ;Turn on selected LEDs MOVFF D_LEDS,PORTD bsf PORTB,7 ;Test signal to measure duty cycle and period comf SCALED,W addlw 1 ;Ontime=256-255-SCALED+1) = SCALED counts movwf TMR0 ; of TMR0 btfss STATUS,Z ;If SCALED=0, then go right to Timer0_Off goto Timer0Done Second bcf FLAGS,Switch ;Go to First next time clrf PORTE ;Turn off LEDs; clrf PORTD bcf PORTB,7 ;Test signal to measure duty cycle and period movlw 100 ;Offtime = 256-(100+SCALED) = 156-SCALED addwf SCALED,W ; counts of TMR0 movwf TMR0 Timer0Done bcf INTCON,T0IF ;Clear interrupt flag goto Poll ;;;;;;; RPG handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Whenever the rotary pulse generator (RPG) generates an interrupt, this ; code handles which LEDs should next be blinked. It shifts the LED bits ; right and loads the leftmost LED bit with the complement of what was in ; the rightmost bit. RPG bcf INTCON,INTF ;Clear RBO/INT flag rrf E_LEDS,F ;Shift right rrf D_LEDS,F bcf E_LEDS,1 ;Copy complement of carry into leftmost LED btfss STATUS,C bsf E_LEDS,1 goto Poll end