20×4 LCD Module Control Using Python

This article is based on my previous article 16×2 LCD Module Control Using Python and 16×2 LCD Module Control With Backlight Switch. 20×4 LCD modules are relatively easy and cheap to obtain. They have the same 16 pin interface as the 16×2 modules but still only require 6 GPIO pins on your Pi (an extra pin is required for the backlight switch).

These modules are compatible with the Hitachi HD44780 LCD controller. There are plenty available on eBay with a variety of backlight colours to choose from.

LCD Module Hardware

The pinout of the module is :

  1. Ground
  2. VCC (Usually +5V)
  3. Contrast adjustment (VO)
  4. Register Select (RS).
    RS=0: Command, RS=1: Data
  5. Read/Write (R/W).
    R/W=0: Write, R/W=1: Read
  6. Enable
  7. Bit 0 (Not required in 4-bit operation)
  8. Bit 1 (Not required in 4-bit operation)
  9. Bit 2 (Not required in 4-bit operation)
  10. Bit 3 (Not required in 4-bit operation)
  11. Bit 4
  12. Bit 5
  13. Bit 6
  14. Bit 7
  15. LED Backlight Anode (+)
  16. LED Backlight Cathode (-)

Usually the device requires 8 data lines to provide data to Bits 0-7. However this LCD can be configured to use a “4 bit” mode which allows you to send data in two chunks (or nibbles) of 4 bits. This reduces the number of GPIO connections you need when interfacing with your Pi.

Here is how I wired up my LCD :

LCD Pin Function Pi Function Pi Pin
01 GND GND P1-06
02 +5V +5V P1-02
03 Contrast
04 RS GPIO7 P1-26
05 RW GND P1-06
06 E GPIO8 P1-24
07 Data 0
08 Data 1
09 Data 2
10 Data 3
11 Data 4 GPIO25 P1-22
12 Data 5 GPIO24 P1-18
13 Data 6 GPIO23 P1-16
14 Data 7 GPIO18 P1-12
15 +5V via 560 ohm
16 GND P1-06

NOTE : The RW pin allows the device to be be put into read or write mode. I tied this pin to ground to prevent the module attempting to send data to the Pi as the Pi can not tolerate 5V inputs on its GPIO header.

Pin 3 : In order to control the contrast you can adjust the voltage presented to Pin 3. I used a 10K ohm trimmer to provide a variable voltage of 0-5V to Pin 3 which allows the contrast to be tweaked.

Pin 15 : This provides power to the backlight LED. In order to allow the backlight to be turned on and off I used a transistor (BC547, BC548 or equivalent) to switch this pin. This required an additional GPIO pin to switch the transistor but allowed my Python script to control the backlight.

Wiring Checks

