[Revised]Working with STM32 and display: Nokia 5110 Graphic LCD

In this revised version of Nokia 5110 Display, we shall interface this type of LCD with STM32 and develop the appropriate driver to driver the screen to display texts and pictures.

In this guid, we shall cover the following:

  • Nokia 5110 Graphic LCD.
  • LCD connection with STM32.
  • SPI driver.
  • Display driver.
  • Main code.
  • Code download.
  • Results.

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. LCD Connection with STM32:

The connection diagram as following:

The connection as following:

STM32F411 Nucleo-64Nokia 5110 GLCD
5VVcc
5VBL
GNDGND
PA5 (SCK SPI1)CLK
PA7 (MOSI SPI1)Din
PA0DC
PA1CE
PA4RST

3. SPI driver:

Before we deal with SPI, refer to this guide for more details about SPI and how find the pins, set alternate function etc.

We start of by creating header file with name of SPI1.h.

Within the header file, include header guard:

#ifndef SPI_H_
#define SPI_H_



#endif /* SPI1_H_ */

Include standard integer:

#include "stdint.h"

SPI Functions:

void SPI1_Pins_Init(void);
void SPI1_Init(void);
void SP11_Transmit(uint8_t *data,uint32_t size);
void SPI1_Receive(uint8_t *data,uint32_t size);

Entire header file:

#ifndef SPI1_H_
#define SPI1_H_

#include "stdint.h"

void SPI1_Pins_Init(void);
void SPI1_Init(void);
void SPI1_Transmit(uint8_t *data,uint32_t size);
void SPI1_Receive(uint8_t *data,uint32_t size);


#endif /* SPI_H_ */

Now, create new source file with name of SPI1.c.

Within the source include the following header file:

#include "SPI1.h"

#include "stm32f4xx.h"

Declare AF05 to hold the alternate function number:

#define AF05	0x05

Populated functions from header file:

void SPI1_Pins_Init(void)
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; //enable clock for GPIOA
	GPIOA->MODER|=GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1; //set PA5, PA6 and PA7 to alternate function mode
	GPIOA->MODER &=~(GPIO_MODER_MODE5_0|GPIO_MODER_MODE6_0|GPIO_MODER_MODE7_0);

	GPIOA->AFR[0]|=(AF05<<GPIO_AFRL_AFSEL5_Pos)|(AF05<<GPIO_AFRL_AFSEL6_Pos)|(AF05<<GPIO_AFRL_AFSEL7_Pos);
}

void SPI1_Init(void)
{
	/*Enable clock access to SPI1 module*/
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

	/*Configure the SPI with the following parameters
	 * Master Mode.
	 * Software slave management
	 * */

	SPI1->CR1=SPI_CR1_MSTR|SPI_CR1_SSI|SPI_CR1_SSM;

	/*Enable SPI1 peripheral */
	SPI1->CR1|=SPI_CR1_SPE;
}

