Mike Cochrane

MindKits 128x64 Graphic LCD and Arduino

MindKits 128x64 Graphic LCD and Arduino
Skip the story, just give me the code.

A while ago I purchased a 128x64 Graphic LCD from MindKits with the intention of connecting it to an Arduino.

Recently I got around to connected it up in serial mode as per the example code:

LCD  Arduino
PIN1 = GND
PIN2 = 5V
RS(CS) = 8; 
RW(SID)= 9; 
EN(CLK) = 3;
PIN15 PSB = GND;

And the example code worked fine. One "problem" was the size of the characters, there were huge. So this large screen was not going to allow me to display a large amount of information, instead it would allow a small amount of information to be displayed in large text.

Graphics Mode

These LCDs have a graphics and a text mode, so I decided to use it in graphics mode and draw the text manually. This would only be practical on an Arduino Mega (or compatible) as I would need 1KB (128x64/8 = 1024 bytes) of memory for a screen buffer, plus memory for the characters.

a

Next problem was to create a font to display on the screen and converting this to an array of bytes:

unsigned int fontPixel[2][10] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Space 
    { 0x00, 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00, 0x00 }  // A (0x41)
}

And create a lookup array to convert an ascii char to its position in the fontPixel array:

unsigned int fontLookup[256] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, };

Now a function to place a character at a specific place in the screen buffer. The font data was 5 bits wide so some characters would be split across the 8 bit bytes of the screen buffer. There is some bit shifting going on to align the characters correctly:


unsigned char screenBuffer[1024];

void LCDPlaceCharacter(unsigned int row, unsigned int col, char character)
{
    if (fontLookup[(int)character] == 0 && (int)character != ' ') {
        /* No character in font */
        return;
    }
    
    /* What bit in the start character does this start? */
    int startBit = (col * 6) % 8;        

    /* Add all 10 character rows */
    for (int i = 0; i < 10; i++) {
        /* Pixel are in the lower 5 bits */
        int pixels = fontPixel[fontLookup[(int)character]][i];
        
        /* Work out what byte this row starts in  */
        int startByte = (i + (row * 10) + 1) * 16;
        if (startBit <= 3) {
            /* All within one byte */
            int bits = pixels << 3 - startBit;
            int mask = 0x1F << 3 - startBit;
            screenBuffer[startByte + ((col * 6) / 8)] &= ~mask; // Clear space
            screenBuffer[startByte + ((col * 6) / 8)] |= bits; // Add character
        } else {
            /* Accross two bytes */
            int bits = pixels >> startBit - 3;
            int mask = 0x1F >> startBit - 3;
            screenBuffer[startByte + ((col * 6) / 8)] &= ~mask; // Clear space
            screenBuffer[startByte + ((col * 6) / 8)] |= bits; // Add character
            
            bits = pixels << (11 - startBit);
            mask = 0x1F << (11 - startBit);
            screenBuffer[startByte + ((col * 6) / 8) + 1] &= ~mask; // Clear space
            screenBuffer[startByte + ((col * 6) / 8) + 1] |= bits; // Add character
        }
    }
}

Displaying a character

Now I could draw a character of my own design where I wanted it by simply:


  LCDPlaceCharacter(0, 0, 'A');
  LCD12864RSPI::DrawFullScreen(screenBuffer);

This worked, but the drawFullScreen was very slow in serial mode as it sent the whole screen buffer and blanked the screen causing the display to flash every redraw. So I created a modified version of drawFullScreen to redraw just the 10 pixel rows that had changed without blanking the screen.

LCD12864RSPI.h void DrawScreenRow(uchar *p, int rowStart); LCD12864RSPI.cpp void LCD12864RSPI::DrawScreenRow(uchar *p, int rowStart = 0) { int row, x, y; int temp; int tmp; for (row = rowStart; row < rowStart + 10; row++) { if (row < 32) { x = 0x80; y = row + 0x80; } else { x = 0x88; y = row - 32 + 0x80; } WriteCommand(y); WriteCommand(x); tmp = row * 16; for (int i = 0; i < 16; i++) { temp = p[tmp++]; WriteData(temp); } } }

Now I had a usable refresh rate.

Final code

After creating a few helper functions, defining more characters, and reducing the font height to 9 pixels, I had what I needed to display text as shown in the photo above. I now had a 21 character by 7 line display.


#include "LCD12864RSPI.h"
#include "font.h";
#define FONT_HEIGHT 9
unsigned char screenBuffer[1024];

void setup() {
    /* Setup the LCD */
    LCDA.Initialise();
    LCDClearScreen();
    
    /* Display string */
    LCDPrintString(0, 0, "Demo", true);
    
    /* Update parts of line then update display */
    LCDPrintString(2, 0, "Left", false);
    LCDPrintString(2, 17, "Right", true);
    
    /* Display negative coloured string */
    LCDPrintNegString(5, 0, " Negative String ", true);
}

void loop() {
  
}

Get the code

Download the code here: LCDDemo.zip. It also includes 'font.in' which is an ascii drawing of the characters, and 'font.php' that converts font.in to font.h (php font.php > font.h).

I'm not sure of the license on the original code, I guess it is public domain. All my new code is licensed under a Creative Commons Attribution 3.0 New Zealand License, just leave my name in the code.

If you want improve this code, feel free. If possible, please make those improvements available to others. There's no github or similar repository for this but I'm happy to create one if there's interest in forking it.

Tags: arduinolcdst7920