PIC 4 bit 16x2 Hitachi LCD driver

Universal HD44780 LCD display driver library - P. Pemberton


NEW! 416028

This is my first decent bit of code for the PICmicro. It's a display control library that was distributed to a few members of the PICList for testing. I have used the serial control library (lcd_ser.inc) extensively - LCDs make great debugging tools. Examine the examples carefully - almost all you need to know is contained in them.

A small bug in the 4-bit driver code was fixed on 26 May 2003. This bug meant that some display panels (such as the Optrex DMC16106C) would miss characters sent to the display.


banks.inc

          LIST
; PIC16-series PAGE-switching macros
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.
;
; Provides:
;       BANK0           Switch to RAM BANK 0
;       BANK1           Switch to RAM BANK 1
;       BANK2           Switch to RAM BANK 2
;       BANK3           Switch to RAM BANK 3
          NOLIST

#ifndef   __BANKS_INCLUDED
 #define  __BANKS_INCLUDED

BANK0     MACRO                         ; Macro to select data RAM BANK 0
          BCF       STATUS,RP0
          BCF       STATUS,RP1
          ENDM

BANK1     MACRO                         ; Macro to select data RAM BANK 1
          BSF       STATUS,RP0
          BCF       STATUS,RP1
          ENDM

BANK2     MACRO                         ; Macro to select data RAM BANK 2
          BCF       STATUS,RP0
          BSF       STATUS,RP1
          ENDM

BANK3     MACRO                         ; Macro to select data RAM BANK 3
          BSF       STATUS,RP0
          BSF       STATUS,RP1
          ENDM
#endif
          LIST


lcd_cmn.inc

; Universal PIC LCD Interface Module
; HD44780 main handler/common functions (LCD_CMN.INC)
; NOTE: DO NOT #INCLUDE THIS FILE - #INCLUDE ONE OF THE BACKENDS INSTEAD!
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.


	CBLOCK
		__LCD_DOCBUFF		; Display Control settings
	ENDC

; Initialize LCD
LCD_INIT:
	MOVLW	B'00000100'		; Init DOCBUFF
	MOVWF	__LCD_DOCBUFF
	CALL	__LCD_INIT		; Init the LCD
	RETURN				; Return

; Display Clear
LCD_CLRSCR:
	MOVLW	B'00000001'		; Clear the LCD display
	CALL	LCD_SENDINS
	RETURN

; Display On/Off - If W=0 then display off else display on
LCD_ONOFF:
	BSF	__LCD_DOCBUFF,	2	; Assume W != 0
	IORLW	0			; Update flags
	BTFSC	STATUS,		Z	; Zero?
	BCF	__LCD_DOCBUFF,	2	; W=0; turn off the display
	MOVF	__LCD_DOCBUFF,	W	; Move control byte into W
	IORLW	.8			; Set command bit
	CALL	LCD_SENDINS		; Send the instruction
	RETURN

; Cursor On/Off - If W=0 then cursor off else cursor on
LCD_CURSOR:
	BSF	__LCD_DOCBUFF,	1	; Assume W != 0
	IORLW	0			; Update flags
	BTFSC	STATUS,		Z	; Zero?
	BCF	__LCD_DOCBUFF,	1	; W=0; turn off the cursor
	MOVF	__LCD_DOCBUFF,	W	; Move control byte into W
	IORLW	.8			; Set command bit
	CALL	LCD_SENDINS		; Send the instruction
	RETURN

; Blinking Cursor On/Off - If W=0 then blinking cursor on else off
LCD_CURSBLINK:
	BSF	__LCD_DOCBUFF,	0	; Assume W != 0
	IORLW	0			; Update flags
	BTFSC	STATUS,		Z	; Zero?
	BCF	__LCD_DOCBUFF,	0	; W=0; turn off the block cursor
	MOVF	__LCD_DOCBUFF,	W	; Move control byte into W
	IORLW	.8			; Set command bit
	CALL	LCD_SENDINS		; Send the instruction
	RETURN

