Home / Projects / PIC Projects / Using a 16×2 Monochrome LCD with the PIC16F84A (Assembly Tutorial)
pcbway

Using a 16×2 Monochrome LCD with the PIC16F84A (Assembly Tutorial)

Introduction

A 16×2 monochrome LCD module is a widely used display in microcontroller projects. It can show two lines of text, each up to 16 characters long, making it ideal for simple user interfaces. These LCDs are based on the HD44780 controller (or compatible), which accepts commands and data via an 8-bit or 4-bit parallel interface.

The LCD works by receiving control signals (RS, R/W, E) and data signals (D0–D7 or D4–D7 for 4-bit mode). In this tutorial, we’ll use 8-bit mode for simplicity. The PIC16F84A will send initialization commands to the LCD, followed by ASCII characters to be displayed.

Understanding the control lines:

  • RS (Register Select):
    • 0 = Instruction/command (e.g., clear display).
    • 1 = Data (character to display).
  • R/W (Read/Write):
    • 0 = Write to LCD.
    • 1 = Read from LCD (rarely needed, we’ll keep it  0.
  • E (Enable):
    • A high-to-low pulse latches the data/command into the LCD.

Once initialized, the LCD can display text by sending ASCII codes as data.


Materials

  • PIC16F84A microcontroller
  • 16×2 HD44780-based monochrome LCD module
  • 4 MHz crystal oscillator + 2 × 22pF capacitors
  • 10kΩ potentiometer (for LCD contrast adjustment)
  • Breadboard and jumper wires
  • 5V regulated power supply

Code (Assembly for PIC16F84A)

We’ll walk through the full code, then explain each part.

Full Program

;===========================================================
; Program: LCD 16x2 Display with PIC16F84A
;===========================================================

    list p=16F84A
    include <p16f84a.inc>

    __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF

;-------------------------------
; Pin connections
; LCD Data -> PORTB
; RS -> RA0
; RW -> RA1 (tied low, write only)
; EN -> RA2
;-------------------------------

    cblock 0x20
        temp
    endc

;-------------------------------
; Reset vector
;-------------------------------
    org 0x00
    goto main

;-------------------------------
; Subroutines
;-------------------------------
delay_ms
    movlw   d'250'
    movwf   temp
d1  nop
    nop
    decfsz  temp, f
    goto d1
    return

lcd_pulse
    bsf     PORTA,2      ; E=1
    call    delay_ms
    bcf     PORTA,2      ; E=0
    call    delay_ms
    return

lcd_cmd
    bcf     PORTA,0      ; RS=0
    movwf   PORTB        ; Send command
    call    lcd_pulse
    return

lcd_data
    bsf     PORTA,0      ; RS=1
    movwf   PORTB        ; Send data
    call    lcd_pulse
    return

lcd_init
    ; Function Set: 8-bit, 2 line, 5x8 font
    movlw   0x38
    call    lcd_cmd

    ; Display ON, Cursor OFF
    movlw   0x0C
    call    lcd_cmd

    ; Clear display
    movlw   0x01
    call    lcd_cmd

    ; Entry mode: auto-increment
    movlw   0x06
    call    lcd_cmd
    return

;-------------------------------
; Main Program
;-------------------------------
main
    bsf     STATUS, RP0
    movlw   0x00
    movwf   TRISB       ; PORTB = output
    movlw   0xF8
    movwf   TRISA       ; RA0-RA2 output, RA3-RA4 input
    bcf     STATUS, RP0

    call    lcd_init

    ; Write "HELLO"
    movlw   'H'
    call    lcd_data
    movlw   'E'
    call    lcd_data
    movlw   'L'
    call    lcd_data
    movlw   'L'
    call    lcd_data
    movlw   'O'
    call    lcd_data

endless
    goto endless

    end

Simulate this code in PICSimulator

Code Explanation

1. Configuration and Setup

    list p=16F84A
    include <P16F84A.inc>
    __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _CP_OFF
  • Sets processor type and includes the register definitions.
  • Configures the microcontroller for High-Speed crystal oscillator, watchdog timer off, power-up timer on, and code protection off.

2. LCD Pin Mapping

  • PORTB is used for LCD data (D0–D7).
  • RA0 = RS, RA1 = R/W (grounded), RA2 = E.
  • Only writing to the LCD is needed, so R/W is tied to ground.

3. Delay Routine

delay_ms
    movlw d'250'
    movwf temp
d1: decfsz temp, f
    goto d1
    return

A simple software delay loop to let the LCD process commands.

4. LCD Enable Pulse

lcd_pulse
    bsf PORTA,2   ; E=1
    call delay_ms
    bcf PORTA,2   ; E=0
    call delay_ms
    return

The LCD latches commands/data on the falling edge of E.

5. Sending Commands and Data

lcd_cmd
    bcf PORTA,0   ; RS=0 (command)
    movwf PORTB
    call lcd_pulse
    return
lcd_data
    bsf PORTA,0   ; RS=1 (data)
    movwf PORTB
    call lcd_pulse
    return

These subroutines separate instructions (clear, cursor) from characters (ASCII).

6. LCD Initialization

movlw 0x38   ; Function set: 8-bit, 2-line, 5x8 font
movlw 0x0C   ; Display ON, cursor OFF
movlw 0x01   ; Clear display
movlw 0x06   ; Entry mode: auto-increment

These commands are required at startup to prepare the LCD.

7. Writing Text

movlw 'H'
call lcd_data

Each ASCII character is written sequentially to the LCD.


Conclusion

In this tutorial, we connected a 16×2 monochrome LCD to a PIC16F84A microcontroller using an 8-bit parallel interface. We wrote initialization and display routines in assembly, giving full control of the LCD at a low level.

This project demonstrates how to:

  • Initialize the LCD in 8-bit mode.
  • Send commands and data separately.
  • Display custom strings of text.

With this foundation, you can extend the program to display sensor data, menu options, or real-time feedback for embedded projects.

Check Also

PIC16F7877A prototype board

Capture and Compare with PIC16F877A

The PIC16F877A microcontroller comes with a Capture Compare PWM (CCP) module which utilizes its 16-bit …

Index