Thursday 7 January 2016

Raspberry PI - Controlling 16 x 2 LCD Display with Python


Connecting the Raspberry PI to a LCD display can be a tricky. Here is how to get a 2 line LCD display connected. Most LCD displays use the same pins and protocol but it uses a minimum of 6 GPIO pins.


Wiring the LCD

The display I am using is the POWERTIP PC1602LRS-FWA-B-Q this is a two line display with back-light it required a 5 volt power supply but the data can be 3.3 volts. I bought this from Element 14 















The LCD display connections are as below this is the same for most displays of this type.
Pin
Signal
Description
1
Vss
Power Supply - (Ground)
2
Vdd
Power Supply + (5 volts)
3
Vo
LCD Contrast - (Via resistor)
4
RS
Register Selection High or Low
5
R/W
Read/Write mode 1=Write 0=Read
6
E
Enable signal to read or write data
7
DB0
Data Bit 0 Not used in 4 bit mode
8
DB1
Data Bit 1 Not used in 4 bit mode
9
DB2
Data Bit 2 Not used in 4 bit mode
10
DB3
Data Bit 3 Not used in 4 bit mode
11
DB4
Data Bit 4
12
DB5
Data Bit 5
13
DB6
Data Bit 6
14
DB7
Data Bit 7
15
A
LED backlight power supply +
16
K
LED backlight power supply -


The strange thing about this particular display is that the pins are in a strange order on the actual board. The actual pin order is 14 down to 1,16,15 it is indicated on the specification sheet and the back of the PCB but not very well as you can see below (I marked the pin numbers in red). So check the pin outs before wiring.

Now we know the connections we can wire the LCD to the Raspberry PI GPIO pins. I connected the following. Pin 5 is set to GND so the LCD can't send 5 volt signals back to the PI.
Pin
Signal
Description
GPIO
RPI Pin
1
Vss
Power Supply - (Ground)
GND
6
2
Vdd
Power Supply + (5 volts)
5V
2
3
Vo
LCD Contrast - (Via resistor)
1.9K to GND
6
4
RS
Register Selection High or Low
25
22
5
R/W
Read/Write mode 1=Write 0=Read
GND
6
6
E
Enable signal to read or write data
24
18
11
DB4
Data Bit 4
23
16
12
DB5
Data Bit 5
27
13
13
DB6
Data Bit 6
15
10
14
DB7
Data Bit 7
14
8
15
A
LED backlight power supply +
5V
2
16
K
LED backlight power supply -
GND
6

Here are the bread board connections


Python program explained

First  import the required functions. GPIO is obvious and I will also be using the system time functions. We are using the BCM notation to indicate what pins to use. Also using variables for the GPIO pins means its easy if you want to change a pin later the same for the delays.

#!/usr/bin/env python
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
LCD_RS = 25         #LCD Reset
LCD_E  = 24         #LCD Enable
LCD_D4 = 23         #LCD Bit 4
LCD_D5 = 27         #LCD Bit 5
LCD_D6 = 15         #LCD Bit 6
LCD_D7 = 14         #LCD Bit 7
LCD_WIDTH = 16      #LCD Device Width
LCD_LINE_1 = 0x80   #LCD RAM address for the 1st Line
LCD_LINE_2 = 0xC0   #LCD RAM address for the 2nd Line

E_PULSE = 0.00005   #LCD Pulse Time
E_DELAY = 0.00005   #LCD Delay Time
LCD_CHR = True
LCD_CMD = False
LCDLine1 = ""
LCDLine2 = ""

How the the LCD interface works
  1. Send LCD Line address to be written.
  2. Set the RS to Low
  3. Set high address in DB 4 to DB7 (Line 1 is 0xC0 so High bits 1100) 
  4. Toggle Enable
  5. Set low address in DB 4 to DB7 (Line 1 is 0xC0 so Low bits 0000) 
  6. Toggle Enable
  7. Send ASCII characters to be written
  8. Set the RS to High
  9. Set ASCII high bits in DB 4 to DB7 for character ("A" High bits 0100) 
  10. Toggle Enable
  11. Set ASCII low bits in DB 4 to DB7 for character ("A" Low bits 0001)  
  12. Toggle Enable
  13. Go to 7 and send the next ASCII character
Function to write the Bytes to the LCD using High and Low bits and toggling the enable pin.

def lcd_byte(bits, mode):
    # Send byte to data pins
    # bits = data
    # mode = True  for character
    #      = False for command


    GPIO.output(LCD_RS, mode)

    # High bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)
    if bits&0x10 == 0x10:
        GPIO.output(LCD_D4, True)
    if bits&0x20 == 0x20:
        GPIO.output(LCD_D5, True)
    if bits&0x40 == 0x40:
        GPIO.output(LCD_D6, True)
    if bits&0x80 == 0x80:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    time.sleep(E_DELAY)
    GPIO.output(LCD_E, True)
    time.sleep(E_PULSE)
    GPIO.output(LCD_E, False)
    time.sleep(E_DELAY)

    # Low bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)
    if bits&0x01 == 0x01:
        GPIO.output(LCD_D4, True)
    if bits&0x02 == 0x02:
        GPIO.output(LCD_D5, True)
    if bits&0x04 == 0x04:
        GPIO.output(LCD_D6, True)
    if bits&0x08 == 0x08:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    time.sleep(E_DELAY)
    GPIO.output(LCD_E, True)
    time.sleep(E_PULSE)
    GPIO.output(LCD_E, False)
    time.sleep(E_DELAY)

