;16LCD-P4.asm
;WWW.MWFTR.COM/book.html
;Charles Kim
;This program is to display an 20x4 LCD module
;by Truly (HD44780 compatible)
;
; 4-bit interfacing
;
; Pin Connection from LCD to 16F877
; LCD (pin#)	16F877 (pin#)
;DB7 (14) -----RB7(40)
;DB6 (13) ----RB6(39)
;DB5 (12) ----RB5(38)
;DB4 (11) ----RB4(37)
;DB3 (10) 
;DB2 (9)
;DB1 (8) 
;DB0 (7) 
;E (6) ------RB2(35)
;RW (5) -----RB3(36)
;RS (4) -----RB1(24)
;Vo (3) -----GND
;Vdd (2) ----+5V
;Vss (1) -----GND
;
;Example code to display:
;	PIC
;	AND
;	LCD
;	DISPLAY
;

      list P = 16F877

STATUS	EQU	0x03
PORTB	EQU	0x06
TRISB	EQU	0x86
RS	EQU	0x01	;RB1
E	EQU	0x02	;RB2
RW	EQU	0x03	;RB3

;RAM arEA
       
	CBLOCK	0x20
		Kount120us   ;Delay count (number of instr cycles for delay)
		Kount100us
		Kount1ms
		Kount10ms
		Kount1s
		Kount10s
		Kount1m
		Temp		;temp storage
	ENDC

;program should start from 0005h
;0004h is allocated to interrupt handler

        org 	0x0000      	
	goto	START		


	org	0x05
Start
	BANKSEL	TRISB
; 1 for input, 0 for output

	movlw	0x00
	movwf	TRISB		;All output

	banksel	PORTB
	clrf	PORTB		;RW set LOW here

;LCD routine starts
	call	delay10ms
	call	delay10ms

	banksel	PORTB
	clrf	PORTB		;RW set LOW here
			;give LCD module to reset automatically
;THE ONLY CHANGE IN 4-BIT INTERFACING 
;EXCEPT 2 SUBROUTINES
;
;Function for 4-bit (only one write must be done)
;In other words, send only the high nibble
;IMPORTANT
	movlw	0x28
	call	hnibble4	
;Fundtion for 4-bit, 2-line display, and 5x8 dot matrix
	movlw	0x28
	call	instw4
;Display On, CUrsor On, No blinking
	movlw	0x0E		;0F would blink
	call	instw4
;DDRAM address increment by one & cursor shift to right
	movlw	0x06
	call	instw4
;DISPLAY CLEAR
CLEAR
	movlw	0x01
	call	instw4

;Set DDRAM ADDRES
	movlw	0x80		;00
	call	instw4
;WRITE DATA in the 1st position of line 1
	movlw	0x50		;P
	call	dataw4	

	movlw	0x49		;I
	call	dataw4

	movlw	0x43		;C
	call	dataw4

;Set DDRAM address for the 1st position of line 2 (40h)

	movlw	0xC0			;B'11000000'
	call	instw4			;RS=0

;Write A, N, D

	movlw	0x41			;A
	call	dataw4
	movlw	0x4E
	call	dataw4			;N
	movlw	0x44
	call	dataw4			;D

;Set DDRAM address for the next 3 characters (L, C, and D) in line #3. (14h) 
;The DDRAM address starts from 14h for the line #3.

	movlw	0x94			;B'10010000'
	call	instw4			;RS=0

;Write the three characters, 'L', 'C', and 'D' to DDRAM.  
;They are displayed at the line #3 from position 1.

	movlw	0x4C			;L
	call	dataw4
	movlw	0x43
	call	dataw4			;C
	movlw	0x44
	call	dataw4			;D

;Set DDRAM address for the next 7 characters (D, I, S, P, L, A, and Y) in line #4.
;The DDRAM address starts from the line #4. (54h)

	movlw	0xD4			
	call	instw4			;RS=0

;Write the seven characters, 'D', 'I', 'S', 'P', 'L', 'A', and 'Y' to DDRAM.  
;They are displayed at the line #4 from position 1.

	movlw	'D'			;D
	call	dataw4
	movlw	'I'			;I
	call	dataw4			
	movlw	'S'			;S
	call	dataw4			;
	movlw	'P'			;P
	call	dataw4
	movlw	'L'			;L
	call	dataw4			
	movlw	'A'			;A
	call	dataw4			
	movlw	'Y'			;Y 
	call	dataw4			

	call	delay1s
	call	delay1s
	call	delay1s
;DISPLAY CLEAR

	movlw	0x01
	call	instw4
;Now let's move the cursor to the home position (position 1 of line #1) 
;and set the DDRAM address to 0.  This is done by the "return home" instruction.

	movlw	0x02
	call	instw4

;Will display arm at line 1
;then alpha, rho, and mu at line 2
;Set DDRAM ADDRES for line 1
	movlw	0x80		;00
	call	instw4
	movlw	'a'
	call	dataw4
	movlw	'r'
	call	dataw4
	movlw	'm'
	call	dataw4	
;Set DDRAM ADDRES for line 2
;CGROM address for alpha, rho, and mu are E0, E6, and E4, respectively
	movlw	0xC0		;00
	call	instw4
	movlw	0xE0
	call	dataw4
	movlw	0xE6
	call	dataw4
	movlw	0xE4
	call	dataw4		
;Now let's move the cursor to the home position (position 1 of line #1) 
;and set the DDRAM address to 0.  This is done by the "return home" instruction.

	movlw	0x02
	call	instw4

	call	delay1s
	call	delay1s
	call	delay1s

	goto	CLEAR

;====SUBROUTINES =====	

;high nibble only write for the first step of 4-bit set up
hnibble4	
	movwf   Temp		;Temp storage
      	movf    Temp,0      	;Now W also holds the data    
      	andlw   0xF0            ; get upper nibble
      	movwf   PORTB		   ; send data to lcd
	call	delay1ms
	bcf	PORTB, RS
	call	delay1ms
	bsf	PORTB, E
	call	delay1ms
	bcf	PORTB, E
	call	delay10ms		;end of high nibble for 4-bit setup
	return	
;	
;subroutine instw (4-bit interface instruction write)
;instruction to be written is stored in W before the call
instw4	
	movwf   Temp		;Temp storage
      	movf    Temp,0      ;Now W also holds the data    
      	andlw   0xF0            ; get upper nibble
      	movwf   PORTB		   ; send data to lcd
	call	delay1ms
	bcf	PORTB, RS
	call	delay1ms
	bsf	PORTB, E
	call	delay1ms
	bcf	PORTB, E
	call	delay10ms		;end of higher nibble
      	swapf   Temp,0		;get lower nibble to W
      	andlw   0xf0  
	movwf	PORTB			;Write to LCD          
  	call	delay1ms
	bcf	PORTB, RS
	call	delay1ms
	bsf	PORTB, E
	call	delay1ms
	bcf	PORTB, E		;end of lower nibble
	call	delay10ms	             
	return

;subroutine dataw (4-bit interface data write)

dataw4	
	movwf   Temp		;Temp storage
      	movf    Temp,0      ;Now W also holds the data    
      	andlw   0xF0            ; get upper nibble
      	movwf   PORTB		   ; send data to lcd
	call	delay1ms
	bsf	PORTB, RS
	call	delay1ms
	bsf	PORTB, E
	call	delay1ms
	bcf	PORTB, E
	call	delay10ms		;end of higher nibble
      	swapf   Temp,0		;get lower nibble to W
      	andlw   0xF0  
	movwf	PORTB			;Write to LCD          
  	call	delay1ms
	bsf	PORTB, RS
	call	delay1ms
	bsf	PORTB, E
	call	delay1ms
	bcf	PORTB, E		;end of lower nibble
	call	delay10ms	             
	return
;
;==========================================================
;DELAY SUBROUTINES 


; 1 instruction cycle for 20MHz clock is 0.2 us
; Therefore 120 uS delay needs 600 instuction cycles
;  600 =199*3 +3 ---->Kount=199=0xC7
;  or  =198*3 +6 ---->Kount=198=0xC6
;  or  =197*3 +9 ---->Kount=197=0xC5

Delay120us
	banksel	Kount120us
	movlw	H'C5'		;D'197'
	movwf	Kount120us
R120us	decfsz	Kount120us
	goto	R120us
	return
;
;100us delay needs 500 instruction cycles
;  500 =166*3 +2 ---->Kount=166=0xA6
;  or  =165*3 +5 ---->Kount=165=0xA5
;  or  =164*3 +8 ---->Kount=164=0xA4
Delay100us
	banksel	Kount100us
	movlw	H'A4'
	movwf	Kount100us
R100us	decfsz	Kount100us
	goto	R100us
	return
;
;1ms delay
Delay1ms
	banksel	Kount1ms
	movlw	0x0A	;10
	movwf	Kount1ms
R1ms	call	delay100us
	decfsz	Kount1ms
	goto	R1ms
	return
;
;10ms delay
; call 100 times of 100 us delay  (with some time discrepancy)
Delay10ms 
	banksel	Kount10ms
	movlw	H'64'	;100
	movwf	Kount10ms
R10ms	call	delay100us
	decfsz	Kount10ms
	goto	R10ms
	return
;
;
	
;1 sec delay
;call 100 times of 10ms delay
Delay1s
	banksel	Kount1s
	movlw	H'64'
	movwf	Kount1s
R1s	call	Delay10ms
	decfsz	Kount1s
	goto	R1s
	return
;
;
;10 s delay
;call 10 tiems of 1 s delay
Delay10s
	banksel	Kount10s
	movlw	H'0A'		;10
	movwf	Kount10s
R10s	call	Delay1s
	decfsz	Kount10s
	goto	R10s
	return
;
;1 min delay
;call	60 times of 1 sec delay
Delay1m
	banksel	Kount1m
	movlw	H'3C'	;60
	movwf	Kount1m
R1m	call	Delay1s
	decfsz	Kount1m
	goto	R1m
	return
;======================================================
	END
