Working with STM32 and display: Nokia 5110 Graphic LCD

In this guide, we shall interface Nokia 5110 graphic LCD with STM32.

In this guide, we will cover the following :

  • Nokia 5110 graphic LCD
  • LCD connection with STM32
  • Code
  • Demo

1.1 Nokia 5110 Graphic LCD:

Remember the days when cell phones were still “dumb,” and they had physical keypads and just a tiny monochrome LCD for a display? Now that iPhones, Galaxies, and the like have revolutionized that market, those little LCDs have to find a new purpose in life: adding customized graphical displays to projects!

Nokia5110 LCD screen is a simple black and white screen but it was the best display for mobile in its time. The usage of the screen was mostly in the Nokia 3310 and 5110. Therefore, the increase of color graphics and smart screens the screen is now the only available in most of the developing projects and small low budget devices. In the LCD, the Liquid crystal method helps to show the output data with a total of 84×48 pixels. The LCD uses the SPI pins to communicate.

1.2 What is Nokia5510 LCD Display?

Nokia 5510 LCD display is a graphics screen LCD display and has been used for a lot of applications. Initially, it was designed only for iconic Nokia 5510 cell phone screen but now we can easily use it for different purposes such as for displaying alphanumeric character, draw lines, shapes and even for displaying bitmap images because of its 84*48 monochrome pixels mean 84 columns and 48 rows. All necessary functions of this display are packed in a single small chip which is operated at very low voltages. It is very low cost more precise, more reliable and easy to use LCD display.

1.3 Nokia5110 Pinout Diagram

The pin configuration of the LCD is almost SPI but in oneway only. The LCD only uses a one-way communication to operate because it doesn’t have to send any data to the microcontroller. However, it has two modes of data and command modes for operating properly.

Nokia5110 LCD Pinout diagram details

1.3 Nokia5510 Pinout Diagram Details

PINSDETAIL
Pin6Power (VCC)The power pin will power up the LCD with external voltage to activate it.
Pin7Back Light (BL)The backlight is the internal color of the LCD. It is just turn on by giving the power input to its pin.
Pin8Ground (GND)The ground is internally common with LED and whole LCD. It helps to make common ground with external power and devices.

1.4 SPI Communication Pins

PINSDETAIL
Pin1ResetThe reset pins are to reset the Nokia5110 display. Here it will help in the programming library to reset it.
Pin2Chip Enable (CE)The enable pin in SPI helps to select the device in case of multiple devices. Here it will active the LCD with the LOW input signal.
Pin3Data/Command (DC)Pin3 helps to switch between the command/data mode. The HIGH signal is for data and LOW signal for command.
Pin4Serial IN (DIN)The “serial in” pin will send the data from the microcontroller/Arduino to the LCD Nokia5110.
Pin5Clock (CLK)The LCD and microcontroller will require a common clock to operate because of their SPI communication. CLK pin will help to make it.

1.5 Nokia5110 Features

  • The LCD is of 84×48 pixels combination, which are useable with any microcontroller.
  • The SPI communication helps to communicate with LCD, which is common in every microcontroller.
  • It is available in multiple backlights.
  • The PCD8544 LCD driver helps to drive it, which is already present on it.
  • Bitmaps images are viewable on the Nokia5110.

1.6 Nokia5110 LCD Applications

  • Most of the pocket games use the LCD, like snake games, etc.
  • Mobile phones have a wide use of LCD.
  • The LCD is still in most of the industrial and commercial applications.

1.7 Nokia5110 LCD Constructions

The Nokia5110 LCD uses a simple liquid crystal method for each pixel to show the data. In the liquid crystal, the three layers help to make a single pixel. The first layer is the glass, second is the polarized sheet and the third one is the crystal molecules. The light passes from one glass towards the other glass but it has to move from two polarized sheets. The polarized sheets are at different angles. So, to pass the light the molecules need to change their positions so the light gets a change in angle according to the second sheet with the help of modules. If light passes from it, then it is viewable by the user but in case of deflection, the LCD will show a dark pixel. However, the liquid crystals always use a light absorption method to show the image.

LCD Driver

