;;;;;;; CH17 for GT QwikPIC board ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Chris Twigg ; ; Use 10 MHz crystal frequency and 2.5 MHz internal clock rate. ; Use I2C bus to: ; Blink either one or two LEDs every two and a half seconds. ; Use pushbutton to determine which LED(s) to blink. ; Read the I2C temp sensor and display the temperature every second. ; Output two sawtooth waveforms from the dual DAC. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Mainline ; Initial ; InitI2C ; SendI2C ; StartI2C ; DeviceError ; DisplayC ; T40 ; StopI2C ; InitLCD ; LoopTime ; DisplayC ; T40 ; Pushbutton ; GetI2C ; StartI2C ; DeviceError ; DisplayC ; T40 ; ReStartI2C ; GetI2CByte ; StopI2C ; Blink ; SendI2C ; StartI2C ; DeviceError ; DisplayC ; T40 ; StopI2C ; Centigrade ; GetI2C ; StartI2C ; DeviceError ; DisplayC ; T40 ; ReStartI2C ; GetI2CByte ; StopI2C ; Int2ASCII ; FXD0808U ; DisplayV ; T40 ; DACwave ; SendI2C ; StartI2C ; DeviceError ; DisplayC ; T40 ; StopI2C ; 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 TEMPVAR ;Temporary local variable I2CADD ;Holds left-justified 7-bit device address I2CBYTES ;Indicates number of bytes to transfer I2CBUF:4 ;I2C buffer for multibyte transfers I2CFLAGS ;Various indicator flags for I2C subroutines ;bit 0: 0 = WREG transfer, single byte ; 1 = INDF transfer, I2CBYTES bytes ;bit 1: 0 = Normal receive ; 1 = Commanded receive ;bit 2: 0 = Slave acknowledged ; 1 = Slave did not acknowledge ;bit 7: Previous pushbutton state BLINKCNT ;Counter for blinking LEDs TEMPCNT ;Counter for updating temperature I2CDISP ;Indicates current I2C LED Display mode, 1, 2, or 3 DAC0 ;DAC variables used for approximating sawtooth DAC1 LCDSTRING:10 ;String for LCD display endc #include ;Math routine variables ;;;;;;; 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 I2COUT macro address,byte ;Single byte I2C transfer (Send) bcf I2CFLAGS,0 ;Single byte transfer mode MOVLF address,I2CADD ;Select I2C device movlw byte ;byte to send to device rcall SendI2C ;transfer byte 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_ rcall Pushbutton ;Check for pushbutton press rcall Blink ;Blink LED(s) rcall Centigrade ;Check temperature rcall DACwave ;Update DAC outputs rcall LoopTime ;Make looptime be ten milliseconds 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'11011000',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 ;Initialize LCD rcall InitI2C ;Initialize I2C bus and devices return ;;;;;;; InitI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine initializes the I2C bus for 100 kHz transfers. ; The temperature sensor (0x48) is set for continuous temperature conversion. InitI2C MOVLF 24,SSPADD ;Initialize for 100 kHz I2C bus MOVLF 0x28,SSPCON1 ;and enable MOVLF 0x02,I2CDISP ;Initialize I2CDISP flag MOVLF 0x80,I2CFLAGS ;Initialize I2CFLAGS MOVLF 0x27,TEMPCNT ;Initialize temperature counter clrf DAC0 ;Clear DAC variables clrf DAC1 bsf I2CFLAGS,0 ;Set for multibyte transfer MOVLF 0x90,I2CADD ;I2C Thermometer address MOVLF 0x02,I2CBYTES ;Set for 2 bytes lfsr 0,I2CBUF ;Point to I2C multibyte buffer MOVLF 0xac,POSTINC0 ;Access Config byte command MOVLF 0x0a,POSTDEC0 ;Config byte rcall SendI2C ;Send control string I2COUT 0x90,0xee ;Start conversion command 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. POINT ClrScr ;Clear the LCD rcall DisplayC rcall LoopTime ;Wait 10 ms 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 ;;;;;;; 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 ;;;;;;; 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 ;Wait until ten milliseconds are up 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 ;;;;;;; StartI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine initiates an I2C transfer by sending a start condition and ; the remote device address, I2CADD. StartI2C bsf I2CFLAGS,2 ;Reset acknowledge flag bcf PIR1,SSPIF ;Clear interrupt flag bsf SSPCON2,SEN ;Generate Start Condition REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until start conditioning done bcf PIR1,SSPIF ;Clear interrupt flag movff I2CADD,SSPBUF ;Send address REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until address sent bcf PIR1,SSPIF ;Clear interrupt flag IF_ SSPCON2,ACKSTAT == 0 ;Check for acknowledge bcf I2CFLAGS,2 ;Indicate that slave acknowledges ELSE_ rcall DeviceError ;Indicate device error on LCD ENDIF_ return ;;;;;;; DeviceError subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays "Device Error" on the LCD when a device does not ; acknowledge. DeviceError POINT DevE1 ;Display "Device" rcall DisplayC POINT DevE2 ;Display "Error" rcall DisplayC goto $ return ;;;;;;; ReStartI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine initiates an I2C transfer by sending a start condition and ; the remote device address, I2CADD. ReStartI2C bcf PIR1,SSPIF ;Clear interrupt flag bsf SSPCON2,RSEN ;Generate ReStart Condition REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until start conditioning done bcf PIR1,SSPIF ;Clear interrupt flag movff I2CADD,SSPBUF ;Send address REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until address sent bcf PIR1,SSPIF ;Clear interrupt flag return ;;;;;;; StopI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine ends an I2C transfer by sending a stop condition. StopI2C bsf SSPCON2,PEN ;Generate Stop Condition REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until start conditioning done bcf PIR1,SSPIF ;Clear interrupt flag return ;;;;;;; SendI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sends a single or multiple bytes to the I2C device at ; address I2CADD. ; ; I2CFLAGS:0 = 0, a single byte from WREG is sent ; 1, I2CBYTES are sent from FSR0 ; ; I2CADD contains an address in the upper 7 bits. SendI2C bcf I2CADD,0 ;Clear lower bit of address to indicate Write rcall StartI2C ;Start I2C transfer IF_ I2CFLAGS,0 == 1 ;Check for multibyte transfer REPEAT_ movff POSTINC0,SSPBUF REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until start conditioning done bcf PIR1,SSPIF ;Clear interrupt flag decf I2CBYTES,F ;Decrement byte indicator UNTIL_ .Z. ELSE_ movwf SSPBUF ;Send data REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until start conditioning done bcf PIR1,SSPIF ;Clear interrupt flag ENDIF_ rcall StopI2C ;End I2C transfer return ;;;;;;; DACwave subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine outputs an approximation of a sawtooth waveform on each DAC ; output. DACwave movlw 0x08 ;add 8 to DAC0 addwf DAC0,F movlw 0x10 ;add 16 to DAC1 addwf DAC1,F MOVLF 0x58,I2CADD ;Address DAC device (0x2C) bsf I2CFLAGS,0 ;Indicate multibyte transfer MOVLF 0x04,I2CBYTES ;Send 4 bytes lfsr 0,I2CBUF+3 ;Point to I2C multibyte buffer movff DAC1,POSTDEC0 ;OUT1 data MOVLF 0x01,POSTDEC0 ;Command to write to OUT1 movff DAC0,POSTDEC0 ;OUT0 data clrf INDF0 ;Command to write to OUT0 rcall SendI2C return ;;;;;;; GetI2C subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine receives single or multiple bytes from the I2C device at ; address I2CADD. ; ; I2CFLAGS:0 = 0, A single byte is read into WREG ; 1, I2CBYTES are read to FSR0 ; 1 = 0, Normal receive ; 1, Commanded receive, send command in WREG first, then read ; ; I2CADD contains an address in the upper 7 bits. GetI2C IF_ I2CFLAGS,1 == 1 ;Check for commanded receive bcf I2CADD,0 ;Clear lower bit of address to indicate Write rcall StartI2C ;Start I2C transfer IF_ I2CFLAGS,2 == 0 ;Check for acknowledge movwf SSPBUF ;Send REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until sequence complete bcf PIR1,SSPIF ;Clear interrupt flag bsf I2CADD,0 ;Set lower bit of address to indicate Read rcall ReStartI2C ;Start I2C transfer ENDIF_ ELSE_ bsf I2CADD,0 ;Set lower bit of address to indicate Read rcall StartI2C ;Start I2C transfer ENDIF_ IF_ I2CFLAGS,2 == 0 ;Check for acknowledge IF_ I2CFLAGS,0 == 1 ;Check for multibyte REPEAT_ rcall GetI2CByte ;Get byte from device movff SSPBUF,POSTINC0 ;Store byte to FSR0 decf I2CBYTES,F ;Decrement byte indicator IF_ .NZ. bcf SSPCON2,ACKDT ;Send acknowledge ELSE_ bsf SSPCON2,ACKDT ;Do not send acknowledge ENDIF_ bsf SSPCON2,ACKEN ;Start acknowledge sequence REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until sequence complete bcf PIR1,SSPIF ;Clear interrupt flag movf I2CBYTES,W ;Check for last byte UNTIL_ .Z. ELSE_ rcall GetI2CByte ;Get byte from device movf SSPBUF,W ;Store byte to WREG bsf SSPCON2,ACKDT ;Do not send acknowledge bsf SSPCON2,ACKEN ;Start acknowledge sequence REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until sequence complete bcf PIR1,SSPIF ;Clear interrupt flag ENDIF_ ENDIF_ rcall StopI2C ;End I2C transfer return ;;;;;;; GetI2CByte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine initiates the I2C receive mode and waits for completion. GetI2CByte bsf SSPCON2,RCEN ;Enable receive REPEAT_ UNTIL_ PIR1,SSPIF == 1 ;Wait until byte received bcf PIR1,SSPIF ;Clear interrupt flag return ;;;;;;; Blink subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine briefly blinks one or both LEDs connected to the IO expansion ; chips via the I2C bus. Blink decf BLINKCNT,F ;Decrement loop counter and return if not zero IF_ .Z. MOVLF 250,BLINKCNT ;Reinitialize BLNKCNT IF_ I2CDISP,1 == 1 IF_ I2CDISP,0 == 1 I2COUT 0x70,0xfe ;Turn on both LEDs I2COUT 0x72,0xfe ELSE_ I2COUT 0x70,0xfe ;Turn on left LED ENDIF_ ELSE_ I2COUT 0x72,0xfe ;Turn on right LED ENDIF_ ELSE_ I2COUT 0x70,0xff ;Turn off both LEDs I2COUT 0x72,0xff ENDIF_ return ;;;;;;; Pushbutton subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine checks the pushbutton connected to the first IO expansion ; chip via the I2C bus and adjusts the I2CDISP flag accordingly. Pushbutton bcf I2CFLAGS,0 ;Single byte transfer bcf I2CFLAGS,1 ;Normal receive MOVLF 0x70,I2CADD ;Point to first IO expander rcall GetI2C ;Read from first IO expander IF_ I2CFLAGS,2 == 0 ;Check for acknowledge xorlw 0x80 ;Check for button press andlw 0x80 ;Mask off button bit IF_ .NZ. xorwf I2CFLAGS,W ;XOR with previous state andlw 0x80 ;Mask off other bits IF_ .Z. bcf PORTA,4 ;Indicate button press with LED bcf I2CFLAGS,7 ;Clear previous state decf I2CDISP,F ;Decrement I2CDISP flag IF_ .Z. MOVLF 0x03,I2CDISP ;Reset I2CDISP flag ENDIF_ ENDIF_ ELSE_ bsf I2CFLAGS,7 ;Set previous state bsf PORTA,4 ;Clear LED ENDIF_ ENDIF_ return ;;;;;;; Int2ASCII subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine converts an integer in AARGB0 to an ascii pointed to by FSR0. ; FSR should point to the least significant digit. ; WREG should contain the number of digits - 1 to generate. Int2ASCII movwf TEMPVAR ;Store number of digits movwf COUNT REPEAT_ MOVLF 0x0a,BARGB0 ;Divide by 10 to get next digit call FXD0808U movf REMB0,W addlw A'0' ;Convert digit to ASCII movwf POSTDEC0 ;Insert into string and move to next digit decf COUNT,F ;Decrement digit counter UNTIL_ .Z. movf AARGB0,W ;Get last digit addlw A'0' ;Convert digit to ASCII movwf INDF0 ;Insert into string REPEAT_ movf INDF0,W ;Check for leading zeros xorlw A'0' IF_ .Z. MOVLF A' ',POSTINC0 ;Blank leading zero ELSE_ movf POSTINC0,W ;Advance FSR2 ENDIF_ decf TEMPVAR,F ;Decrement digit counter UNTIL_ .Z. return ;;;;;;; Centigrade subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine checks the temp sensor connected to the I2C bus and displays ; the reading on the LCD. Centigrade decf TEMPCNT,F ;Decrement temperature update counter IF_ .Z. MOVLF 100,TEMPCNT ;Reset counter bcf I2CFLAGS,0 ;Single byte transfer bsf I2CFLAGS,1 ;Commanded receive MOVLF 0x90,I2CADD ;Point to temp sensor movlw 0xaa ;Read Temperature command rcall GetI2C ;Read from temp sensor IF_ I2CFLAGS,2 == 0 ;Check for acknowledge movwf AARGB0 lfsr 0,LCDSTRING+5 ;Point to least significant digit movlw 0x02 ;Create 3 digits rcall Int2ASCII lfsr 0,LCDSTRING+9 ;Point to last string position clrf POSTDEC0 ;End of line character MOVLF A'C',POSTDEC0 ;Units MOVLF 0xdf,POSTDEC0 MOVLF A' ',POSTDEC0 lfsr 0,LCDSTRING+2 MOVLF A' ',POSTDEC0 ;Blank first two chars MOVLF A' ',POSTDEC0 MOVLF 0x80,INDF0 ;Update cursor positioning code rcall DisplayV ENDIF_ ENDIF_ return #include ;;;;;;; Constant strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LCDstr db 0x33,0x32,0x28,0x01,0x0c,0x06,0x00,0x00 ;Initialization string for LCD ClrScr db 0x01,0x00 ;Clear screen command DevE1 db 0x80," Device ",0x00 ;Device Error message DevE2 db 0xc0," Error ",0x00 ; indicates device did not acknowledge end