The Philips PCD8544 driver chip is connected to a recycled screen from the very popular Nokia 5110 phone. Because the screens are second hand there are often small scratches/blemishes on the glass cover. They are rehoused in a metal box on a PCB with 8 connections. You need to solder on the supplied pins. The screen is a Liquid Crystal Display, LCD.

You can obtain a copy of the documentation for the driver chip from here and here.
AZ Delivery supply a free, 9-page pdf guide to using the device.
If we look more closely at the board, we find that the 8 connection pins are labeled on the bottom surface and there appear to be 4 resistors, near each of the LEDs, which illuminate the screen from the sides.
The two sets of pads are interconnected so you can use either set. (Other suppliers of similar boards do not include resistors, which need to be added to the input line by the user. With these boards, you can control the brightness with a PWM pin supplying power to the LEDs. The connections on such boards may be in a different order – beware!)
There appear to be jumper pads (JP), linking something to GND, but there is no information in the documentation.
Important! This is a 3.3V device and will be damaged if connected directly to 5V.

Pin: Description | Additional information |
1 RST (Reset ) | Input |
2 CE (Chip Enable) | Input |
3 DC (Data/Control) | Input |
4 DIN (Data in) | Input |
5 CLK (Clock) | Input |
6 VCC | Power supply – max. 3.3V! |
7 LIGHT | LED active when connected to GND |
8 GND (Ground) | Ground |
The AZ Delivery documentation gives a Fritzing circuit showing the connections to an Arduino UNO using voltage shifters to protect the display.
This involves a great deal of wiring and extra expense for the voltage shifters.
A more compact and cheaper arrangement is to connect the data lines with 10K Ohm protection resistors as shown below. Make sure that VCC goes to the 3.3V outlet on the Arduino UNO.

