How to use LCD 5110 (PCD 8544) with Raspberry Pi

lcd-5110-interface-with-raspberry-pi

In this article, I will briefly explain on how to create a Python class library to interface with LCD 5110 display module and shares the common APIs with the Arduino LCD5110 library that I previously created.

I won't spent too much time on how the LCD 5110 display module works as it has been discussed in great details previously. I would suggest that you read the article How to use LCD5110 (PCD8544) with Arduino first to get familiar with the LCD 5110 display module or PCD8544 LCD controller before reading this article.

LCD 5110 interface with Raspberry Pi

For those Raspberry Pi with 26-pin P1 header, only one SPI interface is available with two chip enable pins. For those with 40-pin P1 header, two SPI interfaces are accessible via the P1 header. We will use SPI0 which is available for all Raspberry Pi models for interfacing with LCD 5110. CE0 (Chip Enable 0) on Raspberry Pi is used to control the SCE(System Chip Enable) pin of the LCD module.

Install RPi-GPIO and spidev

There are a few things we need to setup before we can start our python programming. I'm using a freshly created SD card with Raspbian Stretch Lite installed for this project. Raspbian Stretch comes with Python 3 installed by default, the installation tool is however not installed by default, first thing we need to do is to install pip3 python package installation tool:

sudo apt-get install python3-pip

Once pip3 installed, we can now install python packages that are need for GPIO and SPI communications.

pip3 install RPi-GPIO
pip3 install spidev

Both RPi-GPIO and spidev are actually available within Raspbian package distribution, and therefore can also be installed using sudo apt-get install python3-rpi-gpio and sudo apt-get install python3-spidev without using pip3

RPi-GPIO is a python package that provide a class to control the GPIO pins on Raspberry Pi. Spidev package provide an API interface for using linux spidev kernel driver.

Activate SPI on Raspberry Pi

SPI was disabled by default on Raspberry Pi, we need to activate it either via raspi-config configuration tool or by modifying the /boot/config.txt file directly.

Activate SPI via raspi-config

Run the following command on terminal:

sudo raspi-config

Navigate to 5. Interfacing Options and press Enter on the keyboard, and select P4 SPI to activate the SPI.

Activate SPI by modifying /boot/config.txt

Alternatively, you could activate SPI by editing the /boot/config.txt file directly.

sudo nano /boot/config.txt

and search for spi, and uncomment the following line from:

#dtparam=spi=on

to:

dtparam=spi=on

Save the file. Reboot the raspberry pi no matter which way you chose to make the change. We are now ready to do our programming.

LCD 5110 Library APIs

I created an Arduino library in the previous article with a set of APIs for communicating with LCD 5110.

- clear()
- cursor(row, col)
- backlight(ON|OFF)
- inverse(true|false)
- printStr(str[])
- printImage(image[])

There is also a private function (or method) _write(mode, ch) which is not part of the public APIs.

