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:
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:
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).
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.
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.
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.
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; } }
The timing diagram of read command is as below.
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); }
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.
//------------------------------------------------------------------------------------------------- // 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++; }
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/