The simplest solution would be to use a 3.3V Arduino microcontroller with direct connection.
Display Pin | Comment | UNO Pin |
1 RST (Reset ) | Input | 3 |
2 CE (Chip Enable) | Input | 4 |
3 DC (Data/Control) | Input | 5 |
4 DIN (Data in) | Input | 6 |
5 CLK (Clock) | Input | 7 |
6 VCC | Max. 3.3V! | 3.3V |
7 LIGHT | To GND on breadboard | GND |
8 GND (Ground) | Ground | GND |
With the black link between pins 7 & 8 the LEDs are always on.
The documentation gives an example sketch to write pixels directly to the display using commands to the PCD8544. This is a pretty advanced method of driving the display and very a tedious method of creating images. You have to count pixels on graph paper and calculate the values of bytes.
// Control Nokia 5110 LCD Display // AZDelivery Example script #define CLK 7 // More convenient pins #define DIN 6 #define DC 5 #define CE 4 #define RST 3 static const byte LOGOTBL[] = { //AZ Delivery Bitmap Logo 0x0F,0x5,0x5,0x0F,0x90,0xD0,0xB0,0x90,0x00,0x00,0x08,0x08,0x08,0x08,0x00, 0x00,0xFF,0x81,0x81,0x81,0x81,0x7E,0x00, 0xFF,0x89,0x89,0x81,0x81,0x81,0x00,0xFF,0x80,0x80,0x80,0x80,0x80,0x00, 0xFF,0x00,0x07,0x38,0xC0,0xC0,0x38,0x07, 0x00,0xFF,0x89,0x89,0x81,0x81,0x81,0x00,0xFF,0x09,0x19,0x29,0x49,0x86, 0x00,0xF,0x90,0x90,0x90,0x90,0xF7 }; // Functions void LcdWriteCmd(byte order) { // Send command to display digitalWrite(DC, LOW); // DC Pin is LOW for commands to the display shiftOut(DIN, CLK, MSBFIRST, order); // Transmit data byte serially // with MSB (most significant byte) first } void LcdWriteData(byte data) { // Send data to display digitalWrite(DC, HIGH); // DC Pin is HIGH for data to the display shiftOut(DIN, CLK, MSBFIRST, data); // Transmit data byte serial // with MSB (most significant byte) first } void LcdWriteLogo() { digitalWrite(CE, LOW); // Chip Enable Pin is LOW, // data transfer is activated LcdWriteCmd(0x80); // Jump to row 0 column 0, Jump Column LcdWriteCmd(0x40); // Skip row // Clear screen completely for(int i=0; i < 504; i++) { LcdWriteData(0x00); // Delete complete display } // Output AZ logo for(int i=0; i < 66; i++) { LcdWriteData(LOGOTBL[i]); // Write logo data into display } digitalWrite(CE, HIGH); // Chip Enable Pin is HIGH, // data transfer is deactivated } void setup() { pinMode(CLK, OUTPUT); // Switch pin to output pinMode(DIN, OUTPUT); // Switch pin to output pinMode(DC, OUTPUT); // Switch pin to output pinMode(CE, OUTPUT); // Switch pin to output pinMode(RST, OUTPUT); // Switch pin to output digitalWrite(RST, HIGH); // Reset signal to Nokia display delay(100); // forward digitalWrite(RST, LOW); delay(100); digitalWrite(RST, HIGH); delay(100); // Initialization of the display according to data sheet digitalWrite(CE, LOW); // Chip Enable Pin is LOW, // data transfer is activated LcdWriteCmd(0x21); // Extended command set LCD LcdWriteCmd(0x90); // set LCD Vop LcdWriteCmd(0x20); // Normal command LCD LcdWriteCmd(0x0C); // LCD Normal Mode digitalWrite(CE, HIGH); // Chip Enable Pin is HIGH, // data transfer is deactivated } void loop() { LcdWriteLogo(); delay(20000); }
On executing the script, I was pretty disappointed at this point as nothing appeared on the screen.
I looked in the datasheet and found that it was possible to change Vop (operational voltage?) and Bias.
I replaced
LcdWriteCmd(0x90); // set LCD Vop
With these 3 lines and ran the script again.
LcdWriteCmd(0xBF); // set LCD Vop LcdWriteCmd(0x04); // Set Temp coefficient LcdWriteCmd(0x13); // Bias mode 1:40
The expected display appeared.

The method works but we need to find an easier method of obtaining the bytes for a screen image. I decided to use Paint.net, to create the image, and LCDAssistant to convert a bitmap file to a list of bytes.
Starting with Paint.net (This could also be done using MS paint)

I defined a drawing area of 84 pixels wide and 48 pixels high. I disabled Antialiasing, added a star shape and some text to make a test piece. I saved the image as a bitmap with automatic depth.
I then loaded the saved image into LCDAssistant and used these settings.

I saved this to create another file called T45Test, which I then opened in Notepad.

I replaced the data block in the original script, and adjusted this loop to cope with the extra data and name change.
// Output Image for(int i=0; i < 504; i++) { LcdWriteData(T45Test[i]); // Write logo data into display }
This quickly produced the expected display.

What we need to be able to do now is write words, graphic elements and numbers to the screen in real-time under program control. Luckily there is an Adafruit library for this device to provide the necessary help.

This library is used with the Adafruit GFX library installed earlier on my computer for another project. Install any dependencies indicated as well. Try the long example script that comes with the library. It may well lack contrast initially.
The library handles contrast (Vop) and Bias slightly differently so I wrote a short script to allow investigation of the best settings to overcome the poor contrast.
// Set up screen visibility PCD8544 & Nokia 5110 screen // Tony Goodhew 9th July 2020 // Tutorial45.com #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> // PINS CLK, DIN, DC, CE, RST Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); void setup() { Serial.begin(9600); display.begin(); // Initialise display int con = 56; // Change up or down by 1 to get int bias = 4; // the best settings for your display display.setBias(bias); display.setContrast(con); display.clearDisplay(); display.setCursor(0,5); // INCLUDE ABOVE AT START OF YOUR OWN SCRIPTS display.println("Tutorial45.com"); display.setCursor(0,15); display.print("Contrast: "); display.println(con); display.setCursor(0,25); display.print("Bias: "); display.println(display.getBias()); display.fillCircle(68, 36, 11, BLACK); display.fillCircle(68, 36, 6, WHITE); display.display(); // Essential instruction to update visible display } void loop() {}
Settings of con = 56 and bias = 4 gave the best contrast without activating the background (white) pixels on my display. Just try running the script with single unit changes to the values of con and bias until you get good visibility.

