PIC Tutorial Three - LCD Modules


For the first parts of this tutorial you require the Main Board and the LCD Board, the later parts will also use the Switch Board, as written the tutorials use the LCD Board on PortA and the Switch Board on PortB. Although the hardware diagram shows a 2x16 LCD, other sizes can be used, I've tested it with a 2x16, 2x20, and 2x40 - all worked equally well. The intention is to develop a useful set of LCD routines, these will be used in the later parts of the tutorials to display various information.

Download zipped tutorial files.

LCD Command Control Codes
Command Binary Hex
D7 D6 D5 D4 D3 D2 D1 D0
Clear Display 0 0 0 0 0 0 0 1 01
Display and Cursor Home 0 0 0 0 0 0 1 x 02 or 03
Character Entry Mode 0 0 0 0 0 1 I/D S 01 to 07
Display On/Off and Cursor 0 0 0 0 1 D U B 08 to 0F
Display/Cursor Shift 0 0 0 1 D/C R/L x x 10 to 1F
Function Set 0 0 1 8/4 2/1 10/7 x x 20 to 3F
Set CGRAM Address 0 1 A A A A A A 40 to 7F
Set Display Address 1 A A A A A A A 80 to FF
I/D: 1=Increment* 0=Decrement
S: 1=Display Shift On 0=Display Shift off*
D: 1=Display On 0=Display Off*
U: 1=Cursor Underline On 0=Cursor Underline Off*
B: 1=Cursor Blink On 0=Cursor Blink Off*
D/C: 1=Display Shift 0=Cursor Move
R/L: 1=Right Shift 0=Left Shift
8/4: 1=8 bit interface* 0=4 bit interface
2/1: 1=2 line mode 0=1 line mode*
10/7: 1=5x10 dot format 0=5x7 dot format*
     
  *=initialisation setting x=don't care

This table shows the command codes for the LCD module, it was taken from an excellent LCD tutorial that was published in the UK magazine 'Everyday Practical Electronics' February 1997 - it can be downloaded as a PDF file from the EPE website. The following routines are an amalgamation of a number of routines from various sources (including the previously mentioned tutorial), plus various parts of my own, the result is a set of reliable, easy to use, routines which work well (at least in my opinion!).

Tutorial 3.1 - requires Main Board and LCD Board.

This program displays a text message on the LCD module, it consists mostly of subroutines for using the LCD module.

;LCD text demo - 4 bit mode
;Nigel Goodwin 2002

	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	ERRORLEVEL	0,	-302	;suppress bank selection messages
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)




		cblock	0x20			;start of general purpose registers
			count			;used in looping routines
			count1			;used in delay routine
			counta			;used in delay routine
			countb			;used in delay routine
			tmp1			;temporary storage
			tmp2
			templcd			;temp store for 4 bit mode
			templcd2	
		endc

LCD_PORT	Equ	PORTA
LCD_TRIS	Equ	TRISA
LCD_RS		Equ	0x04			;LCD handshake lines
LCD_RW		Equ	0x06
LCD_E		Equ	0x07

		org	0x0000

		movlw	0x07
		movwf	CMCON			;turn comparators off (make it like a 16F84)

Initialise	clrf	count
		clrf	PORTA
		clrf	PORTB



SetPorts	bsf 	STATUS,		RP0	;select bank 1
		movlw	0x00			;make all pins outputs
		movwf	LCD_TRIS
		bcf 	STATUS,		RP0	;select bank 0

		call	Delay100		;wait for LCD to settle


		call	LCD_Init		;setup LCD


		clrf	count			;set counter register to zero
Message		movf	count, w		;put counter value in W
		call	Text			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	NextMessage
		call	LCD_Char
		call	Delay255
		incf	count, f
		goto	Message

NextMessage	call	LCD_Line2		;move to 2nd row, first column

		clrf	count			;set counter register to zero
Message2	movf	count, w		;put counter value in W
		call	Text2			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	EndMessage
		call	LCD_Char
		incf	count, f
		goto	Message2

EndMessage	
		
Stop		goto	Stop			;endless loop