The Nokia5110 LCD uses SPI communication to operate. It is due to the driver attached to it. The data from the controllers pass to the driver which store in the buffer and also show on the LCD until it is in the buffer. The new incoming data will able to replace the previous one. The driver only takes the SPI input signal and uses the rest of the pin to operate the LCD. The driver is PCD8544, which is only for 48×84 pixels LCD. Its functionality is viewable by the following block diagram:

block diagram

The driver has its 504bytes GDD RAM. However, Its memory comes with 6 blanks and each blank has 84 segments and each segment can store the 8 bit of data. Therefore, the whole LCD data can store in the GDD RAM of the LCD screen.

Output Pins

The input pins of the driver are the same as the LCD pins which are already in the pin configuration section.  The output pins of the driver are responsible for every function on the LCD screen. However, four types of output pins exist in the driver. The first part is the Row and Column pins. The row pins are 48 and columns are 84 in numbers. Therefore, all these pins connect to each pixel of the LCD and they are responsible for each output by them. The next part is the power part. The VSS, VDD, and VLCD help to power up the LCD and itself. The third part is the test pins that connect with the ground. In the Nokia5110 LCD, they have no use.

The last part is the oscillator pin, to attach an external oscillator. There will be remaining dummy pins that have no use in the LCD screen. In this Nokia5110 LCD, every kind of symbol is drawable. Even images can also show on the screen but first, it requires to convert into the bitmap format.

Working Principle of Nokia 5510 LCD Display

The word LCD stands for liquid crystal display. Its name describes that every LCD display consists of liquid crystal. When an electric current is passed through these crystals then the magnetic field is produced. This magnetic field-aligned the molecules of liquid crystal forcefully, then when they are aligned precisely then they allow the light to pass through these crystals.

The working phenomena of every LCD display are almost the same. But the voltages which are required to produce the magnetic field are different. On the biases of these voltages, the LCD display shows data which is coming from any type of controller. Two types of data have come from the controller’s fist one is word data and second one graphic data. Each data produces a different magnetic field and the LCD display shows this data according to its magnetic field.

2. Connection with STM32:

3. Coding:

We start off by creating new source and header file with name of nokia5110

In the header file

#ifndef __nokia5110_H
#define __nokia5110_H 
#include <stdbool.h>
#include "stm32f4xx.h"                  // Device header
/* PCD8544-specific defines: */
#define LCD_COMMAND  GPIOA->BSRR|=GPIO_BSRR_BR0
#define LCD_DATA     GPIOA->BSRR|=GPIO_BSRR_BS0

#define CS0 GPIOA->BSRR|=GPIO_BSRR_BR1
#define CS1 GPIOA->BSRR|=GPIO_BSRR_BS1

#define RST0 GPIOA->BSRR|=GPIO_BSRR_BR4
#define RST1 GPIOA->BSRR|=GPIO_BSRR_BS4

/* 84x48 LCD Defines: */
#define LCD_WIDTH   84 // Note: x-coordinates go wide
#define LCD_HEIGHT  48 // Note: y-coordinates go high
#define WHITE       0  // For drawing pixels. A 0 draws white.
#define BLACK       1  // A 1 draws black.

