KS0108 Generic Library for PIC18F4550

KS0108 Generic Library for PIC18F4550

Samsung KS0108/KS0107 is one of the most popular dot matrix LCD controllers available in the market. They are commonly seen in the graphics LCDs. In this article, we are going to use a universal library available for KS0108 based GLCDs. It supports displays with 128×64 and 192×64 resolutions and can be used with different microcontroller platforms including Microchip PIC microcontrollers.
In our example, we are using JHD12864 graphical LCD. It has a resolution of 128×64 which means that it can display 8192 pixels on the screen. In comparison with the character LCD, it can display anything on the screen provided by the user. Character LCD only displays the alphanumerical characters. The user can create images, fonts, and other structures and can display on them.

Please refer:

Library Structure

In this example, we are going to implement a custom library for KS0108 based graphics LCDs. The generic library can be downloaded from the link: KS0108 generic library download

Files included in the library are:

  • KS0108.h & KS0108.c – main library file and its header file
  • graphic.c & graphic.h – this contains graphic routines
  • font5x8.h – font definitions for common alphanumeric characters in 5×8 pixel size
  • KS0108-XXXXX.c&.h – Low level driver for different Microcontrollers

The library contains a low-level driver file for PIC16 family, but here we are going to implement it on the PIC18F device (PIC18F4550). So we need to add a separate file named KS0108-PIC18.c (or you may edit that one).

KS0108

This file has the main higher level functions used in our applications. It includes initializing, setting the location, clearing the screen, writing a single character and string, setting a pixel and displaying a bitmap image.

GLCD_Initialize()   - Initialize the graphic LCD
GLCD_GoTo(x, y)     - Set x and y position
GLCD_ClearScreen()  - Clear the screen content
GLCD_WriteChar(charToWrite)     - Write a single character
GLCD_WriteString(stringToWrite) - Write a string on the display
GLCD_SetPixel(x,y,color)        - Set pixel on (x,y) position
GLCD_Bitmap(bmp,x,y,dx,dy)      - Display the bmp array contents sized (dx,dy) in (x,y)

The header file (KS0108.h) also contains some global parameter including screen width, screen height, and commands for various commands. We need to change these values based on our display size. It’s better to refer the datasheet of your display because some displays have inverted pin configurations. But it is rare to have these type of settings changed, so we can go with the default settings.

Graphics and Fonts

The graphics file has three graphics related functions- draw rectangle, circle, and line.

GLCD_Rectangle(x,y,b,a)     - Draw rectangle with co-ordinates (x,y,a,b)
GLCD_Circle(cx,cy,radius)   - Draw a circle with radius in (cy, cy) co-ordinates
GLCD_Line(x1,y1,x2,y2)      - Draw a line between (x1,y1) and (x2,y2)

In font5x8, pixel details for all the 95 alphanumerical characters are defined.

KS0108-PIC18

Here, we are going to define the low level (hardware level) details in this file. It includes the pin interfacing details and low-level functions to enable, transfer data and commands, read status and data etc. We need to define different functions to interact PIC microcontroller with the GLCD. These files may not be present in the library as the PIC18F series, which we are using, has only a PIC16 based file. So we will be creating two files, one header file and the source file for this. Declare all the functions in a header file and define it on the source file.

We require timing diagrams for read and write instructions. Please refer the previous article KS0108 based Graphic LCD interfacing with PIC18F4550 – Part 2 for detailed information on implementation.

Initialization and Other Controls

graphic-lcd-interfacing-with-pic18f4550-circuit-diagram
GLCD interfacing with PIC18F4550

The pin level description is given in below section. We need to define data port, a control port, and their direction registers. The control port pin definition has to be done in the individual manner as their order will be different in different modules. In 128×64 resolution controllers, we don’t need the third KS0108 controllers. We keep it defined for avoiding errors, but we won’t be using it on circuits.

#define KS0108_DATA_PORT    PORTD
#define KS0108_DATA_DIR     TRISD
#define KS0108_DATA_PIN     PORTD

#define KS0108_CTRL_PORT    PORTA               // define port for control port
#define KS0108_CTRL_DIR     TRISA               // define control port direction register

#define KS0108_RS            (1 << 2)           // define RS pin
#define KS0108_RW            (1 << 1)           // define RW pin
#define KS0108_EN            (1 << 0)           // define EN pin

#define KS0108_CS2           (1 << 3)           // define CS2
#define KS0108_CS1           (1 << 4)           // define CS1
#define KS0108_CS3           (1 << 6)           // We only have 2 controllers, it left disconneted
#define KS0108_RST           (1 << 5)           // define RST pin

extern unsigned char screen_x, screen_y;
//-------------------------------------------------------------------------------------------------
// Delay function
//-------------------------------------------------------------------------------------------------
void GLCD_Delay(void)
{
    Nop();
}
//-------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------
void GLCD_InitializePorts(void)
{
    KS0108_CTRL_PORT |= (KS0108_CS1 | KS0108_CS2 | KS0108_CS3);
    KS0108_CTRL_DIR &= ~(KS0108_CS1 | KS0108_CS2 | KS0108_CS3 | KS0108_RS | KS0108_RW | KS0108_EN);
}
//-------------------------------------------------------------------------------------------------
// Enable Controller (0-2)
//-------------------------------------------------------------------------------------------------
void GLCD_EnableController(unsigned char controller)
{
    switch(controller){
        case 0 : KS0108_CTRL_PORT &= ~KS0108_CS1; break;
        case 1 : KS0108_CTRL_PORT &= ~KS0108_CS2; break;
        case 2 : KS0108_CTRL_PORT &= ~KS0108_CS3; break;
        }
}
//-------------------------------------------------------------------------------------------------
// Disable Controller (0-2)
//-------------------------------------------------------------------------------------------------
void GLCD_DisableController(unsigned char controller)
{
    switch(controller)
            {
        case 0 : KS0108_CTRL_PORT |= KS0108_CS1; break;
        case 1 : KS0108_CTRL_PORT |= KS0108_CS2; break;
        case 2 : KS0108_CTRL_PORT |= KS0108_CS3; break;
        }
}

