/*
***********************************************************************************************
*					JHD12864G/J Graphical LCD Display Driver (KS0107/8 controller)
*
* File name		: ks0108.c
* Programmer 	: John Leung, TechToys. Hong Kong
* Web presence  : www.TechToys.com.hk
* Note			: 
* Language		: Keil C 7.09 2K limited version in uVision2 IDE version 2.40
* Hardware		: AT89S52-gLCD-STK1 demo board
* Date			: Version 1.0 (8th May 2006) - (4th July 2006)
***********************************************************************************************
*										DESCRIPTION
*
* This module provides an interface to JHD12864J Graphical LCD module of size 128x64 dots
* Display controller chip is KS0107/8
* LCD pinout function summarized as below
* --------- LCD		MCU -----------------
* pin 1		Vss 	- 0V
* pin 2 	Vdd 	- 5.0V
* pin 3		Vo  	- LCD Drive Voltage (adjust contrast, trimmer to Vee in our case)
* pin 4		D/I		- LCD_DI (H:Data, L:Command (data or command control))
* pin 5		R/W 	- LCD_RW (H:Read, L:Write (read status or write data/command))
* pin 6		E   	- LCD_EN (H/H->L (Strobe signal, H->L effective))
* pin 7:14	DB0:DB7	- LCD_DATA
* pin 15	CS1		- LCD_CS1 (left, high effective)
* pin 16	CS2		- LCD_CS2 (right, high effective)
* pin 17	RST		- LCD_RST (reset pin, low effective)
* pin 18	Vee		- -10.0V, LCD driving voltage. There is a Vee generator built-in LCD
*					  module, thus there is no need to use external generator IC
* pin 19	EL1 / A - LCD_BL (Depends on the backlight option.
*						If it's a LED backlight, it's an anode.
*					  	If it is EL type, this is EL controller input)
* pin 20	EL2 / K	- GND (Cathode for LED-backlight. EL input (OR NC) for EL-backlight model)
***********************************************************************************************
*/
#include <regx52.h>					
#include <intrins.h>				
#include "ks0108.h"		
#if PIXEL_FONT_EN										
#include "sysfont.h"
#endif		

/*
*********************************************************************************************************
*											LOCAL DEFINITIONS
*********************************************************************************************************
*/

#define LCD_STAT_RST		0x10													
#define LCD_STAT_BUSY		0x80
#define LCD_STAT_OFF		0x20
#define DISPLAY_ON			0x3F
#define DISPLAY_OFF     	0x3E
#define DISPLAY_START_LINE	0xC0
#define PAGE_OFFSET			0xB8
#define Y_OFFSET			0x40
#define STATUS_RD			0
#define DATA_RD				1
#define CMD					0
#define DAT					1
#define HIGH				1
#define LOW					0
#define RIGHT				0
#define LEFT				1
#define BOTH				2
#define NONE				3

/*
*********************************************************************************************************
*                         				DISPLAY INITIALIZATION
*
* Description : This function initializes the LCD module by the following steps:
*					1. Enable the backlight
*					2. Reset the LCD module
*					3. Disable the LCD
*					4. Set display start line at the top of the screen
*					5. clear the display by writing 0 to the whole screen
*					6. re-enable the display		
* Arguments   : none
*			
* Returns     : none
* Note		  : This function should be called once before any of the other functions
*********************************************************************************************************
*/
void GDispInit(void)
{
	GDispBackLite(ENABLE);
	GDispReset();
	GDispSwitch(DISABLE);
	GDispWr(CMD,DISPLAY_START_LINE);//set display start line at the top of the LCD
	GDispClr(0x00);					//clear the display
	GDispSwitch(ENABLE);
}


/*
*********************************************************************************************************
*                         				DISPLAY ON/OFF SWITCH
*
* Description : This function turns display ON/OFF by writing the command DISPLAY_ON / DISPLAY_OFF
* Arguments   : (bit) 'sw' 		ENABLE for turning ON defined under ks0108.h
*								DISABLE for turning OFF defined under ks0108.h
*				
* Returns     : none
* Notes		  : Internal status and display RAM data not changed by switching between ON and OFF
*				Data type 'bit' is Keil C specific
*********************************************************************************************************
*/
void GDispSwitch(bit sw)
{
	GDispChipSel(BOTH);
	(sw==ENABLE)? (GDispWr(CMD,DISPLAY_ON)):(GDispWr(CMD,DISPLAY_OFF));
	GDispChipSel(NONE);
}

/*
*********************************************************************************************************
*                         		WRITE A SINGLE BYTE AT A COLUMN# AND PAGE#
*
* Description : This function writes a single byte 'c' to a column position defined by 'col' and 
*				to PAGE# indicated by the variable 'page'
* Arguments   : 'col'		the column position (0-127) of the LCD, 0<=col<128 (MAX_COL_PIXEL in ks0108.h)
*				'page'		the PAGE number (0-7) of the LCD. It is NOT the y coordinate as our usual 
*							Cartesian coordinate system. 
*							It is the PAGE number defined in JHD12864J data sheet
*				'c'			byte to write
* Returns     : none
* Notes		  : This function should has been declared as a local function because it is rarely
*				called in main. However, if we want to make a different version of string display with
*				higher efficiency, we may make use of this function to write byte in vertical manner.
*********************************************************************************************************
*/
void GDispWrByte(unsigned char col, unsigned char page, unsigned char c)
{
	col 	= col%MAX_COL_PIXEL;
	page 	= page%8;

	if(col<(MAX_COL_PIXEL/2))
		{GDispChipSel(LEFT);}
	else
		{GDispChipSel(RIGHT);col=col-(MAX_COL_PIXEL/2);}

	GDispWr(CMD,PAGE_OFFSET|page);	 GDispWr(CMD,Y_OFFSET|col);	//set page and column position
	GDispWr(DAT, c);											//output data to D0-D7
	GDispChipSel(NONE);
}

/*
*********************************************************************************************************
*                         					CLEAR DISPLAY
*
* Description : This function writes a fixed byte to all pages (0-7) of both half. If the byte is 0x00,
*				the action is equivalent to clearing the display (turning all pixels OFF)
* Arguments   : 'pat' 	is the pattern to write to the display in a repetitive manner. Writing 0xFF
*						for example, will turn all pixels BLACK.
*				
* Returns     : none
* Notes		  : This function makes use of GdispWrByte() thus the speed of clearing is fast!
*********************************************************************************************************
*/
void GDispClr(unsigned char pat)
{
	unsigned char col, page;

	for(col=0;col<128;col++)
	{
		for(page=0;page<8;page++) GDispWrByte(col,page,pat);
	}
}


/*
*********************************************************************************************************
*                         				SET PIXEL AT x, y POSITION
*
* Description : This function sets a pixel with color = BLACK / WHITE (defined in ks0108.h) at a position
*				defined by (x,y) following Cartesian coordinate system. Coordinates (0,0) at the 
*				top left corner, coordinates (127,63) at the lower right corner of the LCD screen.
*
* Arguments   : 'x'    0....MAX_COL_PIXEL-1 is matrix position in horizontal direction
*				'y'    0....MAX_ROW_PIXEL-1 is matrix position in vertical direction 
*				'color'  sets BLACK / WHITE standing for pixel ON/OFF
* Returns     : none
* Notes		  : The action GDispRd() has an effect of auto-increment on the x-direction (Y address)
*				Thus, GDispWr(CMD,Y_OFFSET|x) has to be immediate above data write code line
*				(color==BLACK)? GDispWr(DAT,pre_data|(1<<y%8)):GDispWr(DAT,pre_data&~(1<<y%8))
*				Data type 'bit' is Keil C specific
*********************************************************************************************************
*/
void GDispSetPixel(unsigned char x, unsigned char y, bit color)
{	
	unsigned char pre_data;

	x = x%MAX_COL_PIXEL;
	y = y%MAX_ROW_PIXEL;

	if(x<(MAX_COL_PIXEL/2))
		{GDispChipSel(LEFT);}
	else
		{GDispChipSel(RIGHT);x=x-(MAX_COL_PIXEL/2);}

	GDispWr(CMD,PAGE_OFFSET|(y/8));	//set pixel address in vertical direction (page address)
	GDispWr(CMD,Y_OFFSET|x);		//set pixel address in horizontal direction
	pre_data=GDispRd(DATA_RD);		//dummy read required
	pre_data=GDispRd(DATA_RD);		//address pointer auto increment by one
	GDispWr(CMD,Y_OFFSET|x);		//set pixel address in horizontal direction again to offset 
									//the address pointer shift from GDispRd(DATA_RD) above
	(color==BLACK)? GDispWr(DAT,pre_data|(1<<(y%8))):GDispWr(DAT,pre_data&~(1<<(y%8)));

	GDispChipSel(NONE);
}