static const unsigned char ASCII[][5] = {
  // First 32 characters (0x00-0x19) are ignored. These are
  // non-displayable, control characters.
   {0x00, 0x00, 0x00, 0x00, 0x00} // 0x20
  ,{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, 0x13, 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, 0x79, 0x41, 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 [
  ,{0x02, 0x04, 0x08, 0x10, 0x20} // 0x5c \ (keep this to escape the backslash)
  ,{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
};




void delay(int ms);
void setLine(int x0, int y0, int x1, int y1, bool bw);
void setRect(int x0, int y0, int x1, int y1, bool fill, bool bw);
void setCircle (int x0, int y0, int radius, bool bw, int lineThickness);
void setStr(char * dString, int x, int y, bool bw);
void SetBitMap(const char * bitArray);
void clearDisplay(bool bw);
void gotoXY(int x, int y);
void updateDisplay();
void setContrast(int contrast);
void invertDisplay();
void lcdBegin(void);
#endif 

for the source file

#include "nokia5110.h"
void delay(int ms);

unsigned char displayMap_5110[LCD_WIDTH * LCD_HEIGHT / 8] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,0)->(11,7) ~ These 12 bytes cover an 8x12 block in the left corner of the display
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,0)->(23,7)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, // (24,0)->(35,7)
  0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFE, 0x1E, 0x0E, 0x02, 0x00, // (36,0)->(47,7)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (48,0)->(59,7)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,0)->(71,7)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,0)->(83,7)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,8)->(11,15)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,8)->(23,15)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // (24,8)->(35,15)
  0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, // (36,8)->(47,15)
  0xF8, 0xF0, 0xF8, 0xFE, 0xFE, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, // (48,8)->(59,15)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,8)->(71,15)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,8)->(83,15)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,16)->(11,23)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,16)->(23,23)
  0x00, 0x00, 0xF8, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xF3, 0xE0, 0xE0, 0xC0, // (24,16)->(35,23)
  0xC0, 0xC0, 0xE0, 0xE0, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // (36,16)->(47,23)
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x00, 0x00, 0x00, // (48,16)->(59,23)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,16)->(71,23)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,16)->(83,23)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,24)->(11,31)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,24)->(23,31)
  0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // (24,24)->(35,31)
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // (36,24)->(47,31)
  0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, // (48,24)->(59,31)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,24)->(71,31)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,24)->(83,31)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,32)->(11,39)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,32)->(23,39)
  0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, // (24,32)->(35,39)
  0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, // (36,32)->(47,39)
  0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (48,32)->(59,39)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,32)->(71,39)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,32)->(83,39)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (0,40)->(11,47)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (12,40)->(23,47)
  0x00, 0x00, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, // (24,40)->(35,47)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (36,40)->(47,47)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (48,40)->(59,47)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (60,40)->(71,47)
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (72,40)->(83,47) !!! The bottom right pixel!
};








void spi_init(void){
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; //enable clock forn gpio a
RCC->APB2ENR|=RCC_APB2ENR_SPI1EN; //enable clock for spi1
//GPIOA->MODER=0;
GPIOA->MODER|=0xA900;
GPIOA->AFR[0]|=0x55500000;
SPI1->CR1|=0x304;
SPI1->CR2=0;
SPI1->CR1|=SPI_CR1_SPE;
}

void GPIO_init(void)
{
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER|=GPIO_MODER_MODE0_0|GPIO_MODER_MODE1_0|GPIO_MODER_MODE4_0;
}


void spi_write(unsigned char c){
	while(!(SPI1->SR&SPI_SR_TXE)){;} // wait to transmision buffer to be emplty
	SPI1->DR= c;
	while(!(SPI1->SR&SPI_SR_BSY)){;}	
}

void LCDcommand(unsigned char data)
{
CS0;
LCD_COMMAND;
spi_write(data);
CS1;
	
}

void LCDdata(unsigned char data)
{
CS0;
LCD_DATA;
spi_write(data);
CS1;
}



// This function sets a pixel on displayMap to your preferred
// color. 1=Black, 0= white.
void setPixel1(int x, int y, bool bw)
{
  // First, double check that the coordinate is in range.
  if ((x >= 0) && (x < LCD_WIDTH) && (y >= 0) && (y < LCD_HEIGHT))
  {
    int shift = y % 8;

    if (bw) // If black, set the bit.
      displayMap_5110[x + (y/8)*LCD_WIDTH] |= 1<<shift;
    else   // If white clear the bit.
      displayMap_5110[x + (y/8)*LCD_WIDTH] &= ~(1<<shift);
  }
}

// Because I keep forgetting to put bw variable in when setting...
void setPixel(int x, int y)
{
  setPixel1(x, y, BLACK); // Call setPixel with bw set to Black
}

void clearPixel(int x, int y)
{
  setPixel1(x, y, WHITE); // call setPixel with bw set to white
}