; Move cursor - W=offset
LCD_MOVECURS:
	IORLW	B'10000000'		; Set command bit
	CALL	LCD_SENDINS		; Send the instruction
	RETURN

; Move cursor to Line 1
LCD_LINE1:
	MOVLW	0x00			; Move cursor to 0x00
	CALL	LCD_MOVECURS
	RETURN

; Move cursor to Line 2
LCD_LINE2:
	MOVLW	0x40			; Move cursor to 0x40
	CALL	LCD_MOVECURS
	RETURN


lcd_4bit.inc

; Universal PIC LCD Interface Module
; HD44780 4-bit mode transport layer/backend (LCD_4BIT.INC)
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.
;
; Pindefs required:
; 	LCD_D7	The PIC pin connected to the LCD's D7 pin (ex: PORTB,3)
;	LCD_D6	The PIC pin connected to the LCD's D6 pin (ex: PORTB,2)
;	LCD_D5	The PIC pin connected to the LCD's D5 pin (ex: PORTB,1)
;	LCD_D4	The PIC pin connected to the LCD's D4 pin (ex: PORTB,0)
;	LCD_E	The PIC pin connected to the LCD's E pin (ex: PORTB,5)
;	LCD_RS	The PIC pin connected to the LCD's RS pin (ex: PORTB,4)

        CBLOCK
                __LCD_TEMP0
                __LCD_TEMP1
        ENDC

; Send a byte to the LCD, assumes RS already set
__LCD_SEND:
	MOVWF	__LCD_TEMP0		; Store byte in Temp 0
	BCF	LCD_D7			; Clear data bits
	BCF	LCD_D6
	BCF	LCD_D5
	BCF	LCD_D4
	BTFSC	__LCD_TEMP0,	7	; Load high nibble
	BSF	LCD_D7
	BTFSC	__LCD_TEMP0,	6
	BSF	LCD_D6
	BTFSC	__LCD_TEMP0,	5
	BSF	LCD_D5
	BTFSC	__LCD_TEMP0,	4
	BSF	LCD_D4
	BSF	LCD_E			; Strobe E
	GOTO	$+1
	BCF	LCD_E

	BCF	LCD_D7			; Clear data bits
	BCF	LCD_D6
	BCF	LCD_D5
	BCF	LCD_D4
	BTFSC	__LCD_TEMP0,	3	; Load low nibble
	BSF	LCD_D7
	BTFSC	__LCD_TEMP0,	2
	BSF	LCD_D6
	BTFSC	__LCD_TEMP0,	1
	BSF	LCD_D5
	BTFSC	__LCD_TEMP0,	0
	BSF	LCD_D4
	BSF	LCD_E			; Strobe E
	GOTO	$+1
	BCF	LCD_E
	RETURN

; Send an instruction byte to the LCD
LCD_SENDINS:
	BCF	LCD_RS			; RS low=instruction
	CALL	__LCD_SEND		; Send the instruction byte
	CALL	__LCD_SHORTDELAY	; Short delay for instruction
	RETURN

; Send a character to the LCD
LCD_PUTCH:
	BSF	LCD_RS			; RS high=character
	CALL	__LCD_SEND		; Send the character
	CALL	__LCD_SHORTDELAY	; Short delay for data
	RETURN

; Initialize the LCD
__LCD_INIT:
	CALL	__LCD_LONGDELAY		; Long init delay
	MOVLW	B'00110011'		; 2x 8bit resets
	CALL	LCD_SENDINS
	MOVLW	B'00110011'		; 2x 8bit resets
	CALL	LCD_SENDINS
	MOVLW	B'00110010'		; 8bit reset then 4bit reset
	CALL	LCD_SENDINS
	MOVLW	B'00101100'		; Function Set - 4bit, 2 lines
	CALL	LCD_SENDINS
	MOVLW	B'00000110'		; Mode Set - incr. w/crsr shift
	CALL	LCD_SENDINS
	MOVLW	B'00001100'		; Display on, cursor & blink off
	CALL	LCD_SENDINS
	MOVLW	B'00000001'		; Display Clear
	CALL	LCD_SENDINS
	MOVLW	B'00000010'		; Cursor Home
	CALL	LCD_SENDINS
	CALL	__LCD_LONGDELAY		; Long post-init delay
	RETURN

