;------------------------------------------------------------------------------------------------------------------------
; Source code for the PIC16F873 based interfacing with HD44780 based 16x2 LCD.
;------------------------------------------------------------------------------------------------------------------------
;
;	**LCD Pinouts
;	1	Vss -	Ground, 3rd pin of the potentiometer
;  	2	Vcc	-	5V DC, 1st pin of the potentiometer
;  	3 	Vee	-	Middle pin of the potentiometer
;  	4 	RS	-	RC0		(Data - 1, Instruction - 0)
;  	5 	R/W	-	RC1		(R - 1, W - 0)
;	6 	E	-	RC2		(Enable Pulse)
;	7 	DB0	-	RB0		(LSB)
;	8 	DB1	-	RB1
;	9 	DB2	-	RB2
; 	10	DB3	-	RB3		(Lower 4 bits)
; 	11	DB4 -	RB4		(Upper 4 bits)
; 	12	DB5	-	RB5
; 	13	DB6	-	RB6
; 	14	DB7	-	RB7		(MSB)
;
;	Instruction Cycle Time = 1 / (4MHz / 4) = 1us per instruction
;------------------------------------------------------------------------------------------------------------------------

		LIST P=16F873           
		INCLUDE "p16f873.inc"   
;		ERRORLEVEL -302        
		__CONFIG _PWRTE_OFF & _HS_OSC & _WDT_OFF & _LVP_OFF & _BODEN_OFF;  configuration switches

N 				EQU 0x20			
FIXDELAY		EQU 0x21		; General Purpose Registers.

			org 0x00
			nop					; Reserved for ICD II.
			goto start

start 		call initports		; Initialize Ports as output/inputs.
			call INITLCD		; Initialize LCD.

main		call testmsg		; Print test message on LCD

forever		goto $

;------------------------------------------------------------------------------------------------------------------------
; Subroutine to initialize the PORTs as Inputs or Outputs.
;------------------------------------------------------------------------------------------------------------------------

initports	
			banksel PORTB
			clrf PORTB			; Clear PORTs.
			clrf PORTC
		
			banksel TRISB
			movlw 0x00			; Define PORTB and PORTC as Outputs.
			movwf TRISB
			movwf TRISC

			return

;------------------------------------------------------------------------------------------------------------------------
; Initialize the LCD.
;------------------------------------------------------------------------------------------------------------------------

INITLCD		
			BANKSEL PORTB		; Select Bank for PORTB.

			MOVLW	0xE6		; Call for 46ms delay
			CALL 	NDELAY		; Wait for VCC of the LCD to reach 5V
			
			BCF		PORTC, 0	; Clear RS to select Instruction Reg.
			BCF		PORTC, 1	; Clear R/W to write
		
			MOVLW	B'00111011'	; Function Set to 8 bits, 2 lines and 5x7 dot matrix
			MOVWF 	PORTB
			CALL	ENABLEPULSE
			CALL	DELAY50
			CALL	ENABLEPULSE
			CALL	DELAY50
			CALL	ENABLEPULSE
			CALL	DELAY50		; Call 50us delay and wait for instruction completion

			MOVLW	B'00001000'	; Display OFF
			MOVWF	PORTB
			CALL	ENABLEPULSE
			CALL	DELAY50		; Call 50us delay and wait for instruction completion

			MOVLW	B'00000001'	; Clear Display
			MOVWF	PORTB
			CALL	ENABLEPULSE
			MOVLW	0x09		; Call 1.8ms delay and wait for instruction completion				
			CALL	NDELAY		

			MOVLW	B'00000010'	; Cursor Home
			MOVWF	PORTB
			CALL	ENABLEPULSE
			MOVLW	0x09		; Call 1.8ms delay and wait for instruction completion				
			CALL	NDELAY
		
			MOVLW	B'00001111'	; Display ON, Cursor ON, Blinking ON
			MOVWF	PORTB
			CALL	ENABLEPULSE
			CALL	DELAY50		; Call 50us delay and wait for instruction completion

			MOVLW 	B'00000110'	; Entry Mode Set, Increment & No display shift
			MOVWF	PORTB
			CALL	ENABLEPULSE
			CALL	DELAY50		; Call 50us delay and wait for instruction completion

			BSF		PORTC, 0	; Set RS to select Data Reg.
			BCF		PORTC, 1	; Clear R/W to write

			RETURN

