Graphics LCD Interfacing With 8051

It may require some graphics image to be displayed in 8051 based products through a monochrome bitmap LCD. This is considered to be a complex task because of its bus interfacing and several command sequence handling. This article explains how a graphics LCD from TIANMA can be connected to a 8051 micro-controller and pixels can be displayed on it with simple steps.

Theory

Consider interfacing the TIANMA TM12864J1CCWGWA LCD 128x64 pixels module based on the Sitronix ST7567 lcd controller with NXP’s P89V664 8051 micro-controller. The LCD module provides SPI interface to host(micro-controller) communication. We can use GPIO pins of micro-controller to interface with LCD module instead of SPI pins of micro-controller for convenience. We may need to bitbang the data through gpio lines to get the SPI communication over gpio.

/static/images/lcd-interfacing.png
Figure 1. LCD interfacing

Description

The LCD module has a internal memory and a control circuitary, to map the pixel information from the internal memory to LCD display. The internal memory and control circuitary can be accessed by the micro-controller through the SPI bus. The communication to LCD module from the micro-controller are classified as two type of transaction modes as described below.

Control Mode

This supports sending commands to intialize and configure the LCD parameters.

Data Mode

This supports sending the pixel information to be displayed in the LCD

Control Commands

The LCD module need to be configured and initialized with a set of commands by the micro-controller. These commands has to be sent to the LCD module one by one in sequence. The sequence can be found in the data sheet of LCD module.

The mandatory control commands to initialize the LCD are listed below.

START_LINE

to set the start line of the display

INVERSE

to inverse 1’s to 0’s and 0’s to 1’s pixel buffer

COM

to change the pixel orientation from left to right or right to left

SEG

to change the pixel orientation from top to bottom or bottom to top

PWR_CTRL

to enable internal power controllers

RR & EV

to set the display brightness

DISP_ON

to enable display

Pixel Data

The pixel data should be sent through the bus by choosing the data mode. Before sending a pixel data, we may need to select the exact page and the column where to display the data sent. LCD pixels are organized, such a way that 8 rows are combined as a page, thus forming 8 pages as the LCD supports 64 rows. A 8 bit pixel data sent to LCD module would be displayed in single vertical column, as one pixel in each row of the selected page. The page mapping and byte mapping information can be understood from the below diagrams.

GLCD Page Mapping
Figure 2. Page Mapping
GLCD Pixel Mapping
Figure 3. Byte to Pixel mapping

The commands to select the page and column are shown below

PAGE_SEL

to select one out of 8 pages

COL_SEL_L/H

to select one out of 128 columns

The steps to communicate with the LCD module is described below.

Sequence to intialize a graphics LCD
  • Reset the LCD controller

  • Intialize the power control registers

  • Choose the page, column and orientations

  • Switch the display on

Sequence to write command to a LCD
  • Assert chip select

  • Deassert the A0 line to choose command mode

  • Write command through gpio line

  • Deassert the chip select

Sequence to write data to a LCD
  • Assert chip select

  • Assert the A0 line to choose data mode

  • Write data through the gpio line

  • Deassert the chip select

Sequence to write pixel data to LCD
  • Assert chip select

  • Select page with page selection command

  • Select the columm with column selection command

  • Write the data through the bus

  • Deassert the chip select

To have complete display manipulation a buffer has to be maintained in software, if the micro-controller can support a memory of 1 Kile Byte for LCD code, then this can be achieved.

Hardware

It requires five gpio pins to be connected to the LCD module from the micro-controller.

CS

Chip select/Enable to the LCD module

CLK

clock line to the LCD module

DATA

data line to the LCD module

A0

control line to choose between command and data mode

RST

To reset the micro-controller

We have shown a connection interface in the below diagram.

LCD Hardware Circuit
Figure 4. LCD Hardware Circuit

Software

The code for displaying a pixel on the GLCD is given below.

This code has been tested with below tools

  • SDCC

  • P89v664 Micro-controller board

  • LCD module and interface board

NXP’s P89V664 micro-controller has 2 KB of RAM. To enable 2 KB memory, the micro-controller has to be configured for Large Memory Model. You can find the details for configuring Large Memory Model in External Memory HowTo