; Long delay for init
__LCD_LONGDELAY:
	CLRF	__LCD_TEMP0
__LCD_LONGDEL:
	CLRF	__LCD_TEMP1
__LCD_SHORTDEL:
	DECFSZ	__LCD_TEMP1,	F
	GOTO	__LCD_SHORTDEL
	DECFSZ	__LCD_TEMP0,	F
	GOTO	__LCD_LONGDEL
	RETURN

; Short delay between commands
__LCD_SHORTDELAY:
	MOVLW	0x14
	MOVWF	__LCD_TEMP1
__LCD_SHORTLOOP:
	DECFSZ	__LCD_TEMP1,	F
	GOTO	__LCD_SHORTLOOP
	RETURN

; Include the Common LCD Functions
	INCLUDE	"lcd_cmn.inc"


lcd_ser.inc

; Universal PIC LCD Interface Module
; HD44780 serial mode transport layer/backend (LCD_SER.INC)
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.
;
; Pindefs required:
;	LCD_DATA	The PIC pin connected to the A and B pins on the 
;				74LS164 (ex: PORTB,5)
;	LCD_CLOCK	The PIC pin connected to the LS164's CLOCK pin
;				(ex: PORTB,4)

        INCLUDE "banks.inc"

; File register Usage
	CBLOCK
		__LCD_DLAY		; Delay variable
		__LCD_NOTEMP		; Temporary variable for nibble output
		__LCD_TEMP		; Temporary variable
		__LCD_CNTR		; Counter
	ENDC

__LCD_INIT:
        CALL    __LCD_DLAY5             ;  Wait 20 msecs before Reset
        CALL    __LCD_DLAY5
        CALL    __LCD_DLAY5
        CALL    __LCD_DLAY5

        BCF     STATUS,         C       ; RS=0 (instruction)
        MOVLW   0x30                    ; RESET
        CALL    __LCD_NIBBLE            ; Send the nibble
        CALL    __LCD_DLAY5             ; Wait 5mS before resending

        BSF     LCD_DATA		; Send again.
        BCF     LCD_DATA
        CALL    __LCD_DLAY160           ; Wait 160uS before resending

        BSF     LCD_DATA
        BCF     LCD_DATA
        CALL    __LCD_DLAY160           ; Wait 160uS before sending

        BCF     STATUS,         C       ; RS=0 (instruction)
        MOVLW   0x20                    ; Set 4-bit (nibble) mode
        CALL    __LCD_NIBBLE            ; Send the nibble
        CALL    __LCD_DLAY160           ; Wait 160uS before sending

        MOVLW   0x28                    ; Two line mode, 4-bit
        CALL	LCD_SENDINS
        CALL	__LCD_DLAY160

        MOVLW   0x08                    ; Turn off the display
        CALL    LCD_SENDINS

        MOVLW   0x01                    ; Clear the DDRAM
        CALL    LCD_SENDINS
        CALL    __LCD_DLAY5             ; Can take up to 5uS

        MOVLW   0x06                    ; Set cursor direction
        CALL    LCD_SENDINS

        MOVLW   0x0C                    ; Display on
        CALL    LCD_SENDINS
        CALL    __LCD_DLAY160

	RETURN

LCD_PUTCH:                              ; Send a character to the LCD
        MOVWF   __LCD_DLAY              ; Temporary store.

        BSF     STATUS,         C       ; RS=1 (data)
        CALL    __LCD_NIBBLE            ; Send the high nibble

        SWAPF   __LCD_DLAY,     W       ; Low nibble -> W
        BSF     STATUS,         C       ; RS=1 (data)
        CALL    __LCD_NIBBLE            ; Send the low nibble
        RETURN