Function to send ASCII character strings to the lcd_byte function

def lcd_string(message):
    # Send string to display
    message = message.ljust(LCD_WIDTH," ")
    for i in range(LCD_WIDTH):
        lcd_byte(ord(message[i]),LCD_CHR)

A function to initialise the LCD this will be used to clear the display on start-up.

def lcd_init():
    # Initialise display
    lcd_byte(0x33,LCD_CMD)
    lcd_byte(0x32,LCD_CMD)
    lcd_byte(0x28,LCD_CMD)
    lcd_byte(0x0C,LCD_CMD)
    lcd_byte(0x06,LCD_CMD)
    lcd_byte(0x01,LCD_CMD)

The main program block that will call these functions works a follows
  1. Set the GPIO pins to output mode.
  2. Initialise LCD
  3. Write a starting message
  4. Enter a while loop
  5. Wait 1 second
  6. Get the current date and time and store them
  7. Write the time to line 1 and date to line 2
  8. Return to loop start
  9. If keyboard interrupt exit loop (CTRL+C on keyboard)
  10. Write last message
  11. Clean up GPIO pins and exit.
Here is the main function code


if __name__=='__main__':
         try:
                # LCD GPIO Setup
                 GPIO.setup(LCD_E, GPIO.OUT)
                 GPIO.setup(LCD_RS, GPIO.OUT)
                 GPIO.setup(LCD_D4, GPIO.OUT)
                 GPIO.setup(LCD_D5, GPIO.OUT)
                 GPIO.setup(LCD_D6, GPIO.OUT)
                 GPIO.setup(LCD_D7, GPIO.OUT)

                # Initialise display
                 lcd_init()
                 LCDLine1 = "Hello World!"
                 lcd_byte(LCD_LINE_1, LCD_CMD)
                 lcd_string(LCDLine1)
                             
                 while True: 
                     #Wait 1 seconds
                     time.sleep(1)
                     PiTime = time.strftime("Time:  %H:%M:%S", time.localtime())
                     PiDate = time.strftime("Date:%d %b %Y", time.localtime())
                     LCDLine1 = PiDate
                     LCDLine2 = PiTime
                     lcd_byte(LCD_LINE_1, LCD_CMD)
                     lcd_string(LCDLine1)
                     lcd_byte(LCD_LINE_2, LCD_CMD)
                     lcd_string(LCDLine2)
         except KeyboardInterrupt:
             pass
         # Shutdown
         lcd_byte(LCD_LINE_1, LCD_CMD)
         lcd_string(" Goodbye :)")
         lcd_byte(LCD_LINE_2, LCD_CMD)
         lcd_string(" ")
         GPIO.cleanup()


If you are using a 4 line LCD display usually 20 characters long change the following

LCD_WIDTH = 20      #LCD Device Width
LCD_LINE_1 = 0x80   #LCD RAM address for the 1st Line
LCD_LINE_2 = 0xC0   #LCD RAM address for the 2nd Line
LCD_LINE_3 = 0x94   #LCD RAM address for the 3rd Line
LCD_LINE_4 = 0xD4   #LCD RAM address for the 4th Line
E_PULSE = 0.00005   #LCD Pulse Time
E_DELAY = 0.00005   #LCD Delay Time
LCD_CHR = True
LCD_CMD = False
LCDLine1 = ""
LCDLine2 = ""
LCDLine3 = ""
LCDLine4 = ""

Video of the display working

Here is a video of the display working



Here is the complete program in one block.

#!/usr/bin/env python
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
LCD_RS = 25         #LCD Reset
LCD_E  = 24         #LCD Enable
LCD_D4 = 23         #LCD Bit 4
LCD_D5 = 27         #LCD Bit 5

LCD_D6 = 15         #LCD Bit 6
LCD_D7 = 14         #LCD Bit 7
LCD_WIDTH = 16      #LCD Device Width
LCD_LINE_1 = 0x80   #LCD RAM address for the 1st Line
LCD_LINE_2 = 0xC0   #LCD RAM address for the 2nd Line
E_PULSE = 0.00005   #LCD Pulse Time
E_DELAY = 0.00005   #LCD Delay Time
LCD_CHR = True
LCD_CMD = False
LCDLine1 = ""
LCDLine2 = ""


def lcd_init():
    # Initialise display
    lcd_byte(0x33,LCD_CMD)
    lcd_byte(0x32,LCD_CMD)
    lcd_byte(0x28,LCD_CMD)
    lcd_byte(0x0C,LCD_CMD)
    lcd_byte(0x06,LCD_CMD)
    lcd_byte(0x01,LCD_CMD)