Source code can be compiled as shown below using SDCC

sdcc --model-large -DCONFIG_LARGE_MEMORY glcd-hw.c
#include <8052.h>

__sfr __at (0xA1) P4;

#define RST      P2_2
#define LCD_RS   P2_1
#define LCD_CS   P2_3
#define LCD_SCL  1
#define LCD_SDA  4
#define PAGE_SEL 0xB0
#define COL_SEL  0x10

#ifdef CONFIG_LARGE_MEMORY
static unsigned char gfx_buf[8][128];

int _sdcc_external_startup()
{
        __sfr __at (0x8E) AUXR;
        AUXR = 0x0;

        return 0;
}
#endif

void delay(int x)
{
        int n,j;
        for(n = 0; n < x; n++)
                for(j = 0; j <= 50; j++);

}

void glcd_write_data(unsigned char dat)
{
        unsigned char maskdata = 0x80;

        LCD_CS = 0;
        LCD_RS = 1;

        while (maskdata) {
                P4 = P4 & ~LCD_SCL;

                if (dat & maskdata)
                        P4 = P4 | LCD_SDA;
                else
                        P4 = P4 & ~LCD_SDA;

                maskdata >>= 1;
                delay(200);
                P4 = P4 | LCD_SCL;
        }

        LCD_CS = 1;

}

void glcd_write_cmd(unsigned char cmd)
{
        unsigned char maskdata = 0x80;

        LCD_RS = 0;
        LCD_CS = 0;

        while (maskdata) {

                P4 = P4 &  ~LCD_SCL;

                if (cmd & maskdata)
                        P4 = P4 | LCD_SDA;
                else
                        P4 = P4 & ~LCD_SDA;

                maskdata >>= 1;
                delay(2);
                P4 = P4 | LCD_SCL ;

        }
        LCD_CS = 1;
}

void glcd_init(void)
{
        RST = 0;
        delay(1000);
        RST = 1;

        glcd_write_cmd(0xE2);            // S/W RESWT
        glcd_write_cmd(0xA3);            // LCD bias
        glcd_write_cmd(0xAF);            // Display ON
        glcd_write_cmd(0xA0);            // segment direction.
        glcd_write_cmd(0xC8);            // Common Direction.
        glcd_write_cmd(0x22);            // Regultion resistor select  //25
        glcd_write_cmd(0x81);            // EV Select.
        glcd_write_cmd(0x2f);            // Select EV value.
        glcd_write_cmd(0x2f);            // Power control


        glcd_write_cmd(0x40);            // Initial display line 40
        glcd_write_cmd(0xB0);            // Set page address
        glcd_write_cmd(0x10);            // Set coloumn addr  MSB
        glcd_write_cmd(0x00);            // Set coloumn addr LSB
        glcd_write_cmd(0xAF);            // Display ON
        glcd_write_cmd(0xA4);            // A5 .Normal display, all pixels OFF.
        glcd_write_cmd(0xA6);            // A7 .Normal display (Inverse Pixel)
}

void glcd_set_pixel(int x, int y, int color)
{
        int page,col;
        int row_in_page;

        page = y / 8;

        /* Selecting Page */
        glcd_write_cmd(PAGE_SEL | page);

        /* Selecting Column */
        col = ((x & 0xF0) >> 4) | 0x10;
        glcd_write_cmd(COL_SEL|col);
        glcd_write_cmd(x & 0xf);

        /* Pixel location */
        row_in_page = y % 8;

#ifdef CONFIG_LARGE_MEMORY
        if (color)
                gfx_buf[page][col] |= 1 << row_in_page;
        else
                gfx_buf[page][col] &= ~(1 << row_in_page);

        glcd_write_data(gfx_buf[page][col]);
#else
        if (color)
                glcd_write_data(1 << row_in_page);
#endif

}

int main()
{
        int i = 0, j = 0;

        glcd_init();

        for (i = 0; i < 128; i++)
                for (j = 0; j < 64; j++)
                        glcd_set_pixel(i, j, 1);
        return 0;
}