Now that the contrast has been fixed, we can see that the display has a slight aspect ratio ‘feature’ and circles appear as ellipses. The lower pixel count means that there is less space to write and draw when compared to other LCD displays – especially when compared with the SSD1306 128×64 display. However, the screen size and pixels are larger aiding visibility from a greater distance.
SSD1306 128×64 = 8192 pixels
SSD1306 128×32 = 4086 pixels
Nokia 86×48 = 4032 pixels
We are now ready to check how quickly the screen updates in a useful application. I added a 10K Ohm potentiometer on A0 to provide an input to the script. (You will need 2 potentiometers for a later script.)

// Volt Meter on Nokia 5110 // Tony Goodhew 9th July 2020 // Turorial45.com // 10K Ohm potentiometer on A0 #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> // PINS CLK, DIN, DC, CE, RST Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); int sensorPin = A0; // select the input pin for the potentiometer int sensorValue = 0; // variable to store the value coming from the sensor void setup() { Serial.begin(9600); display.begin(); // Initialise display int con = 56; // Change up or down by 1 to get int bias = 4; // the best settings for your display display.setBias(bias); display.setContrast(con); display.clearDisplay(); display.setCursor(0,5); display.println("Tutorial45.com"); display.setCursor(0,15); display.print("Volt Meter"); display.setCursor(17,40); display.print("Volts:"); display.display(); // Static parts of screen written at this point } int bar(int v, float vv) { // Bar graph display.drawLine(0,28,0,40, BLACK); display.fillRect(1,30,80,8,WHITE); // Rub out old bar graph display.fillRect(0,30,v,8,BLACK); // Write new bar graph display.fillRect(55,40,26,10,WHITE); // Rub out old text display.setCursor(55,40); display.print(vv); // Write new text display.display(); // Update display } // ++++++++++++Main Loop ++++++++++++++ void loop() { // Read pot and display values sensorValue = analogRead(sensorPin); // Read the value from the sensor float volts = (sensorValue * 5.0 / 1023.0); // Calculate voltage int x = float(sensorValue * 80.0/1023); // Calculate percentage bar(x,volts); // Redraw bar graph delay(100); // Delay to reduce jitter }