// setLine draws a line from x0,y0 to x1,y1 with the set color.
// This function was grabbed from the SparkFun ColorLCDShield
// library.
void setLine(int x0, int y0, int x1, int y1, bool bw)
{
  int dy = y1 - y0; // Difference between y0 and y1
  int dx = x1 - x0; // Difference between x0 and x1
  int stepx, stepy;

  if (dy < 0)
  {
    dy = -dy;
    stepy = -1;
  }
  else
    stepy = 1;

  if (dx < 0)
  {
    dx = -dx;
    stepx = -1;
  }
  else
    stepx = 1;

  dy <<= 1; // dy is now 2*dy
  dx <<= 1; // dx is now 2*dx
  setPixel1(x0, y0, bw); // Draw the first pixel.

  if (dx > dy)
  {
    int fraction = dy - (dx >> 1);
    while (x0 != x1)
    {
      if (fraction >= 0)
      {
        y0 += stepy;
        fraction -= dx;
      }
      x0 += stepx;
      fraction += dy;
      setPixel1(x0, y0, bw);
    }
  }
  else
  {
    int fraction = dx - (dy >> 1);
    while (y0 != y1)
    {
      if (fraction >= 0)
      {
        x0 += stepx;
        fraction -= dy;
      }
      y0 += stepy;
      fraction += dx;
      setPixel1(x0, y0, bw);
    }
  }
}

// setRect will draw a rectangle from x0,y0 top-left corner to
// a x1,y1 bottom-right corner. Can be filled with the fill
// parameter, and colored with bw.
// This function was grabbed from the SparkFun ColorLCDShield
// library.
void setRect(int x0, int y0, int x1, int y1, bool fill, bool bw)
{
  // check if the rectangle is to be filled
  if (fill == 1)
  {
    int xDiff;

    if(x0 > x1)
      xDiff = x0 - x1; //Find the difference between the x vars
    else
      xDiff = x1 - x0;

    while(xDiff > 0)
    {
      setLine(x0, y0, x0, y1, bw);

      if(x0 > x1)
        x0--;
      else
        x0++;

      xDiff--;
    }
  }
  else
  {
    // best way to draw an unfilled rectangle is to draw four lines
    setLine(x0, y0, x1, y0, bw);
    setLine(x0, y1, x1, y1, bw);
    setLine(x0, y0, x0, y1, bw);
    setLine(x1, y0, x1, y1, bw);
  }
}

// setCircle draws a circle centered around x0,y0 with a defined
// radius. The circle can be black or white. And have a line
// thickness ranging from 1 to the radius of the circle.
// This function was grabbed from the SparkFun ColorLCDShield
// library.
void setCircle (int x0, int y0, int radius, bool bw, int lineThickness)
{
  for(int r = 0; r < lineThickness; r++)
  {
    int f = 1 - radius;
    int ddF_x = 0;
    int ddF_y = -2 * radius;
    int x = 0;
    int y = radius;

    setPixel1(x0, y0 + radius, bw);
    setPixel1(x0, y0 - radius, bw);
    setPixel1(x0 + radius, y0, bw);
    setPixel1(x0 - radius, y0, bw);

    while(x < y)
    {
      if(f >= 0)
      {
        y--;
        ddF_y += 2;
        f += ddF_y;
      }
      x++;
      ddF_x += 2;
      f += ddF_x + 1;

      setPixel1(x0 + x, y0 + y, bw);
      setPixel1(x0 - x, y0 + y, bw);
      setPixel1(x0 + x, y0 - y, bw);
      setPixel1(x0 - x, y0 - y, bw);
      setPixel1(x0 + y, y0 + x, bw);
      setPixel1(x0 - y, y0 + x, bw);
      setPixel1(x0 + y, y0 - x, bw);
      setPixel1(x0 - y, y0 - x, bw);
    }
    radius--;
  }
}

// This function will draw a char (defined in the ASCII table
// near the beginning of this sketch) at a defined x and y).
// The color can be either black (1) or white (0).
void setChar(char character, int x, int y, bool bw)
{
  int column; // temp byte to store character's column bitmap
  for (int i=0; i<5; i++) // 5 columns (x) per character
  {
    column = ASCII[character - 0x20][i];
    for (int j=0; j<8; j++) // 8 rows (y) per character
    {
      if (column & (0x01 << j)) // test bits to set pixels
        setPixel1(x+i, y+j, bw);
      else
        setPixel1(x+i, y+j, !bw);
    }
  }
}