def lcd_string(message):
    # Send string to display
    message = message.ljust(LCD_WIDTH," ")
    for i in range(LCD_WIDTH):
        lcd_byte(ord(message[i]),LCD_CHR)

def lcd_byte(bits, mode):
    # Send byte to data pins
    # bits = data
    # mode = True  for character
    #      = False for command


    GPIO.output(LCD_RS, mode)

    # High bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)
    if bits&0x10 == 0x10:
        GPIO.output(LCD_D4, True)
    if bits&0x20 == 0x20:
        GPIO.output(LCD_D5, True)
    if bits&0x40 == 0x40:
        GPIO.output(LCD_D6, True)
    if bits&0x80 == 0x80:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    time.sleep(E_DELAY)
    GPIO.output(LCD_E, True)
    time.sleep(E_PULSE)
    GPIO.output(LCD_E, False)
    time.sleep(E_DELAY)

    # Low bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)
    if bits&0x01 == 0x01:
        GPIO.output(LCD_D4, True)
    if bits&0x02 == 0x02:
        GPIO.output(LCD_D5, True)
    if bits&0x04 == 0x04:
        GPIO.output(LCD_D6, True)
    if bits&0x08 == 0x08:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    time.sleep(E_DELAY)
    GPIO.output(LCD_E, True)
    time.sleep(E_PULSE)
    GPIO.output(LCD_E, False)
    time.sleep(E_DELAY)

if __name__=='__main__':
         try:
                # LCD GPIO Setup
                 GPIO.setup(LCD_E, GPIO.OUT)
                 GPIO.setup(LCD_RS, GPIO.OUT)
                 GPIO.setup(LCD_D4, GPIO.OUT)
                 GPIO.setup(LCD_D5, GPIO.OUT)
                 GPIO.setup(LCD_D6, GPIO.OUT)
                 GPIO.setup(LCD_D7, GPIO.OUT)

                # Initialise display
                 lcd_init()
                 LCDLine1 = "Hello World!"
                 lcd_byte(LCD_LINE_1, LCD_CMD)
                 lcd_string(LCDLine1)
                              
                 while True:  
                     #Wait 1 seconds
                     time.sleep(1)
                     PiTime = time.strftime("Time:  %H:%M:%S", time.localtime())
                     PiDate = time.strftime("Date:%d %b %Y", time.localtime())
                     LCDLine1 = PiDate
                     LCDLine2 = PiTime
                     lcd_byte(LCD_LINE_1, LCD_CMD)
                     lcd_string(LCDLine1)
                     lcd_byte(LCD_LINE_2, LCD_CMD)
                     lcd_string(LCDLine2)
         except KeyboardInterrupt: 
             pass
         # Shutdown         lcd_byte(LCD_LINE_1, LCD_CMD)
         lcd_string(" Goodbye :)")
         lcd_byte(LCD_LINE_2, LCD_CMD)
         lcd_string(" ")
         GPIO.cleanup()

8 comments:

  1. Replies
    1. A little more information would be helpful.
      But here is my best bet.
      Create a file LCD.py copy the contents from one of the examples, place the file in the home folder and run program using the command line (LX Termina) "sudo python LCD.py"
      For more basic information check these two posts thar show how to get a basic GPIO program running
      http://robsraspberrypi.blogspot.com.au/2016/01/raspberry-pi-basics-part-2-power-on-and.html
      http://robsraspberrypi.blogspot.com.au/2016/01/raspberry-pi-basics-controlling-io.html

      If this does not help please give me the error and more detail

      Delete
  2. This is my 20st tutorial how to run lcd display on raspberry pi, but every time just backlight is turn on and first line is on. when run some python program it do nothing (just backlight and first line is on)
    someone have idea how to fix it??
    btw. I have 1602 LCD module and raspberry pi 2 model B

    ReplyDelete
    Replies
    1. I had a quick look at the 1602 display specification sheet and it seems to be the similar to the one I used but the connections are in order 1 to 16. Pin 3 is important to be connected or the display will be blank you could try connecting directly to GND without a resistor as a test (It could need a different value).
      If you are copy the code from the example you may need to re-do the tabs to the left as copy and paste tends to change tabs to spaces.
      I did find that the last example had some formatting errors I have corrected that so check that it looks the same.
      I would use the last complete example code as that has all that is required to make the LCD work.
      If it still does not work then you may have a bad LCD.
      I hope this helps!

      Delete
    2. Think you, still not working but at least I know where is problem.

      Delete
  3. Thank you for making a guide for this particular application. I have a frustratingly frequent issue with missed steps in my soldering and coding that should hopefully become far less common of an occurrence after looking over your article. I am planning on designing some robot toys for my young son and this guide was just the help I needed.

    Brian Hopkins @ Micro Tips USA

    ReplyDelete

Thank you fro reading my page.
Let me know if there is anything that I could add or change.
Please let me know if this information is helpful.