Here are some sanity checks before you power up your circuit for the first time :

  • Pin 1 (GND), 3 (Contrast), 5 (RW) and 16 (LED -) ( should be tied to ground.
  • Pin 2 should be tied to 5V. Pin 15 should have a resistor inline to 5V to protect the backlight.
  • Pin 7-10 are unconnected
  • Pin 11-14 are connected to GPIO pins on the Pi

Python

The script below is based heavily on the code presented in my 16×2 LCD Module Control With Backlight Switch article. This allows the backlight to be switched on and off as well as some basic text justification.

As usual I am using the excellent RPi.GPIO library to provide access to the GPIO within Python.

The features of this setup include :

  • 10k variable resistor to adjust the contrast
  • 5k variable resistor to adjust the backlight brightness
  • A transistor to allow the backlight to be switched on and off
  • Left, centred and right justified text

The breadboard circuit looks like this :

Contrast Adjustment

Pin 3 is routed to Ground via a 10kohm trimming pot so that the display contrast can be adjusted.

Backlight Brightness and Switching

Pin 15/16 are in series with a 560 ohm and 2K ohm trimming pot via an NPN transitor which is activated by an additional GPIO connection. The LCD backlight is treated in exactly the same way I switch standard LEDs in my previous Control LED Using GPIO Output Pin article. Using a fixed resistance ensures the resistance can never be adjusted below 560 ohm which protects the backlight if you set the trimming pot to zero ohms. The base of the transistor is wired to an additional GPIO pin via a 27K ohm resistor.

Text Justification

The function “lcd_string” accepts a second parameter (1, 2 or 3) which determines how the text is displayed on the screen.

Python

Here is the code :

#!/usr/bin/python
#
# HD44780 20x4 LCD Test Script for
# Raspberry Pi
#
# Author : Matt Hawkins
# Site   : http://www.raspberrypi-spy.co.uk/
# 
# Date   : 09/08/2012
#

# The wiring for the LCD is as follows:
# 1 : GND
# 2 : 5V
# 3 : Contrast (0-5V)*
# 4 : RS (Register Select)
# 5 : R/W (Read Write)       - GROUND THIS PIN
# 6 : Enable or Strobe
# 7 : Data Bit 0             - NOT USED
# 8 : Data Bit 1             - NOT USED
# 9 : Data Bit 2             - NOT USED
# 10: Data Bit 3             - NOT USED
# 11: Data Bit 4
# 12: Data Bit 5
# 13: Data Bit 6
# 14: Data Bit 7
# 15: LCD Backlight +5V**
# 16: LCD Backlight GND

#import
import RPi.GPIO as GPIO
import time

# Define GPIO to LCD mapping
LCD_RS = 7
LCD_E  = 8
LCD_D4 = 25 
LCD_D5 = 24
LCD_D6 = 23
LCD_D7 = 18
LED_ON = 15

# Define some device constants
LCD_WIDTH = 20    # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

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 

# Timing constants
E_PULSE = 0.00005
E_DELAY = 0.00005

def main():
  # Main program block

  GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
  GPIO.setup(LCD_E, GPIO.OUT)  # E
  GPIO.setup(LCD_RS, GPIO.OUT) # RS
  GPIO.setup(LCD_D4, GPIO.OUT) # DB4
  GPIO.setup(LCD_D5, GPIO.OUT) # DB5
  GPIO.setup(LCD_D6, GPIO.OUT) # DB6
  GPIO.setup(LCD_D7, GPIO.OUT) # DB7
  GPIO.setup(LED_ON, GPIO.OUT) # Backlight enable

  # Initialise display
  lcd_init()

  # Toggle backlight off-on
  GPIO.output(LED_ON, False)
  time.sleep(1)
  GPIO.output(LED_ON, True)
  time.sleep(1)

  # Send some centred test
  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_string("--------------------",2) 
  lcd_byte(LCD_LINE_2, LCD_CMD)
  lcd_string("Rasbperry Pi",2)
  lcd_byte(LCD_LINE_3, LCD_CMD)
  lcd_string("Model B",2)
  lcd_byte(LCD_LINE_4, LCD_CMD)
  lcd_string("--------------------",2)    

  time.sleep(3) # 3 second delay 

  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_string("Raspberrypi-spy",3)
  lcd_byte(LCD_LINE_2, LCD_CMD)
  lcd_string(".co.uk",3)  
  lcd_byte(LCD_LINE_3, LCD_CMD)
  lcd_string("",2) 
  lcd_byte(LCD_LINE_4, LCD_CMD)
  lcd_string("20x4 LCD Module Test",2)   

  time.sleep(20) # 20 second delay 

  # Blank display
  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_string("",3)
  lcd_byte(LCD_LINE_2, LCD_CMD)
  lcd_string("",3)  
  lcd_byte(LCD_LINE_3, LCD_CMD)
  lcd_string("",2) 
  lcd_byte(LCD_LINE_4, LCD_CMD)
  lcd_string("",2)    

  time.sleep(3) # 3 second delay  

  # Turn off backlight
  GPIO.output(LED_ON, False)

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,style):
  # Send string to display
  # style=1 Left justified
  # style=2 Centred
  # style=3 Right justified

  if style==1:
    message = message.ljust(LCD_WIDTH," ")  
  elif style==2:
    message = message.center(LCD_WIDTH," ")
  elif style==3:
    message = message.rjust(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) # RS

  # 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__':
  main()

Remember to update the constants at the top of the script to match the GPIO signals you are using on your Pi. As always these GPIO references are the Broadcom signal names as described in my GPIO header article.

This entry was posted in Hardware, Python, Tutorials & Help and tagged , , , . Bookmark the permalink.

7 Responses to 20×4 LCD Module Control Using Python

  1. Karel says:

    This is excellent work, and I think I will get one of these as a external link for my RasPi!

    I do have one question though, what is the model/part number for the 20×4 LCD module, I am having some trouble finding the one pictured and am a little reluctant to get one that isn’t exactly the same ‘just in case’ the pinouts etc are different.

    It looks like HD44780 isn’t what I need to be searching on :)

  2. an ignorant, but eager, noob says:

    Hey there — so I have the breakout board, some wires and the LCD screen. What I don’t have are the various resistors. I am totally eager to get going, but clearly, I have no idea what I am doing. I don’t want to fry the Pi or the LCD screen. But there are folks who are wiring directly from the GPIO to the LCD screens out there, so how neccesary are the resistors?

    I have to find a site to go over the basics of how the breakout board even works. I can’t say I understand anything, but am definitely up to learn. :)

    Thanks for the site, I’ll come back here when I have a little more knowledge of what I am doing.

    • Matt says:

      The resistors are there to limit the current to the backlight input and to divide the voltage to the contrast input.

      The contrast resistors are not required they just allow you to tweak the contrast. The backlight resistor is required otherwise you are in danger of damaging the backlight. Some LCD modules have built in backlight resistors but it isn’t always clear if these are there … so I play it safe.

      I got all my resistors from eBay. It is worth spending a few pounds/dollars getting a collection of the more common values. In my circuits I tend to use 560, 1K, 2K, 5K, 10K and 27K values so I just bought 100 of each.

    • Depending on where you’re based Maplins (in the UK) do a reasonable bundle (of mixed values). Get the usual flesh coloured one’s not the blue ones! They all look similar but once you get used to the colours you’ll be fine.

  3. Alessandro says:

    Great job, very nice project, I’m beginner and I’ll try the project with good result! But I’ve a little question, someone can help me to create a special character for degrees symbols (°) in python? I found a lot of guide but nothing in python!

    • sma23 says:

      Hi,
      searching some documentation on the HD44780 you can find the ° character under the value 223 (0xDF). You can then for example build a string you can send like this:
      line = str(temperature) + chr(223) + ‘C’

      That would make a string with the value of the variable “temperature”,the ° symbol and then a C for Celsius (… 34°C).

      Hope that helps
      Sma

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>