LCD_SENDINS:                            ; Send an instruction to the LCD
        MOVWF   __LCD_DLAY              ; Temporary store

        BCF     STATUS,         C       ; RS=0 (instruction)
        CALL    __LCD_NIBBLE            ; Send the high nibble

        SWAPF   __LCD_DLAY,     W       ; Low nibble -> W
        BCF     STATUS,         C       ; RS=0 (instruction)
        CALL    __LCD_NIBBLE            ; Send the low nibble
        RETURN

__LCD_NIBBLE:                           ; Sends a nibble (4 bits) to the LCD
        MOVWF   __LCD_NOTEMP            ; Temporarily store the nibble
        RRF     __LCD_NOTEMP,   F       ; Save the RS bit
        BSF     STATUS,         C       ; Carry high
        RRF     __LCD_NOTEMP,   F       ; Save the gate bit

        MOVLW   .0                      ; Clear the shift register
        CALL    __LCD_SEND

        MOVF    __LCD_NOTEMP,   W       ; Send the nibble to the LCD
        CALL    __LCD_SEND

        BSF     LCD_DATA                ; Clock in the data
        BCF     LCD_DATA
        RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Send the byte in W to the 74HCT164
__LCD_SEND:
        MOVWF   __LCD_TEMP              ; Store byte
        MOVLW   .8                      ; Eight data bits
        MOVWF   __LCD_CNTR              ; Store in counter
__LCD_SENDLOOP:
        MOVLW   0
        RLF     __LCD_TEMP,	F	; Rotate left into carry
        BCF     LCD_DATA                ; Assume carry low
        BTFSC   STATUS, C               ; Carry high?
        BSF     LCD_DATA                ; Yes. Set port bit
;        GOTO    $+1                     ; Wait for the data line to settle
        BSF     LCD_CLOCK               ; Clock high
;        GOTO    $+1                     ; Make sure the HC164 sees the transition
        BCF     LCD_CLOCK		; Clock low
        DECFSZ  __LCD_CNTR,	F	; Decrement counter
        GOTO    __LCD_SENDLOOP          ; Not zero yet; keep going
        RETURN                          ; Counter=0; return.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

__LCD_DLAY160:                          ; 160uS delay
        MOVLW   .256-(160/4)            ; Loop until carry set
        ADDLW   .1
        BTFSS   STATUS,         C
        GOTO    $-2
        RETURN

__LCD_DLAY5:                            ; 5mS delay
        MOVLW   .4                      ; Set up the Delay
        MOVWF   __LCD_DLAY
        MOVLW   .256 - 0x0E8            ; Inner loop
        ADDLW   1
         BTFSC  STATUS, Z
        DECFSZ  __LCD_DLAY, F           ; Outer loop
        GOTO    $-3
        RETURN

; Include the Common LCD Functions
	INCLUDE	"lcd_cmn.inc"


lcdtest_ser.inc

; Universal PIC LCD Interface Module
; 74LS164-to-LCD test program
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.
;
; Connections:
;	PIC	74LS164		LCD
;	RB7	1 and 2		via 10k resistor to "E"
;	RB6	8
;		5		D4
;		6		D5
;		10		D6
;		11		D7
;		12		RS
;		13			to 1N4148 cathode
;	Vcc	9 and 14	Vcc
;	Vss	7		Vss
;				E	to 1N4148 anode
;
;
; The contrast pin on the LCD should be connected as shown in the LCD module's
; datasheet.
;
; The above circuit is based on one included in an article written by
; Myke Predko for www.rentron.com

;; PIC setup and toplevel includes
	LIST	P=16F874
	INCLUDE	<p16f874.inc>
	INCLUDE	"banks.inc"

; Pindefs
#DEFINE	LCD_DATA	PORTB,	7
#DEFINE	LCD_CLOCK	PORTB,	6

; Variables
	CBLOCK	0x20
	ENDC

; Reset vector
	ORG	0
	GOTO	START
; Interrupt vector
	ORG	4
	GOTO	START

; Start of code
	ORG	5