void SPI1_Transmit(uint8_t *data,uint32_t size)
{
	uint32_t i=0;

	while(i<size)
	{
		/*Wait until TXE is set*/
		while(!(SPI1->SR & (SPI_SR_TXE))){}

		/*Write the data to the data register*/
		SPI1->DR = data[i];
		i++;
	}
	/*Wait until TXE is set*/
	while(!(SPI1->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI1->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI1->DR;
	(void)SPI1->SR;
}

void SPI1_Receive(uint8_t *data,uint32_t size)
{
	while(size)
	{
		/*Send dummy data*/
		SPI1->DR =0;

		/*Wait for RXNE flag to be set*/
		while(!(SPI1->SR & (SPI_SR_RXNE))){}

		/*Read data from data register*/
		*data++ = (SPI1->DR);
		size--;
	}
}

Entire source code:

#include "SPI1.h"

#include "stm32f4xx.h"

#define AF05	0x05


void SPI1_Pins_Init(void)
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; //enable clock for GPIOA
	GPIOA->MODER|=GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1; //set PA5, PA6 and PA7 to alternate function mode
	GPIOA->MODER &=~(GPIO_MODER_MODE5_0|GPIO_MODER_MODE6_0|GPIO_MODER_MODE7_0);

	GPIOA->AFR[0]|=(AF05<<GPIO_AFRL_AFSEL5_Pos)|(AF05<<GPIO_AFRL_AFSEL6_Pos)|(AF05<<GPIO_AFRL_AFSEL7_Pos);
}

void SPI1_Init(void)
{
	/*Enable clock access to SPI1 module*/
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

	/*Configure the SPI with the following parameters
	 * Master Mode.
	 * Software slave management
	 * */

	SPI1->CR1=SPI_CR1_MSTR|SPI_CR1_SSI|SPI_CR1_SSM;

	/*Enable SPI1 peripheral */
	SPI1->CR1|=SPI_CR1_SPE;
}

void SPI1_Transmit(uint8_t *data,uint32_t size)
{
	uint32_t i=0;

	while(i<size)
	{
		/*Wait until TXE is set*/
		while(!(SPI1->SR & (SPI_SR_TXE))){}

		/*Write the data to the data register*/
		SPI1->DR = data[i];
		i++;
	}
	/*Wait until TXE is set*/
	while(!(SPI1->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI1->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI1->DR;
	(void)SPI1->SR;
}

void SPI1_Receive(uint8_t *data,uint32_t size)
{
	while(size)
	{
		/*Send dummy data*/
		SPI1->DR =0;

		/*Wait for RXNE flag to be set*/
		while(!(SPI1->SR & (SPI_SR_RXNE))){}

		/*Read data from data register*/
		*data++ = (SPI1->DR);
		size--;
	}
}

4. Display Driver:

Create new header file with name of Nokia_5110_GLCD.h.

Within the header file, include the header guard:

#ifndef NOKIA_5110_GLCD_H_
#define NOKIA_5110_GLCD_H_



#endif /* NOKIA_5110_GLCD_H_ */

Include the following header files:

#include "stdint.h"
#include "stdbool.h"

Within the header guard, declare the dimensions of the display and black and white values:

/* 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.

Required functions:

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);

Hence, the header file as following:

#ifndef NOKIA_5110_GLCD_H_
#define NOKIA_5110_GLCD_H_

#include "stdint.h"
#include "stdbool.h"


/* 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.


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 /* NOKIA_5110_GLCD_H_ */

Create new source file with name of Nokia_5110_GLCD.c

Within the source file, include the following header file:

#include "Nokia_5110_GLCD.h"
#include "STM32F4xx.h"
#include "delay.h"
#include <stdbool.h>
#include "SPI1.h"

Define some macros for DC, CS and RST as following:

/*Macros of CS, RST and DC*/

#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

Array to hold ASCII characters that can be displayed:

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
};

Frame buffer which will be written to the LCD:

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!
};

Now, we start with the functions.

First function is GPIO initialization for CS,DC and RST pins as following:

static void Nokia_5110_Pins_Init(void)
{
	/*Enable Clock Access to GPIOA*/
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;

	/*Set PA0, PA1 and PA4 as Output*/
	GPIOA->MODER|=GPIO_MODER_MODE0_0|GPIO_MODER_MODE1_0|GPIO_MODER_MODE4_0;
	GPIOA->MODER&=~(GPIO_MODER_MODE0_1|GPIO_MODER_MODE1_1|GPIO_MODER_MODE4_1);

}

This function is defined as static which can be only used in this source file.

Writing either data or command to the LCD:

/*Write data/command functions*/
static void LCDcommand(uint8_t data)
{
	CS0;
	LCD_COMMAND;
	SPI1_Transmit(&data,1);
	CS1;
}

static void LCDdata(uint8_t data)
{
	CS0;
	LCD_DATA;
	SPI1_Transmit(&data,1);
	CS1;
}

Defined as static since no other source file had the right to access them.

Setting the pixel value:

// color. 1=Black, 0= white.
static 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...
static void setPixel(int x, int y)
{
	setPixel1(x, y, BLACK); // Call setPixel with bw set to Black
}

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

Also declared as static.

Initialization function:

//This sends the magical commands to the PCD8544
void lcdBegin(void)
{
	Nokia_5110_Pins_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);
}

Support functions:

// 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();
}

