;;;;;;; CH13 for GT QwikPIC board ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Use 10 MHz crystal frequency. ; Use Timer0 for ten millisecond looptime. ; Blink "Alive" LED every two and a half seconds. ; Use pushbutton to exercise Screens utility. ; Code for time interval measurements for Chapter 13. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Mainline ; Initial ; InitLCD ; LoopTime ; DisplayC ; T40 ; BlinkAlive ; LoopTime ; CyclesToMicrosec ; FXD2408U ; DecimalDisplay ; FXD2408U ; DisplayV ; T40 ; ;;;;;;; Assembler directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC18F452, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON #include P18F452.inc __CONFIG _CONFIG1H, _HS_OSC_1H ;HS oscillator __CONFIG _CONFIG2L, _PWRT_ON_2L & _BOR_ON_2L & _BORV_42_2L ;Reset __CONFIG _CONFIG2H, _WDT_OFF_2H ;Watchdog timer disabled __CONFIG _CONFIG3H, _CCP2MX_ON_3H ;CCP2 to RC1 (rather than to RB3) __CONFIG _CONFIG4L, _LVP_OFF_4L ;RB5 enabled for I/O errorlevel -314, -315 ;Ignore lfsr messages ;;;; ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock 0x000 ;Beginning of Access RAM TMR0LCOPY ;Copy of sixteen-bit Timer0 used by LoopTime TMR0HCOPY INTCONCOPY ;Copy of INTCON for LoopTime subroutine COUNT ;Counter available as local to subroutines ALIVECNT ;Counter for blinking "Alive" LED BYTE ;Eight-bit byte to be displayed BYTESTR:10 ;Display string for binary version of BYTE OLDPORTD ;Holds previous value of inputs LOOP10 ;Scale of ten loop counter TMR1X ;Extension of TMR1 TMR1LBUF ;Temporary buffer for TMR1L TMR1HBUF ;Temporary buffer for TMR1H TMR1XBUF ;Temporary buffer for TMR1X TIMEL ;Lower byte of the time-interval measurement TIMEH ;Upper byte TIMEX ;Extension byte MAXL ;Lower byte of maximum measurement MAXH ;Upper byte MAXX ;Extension byte endc #include ;;;;;;; Equates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ISC equ 0 ;Initiate screen change for slow press ISA equ 1 ;Initiate secondary action for fast press PDONE equ 2 ;Pushbutton action has been taken OLDPB equ 3 ;Old state of pushbutton NEWPB equ 4 ;New state of pushbutton PBthres equ 30 ;Pushbutton threshold for a long press ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest movlw literal movwf dest endm POINT macro stringname MOVLF high stringname, TBLPTRH MOVLF low stringname, TBLPTRL endm DISPLAY macro register movff register,BYTE call ByteDisplay endm TESTSCREEN macro literal movf SCREEN,W sublw literal endm DISPLAYONCE macro stringname btfss PBSTATE,ISC bra $+14 MOVLF high stringname, TBLPTRH MOVLF low stringname, TBLPTRL call DisplayC endm START macro movff TMR1L,TIMEL ;TIME = TMR1 movff TMR1H,TIMEH endm STOP macro movf TIMEL,W ;TIME = TMR1 - TIME subwf TMR1L,W movwf TIMEL movf TIMEH,W subwfb TMR1H,W movwf TIMEH movlw 5 ;Make correction subwf TIMEL,F btfss STATUS,C ;Skip if no borrow decf TIMEH,F endm MAX macro movf TIMEL,W ;Form MAX - TIME subwf MAXL,W movf TIMEH,W subwfb MAXH,W btfss STATUS,C ;If TIME > MAX, then update MAX with TIME movff TIMEL,MAXL btfss STATUS,C movff TIMEH,MAXH endm STARTX macro ;Save TMR1 in TIME movff TMR1X,TIMEX movff TMR1L,TIMEL movf TMR1H,W ;Copy TMR1H to TIMEH and copy bit 7 to N movwf TIMEH btfss STATUS,N ;Does TMR1 = B'0bbbbbbb bbbbbbbb'? movff TMR1X,TIMEX ;If so, then reread TMR1X endm STOPX macro ;Form TIME = TMR1 - TIME movff TMR1X,TMR1XBUF ;Form valid reading in TMR1BUF movff TMR1L,TMR1LBUF movf TMR1H,W movwf TMR1HBUF btfss STATUS,N ;Does TMR1 = B'0bbbbbbb bbbbbbbb'? movff TMR1X,TMR1XBUF ;If so, then reread TMR1X movf TIMEL,W ;Form TIME = TMR1BUF - TIME subwf TMR1LBUF,W movwf TIMEL movf TIMEH,W subwfb TMR1HBUF,W movwf TIMEH movf TIMEX,W subwfb TMR1XBUF,W movwf TIMEX movlw 9 ;Magic = 9; Make correction subwf TIMEL,F btfss STATUS,C decf TIMEH,F btfss STATUS,C decf TIMEX,F endm MAX3 macro movf TIMEL,W ;Form MAX - TIME for three-byte numbers subwf MAXL,W movf TIMEH,W subwfb MAXH,W movf TIMEX,W subwfb MAXX,W ;C=0 if TIME > MAX btfss STATUS,C ;Replace MAX with TIME if C=0 movff TIMEL,MAXL btfss STATUS,C movff TIMEH,MAXH btfss STATUS,C movff TIMEX,MAXX endm ;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 ;Reset vector nop goto Mainline org 0x0008 ;High priority interrupt vector bcf PIR1,TMR1IF ;Clear flag incf TMR1X,F ;and increment TMR1 extension retfie FAST org 0x0018 ;Low priority interrupt vector bra $ ;Trap ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline rcall Initial ;Initialize everything LOOP_ REPEAT_ rcall BlinkAlive ;Blink "Alive" LED rcall LoopTime decf LOOP10,F UNTIL_ .Z. MOVLF 100, LOOP10 STARTX rcall LoopTime movf TMR1H,W ;Create a small random interval xorwf TMR1L,W REPEAT_ decf WREG,F UNTIL_ .Z. STOPX MAX3 movff MAXL,AARGB2 movff MAXH,AARGB1 movff MAXX,AARGB0 rcall CyclesToMicrosec rcall DecimalDisplay ENDLOOP_ ;;;;;;; Initial subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine performs all initializations of variables and registers. Initial MOVLF B'10001110',ADCON1 ;Enable PORTA & PORTE digital I/O pins MOVLF B'11100001',TRISA ;Set I/O for PORTA MOVLF B'11011100',TRISB ;Set I/O for PORTB MOVLF B'11010000',TRISC ;Set I/0 for PORTC MOVLF B'00001111',TRISD ;Set I/O for PORTD MOVLF B'00000000',TRISE ;Set I/O for PORTE MOVLF B'10001000',T0CON ;Set up Timer0 for a looptime of 10 ms MOVLF B'00010000',PORTA ;Turn off all four LEDs driven from PORTA MOVLF B'10000001',T1CON ;Set up Timer1 for time-interval measurements clrf MAXL ;Start maximum time interval at zero clrf MAXH clrf MAXX clrf TEMP MOVLF 1,LOOP10 rcall InitLCD ;Initialize LCD movff PORTD,OLDPORTD ;Initialize "old" value POINT PWmaxStr ;Display line 1 message rcall DisplayC bsf IPR1,TMR1IP ;Assign high priority to TMR1 overflow interrupt bcf PIR1,TMR1IF ;Clear flag bsf PIE1,TMR1IE ;Enable TMR1 overflow interrupts bsf RCON,IPEN ;Enable high/low interrupt structure bsf INTCON,GIEH ;Enable high priority interrupts to CPU return ;;;;;;; InitLCD subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Initialize the Optrex 8x2 character LCD. ; First wait for 0.1 second, to get past display's power-on reset time. InitLCD MOVLF 10,COUNT ;Wait 0.1 second REPEAT_ rcall LoopTime ;Call LoopTime 10 times decf COUNT,F UNTIL_ .Z. bcf PORTE,0 ;RS=0 for command POINT LCDstr ;Set up table pointer to initialization string tblrd* ;Get first byte from string into TABLAT REPEAT_ bsf PORTE,1 ;Drive E high movff TABLAT,PORTD ;Send upper nibble bcf PORTE,1 ;Drive E low so LCD will process input rcall LoopTime ;Wait ten milliseconds bsf PORTE,1 ;Drive E high swapf TABLAT,W ;Swap nibbles movwf PORTD ;Send lower nibble bcf PORTE,1 ;Drive E low so LCD will process input rcall LoopTime ;Wait ten milliseconds tblrd+* ;Increment pointer and get next byte movf TABLAT,F ;Is it zero? UNTIL_ .Z. return ;;;;;;; CyclesToMicrosec subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine converts AARGB0:AARGB1:AARGB2 from cycles to microseconds. ; Microseconds = 0.4 Cycles = (Cycles/10)x4 CyclesToMicrosec MOVLF 10,BARGB0 ;Divide by 10 call FXD2408U bcf STATUS,C ;Multiply by two rlcf AARGB2,F rlcf AARGB1,F rlcf AARGB0,F rlcf AARGB2,F ;Do it again rlcf AARGB1,F rlcf AARGB0,F movf REMB0,W ;Get remainder and double it addwf WREG,W ; as a BCD number daw addwf WREG,W ;Double it again daw addlw 5 ;Round off daw andlw 0xf0 ;Keep just tens digit swapf WREG,W ; and move it to the units position addwf AARGB2,F ; and add it to AARG clrf WREG addwfc AARGB1,F addwfc AARGB0,F return ;;;;;;; DisplayMax subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine takes MAXH:MAXL, converts it to microseconds, and displays ; it on the second line of the LCD. DisplayMax movff MAXL,AARGB2 movff MAXH,AARGB1 clrf AARGB0 rcall CyclesToMicrosec rcall DecimalDisplay return ;;;;;;; T40 subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Pause for 40 microseconds or 40/0.4 = 100 clock cycles. ; Assumes 10/4 = 2.5 MHz internal clock rate. T40 movlw 100/3 ;Each REPEAT loop takes 3 cycles movwf COUNT REPEAT_ decf COUNT,F UNTIL_ .Z. return ;;;;;;;;DisplayC subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine is called with TBLPTR containing the address of a constant ; display string. It sends the bytes of the string to the LCD. The first ; byte sets the cursor position. The remaining bytes are displayed, beginning ; at that position. ; This subroutine expects a normal one-byte cursor-positioning code, 0xhh, or ; an occasionally used two-byte cursor-positioning code of the form 0x00hh. DisplayC bcf PORTE,0 ;Drive RS pin low for cursor-positioning code tblrd* ;Get byte from string into TABLAT movf TABLAT,F ;Check for leading zero byte IF_ .Z. tblrd+* ;If zero, get next byte ENDIF_ REPEAT_ bsf PORTE,1 ;Drive E pin high movff TABLAT,PORTD ;Send upper nibble bcf PORTE,1 ;Drive E pin low so LCD will accept nibble bsf PORTE,1 ;Drive E pin high again swapf TABLAT,W ;Swap nibbles movwf PORTD ;Write lower nibble bcf PORTE,1 ;Drive E pin low so LCD will process byte rcall T40 ;Wait 40 usec bsf PORTE,0 ;Drive RS pin high for displayable characters tblrd+* ;Increment pointer, then get next byte movf TABLAT,F ;Is it zero? UNTIL_ .Z. return ;;;;;;; DisplayV subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine is called with FSR0 containing the address of a variable ; display string. It sends the bytes of the string to the LCD. The first ; byte sets the cursor position. The remaining bytes are displayed, beginning ; at that position. DisplayV bcf PORTE,0 ;Drive RS pin low for cursor positioning code REPEAT_ bsf PORTE,1 ;Drive E pin high movff INDF0,PORTD ;Send upper nibble bcf PORTE,1 ;Drive E pin low so LCD will accept nibble bsf PORTE,1 ;Drive E pin high again swapf INDF0,W ;Swap nibbles movwf PORTD ;Write lower nibble bcf PORTE,1 ;Drive E pin low so LCD will process byte rcall T40 ;Wait 40 usec bsf PORTE,0 ;Drive RS pin high for displayable characters movf PREINC0,W ;Increment pointer, then get next byte UNTIL_ .Z. ;Is it zero? return ;;;;;;; BlinkAlive subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine briefly blinks the LED next to the PIC every two-and-a-half ; seconds. BlinkAlive bsf PORTA,RA4 ;Turn off LED decf ALIVECNT,F ;Decrement loop counter and return if not zero IF_ .Z. MOVLF 250,ALIVECNT ;Reinitialize BLNKCNT bcf PORTA,RA4 ;Turn on LED for ten milliseconds every 2.5 sec ENDIF_ return ;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for Timer0 to complete its ten millisecond count ; sequence. It does so by waiting for sixteen-bit Timer0 to roll over. To obtain ; a period of precisely 10000/0.4 = 25000 clock periods, it needs to remove ; 65536-25000 or 40536 counts from the sixteen-bit count sequence. The ; algorithm below first copies Timer0 to RAM, adds "Bignum" to the copy ,and ; then writes the result back to Timer0. It actually needs to add somewhat more ; counts to Timer0 than 40536. The extra number of 12+2 counts added into ; "Bignum" makes the precise correction. Bignum equ 65536-25000+12+2 LoopTime REPEAT_ UNTIL_ INTCON,TMR0IF == 1 ;Check whether ten milliseconds are up movff INTCON,INTCONCOPY ;Disable all interrupts to CPU bcf INTCON,GIEL bcf INTCON,GIEH movff TMR0L,TMR0LCOPY ;Read 16-bit counter at this moment movff TMR0H,TMR0HCOPY movlw low Bignum addwf TMR0LCOPY,F movlw high Bignum addwfc TMR0HCOPY,F movff TMR0HCOPY,TMR0H movff TMR0LCOPY,TMR0L ;Write 16-bit counter at this moment movf INTCONCOPY,W ;Restore GIEL and GIEH interrupt enable bits andlw B'11000000' iorwf INTCON,F bcf INTCON,TMR0IF ;Clear Timer0 flag return ;;;;;;; DecimalDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Display whatever is in AARGB0:AARGB1:AARGB2 as a decimal number on line 2 ; of the LCD DecimalDisplay lfsr 0,BYTESTR+8 REPEAT_ MOVLF 10,BARGB0 ;Divide AARG by ten call FXD2408U movf REMB0,W ;Get digit iorlw 0x30 ;Convert to ASCII movwf POSTDEC0 ; and move to string movf FSR0L,W ;Done? sublw low BYTESTR UNTIL_ .Z. REPEAT_ ;Blank leading zeros movlw A'0' ;ASCII code for zero subwf PREINC0,W ;Leading zero? IF_ .Z. ;If so, then blank it MOVLF A' ',INDF0 ELSE_ ;Otherwise, done with blanking BREAK_ ENDIF_ movf FSR0L,W ;In any case, stop at least-significant digit sublw low BYTESTR+7 UNTIL_ .Z. lfsr 0,BYTESTR ;Set pointer to display string MOVLF 0xc0,BYTESTR ;Add cursor-positioning code clrf BYTESTR+9 ;and end-of-string terminator rcall DisplayV return ;;;;;;; Constant strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LCDstr db 0x33,0x32,0x28,0x01,0x0c,0x06,0x00 ;Initialization string for LCD PWmaxStr db "\x80PWmax us\x00" ;Maximum pulse width instrument #include end