// setStr draws a string of characters, calling setChar with
// progressive coordinates until it's done.
// This function was grabbed from the SparkFun ColorLCDShield
// library.
void setStr(char * dString, int x, int y, bool bw)
{
  while (*dString != 0x00) // loop until null terminator
  {
    setChar(*dString++, x, y, bw);
    x+=5;
    for (int i=y; i<y+8; i++)
    {
      setPixel1(x, i, !bw);
    }
    x++;
    if (x > (LCD_WIDTH - 5)) // Enables wrap around
    {
      x = 0;
      y += 8;
    }
  }
}

// This function will draw an array over the screen. (For now) the
// array must be the same size as the screen, covering the entirety
// of the display.
// Also, the array must reside in FLASH and declared with PROGMEM.
void SetBitMap(const char * bitArray)
{
  for (int i=0; i<(LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    char c = bitArray[i];
    displayMap_5110[i] = c;
  }
}

// This function clears the entire display either white (0) or
// black (1).
// The screen won't actually clear until you call updateDisplay()!
void clearDisplay(bool bw)
{
  for (int i=0; i<(LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    if (bw)
      displayMap_5110[i] = 0xFF;
    else
      displayMap_5110[i] = 0;
  }
}

// Helpful function to directly command the LCD to go to a
// specific x,y coordinate.
void gotoXY(int x, int y)
{
  LCDcommand(0x80 | x);  // Column.
  LCDcommand( 0x40 | y);  // Row.  ?
}

// This will actually draw on the display, whatever is currently
// in the displayMap array.
void updateDisplay(void)
{
  gotoXY(0, 0);
  for (int i=0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    LCDdata( displayMap_5110[i]);
  }
}

// Set contrast can set the LCD Vop to a value between 0 and 127.
// 40-60 is usually a pretty good range.
void setContrast(int contrast)
{
  LCDcommand(0x21); //Tell LCD that extended commands follow
  LCDcommand( 0x80 | contrast); //Set LCD Vop (Contrast): Try 0xB1(good @ 3.3V) or 0xBF if your display is too dark
  LCDcommand(0x20); //Set display mode
}

/* There are two ways to do this. Either through direct commands
to the display, or by swapping each bit in the displayMap array.
We'll leave both methods here, comment one or the other out if
you please. */
void invertDisplay(void)
{
  /* Direct LCD Command option
  LCDWrite(LCD_COMMAND, 0x20); //Tell LCD that extended commands follow
  LCDWrite(LCD_COMMAND, 0x08 | 0x05); //Set LCD Vop (Contrast): Try 0xB1(good @ 3.3V) or 0xBF if your display is too dark
  LCDWrite(LCD_COMMAND, 0x20); //Set display mode  */

  /* Indirect, swap bits in displayMap option: */
  for (int i=0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    displayMap_5110[i] = ~displayMap_5110[i] & 0xFF;
  }
  updateDisplay();
}

//This sends the magical commands to the PCD8544
void lcdBegin(void)
{
	GPIO_init();
	spi_init();
	RST0;
	delay(1000);
	RST1;
	CS0;
  LCDcommand( 0x21); //Tell LCD extended commands follow
	delay(10);
  LCDcommand( 0xB0); //Set LCD Vop (Contrast)
	delay(10);
  LCDcommand( 0x04); //Set Temp coefficent
	delay(10);
  LCDcommand( 0x14); //LCD bias mode 1:48 (try 0x13)
	delay(10);
  //We must send 0x20 before modifying the display control mode
  LCDcommand( 0x20);
	delay(10);
  LCDcommand( 0x0C); //Set display control, normal mode.
	delay(10);
}

void delay(int ms)
{
	SysTick->LOAD=16000-1;
	SysTick->VAL=0;
	SysTick->CTRL=0x5;
		for (int i=0;i<ms;i++)
		{
			while(!(SysTick->CTRL &0x10000)){}
		}
	SysTick->CTRL=0;
}

You may download the project from here:

4. Demo:

Happy coding 🙂

Add Comment

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