Entire source file:

#include "Nokia_5110_GLCD.h"
#include "STM32F4xx.h"
#include "delay.h"
#include <stdbool.h>
#include "SPI1.h"

/*Macros of CS, RST and DC*/

#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



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
};


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!
};


static void Nokia_5110_Pins_Init(void)
{
	/*Enable Clock Access to GPIOA*/
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;

	/*Set PA0, PA1 and PA4 as Output*/
	GPIOA->MODER|=GPIO_MODER_MODE0_0|GPIO_MODER_MODE1_0|GPIO_MODER_MODE4_0;
	GPIOA->MODER&=~(GPIO_MODER_MODE0_1|GPIO_MODER_MODE1_1|GPIO_MODER_MODE4_1);

}

/*Write data/command functions*/
static void LCDcommand(uint8_t data)
{
	CS0;
	LCD_COMMAND;
	SPI1_Transmit(&data,1);
	CS1;
}

static void LCDdata(uint8_t data)
{
	CS0;
	LCD_DATA;
	SPI1_Transmit(&data,1);
	CS1;
}
// This function sets a pixel on displayMap to your preferred
// color. 1=Black, 0= white.
static 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...
static void setPixel(int x, int y)
{
	setPixel1(x, y, BLACK); // Call setPixel with bw set to Black
}

static 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)
{
	Nokia_5110_Pins_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);
}

5.Main.c:

In main.c file:

#include "delay.h"
#include "Nokia_5110_GLCD.h"
#include "SPI1.h"
#include <math.h>


#define PI 3.14


static const char xkcdSandwich[504] = {
0xFF, 0x8D, 0x9F, 0x13, 0x13, 0xF3, 0x01, 0x01, 0xF9, 0xF9, 0x01, 0x81, 0xF9, 0xF9, 0x01, 0xF1,
0xF9, 0x09, 0x09, 0xFF, 0xFF, 0xF1, 0xF9, 0x09, 0x09, 0xF9, 0xF1, 0x01, 0x01, 0x01, 0x01, 0x01,
0xF9, 0xF9, 0x09, 0xF9, 0x09, 0xF9, 0xF1, 0x01, 0xC1, 0xE9, 0x29, 0x29, 0xF9, 0xF1, 0x01, 0xFF,
0xFF, 0x71, 0xD9, 0x01, 0x01, 0xF1, 0xF9, 0x29, 0x29, 0xB9, 0xB1, 0x01, 0x01, 0x01, 0xF1, 0xF1,
0x11, 0xF1, 0xF1, 0xF1, 0xE1, 0x01, 0xE1, 0xF1, 0x51, 0x51, 0x71, 0x61, 0x01, 0x01, 0xC1, 0xF1,
0x31, 0x31, 0xF1, 0xFF, 0xFF, 0x00, 0x01, 0x01, 0x01, 0x01, 0x60, 0xE0, 0xA0, 0x01, 0x01, 0x81,
0xE1, 0x61, 0x60, 0xC0, 0x01, 0xE1, 0xE1, 0x21, 0x21, 0xE0, 0xC1, 0x01, 0xC1, 0xE1, 0x20, 0x20,
0xFC, 0xFC, 0xE0, 0xE0, 0xC1, 0xE1, 0xE0, 0xC1, 0xE0, 0xE1, 0x01, 0xFC, 0xFC, 0x21, 0x21, 0xE1,
0xC1, 0xE5, 0xE4, 0x01, 0xC1, 0xE0, 0x20, 0x21, 0x20, 0x00, 0x01, 0xFD, 0xFD, 0x21, 0x20, 0xE0,
0x00, 0x00, 0x01, 0x01, 0xC0, 0x61, 0x31, 0x31, 0x21, 0x20, 0xC0, 0x81, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02,
0x03, 0x01, 0x00, 0x01, 0x03, 0xF2, 0x1A, 0x0B, 0x08, 0x0B, 0x1B, 0x10, 0x60, 0xE3, 0x03, 0x00,
0x01, 0x03, 0x02, 0x02, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03,
0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x01, 0x03, 0x02, 0x02, 0x03, 0x01, 0x00, 0x03,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x80, 0x80, 0x80, 0x80, 0x60, 0x3F, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x01, 0x01, 0x02, 0x03, 0x3E, 0xE8, 0xF8, 0xF0, 0xD0, 0x90,
0x18, 0x0F, 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, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x38, 0xFF,
0x0C, 0x38, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33,
0x5F, 0x8F, 0x84, 0x05, 0x07, 0x06, 0x0C, 0x0E, 0x0E, 0x0C, 0x14, 0x34, 0x68, 0x88, 0xD8, 0x70,
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xF0, 0xE0, 0x00, 0xF0, 0xF0, 0x00, 0x80,
0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x20, 0x38,
0x0E, 0x01, 0xC0, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0x0E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xB6, 0xED, 0xC0, 0xC0,
0xC0, 0xE0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xE1, 0xE1, 0xC1,
0xEF, 0xBB, 0x83, 0x86, 0x88, 0xB0, 0x80, 0x80, 0x80, 0x8F, 0x90, 0x90, 0x90, 0x9F, 0x8F, 0x80,
0x9F, 0x9F, 0x87, 0x8D, 0x98, 0x80, 0x8C, 0x9E, 0x92, 0x92, 0x9F, 0xC0, 0xC7, 0xFF, 0xB8, 0x8F,
0x80, 0x90, 0x90, 0xC0, 0xF0, 0x8E, 0x81, 0x80, 0x81, 0x8F, 0xB8, 0xE0, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF,
};