With this, we can create a python class library skeleton to match the Arduino APIs before we start filling the code for each API. Create a file call lcd5110.py with the skeleton shown below and save it at home directory (i.e. at directory ~ or /home/pi/, or the /home/username if you have change the default user name from pi to a different username.

class LCD5110:

    def __init__(self):
        pass

    def clear(self):
        pass

    def cursor(self, row, col):
        pass

    def _write(self, mode, data):
        pass

    def backlight(self, state):
        pass

    def inverse(self, inv):
        pass

    def printStr(self, str):
        pass

    def printImage(self, image):
        pass

LCD5110.__init__()

The most different part between Arduino sketch and Raspberry Pi code in python are related to how the GPIO and SPI been setup. Python's class construct __init__() is equivalent to Arduino's setup() where you put the declaration of all the properties and parameters and codes related to device initialisation.

There are two ways of numbering the IO pins on a Raspberry Pi within RPi.GPIO. GPIO.setmode(GPIO.BCM) refer each GPIO pin based on Broadcom's SOC (System On-chip) assignments; GPIO.setmode(GPIO.BOARD) refer to the pin numbers on the P1 header of the Raspberry Pi board. I personally prefer to use GPIO.BOARD naming convention because it is always works no matter which Raspberry Pi model you are using.

We explicitly set RST, DC , LED and CE0 pins as output lines. We don't have to do the same for SPI pins (MOSI and SCLK) as it will be initialised by spidev library. RPi.GPIO would generate a warming message whenever it detects that a pin has been configured to something other than the default mode (input). We use GPIO.setwarnings(False) to suppress the warning message.

LCD 5110 Class Library

Porting the rest of the codes to Python is quite straigtforward, some with minor changes and most of the changes are related to using pythonic syntax than the Arduino c-alike way of programming. You can compare the Arduino code in my previous article with the python code. And here is the entire LCD5110 Class:

import RPi.GPIO as GPIO
import spidev


class LCD5110:

    LCD_WIDTH = 84
    LCD_HEIGHT = 48

    def __init__(self):
        # Define each P1 header pin
        self.RST = 16
        self.DC = 18
        self.LED = 22
        self.MOSI = 19  # DN on LCD module
        self.SCLK = 23
        self.CE0 = 24   # SCE on LCD module

        self._inverse = False

        GPIO.setmode(GPIO.BOARD)
        GPIO.setwarnings(False)
        GPIO.setup([self.RST, self.DC, self.LED, self.CE0], GPIO.OUT, initial=GPIO.LOW)

        self.spi = spidev.SpiDev()
        self.spi.open(0, 0)  # open SPI0, use CE0 on GPIO as chip enable

        GPIO.output(self.RST, GPIO.HIGH)
        GPIO.output(self.CE0, GPIO.HIGH)
        self._write(0, 0x21)  # Set Extended Command set
        self._write(0, 0xb2)  # Set Vlcd to 6v(LCD Contrast)
        self._write(0, 0x13)  # Set voltage bias system 1: 48(Viewing Angle)
        self._write(0, 0x20)  # Set Normal Command set
        self.clear()         # Clear all display memory and set cursor to 1, 1
        self._write(0, 0x09)  # Set all pixels ON
        self._write(0, 0x0c)  # Set display mode to Normal
        self.backlight(GPIO.LOW)

    def clear(self):
        self.cursor(0, 0)
        max_pixels = int(LCD5110.LCD_WIDTH * LCD5110.LCD_HEIGHT / 8)
        for i in range(max_pixels):
            self._write(1, 0x00)

    def cursor(self, row, col):
        if row not in range(1, int(LCD5110.LCD_HEIGHT / 8) + 1):
            return None
        if col not in range(1, int(LCD5110.LCD_WIDTH / 6) + 1):
            return None
        self._write(0, 0x40 | (row - 1))
        self._write(0, 0x80 | (col - 1) * 6)

    def _write(self, mode, data):
        GPIO.output(self.CE0, GPIO.LOW)
        GPIO.output(self.DC, mode) # Data: mode = 0, Command: mode = 1
        if mode and self._inverse:
            data = ~ data
        self.spi.xfer([data])
        GPIO.output(self.CE0, GPIO.HIGH)

    def backlight(self, state):
        GPIO.output(self.LED, state)

    def inverse(self, inv):
        self._inverse = inv

    def printStr(self, str):
        FONT_TABLE = [
            [0x00, 0x00, 0x00, 0x00, 0x00],   # 0x20, space
            [0x00, 0x00, 0x5f, 0x00, 0x00],   # 0x21, !
            [0x00, 0x07, 0x00, 0x07, 0x00],   # 0x22, "
            [0x14, 0x7f, 0x14, 0x7f, 0x14],   # 0x23,   #
            [0x24, 0x2a, 0x7f, 0x2a, 0x12],   # 0x24, $
            [0x23, 0x12, 0x08, 0x64, 0x62],   # 0x25, %
            [0x36, 0x49, 0x55, 0x22, 0x50],   # 0x26, &
            [0x00, 0x05, 0x03, 0x00, 0x00],   # 0x27, '
            [0x00, 0x1c, 0x22, 0x41, 0x00],   # 0x28, (
            [0x00, 0x41, 0x22, 0x1c, 0x00],   # 0x29, )
            [0x14, 0x08, 0x3E, 0x08, 0x14],   # 0x2a, *
            [0x08, 0x08, 0x3E, 0x08, 0x08],   # 0x2b, +
            [0x00, 0x50, 0x30, 0x00, 0x00],   # 0x2c,,
            [0x08, 0x08, 0x08, 0x08, 0x08],   # 0x2d, -
            [0x00, 0x60, 0x60, 0x00, 0x00],   # 0x2e,.
            [0x20, 0x10, 0x08, 0x04, 0x02],   # 0x2f, /
            [0x3E, 0x51, 0x49, 0x45, 0x3E],   # 0x30, 0
            [0x00, 0x42, 0x7F, 0x40, 0x00],   # 0x31, 1
            [0x42, 0x61, 0x51, 0x49, 0x46],   # 0x32, 2
            [0x21, 0x41, 0x45, 0x4B, 0x31],   # 0x33, 3
            [0x18, 0x14, 0x12, 0x7F, 0x10],   # 0x34, 4
            [0x27, 0x45, 0x45, 0x45, 0x39],   # 0x35, 5
            [0x3C, 0x4A, 0x49, 0x49, 0x30],   # 0x36, 6
            [0x01, 0x71, 0x09, 0x05, 0x03],   # 0x37, 7
            [0x36, 0x49, 0x49, 0x49, 0x36],   # 0x38, 8
            [0x06, 0x49, 0x49, 0x29, 0x1E],   # 0x39, 9
            [0x00, 0x36, 0x36, 0x00, 0x00],   # 0x3a,:
            [0x00, 0x56, 0x36, 0x00, 0x00],   # 0x3b,;
            [0x08, 0x14, 0x22, 0x41, 0x00],   # 0x3c, <
            [0x14, 0x14, 0x14, 0x14, 0x14],   # 0x3d, =
            [0x00, 0x41, 0x22, 0x14, 0x08],   # 0x3e, >
            [0x02, 0x01, 0x51, 0x09, 0x06],   # 0x3f, ?
            [0x32, 0x49, 0x59, 0x51, 0x3E],   # 0x40, @
            [0x7E, 0x11, 0x11, 0x11, 0x7E],   # 0x41, A
            [0x7F, 0x49, 0x49, 0x49, 0x36],   # 0x42, B
            [0x3E, 0x41, 0x41, 0x41, 0x22],   # 0x43, C
            [0x7F, 0x41, 0x41, 0x22, 0x1C],   # 0x44, D
            [0x7F, 0x49, 0x49, 0x49, 0x41],   # 0x45, E
            [0x7F, 0x09, 0x09, 0x09, 0x01],   # 0x46, F
            [0x3E, 0x41, 0x49, 0x49, 0x7A],   # 0x47, G
            [0x7F, 0x08, 0x08, 0x08, 0x7F],   # 0x48, H
            [0x00, 0x41, 0x7F, 0x41, 0x00],   # 0x49, I
            [0x20, 0x40, 0x41, 0x3F, 0x01],   # 0x4a, J
            [0x7F, 0x08, 0x14, 0x22, 0x41],   # 0x4b, K
            [0x7F, 0x40, 0x40, 0x40, 0x40],   # 0x4c, L
            [0x7F, 0x02, 0x0C, 0x02, 0x7F],   # 0x4d, M
            [0x7F, 0x04, 0x08, 0x10, 0x7F],   # 0x4e, N
            [0x3E, 0x41, 0x41, 0x41, 0x3E],   # 0x4f, O
            [0x7F, 0x09, 0x09, 0x09, 0x06],   # 0x50, P
            [0x3E, 0x41, 0x51, 0x21, 0x5E],   # 0x51, Q
            [0x7F, 0x09, 0x19, 0x29, 0x46],   # 0x52, R
            [0x46, 0x49, 0x49, 0x49, 0x31],   # 0x53, S
            [0x01, 0x01, 0x7F, 0x01, 0x01],   # 0x54, T
            [0x3F, 0x40, 0x40, 0x40, 0x3F],   # 0x55, U
            [0x1F, 0x20, 0x40, 0x20, 0x1F],   # 0x56, V
            [0x3F, 0x40, 0x38, 0x40, 0x3F],   # 0x57, W
            [0x63, 0x14, 0x08, 0x14, 0x63],   # 0x58, X
            [0x07, 0x08, 0x70, 0x08, 0x07],   # 0x59, Y
            [0x61, 0x51, 0x49, 0x45, 0x43],   # 0x5a, Z
            [0x00, 0x7F, 0x41, 0x41, 0x00],   # 0x5b, [
            [0x55, 0x2A, 0x55, 0x2A, 0x55],   # 0x5c, back slash
            [0x00, 0x41, 0x41, 0x7F, 0x00],   # 0x5d,]
            [0x04, 0x02, 0x01, 0x02, 0x04],   # 0x5e, ^
            [0x40, 0x40, 0x40, 0x40, 0x40],   # 0x5f, _
            [0x00, 0x01, 0x02, 0x04, 0x00],   # 0x60, `
            [0x20, 0x54, 0x54, 0x54, 0x78],   # 0x61, a
            [0x7F, 0x48, 0x44, 0x44, 0x38],   # 0x62, b
            [0x38, 0x44, 0x44, 0x44, 0x20],   # 0x63, c
            [0x38, 0x44, 0x44, 0x48, 0x7F],   # 0x64, d
            [0x38, 0x54, 0x54, 0x54, 0x18],   # 0x65, e
            [0x08, 0x7E, 0x09, 0x01, 0x02],   # 0x66, f
            [0x0C, 0x52, 0x52, 0x52, 0x3E],   # 0x67, g
            [0x7F, 0x08, 0x04, 0x04, 0x78],   # 0x68, h
            [0x00, 0x44, 0x7D, 0x40, 0x00],   # 0x69, i
            [0x20, 0x40, 0x44, 0x3D, 0x00],   # 0x6a, j
            [0x7F, 0x10, 0x28, 0x44, 0x00],   # 0x6b, k
            [0x00, 0x41, 0x7F, 0x40, 0x00],   # 0x6c, l
            [0x7C, 0x04, 0x18, 0x04, 0x78],   # 0x6d, m
            [0x7C, 0x08, 0x04, 0x04, 0x78],   # 0x6e, n
            [0x38, 0x44, 0x44, 0x44, 0x38],   # 0x6f, o
            [0x7C, 0x14, 0x14, 0x14, 0x08],   # 0x70, p
            [0x08, 0x14, 0x14, 0x18, 0x7C],   # 0x71, q
            [0x7C, 0x08, 0x04, 0x04, 0x08],   # 0x72, r
            [0x48, 0x54, 0x54, 0x54, 0x20],   # 0x73, s
            [0x04, 0x3F, 0x44, 0x40, 0x20],   # 0x74, t
            [0x3C, 0x40, 0x40, 0x20, 0x7C],   # 0x75, u
            [0x1C, 0x20, 0x40, 0x20, 0x1C],   # 0x76, v
            [0x3C, 0x40, 0x30, 0x40, 0x3C],   # 0x77, w
            [0x44, 0x28, 0x10, 0x28, 0x44],   # 0x78, x
            [0x0C, 0x50, 0x50, 0x50, 0x3C],   # 0x79, y
            [0x44, 0x64, 0x54, 0x4C, 0x44],   # 0x7a, z
            [0x00, 0x08, 0x36, 0x41, 0x00],   # 0x7b, {
            [0x00, 0x00, 0x7f, 0x00, 0x00],   # 0x7c, |
            [0x00, 0x41, 0x36, 0x08, 0x00],   # 0x7d,}
            [0x10, 0x08, 0x08, 0x10, 0x08],   # 0x7e, ~
            [0x78, 0x46, 0x41, 0x46, 0x78]    # 0x7f, DEL
        ]
        for ch in str:
            for i in range(5):
                b = FONT_TABLE[ord(ch) - 0x20][i]
                self._write(1, b)
        self._write(1, 0x00)

    def printImage(self, image):
        self.cursor(1, 1)
        pixels = int(LCD5110.LCD_WIDTH * LCD5110.LCD_HEIGHT / 8)
        for i in range(pixels):
            self._write(1, image[i])

How to use the LCD5110 class library

We will create a separate file called example.py to write the example program for testing the LCD5110 class library.

import time
from lcd5110 import LCD5110

eTinkersLogo = [
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf0, 0xf0, 0xf8, 0xfc, 0xfc, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0x10, 0x10, 0xa0, 0x20, 0x40, 0x40, 0x80, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x82, 0x82, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3f, 0x10, 0x11, 0x09, 0x09, 0x04, 0x04, 0x02, 0x7e, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf0, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0,
  0x00, 0x01, 0x07, 0x0f, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x01, 0x00
]

lcd = LCD5110()

while True:

    lcd.cursor(2, 1)
    lcd.printStr(" Hello World!")
    lcd.cursor(4, 2)
    lcd.printStr("e-tinkers.com")
    lcd.cursor(6, 1)
    lcd.inverse(True)
    lcd.printStr("** Nov 2017 **")
    lcd.inverse(False)
    time.sleep(5)
  
    lcd.backlight(True)
    lcd.printImage(eTinkersLogo)
    time.sleep(5)
    lcd.backlight(False)
    lcd.clear()

The program import the library, and create a class instance lcd, the rest of the code is very much similar to the Arduino code, it prints 3 strings at different locations of the screens, with the last string in inverse mode, it will then wait for 5 seconds and turn on the backlight, and print the e-Tinkers logo image, and wait for another 5 seconds before turning the backlight off.

Summary

Now you can write program to use LCD 5110 for both Arduino and Raspberry Pi, in Python (for Raspberry Pi) or in Arduino Sketch. All you need is to include or import the library, but the APIs for both platforms are almost identify.

The complete library can be download from my github repository.

2 comments by readers

  1. I had reliability issues with my aliexpress Nokia 5110 displays.
    I added the following line

    self.spi.max_speed_hz=100000
    

    after the spi port was opened in the __init__(self) function of lcd5110.py

  2. Thank you for this!
    For all the rest – I had to do what Nick wrote. Got the module from ebay. Also, don’t follow the drawing, but rather use the connection table from the git.

Comments are closed.