START:
	CLRF	PORTB			; Clear PORTB
	PAGE1				; Bank 1
	BCF	LCD_DATA		; Data, clock=outputs
	BCF	LCD_CLOCK
	PAGE0				; Bank 0

	CALL	LCD_INIT		; Init the LCD

	CLRF	FSR			; Message loop
MSGLOOP:
	MOVF	FSR,		W
	INCF	FSR,		F
	CALL	MESSAGE
	IORLW	.0
	BTFSC	STATUS,		Z
	GOTO	MSGDONE
	CALL	LCD_PUTCH
	GOTO	MSGLOOP

MSGDONE:
	MOVLW	.1			; Block Cursor on
	CALL	LCD_CURSBLINK
	CALL	LCD_LINE2
	MOVLW	"2"
	CALL	LCD_PUTCH
	MOVLW	"0"
	CALL	LCD_PUTCH
	MOVLW	"."
	CALL	LCD_PUTCH
	MOVLW	"0"
	CALL	LCD_PUTCH
	MOVLW	0x1A
	CALL	LCD_PUTCH
	GOTO	$

MESSAGE:
	ADDWF	PCL,		F
	DT	"Hello.",0

;; Final post-code includes
	INCLUDE	"lcd_ser.inc"

	END


lcdtest_4bit.inc

; Universal PIC LCD Interface Module
; 4-bit LCD test program
; By P. Pemberton, Jr.
; WWW: http://www.philpem.me.uk/
; Email: piclist@philpem.me.uk
; This software is distributed under the BSD License. You are free to do whatever
; you want with this code, but I would appreciate an email first. I would also
; appreciate copies of any modifications you make.
;
; Connections:
;	PIC	LCD
;	RB5	E
;	RB4	RS
;	RB3	D7
;	RB2	D6
;	RB1	D5
;	RB0	D4
;
; The contrast pin on the LCD should be connected as shown in the LCD module's
; datasheet.

;; PIC setup and toplevel includes
	LIST	P=16F874
	INCLUDE	<p16f874.inc>
	INCLUDE	"banks.inc"

; Pindefs
#DEFINE	LCD_D7	PORTB,	3
#DEFINE	LCD_D6	PORTB,	2
#DEFINE	LCD_D5	PORTB,	1
#DEFINE	LCD_D4	PORTB,	0
#DEFINE	LCD_E	PORTB,	5
#DEFINE	LCD_RS	PORTB,	4

; Variables
	CBLOCK	0x20
	ENDC

; Reset vector
	ORG	0
	GOTO	START
; Interrupt vector
	ORG	4
	GOTO	START

; Start of code
	ORG	5

START:
	CLRF	PORTB			; Clear PORTB
	PAGE1				; Bank 1
	BCF	LCD_D7			; Databits=outputs
	BCF	LCD_D6
	BCF	LCD_D5
	BCF	LCD_D4
	BCF	LCD_RS			; RS, E=outputs
	BCF	LCD_E
	PAGE0				; Bank 0

	CALL	LCD_INIT		; Init the LCD

	CLRF	FSR			; Message loop
MSGLOOP:
	MOVF	FSR,		W
	INCF	FSR,		F
	CALL	MESSAGE
	IORLW	.0
	BTFSC	STATUS,		Z
	GOTO	MSGDONE
	CALL	LCD_PUTCH
	GOTO	MSGLOOP

MSGDONE:
	MOVLW	.1			; Blinking Cursor on
	CALL	LCD_CURSBLINK
	CALL	LCD_LINE2
	MOVLW	"2"
	CALL	LCD_PUTCH
	MOVLW	"0"
	CALL	LCD_PUTCH
	MOVLW	"."
	CALL	LCD_PUTCH
	MOVLW	"0"
	CALL	LCD_PUTCH
	MOVLW	0x1A
	CALL	LCD_PUTCH
	GOTO	$

MESSAGE:
	ADDWF	PCL,		F
	DT	"Hello.",0

;; Final post-code includes
	INCLUDE	"lcd_4bit.inc"

	END

Comments: