This is the code I wrote to drive a 128x64 pixel graphic LCD module based on the KS0107 and KS0108 controllers.
The biggest problem with interfacing this module is the terrible datasheet quality- the timing sequence diagrams are very ambiguous, to say the least. You'll likely want to change the duration of the delay in the _lcd_enable() function to match your chip.
The code is for 18F452 @ 22.118 MHz, and written in Microchip C18. I don't think it'd be too hard to port to other compilers, but YMMV.
I've reluctantly removed the font data, since I do not own it, and cannot freely distribute it. There are many starts for fonts available on this site here:-http://piclist.com/techref/datafile/charsets.htm .
The functions that start _lcd* are intended to be for internal use by the library, whereas lcd* are for your use. Of course, that doesn't limit you in what you do at all.
glcd.h
#include
<p18cxxx.h>
#include <delays.h> #define LCD_TRIS TRISB
#define LCD_DATA PORTB
#define ENABLE PORTDbits.RD7
#define RW PORTDbits.RD6
#define DI PORTDbits.RD5
#define RESET PORTDbits.RD4#define
CS1 PORTDbits.RD2#define
CS2 PORTDbits.RD3
#define LEFT 0b01
#define
RIGHT 0b10#define
BOTH 0b11#define
NONE 0b00// internal function prototypes
//
// you would not normally call these directly. but you can
// if you like.
void
_lcd_enable(void);unsigned
char _lcd_status(void);void
_lcd_reset(void);void
_lcd_waitbusy(void);// public function prototypes
//
// call these all you want !
void
lcd_screenon(unsigned char on);void
lcd_cls(void);void
lcd_setpage(unsigned char page);void
lcd_setyaddr(unsigned char y);void
lcd_selectside(unsigned char sides);void
lcd_write (unsigned char data);unsigned
char lcd_read (void);void
lcd_plotpixel(unsigned char rx, unsigned char ry);void
lcd_putchar(char c);void
lcd_puts(char *string);void
lcd_putrs(const rom char *string);
And now glcd.c :-
#include
<p18cxxx.h>#include
"glcd.h"const
rom char font[]= {/* add your own font data
each char is 3x8 pixels. 3 bytes per char. left column, middle column, right column
*/
};
void
_lcd_enable(void){
Delay10TCYx(1);
ENABLE=1;
Delay10TCYx(1);
ENABLE=0;
Delay10TCYx(1);
}
unsigned
char _lcd_status(void){
// returns the lcd status & maintains the TRIS state of the // lcd data port unsigned char _lcd_tris, _status; // save the tris value_lcd_tris = LCD_TRIS;
// read the status LCD_TRIS=0xFF; // all inputsDI=0; RW=1;
// command/read_lcd_enable();
_status = LCD_DATA;
// restore the tris value LCD_TRIS = _lcd_tris;return _status;
}
void
_lcd_reset(void){
// reset the lcd module // datasheet says reset must be low for minimum of 1us // after Vdd clears 4.5v. // from experimentation, this is bullshit. this seems to // work though.Delay10TCYx(250);
// actually .5 msRESET=1;
Delay10TCYx(250); // actually .5 msRESET=0;
// check status, and wait if necessary while (_lcd_status() & 0b00010000){
Delay10TCYx(250); // .5 ms}
}
void
lcd_screenon(unsigned char on){
// turn the display on or offLCD_TRIS=0;
// all outputsRW=0; DI=0;
LATB = 0b00111110 | (on & 0b00000001);
// main screen turn on! _lcd_enable();}
void
lcd_cls(void){
unsigned char x,y; for (x=0; x<8; x++){
// set the page (x)lcd_setpage(x);
// set the y address to 0lcd_setyaddr(0);
// setup for dataLCD_DATA=0; RW=0; DI=1;
// clear the row for (y=0; y<64; y++){
_lcd_enable();
//Delay10TCYx(1);
}
}
}
void
lcd_setpage(unsigned char page){
_lcd_waitbusy();
DI=0; RW=0;
LCD_DATA = 0b10111000 | page;
_lcd_enable();
}
void
lcd_setyaddr(unsigned char y){
_lcd_waitbusy();DI=0; RW=0;
LCD_DATA = 0b01000000 | (y & 0b00111111);
_lcd_enable();
}
void
_lcd_waitbusy(void){
while (_lcd_status() & 0b10000000){
Delay10TCYx(250);
// .5 ms}
}
void
lcd_write (unsigned char data){
_lcd_waitbusy();
DI=1; RW=0;
LCD_TRIS=0;
LCD_DATA = data;
_lcd_enable();
}
void
lcd_selectside(unsigned char sides){
// set a CS pin low to enable it if (sides & LEFT)CS1 = 0;
elseCS1 = 1;
if (sides & RIGHT)CS2 = 0;
elseCS2 = 1;
}
unsigned
char lcd_read (void){
unsigned char _data;
LCD_TRIS=0xFF;
RW = 1; DI=1;
_lcd_enable();
_data = LCD_DATA;
LCD_TRIS=0x00;
return _data;}
void
lcd_plotpixel(unsigned char rx, unsigned char ry){
unsigned char data; //lcd_waitbusy(); // select the correct side if (rx & 64)lcd_selectside(RIGHT);
elselcd_selectside(LEFT);
lcd_setpage( ry >> 3);
lcd_setyaddr( rx & 0b00111111);data = lcd_read(); // dummy read needed here
data = lcd_read();
lcd_setyaddr( rx & 0b00111111);
lcd_write (data | (1 << (ry & 0b111)));
}
void
lcd_putchar(char c){
int base;base = c - 32;
base *= 3;
lcd_write(font[base]);
lcd_write(font[base + 1]);
lcd_write(font[base + 2]);
lcd_write(0);
}
void
lcd_putrs(const rom char *string){
char i=0; while (string[i] != 0)lcd_putchar(string[i++]);
}
void
lcd_puts(char *string){
char i=0; while (string[i] != 0)lcd_putchar(string[i++]);
}
Comments:
Questions:
Dear all,
I've got a simple question: The functin "Delay10TCYx", where is it defined? Is it a 'standard C-function, likt 'printf'... or is it defined in some pic18f.. header file?
Thanks and regards
Uwe
Hi,
thank you for this piece of code. I'm trying to use it, but I have some difficulities. At beginning of code I set all pins as outputs and then call function lcd_cls(), but then PIC "freezes" :-(. In this function lcd_setpage(x) is ended properly (include _lcd_wait_busy), but in function lcd_setyaddr(0), where _lcd_wait_busy is called again, it freezes in _lcd_wait_busy on that while loop. I have tried increase that time wait before checking status again, but still same problem. I'm using PIC18F452 on 4MHz. Connection of LCD is OK (tested and work well with MikroC development studio). Or have I to call another function before cls? Now my order is:
lcd_cls();
lcd_plotpixel (1,1);
Thank you very much! Michal