Twisting the knob of the potentiometer changes the length of the bar and updates the voltage value. Notice the slight scratch marks on the bottom left corner of the bar graph.
Nokia 5110 Arduino Project with the AZ-Delivery with PCD8544 84×48 Pixel LCD Display
This project illustrates how the display can be changed under the control of a couple of 10K Ohm potentiometers. The potentiometers control the left/right and up/down position of a circular sight in which the player moves to cover the target, indicated by a small cross. As soon as the centers are identical the ‘target’ is destroyed and another target appears at a random position. There are 5 targets in a game. The search time is recorded and displayed at the end of the game.
// Game on Nokia 5110 – Search and Destroy // Tony Goodhew 12th July 2020 // Turorial45.com // 10K Ohm potentiometers on A0 and A1 #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> // PINS CLK, DIN, DC, CE, RST Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); int xPin = A0; // x potentiometer int yPin = A1; // y potentiometer int ox = 0; // Old sight position - ring int oy = 0; int nx = 20; // New sight position int ny = 20; int tx = 15; // Target position int ty = 15; int count = 0; // Targets destroyed float t1 = 0; // Time increment – for a single target unsigned long t0;// millis() value when target appears float total = 0; // Total time spent searching void setup() { Serial.begin(9600); // Ready for debugging display.begin(); // Initialise display int con = 56; // Change up or down by 1 at a time to get int bias = 4; // the best settings for your display display.setBias(bias); display.setContrast(con); display.clearDisplay(); display.setTextSize(2); // Large text for Title screen display.setCursor(0, 0); display.print("Search"); display.setCursor(0, 16); display.print("and"); display.setCursor(0, 32); display.print("Destroy"); display.display(); delay(3000); display.setTextSize(1); // Normal size text for game play display.clearDisplay(); } int target(int x, int y, int c){ // Show target cross display.drawLine(x-2,y,x+2,y,c); display.drawLine(x,y-2,x,y+2,c); } int finish(){ // Final screen to display total time display.clearDisplay(); display.setTextSize(1); display.setCursor(5,5); display.print("You took:"); display.setTextSize(2); display.setCursor(20,18); display.print(total); display.setTextSize(1); display.setCursor(0,40); display.print(count); display.setCursor(8,40); display.print(" Targets hit"); display.display(); while(1){} // Wait for ever - HALT } int ring(int x, int y, int c) { // Draw a ring - sight display.drawCircle(x,y,3,c); // Radius is 3 pixels } int blob(int x, int y, int c){ // Fill a ring - target destroyed display.fillCircle(x,y,3,c); } int flash(int x,int y){ // Flash location of destroyed target t1 = ((millis() - t0)/1000.0); // Incremental search time in seconds total = total + t1; // Update total searching time randomSeed(int(t1*100.0)); // Improve randomness display.clearDisplay(); display.display(); display.setCursor(0,0); display.print(total); count = count + 1; // Update targets destroyed display.setCursor(78,0); // Show count - top right display.print(count); display.display(); for (int i = 0; i<3; i++){ // Flash the blob - A hit! blob(x,y,1); display.display(); delay(300); blob(x,y,0); display.display(); delay(300); } t0 = millis(); // Reset incremental timer if (count == 5){finish();} // Enough targets destroyed? } // ++++++++++++Main Loop ++++++++++++++ void loop() { target(tx,ty,1); // Show target nx = 3 + int(analogRead(xPin) /14); // Get x position of sight ny = 43 - int(analogRead(yPin) /31); // Get y position of sight ring(ox,oy,0); // Blank old sight ring(nx,ny,1); // Show new sight ox = nx; // New position becomes old position oy = ny; display.display(); if ((ox == tx) & (oy == ty)){ // Is it a hit? flash(tx,ty); // Yes – Explode! Update time and targets hit tx = random(76) + 3; // Get new random target position ty = random(33) + 10; } }



Conclusion
This display is a 3.3-volt device and requires additional wiring and protection with either voltage shifters or 10K resistors when used with 5-volt boards like the UNO. After initial disappointments with the documentation and the poor contrast of the display I now quite like this device. It may be rather low in pixel count, supporting just 6 short lines of text, but it has a bigger display diagonal than the SSD1306 128×64, making it easier to read from a distance. It uses 5 data lines rather than the 2 used with I2C devices. It is quick enough for simple games and real-time feedback.
Additional things to try:
- Exchange the simple potentiometer for an analog potentiometer joystick and see if this brings down your overall time with the game.
- Use Paint.net and LCDAssistant to create a series of images to display one after another in a slide show.
- Connect a distance sensor such as an HC-SR04 Ultrasonic or VL53L0X Time of Flight sensor and display the distance as you move your hand back and forth in front of the sensor.
- Add an accelerometer and build a 2-dimensional ‘bubble-level’.
Hint
If your instructions are not appearing on the screen just check that you have ended the sequence with
display.display();
This is the essential instruction to force your changes to the screen and it is so easy to forget it.