/*
*********************************************************************************************************
*                         				WRITE A PATTERN AT X, Y COORDINATES
*
* Description : This function writes a particular pattern of size patWidth and patHeight defined under 
*				the pattern structure in ks0108.h at a position defined by (x,y) following Cartesian 
*				coordinate system. Pixel set in horizontal direction.
*				*** Pixels displayed in horizontal manner ***
* Arguments   : 'x'    		0....MAX_COL_PIXEL-1 is matrix position in horizontal direction
*							top left corner of the pattern to write
*				'y'    		0....MAX_ROW_PIXEL-1 is matrix position in vertical direction
*							top left corner of the pattern to write
*				'*pPat'		pointer to pattern structure 
*				'color'  	sets BLACK / WHITE standing for pixel ON/OFF
* Returns     :	none
* Notes		  : Data type 'bit' is Keil C specific
*********************************************************************************************************
*/
#if PIXEL_PAT_EN
void GDispPixPatAt(unsigned char x, unsigned char y, pattern* pPat, bit color)
{
 	unsigned char i, j, bit_idx, pixelByte, patYsize, patByte;

	patYsize  = pPat->patHeight;
	patByte   = (pPat->patWidth)>>3;	//number of byte = width of icon/8

	for(i=0;i<patYsize;i++)
	{
		for(j=0;j<patByte;j++)
		{
			pixelByte = pPat->patBody[i*patByte+j];
			for(bit_idx=0;bit_idx<8;bit_idx++)
				{
				 (pixelByte&(0x80>>bit_idx))? GDispSetPixel(x+bit_idx+8*j, y+i, color):\
				  GDispSetPixel(x+bit_idx+8*j, y+i, !color);
				}
		}
	}
 
}
#endif

/*
*********************************************************************************************************
*               			CONVERT A STRING TO PIXEL DATA AND DISPLAY AT x,y
*
* Description : This function outputs a string in graphic mode (pixel-by-pixel) at a position defined 
*				by (x,y) following Cartesian coordinate system with variable font spacing defined by 
*				fontSpace. Character wrapping is allowed.
*				Enable switch PIXEL_FONT_EN under ks0108.h to use this function
*				*** REQUIRE AT LEAST ONE FONT SET (sysfont.h) ****
*
* Arguments   : 'x'    		0....MAX_COL_PIXEL-1 is matrix position in horizontal direction,
*							top left corner of each character to write
*				'y'    		0....MAX_ROW_PIXEL-1 is matrix position in vertical direction,
*							top left corner of each character to write
*				'*pStr'		pointer to an ASCII string 
*				'fontSpace' font spacing from 1 to 127
*				'color'		WHITE / BLACK
* Returns     : none
* Notes		  : Example 
*				....
*				main() 
*				{ 
*					GDispPixFontAt(0,20,"Hello!",1,BLACK); 	//Display the ASCII string "Hello!"
*															//at (x,y)=(0,20), font space = 1
*															//color in BLACK
*				}
*
*				Data type 'bit' is Keil C specific
*********************************************************************************************************
*/
#if PIXEL_FONT_EN
void GDispPixStrAt(unsigned char x, unsigned char y, unsigned char *pStr, unsigned char fontSpace, bit color)
{
	unsigned char pixelByte,bit_idx, i;					//loop counters 
	unsigned char c,fwidth, fheight;					//character, font width & height store

	while (*pStr)
	{
		c = *pStr++ - 32;
				
		fwidth = SYS_FNT[c].fontWidth;
		fheight = 8;

		if((x+fwidth)>MAX_COL_PIXEL) 					//character wrapping here				
		{
			x=0;
			y+=fheight;
		}

		if((y+fheight)>MAX_ROW_PIXEL)					//check for y boundary				
		{
			y=0;
		}

		for(i=0;i<fheight;i++)
		{
			pixelByte = SYS_FNT[c].fontBody[i];
			for(bit_idx=0; bit_idx<fwidth; bit_idx++)
				{
					if((pixelByte)&(0x80>>bit_idx)) GDispSetPixel(x+bit_idx, y+i, color);
				}
		}
		
		x = x+fwidth+fontSpace;
	}	
}
#endif