;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init	movlw	0x20			;Set 4 bit mode
		call	LCD_Cmd

		movlw	0x28			;Set display shift
		call	LCD_Cmd

		movlw	0x06			;Set display character mode
		call	LCD_Cmd

		movlw	0x0d			;Set display on/off and cursor command
		call	LCD_Cmd

		call	LCD_Clr			;clear display

		retlw	0x00

; command set routine
LCD_Cmd		movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high
		call 	Delay5
		retlw	0x00

LCD_CharD	addlw	0x30
LCD_Char	movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high
		call 	Delay5
		retlw	0x00

LCD_Line1	movlw	0x80			;move to 1st row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line2	movlw	0xc0			;move to 2nd row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line1W	addlw	0x80			;move to 1st row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_Line2W	addlw	0xc0			;move to 2nd row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_CurOn	movlw	0x0d			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_CurOff	movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_Clr		movlw	0x01			;Clear display
		call	LCD_Cmd
		retlw	0x00

LCD_HEX		movwf	tmp1
		swapf	tmp1,	w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		movf	tmp1, w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		retlw	0x00

Delay255	movlw	0xff		;delay 255 mS
		goto	d0
Delay100	movlw	d'100'		;delay 100mS
		goto	d0
Delay50		movlw	d'50'		;delay 50mS
		goto	d0
Delay20		movlw	d'20'		;delay 20mS
		goto	d0
Delay5		movlw	0x05		;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7			;delay 1mS
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0
		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

Pulse_e		bsf	LCD_PORT, LCD_E
		nop
		bcf	LCD_PORT, LCD_E
		retlw	0x00

;end of LCD routines

HEX_Table  	ADDWF   PCL       , f
            	RETLW   0x30
            	RETLW   0x31
            	RETLW   0x32
            	RETLW   0x33
            	RETLW   0x34
            	RETLW   0x35
            	RETLW   0x36
            	RETLW   0x37
            	RETLW   0x38
            	RETLW   0x39
            	RETLW   0x41
            	RETLW   0x42
            	RETLW   0x43
            	RETLW   0x44
            	RETLW   0x45
            	RETLW   0x46


Text		addwf	PCL, f
		retlw	'H'
		retlw	'e'
		retlw	'l'
		retlw	'l'
		retlw	'o'
		retlw	0x00

Text2		ADDWF   PCL, f
            	RETLW   'R'
            	RETLW   'e'
            	RETLW   'a'
            	RETLW   'd'
            	RETLW   'y'
            	RETLW   '.'
            	RETLW   '.'
            	RETLW   '.'
            	RETLW   0x00


		end      
      

As usual, first we need to set things up, after the normal variable declarations and port setting we reach 'call LCD_Init', this sets up the LCD module. It first waits for 100mS to give the module plenty of time to settle down, we then set it to 4 bit mode (0x20) and set the various options how we want them - in this case, Display Shift is On (0x28), Character Entry Mode is Increment (0x06), and Block Cursor On (0x0D). Once the LCD is setup, we can then start to send data to it, this is read from a table, exactly the same as the LED sequencer in the earlier tutorials - except this time we send the data to the LCD module (using LCD_Char) and use a 0x00 to mark the end of the table, thus removing the need to maintain a count of the characters printed. Once the first line is displayed we then sent a command to move to the second line (using call LCD_Line2), and then print the second line from another table. After that we enter an endless loop to leave the display as it is.

This program introduces a new use of the 'goto' command, 'goto $+2' - '$' is an MPASM arithmetic operator, and uses the current value of the program counter, so 'goto $+2' means jump to the line after the next one - 'goto $+1' jumps to the next line, and may seem pretty useless (as the program was going to be there next anyway), but it can be extremely useful. A program branch instruction (like goto) uses two instruction cycles, whereas other instructions only take one, so if you use a 'nop' in a program it takes 1uS to execute, and carries on from the next line - however, if you use 'goto $+1' it still carries on from the next line, but now takes 2uS. You'll notice more use of the 'goto $' construction in later tutorials, if you are checking an input pin and waiting for it to change state you can use 'goto $-1' to jump back to the previous line, this saves allocating a label to the line that tests the condition.

This is a table of the LCD subroutines provided in these programs, you can easily add more if you wish - for instance to set a line cursor rather than a block one, if you find you are using a particular feature a lot you may as well make a subroutine for it.

LCD Subroutines
LCD_Init Initialise LCD Module
LCD_Cmd Sent a command to the LCD
LCD_CharD Add 0x30 to a byte and send to the LCD
(to display numbers as ASCII)
LCD_Char Send the character in W to the LCD
LCD_Line1 Go to start of line 1
LCD_Line2 Go to start of line 2
LCD_Line1W Go to line 1 column W
LCD_Line2W Go to line 2 column W
LCD_CurOn Turn block cursor on
LCD_CurOff Turn block cursor off
LCD_Clr Clear the display
LCD_HEX Display the value in W as Hexadecimal

Tutorial 3.2 - requires Main Board and LCD Board.

This program displays a text message on the top line and a running 16 bit counter on the bottom line, with the values displayed in both decimal and hexadecimal , it consists mostly of the previous subroutines for using the LCD module, plus an extra one for converting from 16 bit hexadecimal to decimal.

;LCD 16 bit counter
;Nigel Goodwin 2002

	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	ERRORLEVEL	0,	-302	;suppress bank selection messages
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)




		cblock	0x20			;start of general purpose registers
			count			;used in looping routines
			count1			;used in delay routine
			counta			;used in delay routine
			countb			;used in delay routine
			tmp1			;temporary storage
			tmp2
			templcd			;temp store for 4 bit mode
			templcd2

        		NumL			;Binary inputs for decimal convert routine
	        	NumH	

        		TenK			;Decimal outputs from convert routine
	        	Thou	
        		Hund	
	        	Tens	
        		Ones	
		endc

LCD_PORT	Equ	PORTA
LCD_TRIS	Equ	TRISA
LCD_RS		Equ	0x04			;LCD handshake lines
LCD_RW		Equ	0x06
LCD_E		Equ	0x07

		org	0x0000

		movlw	0x07
		movwf	CMCON			;turn comparators off (make it like a 16F84)

Initialise	clrf	count
		clrf	PORTA
		clrf	PORTB
		clrf	NumL
		clrf	NumH



SetPorts	bsf 	STATUS,		RP0	;select bank 1
		movlw	0x00			;make all pins outputs
		movwf	LCD_TRIS
		bcf 	STATUS,		RP0	;select bank 0

		call	LCD_Init		;setup LCD


		clrf	count			;set counter register to zero
Message		movf	count, w		;put counter value in W
		call	Text			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	NextMessage
		call	LCD_Char
		incf	count, f
		goto	Message

NextMessage	call	LCD_Line2		;move to 2nd row, first column
	
		call	Convert			;convert to decimal
		movf	TenK,	w		;display decimal characters
		call	LCD_CharD		;using LCD_CharD to convert to ASCII
		movf	Thou,	w
		call	LCD_CharD
		movf	Hund,	w
		call	LCD_CharD		
		movf	Tens,	w
		call	LCD_CharD
		movf	Ones,	w
		call	LCD_CharD
		movlw	' '			;display a 'space'
		call	LCD_Char
		movf	NumH,	w		;and counter in hexadecimal
		call	LCD_HEX
		movf	NumL,	w
		call	LCD_HEX
		incfsz	NumL, 	f
		goto	Next
		incf	NumH,	f
Next		call	Delay255		;wait so you can see the digits change
		goto	NextMessage


;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init	call	Delay100		;wait for LCD to settle

		movlw	0x20			;Set 4 bit mode
		call	LCD_Cmd

		movlw	0x28			;Set display shift
		call	LCD_Cmd

		movlw	0x06			;Set display character mode
		call	LCD_Cmd

		movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd			;Set cursor off

		call	LCD_Clr			;clear display

		retlw	0x00

; command set routine
LCD_Cmd		movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high
		call 	Delay5
		retlw	0x00

LCD_CharD	addlw	0x30			;add 0x30 to convert to ASCII
LCD_Char	movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high
		call 	Delay5
		retlw	0x00

LCD_Line1	movlw	0x80			;move to 1st row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line2	movlw	0xc0			;move to 2nd row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line1W	addlw	0x80			;move to 1st row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_Line2W	addlw	0xc0			;move to 2nd row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_CurOn	movlw	0x0d			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_CurOff	movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_Clr		movlw	0x01			;Clear display
		call	LCD_Cmd
		retlw	0x00

LCD_HEX		movwf	tmp1
		swapf	tmp1,	w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		movf	tmp1, w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		retlw	0x00

Delay255	movlw	0xff		;delay 255 mS
		goto	d0
Delay100	movlw	d'100'		;delay 100mS
		goto	d0
Delay50		movlw	d'50'		;delay 50mS
		goto	d0
Delay20		movlw	d'20'		;delay 20mS
		goto	d0
Delay5		movlw	0x05		;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7			;delay 1mS
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0
		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

Pulse_e		bsf	LCD_PORT, LCD_E
		nop
		bcf	LCD_PORT, LCD_E
		retlw	0x00

;end of LCD routines

HEX_Table  	ADDWF   PCL       , f
            	RETLW   0x30
            	RETLW   0x31
            	RETLW   0x32
            	RETLW   0x33
            	RETLW   0x34
            	RETLW   0x35
            	RETLW   0x36
            	RETLW   0x37
            	RETLW   0x38
            	RETLW   0x39
            	RETLW   0x41
            	RETLW   0x42
            	RETLW   0x43
            	RETLW   0x44
            	RETLW   0x45
            	RETLW   0x46


Text		addwf	PCL, f
		retlw	'1'
		retlw	'6'
		retlw	' '
		retlw	'B'
		retlw	'i'
		retlw	't'
		retlw	' '
		retlw	'C'
		retlw	'o'
		retlw	'u'
		retlw	'n'
		retlw	't'
		retlw	'e'
		retlw	'r'
		retlw	'.'
		retlw	0x00

;This routine downloaded from http://www.piclist.com
Convert:                        ; Takes number in NumH:NumL
                                ; Returns decimal in
                                ; TenK:Thou:Hund:Tens:Ones
        swapf   NumH, w
	iorlw	B'11110000'
        movwf   Thou
        addwf   Thou,f
        addlw   0XE2
        movwf   Hund
        addlw   0X32
        movwf   Ones

        movf    NumH,w
        andlw   0X0F
        addwf   Hund,f
        addwf   Hund,f
        addwf   Ones,f
        addlw   0XE9
        movwf   Tens
        addwf   Tens,f
        addwf   Tens,f

        swapf   NumL,w
        andlw   0X0F
        addwf   Tens,f
        addwf   Ones,f

        rlf     Tens,f
        rlf     Ones,f
        comf    Ones,f
        rlf     Ones,f

        movf    NumL,w
        andlw   0X0F
        addwf   Ones,f
        rlf     Thou,f

        movlw   0X07
        movwf   TenK

                    ; At this point, the original number is
                    ; equal to
                    ; TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
                    ; if those entities are regarded as two's
                    ; complement binary.  To be precise, all of
                    ; them are negative except TenK.  Now the number
                    ; needs to be normalized, but this can all be
                    ; done with simple byte arithmetic.

        movlw   0X0A                             ; Ten
Lb1:
        addwf   Ones,f
        decf    Tens,f
        btfss   3,0
        goto   Lb1
Lb2:
        addwf   Tens,f
        decf    Hund,f
        btfss   3,0
        goto   Lb2
Lb3:
        addwf   Hund,f
        decf    Thou,f
        btfss   3,0
        goto   Lb3
Lb4:
        addwf   Thou,f
        decf    TenK,f
        btfss   3,0
        goto   Lb4

        retlw	0x00


		end      
	  

Tutorial 3.3 - requires Main Board and LCD Board.

This program displays a text message on the top line and a running 16 bit counter on the bottom line, just as the last example, however, instead of using the Delay calls this version waits until the LCD Busy flag is clear. The LCD module takes time to carry out commands, these times vary, and the previous tutorials used a delay more than long enough to 'make sure' - however, the modules have the capability of signalling when they are ready, this version uses that facility and avoids any unnecessary delays. I've also used the LCD_Line2W routine to position the numbers further to the right and demonstrate the use of the routine, another slight change is that the tables have been moved to the beginning of program memory, this was done because it's important that tables don't cross a 256 byte boundary, so putting them at the start avoids this.

;LCD 16 bit counter - using LCD Busy line
;Nigel Goodwin 2002

	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	ERRORLEVEL	0,	-302	;suppress bank selection messages
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)




		cblock	0x20			;start of general purpose registers
			count			;used in looping routines
			count1			;used in delay routine
			counta			;used in delay routine
			countb			;used in delay routine
			tmp1			;temporary storage
			tmp2
			templcd			;temp store for 4 bit mode
			templcd2

        		NumL			;Binary inputs for decimal convert routine
	        	NumH	

        		TenK			;Decimal outputs from convert routine
	        	Thou	
        		Hund	
	        	Tens	
        		Ones	
		endc

LCD_PORT	Equ	PORTA
LCD_TRIS	Equ	TRISA
LCD_RS		Equ	0x04			;LCD handshake lines
LCD_RW		Equ	0x06
LCD_E		Equ	0x07

		org	0x0000
		goto	Start

HEX_Table  	ADDWF   PCL       , f
            	RETLW   0x30
            	RETLW   0x31
            	RETLW   0x32
            	RETLW   0x33
            	RETLW   0x34
            	RETLW   0x35
            	RETLW   0x36
            	RETLW   0x37
            	RETLW   0x38
            	RETLW   0x39
            	RETLW   0x41
            	RETLW   0x42
            	RETLW   0x43
            	RETLW   0x44
            	RETLW   0x45
            	RETLW   0x46


Text		addwf	PCL, f
		retlw	'1'
		retlw	'6'
		retlw	' '
		retlw	'B'
		retlw	'i'
		retlw	't'
		retlw	' '
		retlw	'C'
		retlw	'o'
		retlw	'u'
		retlw	'n'
		retlw	't'
		retlw	'e'
		retlw	'r'
		retlw	'.'
		retlw	0x00

Start		movlw	0x07
		movwf	CMCON			;turn comparators off (make it like a 16F84)

Initialise	clrf	count
		clrf	PORTA
		clrf	PORTB
		clrf	NumL
		clrf	NumH



SetPorts	bsf 	STATUS,		RP0	;select bank 1
		movlw	0x00			;make all pins outputs
		movwf	LCD_TRIS
		movwf	TRISB
		bcf 	STATUS,		RP0	;select bank 0

		call	LCD_Init		;setup LCD


		clrf	count			;set counter register to zero
Message		movf	count, w		;put counter value in W
		call	Text			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	NextMessage
		call	LCD_Char
		incf	count, f
		goto	Message

NextMessage	movlw	d'2'
		call	LCD_Line2W		;move to 2nd row, third column
	
		call	Convert			;convert to decimal
		movf	TenK,	w		;display decimal characters
		call	LCD_CharD		;using LCD_CharD to convert to ASCII
		movf	Thou,	w
		call	LCD_CharD
		movf	Hund,	w
		call	LCD_CharD		
		movf	Tens,	w
		call	LCD_CharD
		movf	Ones,	w
		call	LCD_CharD
		movlw	' '			;display a 'space'
		call	LCD_Char
		movf	NumH,	w		;and counter in hexadecimal
		call	LCD_HEX
		movf	NumL,	w
		call	LCD_HEX
		incfsz	NumL, 	f
		goto	Next
		incf	NumH,	f
Next		call	Delay255		;wait so you can see the digits change
		goto	NextMessage


;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init	call 	LCD_Busy		;wait for LCD to settle

		movlw	0x20			;Set 4 bit mode
		call	LCD_Cmd

		movlw	0x28			;Set display shift
		call	LCD_Cmd

		movlw	0x06			;Set display character mode
		call	LCD_Cmd

		movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd			;Set cursor off

		call	LCD_Clr			;clear display

		retlw	0x00

; command set routine
LCD_Cmd		movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 0
		call	Pulse_e			;Pulse the E line high
		call 	LCD_Busy
		retlw	0x00

LCD_CharD	addlw	0x30			;add 0x30 to convert to ASCII
LCD_Char	movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high
		call 	LCD_Busy
		retlw	0x00

LCD_Line1	movlw	0x80			;move to 1st row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line2	movlw	0xc0			;move to 2nd row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line1W	addlw	0x80			;move to 1st row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_Line2W	addlw	0xc0			;move to 2nd row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_CurOn	movlw	0x0d			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_CurOff	movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_Clr		movlw	0x01			;Clear display
		call	LCD_Cmd
		retlw	0x00

LCD_HEX		movwf	tmp1
		swapf	tmp1,	w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		movf	tmp1, w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		retlw	0x00

Delay255	movlw	0xff			;delay 255 mS
		goto	d0
Delay100	movlw	d'100'			;delay 100mS
		goto	d0
Delay50		movlw	d'50'			;delay 50mS
		goto	d0
Delay20		movlw	d'20'			;delay 20mS
		goto	d0
Delay5		movlw	0x05			;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7			;delay 1mS
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0
		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

Pulse_e		bsf	LCD_PORT, LCD_E
		nop
		bcf	LCD_PORT, LCD_E
		retlw	0x00

LCD_Busy
		bsf	STATUS,	RP0		;set bank 1
		movlw	0x0f			;set Port for input
		movwf	LCD_TRIS
		bcf	STATUS,	RP0		;set bank 0
		bcf	LCD_PORT, LCD_RS	;set LCD for command mode
		bsf	LCD_PORT, LCD_RW	;setup to read busy flag
		bsf	LCD_PORT, LCD_E
		swapf	LCD_PORT, w		;read upper nibble (busy flag)
		bcf	LCD_PORT, LCD_E		
		movwf	templcd2 
		bsf	LCD_PORT, LCD_E		;dummy read of lower nibble
		bcf	LCD_PORT, LCD_E
		btfsc	templcd2, 7		;check busy flag, high = busy
		goto	LCD_Busy		;if busy check again
		bcf	LCD_PORT, LCD_RW
		bsf	STATUS,	RP0		;set bank 1
		movlw	0x00			;set Port for output
		movwf	LCD_TRIS
		bcf	STATUS,	RP0		;set bank 0
		return

;end of LCD routines



;This routine downloaded from http://www.piclist.com
Convert:                        ; Takes number in NumH:NumL
                                ; Returns decimal in
                                ; TenK:Thou:Hund:Tens:Ones
        swapf   NumH, w
	iorlw	B'11110000'
        movwf   Thou
        addwf   Thou,f
        addlw   0XE2
        movwf   Hund
        addlw   0X32
        movwf   Ones

        movf    NumH,w
        andlw   0X0F
        addwf   Hund,f
        addwf   Hund,f
        addwf   Ones,f
        addlw   0XE9
        movwf   Tens
        addwf   Tens,f
        addwf   Tens,f

        swapf   NumL,w
        andlw   0X0F
        addwf   Tens,f
        addwf   Ones,f

        rlf     Tens,f
        rlf     Ones,f
        comf    Ones,f
        rlf     Ones,f

        movf    NumL,w
        andlw   0X0F
        addwf   Ones,f
        rlf     Thou,f

        movlw   0X07
        movwf   TenK

                    ; At this point, the original number is
                    ; equal to
                    ; TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
                    ; if those entities are regarded as two's
                    ; complement binary.  To be precise, all of
                    ; them are negative except TenK.  Now the number
                    ; needs to be normalized, but this can all be
                    ; done with simple byte arithmetic.

        movlw   0X0A                             ; Ten
Lb1:
        addwf   Ones,f
        decf    Tens,f
        btfss   3,0
        goto   Lb1
Lb2:
        addwf   Tens,f
        decf    Hund,f
        btfss   3,0
        goto   Lb2
Lb3:
        addwf   Hund,f
        decf    Thou,f
        btfss   3,0
        goto   Lb3
Lb4:
        addwf   Thou,f
        decf    TenK,f
        btfss   3,0
        goto   Lb4

        retlw	0x00


		end