Interfacing 16x2 LCD with Raspberry Pi using GPIO & Python

In my previous post I had used an 8 bit i2c port expander to drive the 16x2 LCD. It saved precious GPIO pins but added complexity and cost. In this post I will be using the RPi.GPIO library and Python to control the LCD.The LCD used in this post is based on Hitachi HD44780 LCD controller.Although the LCD has 16 pins available for interfacing, using the 4 bit mode only 6 GPIO pins are required ( RS,E,D4,D5,D6,D7).                                                                               


  LCD Pin    Pi Pin
     01  <------>  GPIO-06
     02  <------>  GPIO-02
     03  <------>  GPIO-06
     04  <------>  GPIO-26
     05  <------>  GPIO-06
     06  <------>  GPIO-24
     07
     08
     09
     10
     11  <------>  GPIO-22
     12  <------>  GPIO-18
     13  <------>  GPIO-16
     14  <------>  GPIO-12
     15  +5V    
     16  <------>  GPIO-06

NOTE : With the help of  RW pin the device can be set to read/write mode.Setting [R/W=0] will write to the register and setting [R/W=1] will read from the register.To display data on LCD read access is not required,so the RW in connected to GND. This ensures that there is no outbound data from HD44780 as Pi cannot tolerate 5V.

You can check the pinout of Pi from here.

Code:-

HD44780 based display can be controlled using any programming environment.Here I have used Python & RPi.GPIO library to provide access to the GPIO.


#!/usr/bin/python

import RPi.GPIO as GPIO
from time import sleep
class HD44780:

    def __init__(self, pin_rs=7, pin_e=8, pins_db=[25, 24, 23, 18]):

        self.pin_rs=pin_rs
        self.pin_e=pin_e
        self.pins_db=pins_db

        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.pin_e, GPIO.OUT)
        GPIO.setup(self.pin_rs, GPIO.OUT)
        for pin in self.pins_db:
            GPIO.setup(pin, GPIO.OUT)

        self.clear()

    def clear(self):
        """ Blank / Reset LCD """

        self.cmd(0x33) # $33 8-bit mode
        self.cmd(0x32) # $32 8-bit mode
        self.cmd(0x28) # $28 8-bit mode
        self.cmd(0x0C) # $0C 8-bit mode
        self.cmd(0x06) # $06 8-bit mode
        self.cmd(0x01) # $01 8-bit mode

    def cmd(self, bits, char_mode=False):
        """ Send command to LCD """

        sleep(0.001)
        bits=bin(bits)[2:].zfill(8)

        GPIO.output(self.pin_rs, char_mode)

        for pin in self.pins_db:
            GPIO.output(pin, False)

        for i in range(4):
            if bits[i] == "1":
                GPIO.output(self.pins_db[::-1][i], True)

        GPIO.output(self.pin_e, True)
        GPIO.output(self.pin_e, False)

        for pin in self.pins_db:
            GPIO.output(pin, False)

        for i in range(4,8):
            if bits[i] == "1":
                GPIO.output(self.pins_db[::-1][i-4], True)


        GPIO.output(self.pin_e, True)
        GPIO.output(self.pin_e, False)

    def message(self, text):
        """ Send string to LCD. Newline wraps to second line"""

        for char in text:
            if char == '\n':
                self.cmd(0xC0) # next line
            else:
                self.cmd(ord(char),True)

if __name__ == '__main__':

    lcd = HD44780()
    lcd.message("Raspberry Pi\n  Take a byte!")

10 comments

  1. Nice code :)
    one little improvement would be to clean up the GPIO at the end :)

    if __name__ == '__main__':
        lcd = HD44780()
        lcd.message("Raspberry Pi\n Take a byte!")
        GPIO.cleanup()

    This will help prevent these warning's from being produced:

    file.py:14: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
      GPIO.setup(self.pin_e, GPIO.OUT)
    file.py:15: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
      GPIO.setup(self.pin_rs, GPIO.OUT)
    file.py:17: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
      GPIO.setup(pin, GPIO.OUT)

    Other than that great help :)

    Regards
    Tim

    ReplyDelete
  2. Traceback (most recent call last):
    File "raspi.py", line 51, in
    lcd = HD44780()
    File "raspi.py", line 9, in __init__
    GPIO.setmode(GPIO.BCM)
    AttributeError: 'module' object has no attribute 'setmode'
    --------------------------------------------------------------------------
    Can anyone help me please? I don't know where is problem.

    ReplyDelete
  3. What i find difficult is to discover a blog that may capture me for a minute but your blog is different. Bravo.
    Dreambox Cccam | Dreambox Cardsharing

    ReplyDelete
  4. Thanks!
    I took a one-line-16-chars display from a fax machine, and it worked like a charm.
    Funny thing - it acts like a two-lines display, of 8 chars each (to show "1234567890" you need to use message("1234578\n90")).

    ReplyDelete
  5. This script worked for me, but I have 20x4 LCD and this version of script is for 16x2 LCD.. help please...

    ReplyDelete
  6. Thanks ! .. worked. But if i am using 20x4 display like JHD204A.. which has same number of pins... what changes has to be done in the code...

    ReplyDelete
  7. Hi, Thanks for code!

    For 4x20 display, just change message function as following:

    def message(self, text):
    for char in text:
    if char == '\1':
    self.cmd(0xC0) # next line
    elif char == '\2':
    self.cmd(0x94)
    elif char == '\3':
    self.cmd(0xD4)
    else:
    self.cmd(ord(char),True)

    and in main while loop use:

    lcd.message(" "+s[:20]+"\1"+s[20:40]+"\2"+s[40:60]+"\3"+s[60:80])

    No other changes needed.

    ReplyDelete
  8. I've reproduced it on my PaspPi within 10 min, thanks a lot!

    ReplyDelete