;;;;;;; P2 for QwikFlash board ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Use 10 MHz crystal frequency. ; Use Timer0 for ten millisecond looptime. ; Blink "Alive" LED every two and a half seconds. ; Display PORTD as a binary number. ; Toggle C2 output every ten milliseconds for measuring looptime precisely. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Mainline ; Initial ; InitLCD ; LoopTime ; BlinkAlive ; ByteDisplay (DISPLAY macro) ; DisplayC ; T40 ; DisplayV ; T40 ; LoopTime ; ;;;;;;; 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 endc ;;;;;;; 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 ;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 ;Reset vector nop goto Mainline org 0x0008 ;High priority interrupt vector goto $ ;Trap org 0x0018 ;Low priority interrupt vector goto $ ;Trap ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline rcall Initial ;Initialize everything ;LOOP_ L1 btg PORTC,RC2 ;Toggle pin, to support measuring loop time rcall BlinkAlive ;Blink "Alive" LED DISPLAY PORTD ;Display PORTD as a binary number rcall LoopTime ;Make looptime be ten milliseconds ;ENDLOOP_ bra L1 PL1 ;;;;;;; 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 rcall InitLCD 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_ L2 rcall LoopTime ;Call LoopTime 10 times decf COUNT,F ;UNTIL_ .Z. bnz L2 RL2 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_ L3 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. bnz L3 RL3 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_ L4 decf COUNT,F ;UNTIL_ .Z. bnz L4 RL4 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. bnz L5 tblrd+* ;If zero, get next byte ;ENDIF_ L5 ;REPEAT_ L6 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. bnz L6 RL6 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_ L7 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? bnz L7 RL7 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. bnz L8 MOVLF 250,ALIVECNT ;Reinitialize BLNKCNT bcf PORTA,RA4 ;Turn on LED for ten milliseconds every 2.5 sec ;ENDIF_ L8 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_ L9 ;UNTIL_ INTCON,TMR0IF == 1 ;Wait until ten milliseconds are up btfss INTCON,TMR0IF bra L9 RL9 movff INTCON,INTCONCOPY ;Disable all interrupts to CPU 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 GIEH interrupt enable bit andlw B'10000000' iorwf INTCON,F bcf INTCON,TMR0IF ;Clear Timer0 flag return ;;;;;;; ByteDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Display whatever is in BYTE as a binary number. ByteDisplay POINT BYTE_1 ;Display "BYTE=" rcall DisplayC lfsr 0,BYTESTR+8 ;REPEAT_ L10 clrf WREG rrcf BYTE,F ;Move bit into carry rlcf WREG,F ;and from there into WREG iorlw 0x30 ;Convert to ASCII movwf POSTDEC0 ; and move to string movf FSR0L,W ;Done? sublw low BYTESTR ;UNTIL_ .Z. bnz L10 RL10 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 BYTE_1 db "\x80BYTE= \x00" ;Write "BYTE=" to first line of LCD BarChars ;Bargraph user-defined characters db 0x00,0x48 ;CGRAM-positioning code db 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 ;Column 1 db 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98 ;Columns 1,2 db 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c ;Columns 1,2,3 db 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e ;Columns 1,2,3,4 db 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f ;Column 1,2,3,4,5 db 0x00 ;End-of-string terminator end