PIC 4 bit 16x2 Hitachi LCD driver

by Barry


NEW! 416028

Another simple Hitachi LCD display control routine. This ASM code can drive a display with a PIC16F628 with the control lines on any PORTB pins - the actual connection are easily redefined in the code by changing the initial definitions.

; LCD driver by Barry (tron at zoidberg dot nl)
	include	p16f628.inc
	list p=16f628
	__config (_CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_CLKOUT & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF)

	; LCD control lines
	#define	LCD_RS	5		; 0 = Command, 1 = Data
	#define LCD_RW	6		; 0 = Write, 1 = Read
	#define LCD_E	4		; 1 to send data
	#define LCD_D4	0		; 4 data lines
	#define LCD_D5	1
	#define LCD_D6	2
	#define LCD_D7	3
	
; Registers we will be using
	#define R_DEL1			0x20	; Delay register
	#define R_DEL2			0x21	; Delay register
	#define R_WDATA_TMP1	0x22
	#define R_WDATA_TMP2	0x23
	#define R_SEND_W_TMP	0x24
	#define	R_WTMP			0x27	; W storage
	#define	R_STMP			0x28	; STATUS storage
	#define	R_STMP1			0x2E	; Send temp register
	#define R_PSEC			0x2F

	org	0
	goto	init

; Blank interrupt routine - simply saves and restores status & W
; Add code to do something useful...	
	org 4
	movwf	R_WTMP		; Save W & STATUS
	swapf	STATUS, W
	movwf	R_STMP
	bcf		STATUS, RP0
	
int_cleanup:
	movlw	b'11111000'	; Clear interrupt bits
	andwf	INTCON, F
	swapf	R_STMP, W	; Restore W & STATUS
	movwf	STATUS
	swapf	R_WTMP, F
	swapf	R_WTMP, W
	retfie

; Init sets all I/O lines as outputs and selects PORTA as
; I/O lines by turning the comparators off.
init:
	clrf	PORTB
	clrf	PORTA
	movlw	0x07
	movwf	CMCON		; Comparators off - PORTA on
	bsf		STATUS, RP0	; Select bank 1
	movlw	b'00000000'	; Select PORTB as outputs
	movwf	TRISB
	movlw	b'00000000'	; Select PORTA as outputs
	movwf	TRISA
	bcf		STATUS, RP0	; Select bank 0

	; Begin the LCD initialisation
	; Power up timer should give delay, but do it anyway
	movlw	0x14
	call	delay
	
	; Send the command to select 4-bit mode first
	bcf		PORTB, LCD_RS
	bcf		PORTB, LCD_RW
	movlw	0x02			; Still in 8-bit, so appears as 0x20 to LCD
	call	w_to_data
	call	pulse_e

; Should now be in 4-bit mode - send init
	movlw	b'00101000'
	call	send_w
	
	movlw	b'00001110'
	call	send_w
	
	movlw	b'00000110'
	call	send_w

main_loop:
	bcf		PORTB, LCD_RS	; Command mode
	movlw	b'00000010'		; Return cursor to home
	call	send_w
	bsf		PORTB, LCD_RS	; Data mode
	call	delay_1ms		; Takes a couple of ms
	
	movlw	'H'
	call	send_w
	movlw	'e'
	call	send_w
	movlw	'l'
	call	send_w
	movlw	'l'
	call	send_w
	movlw	'o'
	call	send_w
	movlw	'!'
	call	send_w
	
	goto	main_loop
	
; ----------------------

send_w
	movwf	R_SEND_W_TMP
	swapf	R_SEND_W_TMP, F
	movlw	0x0F
	andwf	R_SEND_W_TMP, W
	call	w_to_data
	call	pulse_e
	
	swapf	R_SEND_W_TMP, F
	movlw	0x0F
	andwf	R_SEND_W_TMP, W
	call	w_to_data
	call	pulse_e
	return

w_to_data
	movwf	R_WDATA_TMP1
	movf	PORTB, W
	movwf	R_WDATA_TMP2
	bcf		R_WDATA_TMP2, LCD_D4
	bcf		R_WDATA_TMP2, LCD_D5
	bcf		R_WDATA_TMP2, LCD_D6
	bcf		R_WDATA_TMP2, LCD_D7
	btfsc	R_WDATA_TMP1, 0
		bsf		R_WDATA_TMP2, LCD_D4
	btfsc	R_WDATA_TMP1, 1
		bsf		R_WDATA_TMP2, LCD_D5
	btfsc	R_WDATA_TMP1, 2
		bsf		R_WDATA_TMP2, LCD_D6
	btfsc	R_WDATA_TMP1, 3
		bsf		R_WDATA_TMP2, LCD_D7
	movf	R_WDATA_TMP2, W
	movwf	PORTB
	
	return
	
pulse_e
	bsf		PORTB, LCD_E
	nop
	nop
	nop
	nop
	bcf		PORTB, LCD_E
	call	delay_1ms
	call	delay_1ms
	return

; Calls the delay_1ms routine the number of times specified by
; the W register.
delay
	movwf	R_DEL2
delay_loop
	call	delay_1ms
	decfsz	R_DEL2, F
		goto	delay_loop
	return
	
; When called gives a delay of about 1000 cycles, or 1ms at 4Mhz
; before the next instruction is executed.
delay_1ms
	movlw	d'248'
	movwf	R_DEL1
delay_1ms_loop
	nop
	decfsz	R_DEL1, F
		goto	delay_1ms_loop
	return

	end

Stephen van Schalkwyk Says: " I am quite new to microprocessors and this code is going to be a great help - many thanks!
Stephen
"