Most of the cheap and widely available 16×2 character LCD’s are driven by Hitachi’s HD44780 driver, and is an essential part for any project that displays information. The 8 bits needed per character are transferred either on 8 parallel lines or sequentially on 4 lines which will require at least 6 I/O pins to talk to the LCD. Although its not a problem when used with AVR or other micros having abundant I/O pins, these inexpensive integrated HD44780 LCD displays can result in a messy setup over complicating things for simple projects. However, if you use an LCD module with I2C interface, you only need 2 data lines to talk to the LCD. These modules are powered by a PCF8574 chip which converts a parallel signal into I2C and vice-versa. The Raspberry Pi sends data to the PCF8574 via I2C and the PCF8574 converts the I2C signal into a 4 bit parallel signal, which is relayed to the HD44780.
This tutorial is written in mind considering you have a clean install of Raspbian without any additional configurations. I2C and SPI interfaces of your Raspberry Pi aren’t enabled by default, and need some extra configuration before you can use them. You need to run
sudo raspi-config to enable it. Navigate to “Advanced Options” and enable automatic loading of the kernel module.
Connecting an LCD with an I2C backpack is pretty self-explanatory. Connect the SDA pin on the Pi to the SDA pin on the LCD, and the SCL pin on the Pi to the SCL pin on the LCD. The ground and Vcc pins will also need to be connected. Most LCDs can operate with 3.3V, but they’re meant to be run on 5V, so connect it to the 5V pin of the Pi if possible. Also make sure to use a logic level shifter if your LCD is powered by 5V just to be on the safer side 🙂
I2C-Tools and SMBus
By default, I2C-tools and SMBus comes pre-installed with the latest version of Raspbian. However, if its not installed or you are not 100% sure you can install them by executing the below commands on the terminal.
sudo apt-get install i2c-tools
sudo apt-get install python-smbus
Now with your serial LCD connected with Pi, enter
sudo i2cdetect -y 1 at the terminal and if everything is working as expected it should print a table of addresses for each I2C device connected to your Pi. In my case the I2C address of my LCD is 0x27. Make sure to take note of this number as we’ll need it later.
The last and final step of interfacing our I2C 16×2 LCD with Raspberry Pi is to create a python script and display a sample text. Make sure to follow the previous steps before attempting to execute this code. Save the below code as pylcdlib.py and execute it.
# Credit for this code goes to "natbett" of the Raspberry Pi Forum 18/02/13 # https://www.raspberrypi.org/forums/viewtopic.php?t=34261&p=378524 # Before running this code make sure to run sudo i2cdetect -y 1 # and match the LCD address of your device import smbus from time import * # LCD Address ADDRESS = 0x27 # commands LCD_CLEARDISPLAY = 0x01 LCD_RETURNHOME = 0x02 LCD_ENTRYMODESET = 0x04 LCD_DISPLAYCONTROL = 0x08 LCD_CURSORSHIFT = 0x10 LCD_FUNCTIONSET = 0x20 LCD_SETCGRAMADDR = 0x40 LCD_SETDDRAMADDR = 0x80 # flags for display entry mode LCD_ENTRYRIGHT = 0x00 LCD_ENTRYLEFT = 0x02 LCD_ENTRYSHIFTINCREMENT = 0x01 LCD_ENTRYSHIFTDECREMENT = 0x00 # flags for display on/off control LCD_DISPLAYON = 0x04 LCD_DISPLAYOFF = 0x00 LCD_CURSORON = 0x02 LCD_CURSOROFF = 0x00 LCD_BLINKON = 0x01 LCD_BLINKOFF = 0x00 # flags for display/cursor shift LCD_DISPLAYMOVE = 0x08 LCD_CURSORMOVE = 0x00 LCD_MOVERIGHT = 0x04 LCD_MOVELEFT = 0x00 # flags for function set LCD_8BITMODE = 0x10 LCD_4BITMODE = 0x00 LCD_2LINE = 0x08 LCD_1LINE = 0x00 LCD_5x10DOTS = 0x04 LCD_5x8DOTS = 0x00 # flags for backlight control LCD_BACKLIGHT = 0x08 LCD_NOBACKLIGHT = 0x00 En = 0b00000100 # Enable bit Rw = 0b00000010 # Read/Write bit Rs = 0b00000001 # Register select bit class i2c_device: def __init__(self, addr, port=1): self.addr = addr self.bus = smbus.SMBus(port) # Write a single command def write_cmd(self, cmd): self.bus.write_byte(self.addr, cmd) sleep(0.0001) class lcd: #initializes objects and lcd def __init__(self): self.lcd_device = i2c_device(ADDRESS) self.lcd_write(0x03) self.lcd_write(0x03) self.lcd_write(0x03) self.lcd_write(0x02) self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE) self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON) self.lcd_write(LCD_CLEARDISPLAY) self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT) sleep(0.2) # clocks EN to latch command def lcd_strobe(self, data): self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT) sleep(.0005) self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT)) sleep(.0001) def lcd_write_four_bits(self, data): self.lcd_device.write_cmd(data | LCD_BACKLIGHT) self.lcd_strobe(data) # write a command to lcd def lcd_write(self, cmd, mode=0): self.lcd_write_four_bits(mode | (cmd & 0xF0)) self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0)) # put string function def lcd_display_string(self, string, line): if line == 1: self.lcd_write(0x80) if line == 2: self.lcd_write(0xC0) if line == 3: self.lcd_write(0x94) if line == 4: self.lcd_write(0xD4) for char in string: self.lcd_write(ord(char), Rs) # clear lcd and set to home def lcd_clear(self): self.lcd_write(LCD_CLEARDISPLAY) self.lcd_write(LCD_RETURNHOME) display = lcd() display.lcd_display_string("RaspiNews - 16x2", 1) display.lcd_display_string("I2C LCD Demo..", 2)
Note: If the I2C address for your LCD module is different from this sample code make sure to change it.