16x2 LCD Interfacing with 8051 in 4-bit Mode:

 

The Assembly Code:

 
U   EQU 31      ;memory location to hold upper nibble
L   EQU 32      ;memory location to hold lower nibble

PORT    EQU P1      ;data port to connect lcd
RS  EQU P2.0        ;rs pin connection
RW  EQU P2.1        ;rw pin connection
EN  EQU P2.2        ;en pin connection

    ORG 0000H
    CLR RW
    ACALL   INIT
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #'E'     ;print "  esrt. "
    ACALL   LCD_DATA    ;in first line of lcd
    MOV A, #'S'
    ACALL   LCD_DATA
    MOV A, #'R'
    ACALL   LCD_DATA
    MOV A, #'T'
    ACALL   LCD_DATA
    MOV A, #'.'
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA

    MOV A, #0C0H    ;switch to 2nd line of lcd
    ACALL   LCD_CMD

    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #' '     ;print " lab "
    ACALL   LCD_DATA    ;in second line of lcd
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA
    MOV A, #'L'
    ACALL   LCD_DATA
    MOV A, #'A'
    ACALL   LCD_DATA
    MOV A, #'B'
    ACALL   LCD_DATA
    MOV A, #' '
    ACALL   LCD_DATA

    SJMP    $       ;infinite long loop

; separator for upper and lower nibble
;++++++++++++++++++++++++++++++++++++++

SEPARATOR:
    MOV U, A        ;save a at temp location u
    ANL U, #0F0H    ;mask it  with 0fh (28h & f0h = 20h)
    SWAP    A       ;swap nibble (28h => 82h)
    ANL A, #0F0H    ;mask it with 0fh (82h & f0h = 80h)
    MOV L, A        ;save it at temp location l
    RET         ;return

; move to port sub-routine
MOVE_TO_PORT:
    MOV PORT, A     ;put content of a to port
    SETB    EN      ;make en high
    ACALL   DELAY       ;call a short delay routine
    CLR EN      ;clear en
    ACALL   DELAY       ;short delay
    RET         ;return

; lcd command write sub-routine

LCD_CMD:
    CLR RS      ;clear rs, going to send command
    ACALL   SEPARATOR   ;separate the command and save to u and l
    MOV A, U        ;copy u to a
    ACALL   MOVE_TO_PORT    ;move content of a to port
    MOV A, L        ;copy l to a
    ACALL   MOVE_TO_PORT    ;move content of a to port
    RET         ;return

; lcd data write sub-routine

LCD_DATA:
    SETB    RS      ;rs=1, going to send data
    ACALL   SEPARATOR   ;separate the data and save to u & l
    MOV A, U        ;copy u to a
    ACALL   MOVE_TO_PORT    ;send it to lcd
    MOV A, L        ;copy l to a
    ACALL   MOVE_TO_PORT    ;send it to lcd
    RET         ;return

;lcd initialization sub-routine

INIT:
    ACALL   DELAY       ;some delay to lcd after power on
    ACALL   DELAY

    MOV PORT, #20H  ;send 20h to lcd to set 4 bit mode
    CLR RS      ;after that we can use lcd_cmd
    SETB    EN      ;make en switching
    ACALL   DELAY
    CLR EN

    MOV A, #28H
    ACALL   LCD_CMD
    MOV A, #0CH
    ACALL   LCD_CMD
    MOV A, #06H
    ACALL   LCD_CMD
    MOV A, #01H
    ACALL   LCD_CMD
    RET

; a delay sub-routine

DELAY:
    MOV R0, #10H
L2: MOV R1, #0FH
L1: DJNZ    R1, L1
    DJNZ    R0, L2
    RET

    END

The code and Proteus MOdel can be downloaded from here and here