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
andspidev
are actually available within Raspbian package distribution, and therefore can also be installed usingsudo apt-get install python3-rpi-gpio
andsudo apt-get install python3-spidev
without usingpip3
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.
I had reliability issues with my aliexpress Nokia 5110 displays.
I added the following line
after the spi port was opened in the __init__(self) function of lcd5110.py
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.