int main(void)
{
	SPI1_Pins_Init();
	SPI1_Init();
	lcdBegin();
	setContrast(50);
	updateDisplay();

	while(1)
	{
		setStr("STM32 NOKIA5110 bare metal", 0, LCD_HEIGHT-8, BLACK);
		updateDisplay();
		delay(1000);
		for (int i=0; i<5; i++)
		{
		invertDisplay();
		delay(200);
		invertDisplay();
		delay(200);
		}
		delay(2000);


		clearDisplay(WHITE);
		int x0 = LCD_WIDTH/2;
		int y0 = LCD_HEIGHT/2;
		for (float i=0; i<2*PI; i+=PI/8)
		{
		const int lineLength = 24;
		int x1 = x0 + lineLength * sin(i);
		int y1 = y0 + lineLength * cos(i);
		setLine(x0, y0, x1, y1, BLACK);
		//updateDisplay();
		delay(100);
		}
		clearDisplay(WHITE); // Start fresh
		for (int x=0; x<LCD_WIDTH; x+=8)
		{ // Swipe right black
		setRect(0, 0, x, LCD_HEIGHT, 1, BLACK);
		// updateDisplay();
		delay(10);
		}
		for (int x=0; x<LCD_WIDTH; x+=8)
		{
		setRect(0, 0, x, LCD_HEIGHT, 1, WHITE);
		//updateDisplay();
		delay(10);
		}
		for (int x=0; x<12; x++)
		{
		setRect(0, 0, x, LCD_HEIGHT, 1, 1);
		setRect(11, 0, x+12, LCD_HEIGHT, 1, BLACK);
		setRect(23, 0, x+24, LCD_HEIGHT, 1, BLACK);
		setRect(35, 0, x+36, LCD_HEIGHT, 1, BLACK);
		setRect(47, 0, x+48, LCD_HEIGHT, 1, BLACK);
		setRect(59, 0, x+60, LCD_HEIGHT, 1, BLACK);
		setRect(71, 0, x+72, LCD_HEIGHT, 1, BLACK);
		updateDisplay();
		delay(10);
		}
		setRect(25, 10, 45, 30, 0, WHITE);
		setRect(35, 20, 55, 40, 0, WHITE);
		setLine(25, 10, 35, 20, WHITE);
		setLine(45, 30, 55, 40, WHITE);
		setLine(25, 30, 35, 40, WHITE);
		setLine(45, 10, 55, 20, WHITE);
		updateDisplay();
		delay(2000);
		clearDisplay(WHITE);
		SetBitMap(xkcdSandwich);
		delay(2000);
	}

}

6. Code:

You may download the code from here:

7. Results:

Add Comment

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