Interfacing an Arduino with LCDs

Introduction

This is the second article in a three part series I am writing on the Arduino hardware platform. This article focuses on wiring an Arduino to a character LCD and a graphic LCD, and includes static and animated demos to show off the capabilities of each display.

Character and Graphic LCDs

After I initially discovered the Arduino platform, I immediately noticed a wide variety of components that can be connected to an Arduino – everything from inexpensive LEDs, to a moderately priced Ethernet shield, to an outrageous and over-the-top tank shield (which sells for nearly $200!). While shopping for my Arduino, I noticed that LCDs were fairly inexpensive so I purchased a $10 16×2 character LCD, and an $18 128×64 Graphic LCD.

Character LCDs and graphic LCDs are completely different devices and require different libraries and APIs to drive them. Fortunately, both devices are supported by the Arduino community. For my character LCD, I used the LiquidCrystal Library, and for my graphic LCD, I used the KS0108 Graphics LCD library.Interfacing an Arduino with LCDs

Character LCD

Connecting a character LCD and programming it was a breeze and I didn’t run into any problems. I simply followed the instructions and wiring diagram in the Arduino Character LCD Tutorial and everything worked as expected. After running the LCD_example sample sketch, I wrote a sketch to take advantage of my character LCD called HelloCodeProject:

Hide   Shrink   Copy Code
/*
    HelloCodeProject.cpp, based off of LCD_example from
    http://www.hacktronics.com/Tutorials/arduino-character-lcd-tutorial.html
*/

#include <LiquidCrystal.h>

const int BACK_LIGHT = 13;          // Pin 13 will control the backlight

// Connections:
// RS (LCD pin 4) to Arduino pin 12
// RW (LCD pin 5) to Arduino pin 11
// Enable (LCD pin 6) to Arduino pin 10
// LCD pin 15 to Arduino pin 13
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
LiquidCrystal g_lcd(12, 11, 10, 5, 4, 3, 2);

void setup()
{
    pinMode(BACK_LIGHT, OUTPUT);
    digitalWrite(BACK_LIGHT, HIGH);     // Turn backlight on. 
                // Replace 'HIGH' with 'LOW' to turn it off.
    g_lcd.clear();                      // Start with a blank screen
    g_lcd.setCursor(0, 0);              // Set the cursor to the beginning
    g_lcd.print("Hello,");
    g_lcd.setCursor(0, 1);              // Set the cursor to the next row
    g_lcd.print("CodeProject");
}

void loop()
{
}

 TickerTape

The second sketch I wrote was TickerTape, which simulates a ticker tape message scrolling across the display. Since TickerTape was the first sketch that I wrote which used an algorithm (the algorithm uses a single buffer for the message, an int to keep track of the starting point of the message to display, and takes into consideration when the message ‘wraps’ around the end of the buffer), I decided to code the algorithm in a native Win32 C/C++ application first. Since the Arduino has only limited debugging capabilities, I felt that coding the algorithm first in an environment which supports line level debugging and breakpoints would be faster than trying to debug within the Arduino using a bunch of Serial.println() statements. After I verified that the algorithm worked, I coded up the TickerTape sketch:

/*
    TickerTape.cpp, based off of LCD_example from
    http://www.hacktronics.com/Tutorials/arduino-character-lcd-tutorial.html
*/

#include <LiquidCrystal.h>

const int BACK_LIGHT = 13;          // Pin 13 will control the backlight
const char* MESSAGE = "Example 2: Hello, CodeProject. ";
const int MESSAGE_LENGTH = 31;
const int DISPLAY_WIDTH = 16;

// Connections:
// RS (LCD pin 4) to Arduino pin 12
// RW (LCD pin 5) to Arduino pin 11
// Enable (LCD pin 6) to Arduino pin 10
// LCD pin 15 to Arduino pin 13
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
LiquidCrystal g_lcd(12, 11, 10, 5, 4, 3, 2);

void setup()
{
    pinMode(BACK_LIGHT, OUTPUT);
    digitalWrite(BACK_LIGHT, HIGH);     // Turn backlight on. 
                        // Replace 'HIGH' with 'LOW' to turn it off.
    g_lcd.clear();                      // Start with a blank screen
    Serial.begin(9600);
}

void loop()
{
    static int s_nPosition = 0;
    int i;
    if(s_nPosition < (MESSAGE_LENGTH - DISPLAY_WIDTH))
    {
        for(i=0; i<DISPLAY_WIDTH; i++)
        {
            g_lcd.setCursor(i, 0);
            g_lcd.print(MESSAGE[s_nPosition + i]);
        }
    }
    else
    {
        int nChars = MESSAGE_LENGTH - s_nPosition;
        for(i=0; i<nChars; i++)
        {
            g_lcd.setCursor(i, 0);
            g_lcd.print(MESSAGE[s_nPosition + i]);
        }

        for(i=0; i<(DISPLAY_WIDTH - nChars); i++)
        {
            g_lcd.setCursor(nChars + i, 0);
            g_lcd.print(MESSAGE[i]);
        }
    }

    s_nPosition++;
    if(s_nPosition >= MESSAGE_LENGTH)
    {
        s_nPosition = 0;
    }

    delay(500);
}

My second attempt compressed the image by using run length encoding. Since much of the image’s pixels repeat, I thought this might be an efficient way to reduce the data needed for the image. The format of each line was:

Hide   Copy Code
    [1,0,-42], run1, run2, ..., runN, -1

The first int is a marker. 1 indicates the first run of the line is black, 0 indicates the first run of the line is white, and -42 indicates the array is finished. The second int indicates the run length and toggles back and forth between black and white until -1 is found, which indicates the end of the row. As an example, if there was:

Hide   Copy Code
    1, 64, 32, 32, -1

…64 black pixels would be drawn, then 32 white pixels, then 32 black pixels.

This worked better as the sketch size was now small enough to upload to the Arduino, however the array was too large for the Arduino’s stack. When I included the entire array, the Arduino crashed and it kept restarting itself over and over, and I found that if I included only 1/4 of the total array it worked fine, so the RLE approach didn’t work either.Interfacing an Arduino with LCDs Schematic

After doing some investigation, I discovered that large amounts of data need to be stored in flash memory using the PROGMEM keyword. Using PROGMEM, the raw array is declared as:

Hide   Copy Code
    static prog_uchar bits[] PROGMEM = {

Data is then read from flash memory via pgm_read_byte_near():

Hide   Copy Code
    char val = pgm_read_byte_near(bits + (i + (128 * j)));

…and then drawn one pixel at a time via GLCD.SetDot().

In addition to discovering that I needed to store the data in flash memory, I also learned that the beta version of the KS0108 library also included a new function, DrawBitmap(). The second version of CodeProjectLogo (just comment out the #define USE_SET_DOT statement) declares the array as:

Hide   Copy Code
static uint8_t newBitmap[] PROGMEM = {

…and draws the bitmap via GLCD.DrawBitmap()

 

For more detail: Interfacing an Arduino with LCDs


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

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

Scroll to Top