Read Data/Status

The timing diagram of read command is as below.

graphic-lcd-read-timing-diagram

Using the timing diagram, we can write the functions for reading status and reading data as follows.

//-------------------------------------------------------------------------------------------------
// Read Status from specified controller (0-2)
//-------------------------------------------------------------------------------------------------
unsigned char GLCD_ReadStatus(unsigned char controller)
{
    unsigned char status;
    KS0108_DATA_DIR = 0xFF;
    KS0108_CTRL_PORT |= KS0108_RW;
    KS0108_CTRL_PORT &= ~KS0108_RS;
    GLCD_EnableController(controller);
    KS0108_CTRL_PORT |= KS0108_EN;
    GLCD_Delay();
    status = KS0108_DATA_PIN;
    KS0108_CTRL_PORT &= ~KS0108_EN;
    GLCD_DisableController(controller);
    return status;
}

//-------------------------------------------------------------------------------------------------
// Read data from current position
//-------------------------------------------------------------------------------------------------
unsigned char GLCD_ReadData(void)
{
    unsigned char data;
    while(GLCD_ReadStatus(screen_x / 64)&DISPLAY_STATUS_BUSY);
    KS0108_DATA_DIR = 0xFF;
    KS0108_CTRL_PORT |= (KS0108_RW | KS0108_RS);
    GLCD_EnableController(screen_x / 64);
    //GLCD_Delay();
    KS0108_CTRL_PORT |= KS0108_EN;
    GLCD_Delay();
    data = KS0108_DATA_PIN;
    KS0108_CTRL_PORT &= ~KS0108_EN;
    GLCD_DisableController(screen_x / 64);
    screen_x++;
    return data;
}

//-------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------
unsigned char GLCD_ReadByteFromROMMemory(char * ptr)
{
  return *(ptr);
}

Write Data/command to the GLCD

During the write function, R/W becomes low as given in the timing diagram below. The D/I decides whether it is an instruction or data.

graphic-lcd-write-timing-diagram

//-------------------------------------------------------------------------------------------------
// Write command to specified controller
//-------------------------------------------------------------------------------------------------
void GLCD_WriteCommand(unsigned char commandToWrite, unsigned char controller)
{
    while(GLCD_ReadStatus(controller)&DISPLAY_STATUS_BUSY);
    KS0108_DATA_DIR = 0x00;
    KS0108_CTRL_PORT &= ~(KS0108_RW | KS0108_RS);
    GLCD_EnableController(controller);
    KS0108_DATA_PORT = commandToWrite;
    KS0108_CTRL_PORT |= KS0108_EN;
    GLCD_Delay();
    KS0108_CTRL_PORT &= ~KS0108_EN;
    GLCD_DisableController(controller);
}

//-------------------------------------------------------------------------------------------------
// Write data to current position
//-------------------------------------------------------------------------------------------------
void GLCD_WriteData(unsigned char dataToWrite)
{
    while(GLCD_ReadStatus(screen_x / 64)&DISPLAY_STATUS_BUSY);
    KS0108_DATA_DIR = 0x00;
    KS0108_CTRL_PORT &= ~KS0108_RW;
    KS0108_CTRL_PORT |= KS0108_RS;
    KS0108_DATA_PORT = dataToWrite;
    GLCD_EnableController(screen_x / 64);
    KS0108_CTRL_PORT |= KS0108_EN;
    GLCD_Delay();
    KS0108_CTRL_PORT &= ~KS0108_EN;
    GLCD_DisableController(screen_x / 64);
    screen_x++;
}

The Main Function

Using the above functions, we can write an example code for displaying a string, drawing a circle and drawing a rectangle (in the main file).

#include <xc.h>
#include "config.h"
#include "KS0108.h"
#include "graphic.h"
#include "delay.h"              // header file with delay functions
#include "KS0108-PIC18.h"
#include "font5x8.h"
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 20000000
#endif

void main (void)
    {
        CMCON |= 7;             // disable comparators
        ADCON1=0x0F;            // disable analog inputs
        GLCD_Initialize();      // Initialize GLCd
        GLCD_ClearScreen();     // Clear screen
    while(1)
        {
        GLCD_WriteString("ETIQ TECHNOLOGIES");  // Write text
        Delay_s(2);             // 2 second delay
        GLCD_Circle(64, 32, 28);                // Draw a circle
        Delay_s(2);
        GLCD_ClearScreen(); 
        GLCD_Rectangle(30, 12, 70, 40);          // Draw a rectangle
        Delay_s(2);
        GLCD_ClearScreen();
        }
}

The above example is available at our Github repository: https://github.com/etiq/OpenLab-GLCD-example/