/*
*********************************************************************************************************
*                   						LOW LEVEL RESET    			
*
* Description : This function performs a low level reset for the LCD module
* Arguments   : none
*			
* Returns     : none
* Notes		  : Hardware specific.
*********************************************************************************************************
*/
void GDispReset(void)
{
	unsigned char i;

	LCD_RST = LOW;
	for(i=0;i<0xFF;i++);		//software delay
	LCD_RST = HIGH;
	for(i=0;i<0xFF;i++);		//software delay
}

/*
*********************************************************************************************************
*                   					CHIP SELECT FOR CS1 OR CS2      			
*
* Description : This function performs a "chip select" for the left, right, both, or none LCD portion
* Arguments   : 'wing' can be 	LEFT
*								RIGHT
*								BOTH
*								NONE	defined under ks0108.c
*			
* Returns     : none
* Notes		  : Hardware specific.
*********************************************************************************************************
*/
void GDispChipSel(unsigned char wing)
{
	if(wing==LEFT)
		{LCD_CS1=HIGH;LCD_CS2=LOW;}
	else if(wing==RIGHT)
		{LCD_CS1=LOW;LCD_CS2=HIGH;}
	else if (wing==BOTH)
		{LCD_CS1=HIGH;LCD_CS2=HIGH;}
	else if (wing==NONE)
		{LCD_CS1=LOW;LCD_CS2=LOW;}
} 

/*
*********************************************************************************************************
*                         		LOW LEVEL DATA OR COMMAND WRITE TO LCD
*
* Description : This function performs low level data/command write to LCD
* Arguments   : (bit)'type' 			is the data type, DAT or CMD as defined under ks0108.c
*										DAT stands for data, CMD stands for command
*				(unsigned char) 'dat' 	is the data written to the LCD module, defined by the data bus
*										connection defined under ks0108.h
* Returns     : none
* Notes		  : Hardware specific. The type bit is specific to Keil C complier.
*********************************************************************************************************
*/
void GDispWr(bit type, unsigned char dat)
{
	(type==DAT)?(LCD_DI=HIGH):(LCD_DI=LOW);
	LCD_RW = LOW;
	LCD_EN = HIGH; 
	LCD_DATA = dat;
	_nop_();
	LCD_EN = LOW;
}


/*
*********************************************************************************************************
*                         					GDispStatRd
*
* Description : This function reads status of the LCD
* Arguments   : 'type' 	STATUS 	for a status read
*						DAT 	for a data read		defined under ks0108.c
*			
* Returns     : status byte / data byte return from the LCD data port
* Notes		  : Hardware specific. The type bit is specific to Keil C complier.
*********************************************************************************************************
*/
unsigned char GDispRd(bit type)
{
	unsigned char lcd_status;
	
	LCD_DATA = 0xFF;						//set data port as input to read LCD status
	(type==DATA_RD)?(LCD_DI=HIGH):(LCD_DI=LOW);
	LCD_RW = HIGH;
	LCD_EN = HIGH; 
	_nop_();
	lcd_status = LCD_DATA;
	LCD_EN = LOW;

	return (lcd_status);
}

