PIC Projects

Driving LCD 4-bit mode

In this project the PIC is used to control an LCD (Liquid Crystal Display). The LCDs usually support either a serial interface or a parallel one. Serial devices are usually more expensive, since they contain a decent electronics that implement a serial interface. Concerning the parallel mode, there are two interfaces: an 8-bit mode and 4-bit mode. The difference is in the number of control pins necessary to drive LCD. In the 4-bit mode this number is 6.

The LCD I use is LCM-S01602DSR/D manufactured by Lumex. It contains 2 rows of 16 displayed characters and uses an 5x8 font. Actually, the LCD can store a line of 40 characters internally and can scroll it back and force. It is avaliable from digikey.com for about $15. The test program displays a string in both rows of the display module.

The hardware is extremely simple and contains just one element besides the PIC and LCD - the pot for adjusting the LCD contrast. To achieve the maximum contrast the Vo pin of the LCD can be just connected to the ground, thus eliminating a need in the pot. There are 6 control lines connecting the LCD with the PIC.

Schematic Layout

The messages to display (msg1 and msg2) are stored as read-only arrays and put at the beginning of the code to make sure that they occur within the first address block. This is needed for a correct (and simple) implementation of the computed goto technique. The code starts with initializing the PIC and LCD according to the specification.

Two loops in the main program part are intended to output the first and the second zero-terminated strings, respectively. The LCD interface is served with 3 methods: initLCD(), writeCMD(), and writeDATA(). The first one provides necessary delays to initialize the LCD electronics right after power on and setup the 4-bit mode and other stuff. The second and the third methods are practically identical and are intended to passing a command or a data byte to the LCD, respectively. Since the 4-bit interface is used, the byte is transferred in two steps, one nibble after another. A 160μsec delay is needed after all instructions excluding clearing the display. The last instruction as well as any data transfer needs a 5 msec delay for the LCD to process it.

 TITLE  "lcd4.asm"              	; working with an LCD in the 4-bit mode
 List P=16F84a, R=DEC
 INCLUDE "p16F84a.inc"

; data segment
 CBLOCK 0x00C                   
 del, cmd				; variables for passing the parameters
 temp, i				; local variables 
 ENDC

; code segment
 PAGE
 __CONFIG _CP_OFF & _PWRTE_ON  & _WDT_OFF & _HS_OSC

  org 0                      		; start program at the beginning of mem
	goto start

msg1					; first message to display
	addwf	PCL, f
	dt "  Welcome to", 0

msg2					; second message to display
	addwf	PCL, f
	dt "PIC programming", 0

start
  	bsf    	STATUS, RP0      	; change to BANK 1	
  	clrf    TRISB ^ 0x80      	; enable RB1-RB7 for output 
  	movlw  	0x7F              	; enable internal Pull-Ups
  	movwf  	OPTION_REG ^ 0x80
  	bcf    	STATUS, RP0        	; back to BANK 0

	call 	initLCD        		; initialize LCD display

; output of the top row (msg1)
	clrf 	i			
L1	movf	i, w
	call	msg1			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	goto 	L2			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L1

L2	movlw	0xC0
	call 	writeCMD		; move the cursor to the second line
	clrf	i			; clear the index of the second string

; output the bottom row (msg2)
L3 	movf	i, w					
	call	msg2			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	L4			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L3

L4  goto $				; endless loop


; procedures
initLCD					; initialize LCD right after power up
	movlw	15
	movwf	del
	call 	delay			; a 15 msec delay after power is on

	movlw	3
	movwf	PORTB			; start LCD init process
	movlw	5
	movwf	del	
	call 	delay			; wait for 5 msec	

	movlw	3
	movwf	PORTB			; start LCD init process
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4
	movlw	5
	movwf	del	
	call 	delay			; wait for 5 msec

	bsf	PORTB, 4		; repeat the reset command (2nd time)
	bcf	PORTB, 4
	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	bsf	PORTB, 4		; repeat the reset command (3rd time)
	bcf	PORTB, 4
	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	movlw	2			; initialize LCD 4-bit mode
	movwf	PORTB
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4

	movlw	0x28			; 2-line mode
	call	writeCMD

	movlw	1
	call 	writeCMD		; clear display

	movlw	5			; wait for 5 msec after clearing
	movwf	del
	call 	delay

	movlw	0x06			; cursor move after each char
	call 	writeCMD

	movlw	0x0E			; turn on LCD and enable cursor
	call 	writeCMD
	return

writeCMD				; write to LCD a 1-byte command in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the high 4 bits in W
	movwf	PORTB
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3
	return

writeDATA				; write to LCD a 1-byte data in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the lower 4 bits in W	
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	5			; wait for 5 msec
	movwf	del
	call 	delay
	return

delay					; a delay for del milliseconds
	movlw 	200
	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F84A @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del,f
	goto 	delay
	return

 end

Download lcd4.asm


Last modified:Mon, Jan 23, 2023.

15534