HUM: GRAPHIC LCD DISPLAY 128X64
You are a beginner with Dasduino. Or electronics? A specific module caught your eye, but you do not know how to use it? Do not worry, HUM is here for you! How to Use Module (HUM) is a blog tutorials series by Soldered where you will find all you need in order to begin working with your favorite module. Tutorials include: technical characteristics, work principle, instructions on how to connect module with Dasduino and the basic code. Everything else is left to your imagination.
INTRODUCTION
In this tutorial we will write about graphic display, why and when to use it, how to connect it to Croduino and how to program it to display what we want. The reason why to use graphic display instead of a regular 16×2 LCD speaks for itself, and that is the fact that besides text, we can also display graphics, including symbols, tags, images and even the text which has a different font than the one on a regular display. Another reason why to use a graphic display is the fact that it is possible to perform a very simple GUI (Graphic User Interface) which makes controlling and adjusting some of your projects much more intuitive and easier. As the first step towards that, let’s learn how to control our display.
HOW THE MODULE WORKS
In the picture you can see the back side of our graphic display. You can see that there are 20 pins used for power supplying the display, communication, adjusting contrast, and background LED lighting. But, there is another very important thing on it, which is not visible at first, and that is the selection between the parallel and serial communication. Namely, this display (more precisely, the chip, i.e. display controller) supports serial SPI communication and parallel communication. About the controller a bit later in the tutorial.
By switching the middle pad with the pad next to the letter S, the display receives serial communication, and if the middle pad is switched with the pad next to the letter P, the display uses parallel communication. The look of these pads (JP2) and their location on the display is visible in the following picture:
In order to be able to connect LCD and Dasduino, it is necessary to know which function does an individual terminal on the 20 pin connector have. The following image shows the function of each of these 20 pins.
Some displays have the choice of parallel and serial communication on the connector itself (PSB Pin). In that case, it is not necessary to switch the previously mentioned pads. In case of serial communication, RS pin is actually CS (Chip select), RW is DIN (Data in or MOSI with the SPI communication), and E as SCK (Serial Clock), pins DB0 and DB7 are not used. If you want simplicity and each used pin is important, and you can risk a bit slower display, we recommend using serial communication (SPI), but if you want to connect something else besides the display to the SPI, we advise you to use display in the parallel mode (it is not that the display will not work, but some modules do not like to share SPI with another module, so you should bear that in mind). There are three more pins left to explain, and those are RST, VOUT and V0. RST pin is the pin that restarts the whole display to its initial state. Some libraries use this pin to initialize (i.e. restart) the display itself during the initialization of the library. If a library (or even some Arduino code) does not require that pin, it is necessary to connect it to the voltage power supply (+5VDC) in order for the display to be in a normal mode ready to receive commands and data. V0 pin determines the desired contrast, but the adjusting itself is a bit different than the one of 16×2 display. With it, we have connected the variable resistor between V0 and mass (GND). In this case, the variable resistor is connected between V0 and VOUT because for adjusting the contrast on this display we need a negative voltage, which can be generated by that same display. Some displays already have a built-in SMD trimmer for that purposes, so in that case, we do not need to connect any resistor between V0 and VOUT, only adjust the small SMD trimmer that will adjust the LCD’s contrast.
It is mentioned that the display has a controller on it (one of the three epoxy circles on the back side of the display), and it is about the ST7920 integrated circuit, the two remaining chips are ST7921 that increase the number of pixells the controller can manage. The controller itself has several work modes, and one of them is that it can be used as a regular alphanumeric display that has a significantly larger font than the regular 16×2 display or can even work in the extended set of commands, and work as a graphic display. We will remain here on the extended set of commands that allows “drawing” different shapes on it. If you want to deal with the first work mode, the way you can do it can be found in the display’s datasheet.
Finally, we will say what types of LCD displays can be found. Some main division is that there are three types: monochromatic (black-white), grayscale (can display shades of one color, e.g. gray) and color. Our display belongs to monochromatic ones because it can display only the pixel that is turned on or off. Those who can change the intensity of the turned on pixel are grayscale displays and they are difficult to obtain and expensive, therefore they are rarely seen in some projects. And lastly, the most interesting ones are color displays, which exist in different versions (serial, parallel communication or even startup from an SD card), of different resolutions and with different amounts of color bits per pixel (from 8 to 24 bits), with or without touchscreen. Such displays are easily accessible.
HOW TO CONNECT IT
In this part of the tutorial, we will show you how to connect Dasduino to the LCD. We should note that there will be two schemes, one of them showing how to use LCD in serial communication, and the other how to use it in parallel.
A scheme for serial (SPI) communication, which is used by the ST7920 GFX Library.
Scheme for parallel communication, which is used by the u8g library.
ARDUINO CODE
As we have already said, we will use two operation modes, parallel and serial communication. In order to make writing of the code easier, we will use libraries (one is ST7920 GFX Library which is based on the well-known Adafruit GFX Library (check the instructions for Adafruit GFX library here), only for now it does not support parallel communication, and the other is U8G library which supports both serial and parallel communication, but is somewhat more complicated to handle). If you do not know how to install libraries, we advise you to check our tutorial about it.
First we will deal with the ST7920 GFX Library, so open Arduino IDE, then FIle->Examples->ST7920_GFX_Library->ST7920_graphic_test and upload the code to Dasduino. If everything is fine, the display will show the test program for display. If that does not happen, check the connections.
Now that we know that everything is working, let us make a simple display of some image and text.
#include "ST7920_GFX_Library.h" //Adding library for graphic display control based on the ST7920 controller.
#include "Adafruit_GFX.h" //Adding Adafruit GFX library which is responsible for drawing different shapes on the display.
#include <SPI.h> //Adding library for SPI serial communication.
//This library uses serial communication (SPI communication). In order to select serial communication mode
//with the display, it is necessary to put the PSB pin to 0V (ground). Otherwise, the display will not work and nothing will be displayed on it.
#define CS_PIN 10 //Defining to which Croduino pin we want to connect CS(RS) LCD pin. In this case it is Croduino pin 10.
#define velicinaSlikeX 56
#define velicinaSlikeY 56
ST7920 display(CS_PIN);
//LCD library constructor.
//Bitmap image (e-radionica's logo. :) )
//To convert the image into a series of data for Arduino, it is necessary to open this link:
static
const
uint8_t logo[]
PROGMEM
= {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x03,0xff,0xc0,0x00,0x00,
0x00,0x00,0x1f,0xff,0xf8,0x00,0x00,
0x00,0x00,0x7f,0xff,0xfe,0x00,0x00,
0x00,0x01,0xff,0xff,0xff,0x80,0x00,
0x00,0x03,0xfc,0x00,0x3f,0xe0,0x00,
0x00,0x0f,0xe0,0x00,0x07,0xf0,0x00,
0x00,0x1f,0x80,0x00,0x01,0xf8,0x00,
0x00,0x3f,0x00,0x00,0x00,0xfc,0x00,
0x00,0x7e,0x00,0x00,0x00,0x3e,0x00,
0x00,0xf8,0x00,0x00,0x00,0x1f,0x00,
0x00,0xf0,0x00,0x00,0x00,0x0f,0x00,
0x01,0xf0,0x00,0x00,0x00,0x07,0x80,
0x03,0xe0,0x00,0x00,0x00,0x07,0xc0,
0x03,0xc0,0x00,0x00,0x00,0x03,0xc0,
0x07,0xc0,0x3f,0xff,0x80,0x01,0xe0,
0x07,0x80,0xff,0xff,0x80,0x01,0xe0,
0x0f,0x83,0xff,0xff,0xfe,0x00,0xf0,
0x0f,0x07,0xf0,0x31,0xfe,0x00,0xf0,
0x0f,0x0f,0x37,0x31,0xfe,0x00,0xf0,
0x0e,0x0e,0x3f,0x79,0xc0,0x00,0x78,
0x1e,0x1c,0x3f,0x79,0xc0,0x00,0x78,
0x1e,0x1c,0x02,0x31,0xc0,0x00,0x78,
0x1e,0x38,0x00,0x01,0xc0,0x00,0x78,
0x1f,0xff,0x8e,0x01,0xfc,0x00,0x78,
0x1f,0xff,0xff,0x01,0xfc,0x00,0x78,
0x1f,0xff,0xfe,0x01,0xfc,0x00,0x78,
0x00,0x38,0x00,0x01,0xc0,0x00,0x78,
0x00,0x1c,0x1c,0x21,0xc0,0x00,0x78,
0x00,0x1c,0x3c,0x71,0xc0,0x00,0x78,
0x00,0x1e,0x1c,0xf1,0xc0,0x00,0x78,
0x00,0x0f,0x18,0x71,0xfe,0x00,0x78,
0x00,0x07,0xf8,0xe1,0xfe,0x00,0xf0,
0x07,0x03,0xf8,0xc1,0xfe,0x00,0xf0,
0x07,0x81,0xff,0xff,0xfe,0x00,0xf0,
0x07,0x80,0x7f,0xff,0x80,0x01,0xe0,
0x07,0xc0,0x00,0x00,0x00,0x01,0xe0,
0x03,0xc0,0x00,0x00,0x00,0x03,0xc0,
0x03,0xe0,0x00,0x00,0x00,0x07,0xc0,
0x01,0xf0,0x00,0x00,0x00,0x07,0x80,
0x00,0xf8,0x00,0x00,0x00,0x0f,0x00,
0x00,0xfc,0x00,0x00,0x00,0x1f,0x00,
0x00,0x7e,0x00,0x00,0x00,0x3e,0x00,
0x00,0x3f,0x00,0x00,0x00,0xfc,0x00,
0x00,0x1f,0x80,0x00,0x01,0xf8,0x00,
0x00,0x0f,0xe0,0x00,0x07,0xf0,0x00,
0x00,0x03,0xfc,0x00,0x3f,0xc0,0x00,
0x00,0x01,0xff,0xff,0xff,0x80,0x00,
0x00,0x00,0x7f,0xff,0xfe,0x00,0x00,
0x00,0x00,0x1f,0xff,0xf8,0x00,0x00,
0x00,0x00,0x01,0xff,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void
setup
() {
display.begin();
//Initialization of the library
display.clearDisplay();
//Clear everything from cache (buffer) of the display
//Draw the bitmap image in the default space (x = 35, y = 0) of 56x56 pixel size.
display.drawBitmap(35, 0, logo, velicinaSlikeX, velicinaSlikeY, BLACK);
display.setCursor(20, 57);
//Set the text cursor to coordinate x = 20, y = 57
display.setTextColor(WHITE, BLACK);
//Now we want the text to be in the color of the background, and everything around it in the color of the pixel
display.print(
"e-radionica.com"
);
//Display the text
display.display();
//Print out all data on the display (until this function is called, nothing will be printed out on the display).
}
void
loop
() {
display.setTextColor(BLACK, WHITE);
//Now we want the text to be in the color of the pixel, and everything around it in the color of the background
display.setCursor(0,0);
//We want the text to be displayed in the upper left corner
display.print(
millis
()/1000);
//Display how long Croduino has been turned on.
display.display();
//Print it all out on the display.
delay
(990);
//Wait a bit until the next display.
}
Now let’s make something a bit more complicated, and that is a very simple clock (it will not be too accurate because counting seconds is based on Arduino’s millis(); function, but it is great for the example how to make display of the clock on the graphic display).
#include "ST7920_GFX_Library.h" //Adding library for graphic display control based on the ST7920 controller.
#include "Adafruit_GFX.h" //Adding Adafruit GFX library which is responsible for drawing different shapes on the display.
#include <SPI.h> //Adding library for SPI serial communication.
//This library uses serial communication (SPI communication). In order to select serial communication mode
//with the display, it is necessary to put the PSB pin to 0V (ground). Otherwise, the display will not work and nothing will be displayed on it.
#define CS_PIN 10 //Defining to which Croduino pin we want to connect CS (RS) LCD pin. In this case, it is Croduino pin 10.
#define satX 70 //Defining x coordinate where the analog clock will be located on the display.
#define satY 31 //Defining y coordinate where the analog clock will be located on the display.
#define pinMinute 2 //Defining Croduino pin using which we will be setting minutes
#define pinSati 3 //Defining Croduino pin using which we will be setting hours
ST7920 display(CS_PIN);
//LCD library constructor.
int
sekunde = 0, minute = 0, sati = 0;
//Defining and initialization of the clock variables.
unsigned
long
time1 = 0;
void
setup
() {
display.begin();
//Initialize the library for LCD
display.clearDisplay();
//Clear the entire LCD cache in order to begin drawing something on the display.
pinMode
(pinMinute,
INPUT_PULLUP
);
//Pin using which we want to set minutes must be defined as input on the Croduino.
pinMode
(pinSati,
INPUT_PULLUP
);
//Pin using which we want to set hours must be defined as input on the Croduino.
}
char
digitalniSat[9];
void
loop
() {
if
((unsigned
long
)(
millis
() - time1) >= 1000) {
//Wait one second (1000 milliseconds), and then increase seconds
time1 =
millis
();
ispisiSat();
//Function that calls displaying the whole clock (both analog and digital) on the display.
sekunde++;
//Increase seconds by one.
if
(sekunde > 59) {
//If a minute has passed (60 seconds), return seconds to zero and increase minutes.
sekunde = 0;
minute++;
}
if
(minute > 59) {
//If an hour has passed (60 minutes), return minutes to zero and increase hours.
minute = 0;
sati++;
}
if
(sati > 23) sati = 0;
//If a day has passed, return hours to zero.
}
if
(!
digitalRead
(pinMinute)) {
//If we are setting minutes.
sekunde = 0;
//First return seconds to zero, so that they do not reach 59 and increase minutes by one
minute++;
//Then increase minutes by one
if
(minute > 59) minute = 0;
//Check if minutes have passed 59, if they have, start setting minutes from the beginning.
ispisiSat();
//Print out new time on the display.
delay
(250);
//A small break between settings.
}
if
(!
digitalRead
(pinSati)) {
//If we are setting hours
sekunde = 0;
//First return seconds to zero, so that they do not reach 59 and increase hours by one
sati++;
//Then increase hours by one
if
(sati > 23) sati = 0;
//Check if hours have passed 23, if they have, start setting hours from the beginning.
ispisiSat();
//Print out new time on the display. Ispisi novo vrijeme na display.
delay
(250);
//A small break between settings. Mala pauza izmedju podesavanja.
}
}
void
ispisiSat() {
display.clearDisplay();
//Clear everything from the display
sprintf
(digitalniSat,
"%2d:%02d:%02d"
, sati, minute, sekunde);
//Display digital clock using the sprintf function. This function works as the printf function, except that it saves everything to string which is in this case digitalniSat
display.setCursor(0, 0);
//We want the digital clock to be in the upper left corner
display.print(digitalniSat);
//Display the digital clock.
display.drawCircle(satX, satY, 30, BLACK);
//Draw the border (frame) of the analog clock that has a 30-pixel radius.
display.drawCircle(satX, satY, 29, BLACK);
//In order for the border to be thicker, we draw another circle for one pixel smaller radius than the previous one.
//A little bit of math. :) For drawing hands, we use the drawLine function, which requires the initial x and y coordinate, and the final x and y coordinate.
//The initial ones are not the problem, those are the center of the circle and those describing the clock. We do the final ones using trigonometric functions.
//Using the cosine function we calculate the final x coordinate. Since cos() function requires the argument to be in radians, we must convert our calculation from degrees to radians (deg*PI/180).
//We must calculate how many degrees is one second. This can be done if we divide 360/60 = 6. So, we multiply seconds by 6 in order to get degrees.
//We still need to subtract PI half, because the clock starts so that the hands are placed vertically, not horizontally.
//In the end we just move the calculation by the position of the clock, i.e. the initial coordinate of the line (satX and satY).
//The number in front of sin and cos function tells us about how long the hand will be (for seconds it is 25 pixels).
int
x0 = 25 *
cos
(((sekunde * 6.0) / 180 * PI) - PI / 2) + satX;
int
y0 = 25 *
sin
(((sekunde * 6.0) / 180 * PI) - PI / 2) + satY;
display.drawLine(satX, satY, x0, y0, BLACK);
x0 = 20 *
cos
(((minute * 6.0) / 180 * PI) - PI / 2) + satX;
y0 = 20 *
sin
(((minute * 6.0) / 180 * PI) - PI / 2) + satY;
display.drawLine(satX, satY, x0, y0, BLACK);
//Clocks have another addition, as minutes increase, the hand for hours also moves slowly.
//Since one hour moves for 30 degrees, and one hour has 60 minutes, it is necessary to divide minutes by two and add it to the total rotary angle of the hand for hours.
x0 = 20 *
cos
((((sati * 30.0) + (minute/2)) / 180 * PI) - PI / 2) + satX;
y0 = 20 *
sin
((((sati * 30.0) + (minute/2)) / 180 * PI) - PI / 2) + satY;
display.drawLine(satX, satY, x0, y0, BLACK);
display.display();
//In the end draw the whole view on the display.
}
Now that we have tried out serial communication, we will try out the parallel one using u8g library. The advantage of this library is the fact that it uses very little RAM, so it can be adapted into some more complex projects. But, this is also a drawback, because it is significantly slower than the ST7920 GFX Library. Library’s instructions (along with all the functions it uses) can be found here.
#include "U8glib.h"
//Pins on Croduino-> 8 bit communication: D0..D7: 2, 3, 4, 5, 6, 7, 8, 9, E=10, RS=11, RW=12, PBS = +5V, RST = +5V
U8GLIB_ST7920_128X64_4X u8g(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
void
setup
() {
u8g.setRot180();
//Rotating display 180 degrees
}
void
loop
() {
u8g.firstPage();
//This is the function that starts drawing on the display
do
{
//The display is divided into more pages (parts).
draw();
//We call the function that contains everything we want to display
}
while
( u8g.nextPage() );
//After it has displayed one page, go to the next one, until it shows all pages or the entire display
delay
(990);
//Wait a bit until the next display
}
void
draw() {
u8g.setFont(u8g_font_fixed_v0);
//Select which font you want to print out on the display (list of fonts: https://github.com/olikraus/u8glib/wiki/fontsize)
u8g.setFontPosTop();
//We want the reference position for printing the string to be in the upper left corner of that string
u8g.setPrintPos(0,0);
//X i Y coordinates where the string wants to print out on the display
u8g.print(
"e-radionica.com"
);
//Display of the desired string on the default position
u8g.setPrintPos(30,50);
//Changing coordinates for displaying the new string
u8g.print(
millis
()/1000);
//Printing of the new string on the display
}