3-Wire LCD Interface For 8051 Systems
Submitted by: Vidyasagaran P. (v_sagar_p@yahoo.com)

; ------------- READS51 generated header -------------- 
; module  : C:\Rigel\Reads51\Work\LCD\LCD.asm
; created : 20:34:08, Thursday, August 15, 2002
; author  : Vidyasagaran P. (v_sagar_p@yahoo.com)
; This file contains all the routines needed to manipulate the LCD display
; Please note that these routines are hardware wiring specific and will need
; changes if the wiring changes. You may have to adjust the delay (DLY_IDX) depending on
; the processor speed.
;       +-------+                                       +---------------+
;       |   P0.0+-------------------------------------->+E     LCD      |
;       |       |               +----------+            |               |
;       |   P0.1+-------------->+CLK     D5+----------->+RS          R/W+---+
;       |   P0.2+-------------->+INPUT D0-4+--------\-->+D4-D7          |   |
;       |       |               |          |     +--\-->+D0-D3          |   - Gnd
;       +-------+               +----------+     |      +---------------+
;         8051                    74HC164        -Gnd    LCD display
; How data is loaded into the LCD
;   
;       1. clock the upper nibble in and use E bit to latch the higher 4 bits
;       2. clock the lower nibble in similarly
; ----------------------------------------------------- 
;
; The following definitions are as per the ABOVE hardware wiring.
; It assumes 3 port pins used for display functions. First one directly connected to LCD
; E (enable) bit and reset two are connected to an 8 bit output shift register as below.

#include <sfr51.inc>

;----------------------- PORTING Changes to be done in the following ---------
#define LCD_PORT    	P3  	;Port used for LCD interface
#define BASE    	000     ;where the code this located
#define DLY_IDX 	2FH     ;depends on speed of processor

#define EBIT    LCD_PORT.0    ;LCD Ebit pin
#define S2P_CLK LCD_PORT.1    ;Serial to parallel convertor 74HC164 clock pin
#define S2P_IN  LCD_PORT.2    ;Serial to parallel convertor 74HC164 serial input


CodeSeg segment code

Public  _Init_LCD
Public  _display_string
    
        rseg Codeseg
        org     BASE

_Init_LCD:
        clr     EBIT       ;reset the LCD E bit used. Other pins need not be cleared.
	
        acall   init_lcd
        mov     DPTR,#Init_msg
        acall   _display_string
        ret

;-----------------------------------------------------------
;clear_home_cursor - put the cursor back to first char
clear_home_cursor:
        ;clear display
        clr     c
        mov     acc,#00000001b
        acall   _write_char

        ;home cursor
        clr     c
        mov     acc,#00000010b
        acall   _write_char
        ret

;-----------------------------------------------------------
;_display_string is a function which assumes the address of the string
;to be passed in DPTR, in the code memory. Strin must have a NULL at the end.
_display_string:
        mov     acc,r1          ;save R1
        push    acc
        mov     acc,r0          ;save R0, as this is destroyed in _write_char
        push    acc

        acall   clear_home_cursor
        mov     r1,#0           ;index count for next character
display_string_loop:
        mov     a,r1            ;move index count to accumulator
        movc    a,@a+DPTR       ;next char to go
        jz      display_string_out      ;is it a null?
        setb    c               ;if not null set carry to write char
        acall   _write_char     ;call write_char function
        inc     r1              ;next char to go
        sjmp    display_string_loop    ;repeat till all chars are done
display_string_out:

        pop     acc             ;restore R0 as write_char destroys this
        mov     r0,acc
        pop     acc             ;restore R1
        mov     r1,acc
        ret

;-----------------------------------------------------------
;A character is passed in to this routine in accumulator
;this routine assumes the following wiring from uP to the LCD.
;R0 is destroyed. But exported functions "display_string" saves this,
;(a precausion which is optimised also!)
_write_char:
        mov     ov,c            ;store state of c for second nibble write
        mov     r0,#5           ;we need to shift these many times
        setb    ac              ;this indicates second nibble needs to be written yet

write_char_loop:
        mov     S2P_IN,c        ;serial input bit
        clr     S2P_CLK         ;clock once
        setb    S2P_CLK
        rlc     a               ;get c flag set as per data bits from next time
        djnz    r0,write_char_loop

        ;make the LCD latch the value
        setb    EBIT            ;pulse Ebit for one time for the first nibble
        clr     EBIT

        jnb     ac,write_char_over
        clr     ac              ;get out after the second nibble written

        rrc     a               ;remove that extra rotation
        mov     c,ov            ;restore the original carry to write second nibble
        mov     r0,#5           ;we need to write these many times
        sjmp    write_char_loop

write_char_over:
        jb      ov,write_char_nodelay   ;for command writes delay is needed, not for chars!
        acall   big_dly
write_char_nodelay:
        ret

;----------------------------------------------------------
;write_one_nibble : writes one Most significant nibble in a passed char
;to the LCD. Caller should set the C to indicate write to LCD command
;Assumes left roation of bits and hence wiring of data bits from Latch
;to be accordingly
;contents of R0 is destroyed
write_one_nibble:
        mov     r0,#5           ;we need to write these many times

write_one_nibble_loop:
        mov     S2P_IN,c        ;serial input bit
        clr     S2P_CLK         ;clock once
        setb    S2P_CLK
        rlc     a               ;get c flag set as per data bits from second time
        djnz    r0,write_one_nibble_loop

        ;make the LCD latch the value
        setb    EBIT            ;pulse Ebit for one time for the first nibble
        clr     EBIT
        acall   big_dly
        ret

;----------------------------------------------------------
;a big delay for the LCD to settle after each init stuff. Some places this
;delay seems to be very critical. I have put more since it doesn't take much
;time and also since it is only one time init.
big_dly:
        mov     r0,#DLY_IDX
odly:
        mov     acc,#FFH
dly:
        djnz    acc,dly        ;Simulater virtually hangs here. So comment this during simulation
        djnz    r0,odly
        ret

;----------------------------------------------------------
;init_lcd : the following routine works fine from the first write after
;the power is applied. "write_one_nibble" is used to change the  LCD mode
;from 8 bit interface to 4 bit one. For this write alone the D0-D3 is taken
;as 0000 as they are hardwired to ground.
init_lcd:
        ;set display width
        clr     c
        mov     acc,#00100000b
        acall   write_one_nibble       

        ;enable display and cursor
        clr     c
        mov     acc,#00001100b          ;no cursor and no blink
        acall   _write_char

        ;clear display
        clr     c
        mov     acc,#00000001b
        acall   _write_char

        ;home cursor
        clr     c
        mov     acc,#00000010b
        acall   _write_char
        ret

;-----------------------------------------------------------
Init_msg:
        DB      "Display OK",0

        end

Back to the 8051 projects section HERE