;------------------------------------------------------------------------------------------------------------------------
; Enable Pulse for writing or reading instructions or data
;------------------------------------------------------------------------------------------------------------------------

ENABLEPULSE	BCF	PORTC, 2		; 2us LOW followed by 3us HIGH Enable Pulse and 2us LOW.
			NOP
			NOP
			BSF	PORTC, 2
			NOP
			NOP
			NOP
			BCF PORTC, 2
			NOP
			NOP
			RETURN

;------------------------------------------------------------------------------------------------------------------------
; N DELAY SUBROUTINE, delay in multiples of 200us up to 200us*255 = 51ms (or more)
;------------------------------------------------------------------------------------------------------------------------

NDELAY
			MOVWF N				; N is delay multiplier
NOTOVER		CALL DELAY200		; Call for 200us
			DECFSZ N, 1			; Decrease N by 1
			GOTO NOTOVER		; The delay isn't done
			RETURN
	
;------------------------------------------------------------------------------------------------------------------------
; FIXED 200us DELAY (Possibly more due to execution time of the DECFSZ instruction.)
;------------------------------------------------------------------------------------------------------------------------

DELAY200	
			MOVLW 0x42			; 66 LOOPS
			MOVWF FIXDELAY		; 200us fixed delay
NOTDONE200	DECFSZ FIXDELAY, 1 	; Decrement of FIXDELAY
			GOTO NOTDONE200		; If 200us isn't up go back to NOTDONE200
			RETURN				; If 200us is up then return to instruction.

;------------------------------------------------------------------------------------------------------------------------
; FIXED 50us DELAY (Possibly more due to execution time of the DECFSZ instruction.)
;------------------------------------------------------------------------------------------------------------------------

DELAY50	
			MOVLW 0x10			; 16 LOOPS
			MOVWF FIXDELAY		; 50us fixed delay
NOTDONE50	DECFSZ FIXDELAY, 1 	; Decrement of FIXDELAY
			GOTO NOTDONE50		; If 50us isn't up go back to NOTDONE50
			RETURN				; If 50us is up then return to instruction.

;------------------------------------------------------------------------------------------------------------------------
; FIXED 20us DELAY (Possibly more due to execution time of the DECFSZ instruction.)
;------------------------------------------------------------------------------------------------------------------------

DELAY20	
			MOVLW 0x7			; 7 LOOPS
			MOVWF FIXDELAY		; 50us fixed delay
NOTDONE20	DECFSZ FIXDELAY, 1 	; Decrement of FIXDELAY
			GOTO NOTDONE20		; If 50us isn't up go back to NOTDONE50
			RETURN				; If 50us is up then return to instruction.

;------------------------------------------------------------------------------------------------------------------------
; Fast Directive to write characters to LCD.
;------------------------------------------------------------------------------------------------------------------------

PUTCHAR
			MOVWF PORTB			; A quicker way of writing characters to LCD.
			CALL ENABLEPULSE
			CALL CHKBUSY
			RETURN

;------------------------------------------------------------------------------------------------------------------------
; Subroutine to check for the BUSY flag. Mostly used for instructions that follows up a character write.
;------------------------------------------------------------------------------------------------------------------------

CHKBUSY
			bcf	PORTC, 0		; Clear RS to select Instruction Reg.
			bsf	PORTC, 1		; Set R/W to read.

			banksel TRISB		; Select Bank for TRISC.
			movlw 0xFF			; Define all PORTC Pins as Inputs.
			movwf TRISB

			banksel PORTC		; Select Bank for PORTC.
			bsf PORTC, 2		; I tried to write my own code for this part initially but I wasn't successful.
			movf PORTB, w		; Therefore, I implemented a portion of Peter Ouwehand's LCD Code.
			bcf PORTC, 2		; Will look more into the BUSY flag of the LCD.
			andlw 0x80			; Credits to Peter Ouwehand for his code here. :)
			btfss STATUS, Z
			goto CHKBUSY

			banksel TRISB		; Select Bank for TRISB.
			movlw 0x00			; Define all PORTC Pins as Outputs.
			movwf TRISB
		
			banksel PORTC		; Select Bank for PORTA, B, and C.
			bsf PORTC, 0		; Set RS to select Data Register.
			bcf PORTC, 1		; Clear R/W to write.
			
			return

;------------------------------------------------------------------------------------------------------------------------
; Position Cursor to the next line.
;------------------------------------------------------------------------------------------------------------------------

nextline
			banksel PORTC
			bcf PORTC, 0	; Select Instructions Register.
			bcf PORTC, 1	; Select Write.

			movlw b'11000000'	; Shift cursor to second line at 0x40 RAM address on LCD.
			call PUTCHAR

			return

;------------------------------------------------------------------------------------------------------------------------
; Clear screen and Cursor home.
;------------------------------------------------------------------------------------------------------------------------

clrscreen
			banksel PORTC
			bcf PORTC, 0		; Clear RS to select Instructions Register.
			bcf PORTC, 1		; Clear R/W to select Write.

			banksel PORTB
			MOVLW B'00000001'	; Clear Display
			call PUTCHAR

			return

;------------------------------------------------------------------------------------------------------------------------
; Position Cursor to home position.
;------------------------------------------------------------------------------------------------------------------------

cursorhome
			banksel PORTC
			bcf PORTC, 0	; Select Instructions Register.
			bcf PORTC, 1	; Select Write.

			movlw b'00000010'	; Position cursor to home position.
			call PUTCHAR

			return

;------------------------------------------------------------------------------------------------------------------------
; A test message.
;------------------------------------------------------------------------------------------------------------------------

testmsg		movlw A'I'			; Displays 	"I m working fine"
			call PUTCHAR		;			"Line 2 is alrite"
			movlw A' '
			call PUTCHAR
			movlw A'm'
			call PUTCHAR
			movlw A' '
			call PUTCHAR
			movlw A'w'
			call PUTCHAR
			movlw A'o'
			call PUTCHAR
			movlw A'r'
			call PUTCHAR
			movlw A'k'
			call PUTCHAR
			movlw A'i'
			call PUTCHAR
			movlw A'n'
			call PUTCHAR
			movlw A'g'
			call PUTCHAR
			movlw A' '
			call PUTCHAR
			movlw A'f'
			call PUTCHAR
			movlw A'i'
			call PUTCHAR
			movlw A'n'
			call PUTCHAR
			movlw A'e'
			call PUTCHAR

			call nextline

			movlw A'L'
			call PUTCHAR
			movlw A'i'
			call PUTCHAR
			movlw A'n'
			call PUTCHAR
			movlw A'e'
			call PUTCHAR
			movlw A' '
			call PUTCHAR
			movlw A'2'
			call PUTCHAR
			movlw A' '
			call PUTCHAR
			movlw A'i'
			call PUTCHAR
			movlw A's'
			call PUTCHAR
			movlw A' '
			call PUTCHAR
			movlw A'a'
			call PUTCHAR
			movlw A'l'
			call PUTCHAR
			movlw A'r'
			call PUTCHAR
			movlw A'i'
			call PUTCHAR
			movlw A't'
			call PUTCHAR
			movlw A'e'
			call PUTCHAR

			return

;------------------------------------------------------------------------------------------------------------------------
; End of programme.
;------------------------------------------------------------------------------------------------------------------------

			end