/*
*********************************************************************************************************
*                         					GDispBackLite
*
* Description : This function control the LCD backlight ENABLE -OR- DISABLE
* Arguments   : 'bit' 	ENABLE 	for backlight ON
*						DISABLE	for backlight OFF
*			
* Returns     : none
* Notes		  : Hardware specific. The type bit is specific to Keil C complier.
*********************************************************************************************************
*/
void GDispBackLite(bit ctrl)
{
	(ctrl==ENABLE)?(LCD_BL=LOW):(LCD_BL=HIGH);
}
/*-------------------------------------------------------------------------------
Draw a rectangle on the LCD
	GLCD_Rectangle (Xaxis1,Yaxis1,Xaxis2,Yaxis2)
		Xaxis1 = absciss top-left (in pixels)
		Yaxis1 = ordinate top-left (in pixels)
		Xaxis2 = absciss bottom-right (in pixels)
		Yaxis2 = ordinate bottom-right (in pixels)
-------------------------------------------------------------------------------*/
void GDispRec(unsigned char Xaxis1,unsigned char Yaxis1, unsigned char Xaxis2,unsigned char Yaxis2,bit color)
{
  	unsigned char CurrentValue;

	/* Draw the two horizontal lines */
  	for (CurrentValue = 0; CurrentValue < Xaxis2 - Xaxis1+ 1; CurrentValue++) 
  	{
		GDispSetPixel(Xaxis1 + CurrentValue, Yaxis1,color);
		GDispSetPixel(Xaxis1 + CurrentValue, Yaxis2,color);
	}
  	
  	/* draw the two vertical lines */
  	for (CurrentValue = 0; CurrentValue < Yaxis2 - Yaxis1 + 1; CurrentValue++)	
  	{
		GDispSetPixel(Xaxis1, Yaxis1 + CurrentValue,color);
		GDispSetPixel(Xaxis2, Yaxis1 + CurrentValue,color);
	}
}

/*-------------------------------------------------------------------------------
Draw a circle on the LCD
	GLCD_Circle (CenterX, CenterY, Radius)
		CenterX = Center absciss (in pixels)
		CenterY = Center ordinate (in pixels) 
		Radius  = Radius (in pixels)
-------------------------------------------------------------------------------*/
void GDispCir(unsigned char CenterX, unsigned char CenterY, unsigned char Radius,bit color)
{
	int tswitch=0, y=0, x=0;
	unsigned char d;

	d = CenterY - CenterX;
	y = Radius;
	tswitch = 3 - 2 * Radius;
	while (x <= y) 
	{
		GDispSetPixel(CenterX + x, CenterY + y,color);
		GDispSetPixel(CenterX + x, CenterY - y,color);
		
		GDispSetPixel(CenterX - x, CenterY + y,color);
		GDispSetPixel(CenterX - x, CenterY - y,color);
		
		GDispSetPixel(CenterY + y - d, CenterY + x,color);
		GDispSetPixel(CenterY + y - d, CenterY - x,color);
		GDispSetPixel(CenterY - y - d, CenterY + x,color); 
		GDispSetPixel(CenterY - y - d, CenterY - x,color);

		if (tswitch < 0) 
			tswitch += (4 * x + 6);
		else 
		{
			tswitch += (4 * (x - y) + 10);
			y--;
		}
	
		x++;
	}
}
void GDispPic(unsigned char *Picture)
{
  unsigned char x,y,ind_bit;
  code unsigned char tab_comp[] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
  
  for(y=0;y<63;y++) 
    for(x=0;x<16;x++) 
	  for(ind_bit=0;ind_bit<8;ind_bit++) 	   
       GDispSetPixel(((x*8)+ind_bit),y,Picture[y*16+x] & tab_comp[ind_bit]);
}
//------------------------------------------------------ EOF ----------------------------

