/*--------------------------------------------*/
/*----------------header files----------------*/
/*--------------------------------------------*/

#include <REG51xD2.h>
#include "LCD.h"
#include "SPI.h"
#include "font.h"

/*
void Delay(void){	
 unsigned char DelayCounter;
   	DelayCounter = 1;						
	do{ 
		DelayCounter--;						
	} 
	while(DelayCounter);					
}
*/
/*------------------------zpozdení 100us------------------------*/
/* V X2 modu pri frekvenci 30MHz trvá jeden strojní cyklus 0,2us
 Tato cást kódu je v asm:				MOV R7,#250
							 navesti:	DJNZ r7,navesti
										RET
 Instrukce DJNZ je dvoucyklová => 250*2 = 500 strojních cyklu	
 500 strojních cyklu * 0,2us = 100us						    */
void Delay100us(void){	// zpozdovací smycka 100us
 unsigned char DelayCounter;	// pomocná promenná pro cyklus while

   	DelayCounter = 250;			// hodnota pro 30Mhz kmitocet
	do{ 
		DelayCounter--;			// dekrementace			
	} 
	while(DelayCounter != 0);	// cykluje dokat není 0			
}
/*------------------------zpozdení ms-------------------------*/
void DelayMS(unsigned int DelayTimeMS){	// ms zpozdení s parametrem pocet ms
 char i;								// pomocná promenná pro cyklus for
	do{	
		for (i = 0; i < 10; i++){		// 10x volána funkce 100us
			Delay100us();				// volání funkce 
		}
		DelayTimeMS--;				    // dekrementace	
	} 
	while(DelayTimeMS != 0);		    // cykluje dokat není 0
}
/*
void SendLCD(unsigned char type,  unsigned char dat){	 // hardware SPI zápis do LCD
    MOSI = 0;				// vynulování MOSI
	SCK = 0;				// vynulování SCK
    LCD_CS(0);				// aktivace LCD
	LCD_DATA(type);  		// nastavení MOSI podle typu zda se jedná o data nebo instrukce
	SCK = 0;				// hodinový signál do nuly
	Delay();				// krátké zpozdení
	SCK = 1;				// hodinový signál do jednicky
	Delay();				// krátké zpozdení
	SPCON |=  0x40;			// spustení SPI
    SPDAT = dat; 			// data do SPDAT			
    while(!(SPSTA & 0x80));	// cekání na príznak odeslání dat			
	SPSTA |=  0x7F;			// vynulování príznaku odeslání dat	
    LCD_CS(1);				// deaktivace LCD			
    SPCON &=  ~0x40;		// zastavení SPI
}
*/

/*-------------------zaslání príkazu do LCD-------------------*/
void SendLCD(int type,unsigned char dat){	// software SPI s parametrem typ dat(instrukce,data) a data
 char j;						// pomocná promenná pro cyklus for
 unsigned int temp;				// pomocná promenná pro upravu na 9.bitový príkaz, kde 9. sit signalizuje zda-li se jedná o data ci instrukce
	temp = dat;					// úprava dat na 9. bitový príkaz
	temp |= type;				// úprava dat na 9. bitový príkaz
	temp <<= 7;					// úprava dat na 9. bitový príkaz
	LCD_CS(0);					// aktivace LCD
    for(j=0; j<9;j++){			// cyklus zaslání 9.bitového príkazu
		SCK = 0;				// hodinový signál do log.0
    	MOSI = (temp & 0x8000);	// zaslání bitu na MOSI
      	temp<<=1;				// posunutí o 1
      	SCK = 1;				// hodinový signál do log.1
   	}
    LCD_CS(1);					// deaktivace LCD
	MOSI = 1;					// nastavení MOSI do log. 1
	SCK = 1;					// nastavení SCK do log. 1
}

/*----------------------inicializace LCD----------------------*/
void init_LCD(void){
/*----------EPSON radic----------*/
/*	LCD_RESET(0);
	DelayMS(100);
	LCD_RESET(1);
	DelayMS(100);

	SendLCD(LCDCommand,DISCTL);
	SendLCD(LCDData,0x00);
	SendLCD(LCDData,0x20);
	SendLCD(LCDData,0x00);


	SendLCD(LCDCommand,COMSCN);
	SendLCD(LCDData,0x01);

	SendLCD(LCDCommand,OSCON);

	SendLCD(LCDCommand,SLPOUT);

	SendLCD(LCDCommand,VOLCTR);
	SendLCD(LCDData,0x32);
	SendLCD(LCDData,0x03);

	SendLCD(LCDCommand,PWRCTR);
	SendLCD(LCDData,0x0F);	 

	DelayMS(100);

	SendLCD(LCDCommand,DISINV);

	SendLCD(LCDCommand,DATCTL);
    SendLCD(LCDData,0x01);
	SendLCD(LCDData,0x00);
	SendLCD(LCDData,0x02);

	SendLCD(LCDCommand,NOP);

	DelayMS(100);
	SendLCD(LCDCommand,DISON);*/

/*----------PHILLIPS radic----------*/	
	LCD_RESET(0);					// reset LCD
	DelayMS(100);					// zpozdení 100ms
	LCD_RESET(1);					// reset LCD
	DelayMS(100);					// zpozdení 100ms

	SendLCD(LCDCommand,SLEEPOUT);		

	SendLCD(LCDCommand,INVON);		// príkaz inverze LCD

	SendLCD(LCDCommand,COLMOD);		// príkaz nastavení barevného formátu
	SendLCD(LCDData,0x03);			// data nastavení 0x03 - 12b. barvy na pixel

	SendLCD(LCDCommand,MADCTL);		// príkaz nastavení prístupu k datum v RAM
	SendLCD(LCDData,0xC8);			// data nastavení 0xC8 - zrcadlení x, zrcadlení y, otocení RGB

	SendLCD(LCDCommand,SETCON);	   	// príkaz nastavení kontrastu
	SendLCD(LCDData,0x3F);			// hodnota kontrastu

	DelayMS(100);					// krátké zpozdení

//	SendLCD(LCDCommand,DISPON);

}
/*---------------vymazání celého obrazu na LCD----------------*/
/*---------------s parametrem barvy(mazání)-------------------*/
void LCDClearScreen(int color){	
unsigned int j; 					// pomocná promenná pro cyklus for

	SendLCD(LCDCommand,PASET);		// príkaz nastavení adresy rádku 
	SendLCD(LCDData,0);				// data (start adresa) - od 0
	SendLCD(LCDData,131);			// data (stop adresa) - do 131
	
	SendLCD(LCDCommand,CASET);		// príkaz nastavení adresy sloupce
	SendLCD(LCDData,0);				// data (start adresa) - od 0
	SendLCD(LCDData,131);			// data (stop adresa) - do 132
	
	SendLCD(LCDCommand,RAMWR);		// príkaz zápis do RAM LCD

	for(j=0;j<((132*132)/2);j++){							// cyklus zápisu pres vsechny pixely LCD
		SendLCD(LCDData,((color>>4)&0x00FF));			   	// první byte dat barvy(cervená a zelená) pro 1. pixel 
		SendLCD(LCDData,(((color&0x0F)<<4)|(color>>8)));    // druhý byte barvy(modrá) pro 1. pixel a (cervená) pro 2. pixel
		SendLCD(LCDData,(color&0x00FF));						// tretí byte barvy(zelená a modrá) pro 2. pixel
	 }

}				  
/*--------------zobrazení urcitého pixelu na LCD--------------*/
/*--------------s parametrem souradnice x---------------------*/
/*--------------s parametrem souradnice y---------------------*/
/*--------------s parametrem barvy pixelu---------------------*/
void LCDSetPixel(int x, int y, int color){	
    SendLCD(LCDCommand,PASET);		// príkaz nastavení adresy rádku 
	SendLCD(LCDData,x);				// data (start adresa) - jedná se o jeden pixel
	SendLCD(LCDData,x);				// data (start adresa) - jedná se o jeden pixel

	SendLCD(LCDCommand,CASET);		// príkaz nastavení adresy sloupce
	SendLCD(LCDData,y);				// data (start adresa) - jedná se o jeden pixel
	SendLCD(LCDData,y);				// data (start adresa) - jedná se o jeden pixel
	
	SendLCD(LCDCommand,RAMWR);		// príkaz zápis do RAM LCD
 	SendLCD(LCDData,((color >> 4) & 0x00FF));						// první byte dat barvy(cervená a zelená) pro 1. pixel 
	SendLCD(LCDData,(((color & 0xF) << 4) | ((color >> 8) & 0xF)));	// druhý byte barvy(modrá) pro 1. pixel a (cervená) pro 2. pixel
	SendLCD(LCDData,(color & 0x00FF));								// tretí byte barvy(zelená a modrá) pro 2. pixel
}
/*-------------------zobrazení linky na LCD--------------------*/
/*-------------------s parametrem souradnice - odkud x0-------*/
/*-------------------s parametrem souradnice - odkud y0-------*/
/*-------------------s parametrem souradnice - kam x1---------*/
/*-------------------s parametrem souradnice - kam y1---------*/
/*-------------------s parametrem barvy vykreslene linky------*/
void LCDSetLine(int x0, int y0, int x1, int y1, int color){
 int dy = y1-y0;					// promenná - rozdíl mezi koncovým a pocátecním bodem
 int dx = x1-x0;					// promenná - rozdíl mezi koncovým a pocátecním bodem
 int stepx, stepy;					// pomocná promenná 
 int fraction;

	if(dy < 0){						// test jestliže je rozdíl dy záporný
		dy = -dy; 					// dy je záporné
		stepy = -1;					// krok v y o -1
	}
	else stepy = 1;					// rozdíl dy je kladný krok v y o 1
	if(dx < 0){						// test jestliže je rozdíl dx záporný
		dx = -dx; 					// dx je záporné
		stepx = -1;					// krok v x o -1
	}
	else stepx = 1;					// krok v x o 1
	dy <<= 1;						// rozdíl dy *2
	dx <<= 1;						// rozdíl dx *2
	LCDSetPixel(x0,y0,color);		// vykreslení pocátecního pixelu
	if(dx > dy){					// test zda-li je rozdíl v x vetší jak v y
		fraction = dy-(dx>>1);		// rozdíl mezi (2*dy) a dx
		while (x0 != x1) {			// cyklus dokud se nerovná souradnice x0 s x1
			if (fraction >= 0) {	// test zda-li je rozdíl roven, nebo vetsí jak 0
				y0 += stepy;		// pripoctení kroku(1 nebo -1) k souradnici y0
				fraction -= dx; 	// odectení od rozdílu(2*dy-dx) hodnota 2*dx
			}
			x0 += stepx;			// pripoctení kroku(1 nebo -1) k souradnici x0
			fraction += dy; 		// pripoctení k rozdílu(2*dy-dx) hodnota 2*dy
			LCDSetPixel(x0, y0, color);	// vykreslení pixelu o souradnici x0,y0 a požadované barvy
		}
	 } else {					   	// rozdíl v x není vetší jak v y
		fraction = dx - (dy >> 1);	// rozdíl mezi (2*dx) a dy
		while (y0 != y1) {			// cyklus dokud se nerovná souradnice y0 s y1
			if (fraction >= 0) {	// test zda-li je rozdíl roven, nebo vetsí jak 0
				x0 += stepx;		// pripoctení kroku(1 nebo -1) k souradnici x0
				fraction -= dy;		// odectení od rozdílu(2*dx-dy) hodnota 2*dy
			}
			y0 += stepy;			// pripoctení kroku(1 nebo -1) k souradnici y0
			fraction += dx;			// pripoctení k rozdílu(2*dx-dy) hodnota 2*dx
			LCDSetPixel(x0, y0, color);	// vykreslení pixelu o souradnici x0,y0 a požadované barvy
		}
	 }
}					   
/*-----------------kreslení obdélníku na LCD------------------*/
/*-----------------s parametrem souradnice - odkud x0---------*/
/*-----------------s parametrem souradnice - odkud y0---------*/
/*-----------------s parametrem souradnice - kam x1-----------*/
/*-----------------s parametrem souradnice - kam y1-----------*/
/*-----------------s parametrem vyplnit ci jen orámovat fill/nofill*/
/*-----------------s parametrem barvy vykreslenho obdelníku---*/
void LCDSetRect(int x0, int y0, int x1, int y1, unsigned char fill, int color) {
    int xmin, xmax, ymin, ymax;				// pomocné pro výpocet souradnic obdélníku
    int i;									// pomocná promenná pro cyklus for

	if (fill == FILL){						// test zda-li bude plný obdélník, nebo jek orámování
		if(x0 <= x1)xmin = x0;				// test zda-li je souradnice x0 <= x1 -> mensí je x0
		else xmin = x1;						// mensí je x1
		if(x0 > x1)xmax = x0;			   	// test zda-li je souradnice x0 > x1 -> vetsí je x0
		else xmax = x1;						// vetsí je x1
		if(y0 <= y1)ymin = y0;				// test zda-li je souradnice y0 <= y1 -> mensí je y0
		else ymin = y1;						// mensí je y1
		if(y0 > y1)ymax = y0;				// test zda-li je souradnice y0 > y1 -> vetsí je y0
		else ymax = y1;						// vetsí je y1
	
		SendLCD(LCDCommand,PASET);			// príkaz nastavení adresy rádku
		SendLCD(LCDData,xmin);				// data (start adresa) - od xmin
		SendLCD(LCDData,xmax);			 	// data (stop adresa) - do xmax

		SendLCD(LCDCommand,CASET);		   	// príkaz nastavení adresy sloupce
		SendLCD(LCDData,ymin);				// data (start adresa) - od ymin
		SendLCD(LCDData,ymax);				// data (stop adresa) - do ymax


		SendLCD(LCDCommand,RAMWR);			// príkaz zápis do RAM LCD


		for (i = 0; i < ((((xmax - xmin + 1) * (ymax - ymin + 1)) / 2) + 1); i++) {	// cyklus pres celý obdélník(deleno 2 z duvodu vykreslování 2 pixelu naráz)
 			SendLCD(LCDData,((color >> 4) & 0x00FF));									// první byte dat barvy(cervená a zelená) pro 1. pixel
			SendLCD(LCDData,(((color & 0x0F) << 4) | ((color >> 8) & 0xF)));			// druhý byte barvy(modrá) pro 1. pixel a (cervená) pro 2. pixel
			SendLCD(LCDData,(color & 0x00FF));										// tretí byte barvy(zelená a modrá) pro 2. pixel
		}
	} 
	else {									// nebude plný obdélník, ale je orámování
		LCDSetLine(x0, y0, x1, y0, color);	// volání funkce pro kreslení linky 
		LCDSetLine(x0, y1, x1, y1, color);	// volání funkce pro kreslení linky 
		LCDSetLine(x0, y0, x0, y1, color);	// volání funkce pro kreslení linky 
		LCDSetLine(x1, y0, x1, y1, color);	// volání funkce pro kreslení linky 
	}
}
/*-------------------kreslení znaku na LCD--------------------*/
/*-------------------s parametrem vykresleného znaku----------*/
/*-------------------s parametrem souradnice x----------------*/
/*-------------------s parametrem souradnice y----------------*/
/*-------------------s parametrem velikost znaku--------------*/
/*-------------------s parametrem barvy znaku-----------------*/
/*-------------------s parametrem barvy pozadí znaku----------*/
void LCDPutChar(char c, int x, int y, unsigned char size, int fColor, int bColor) 
{
	int i,j; 								 // pomocné promenné pro cykly for
	unsigned char nCols;					 // promenná pro pocet sloupcu znaku
	unsigned char nRows;					 // promenná pro pocet rádku znaku
	unsigned char nBytes;					 // promenná pro pocet bytu znaku
	unsigned char PixelRow;					 // promenná pro získání bytu rádku z tabulky znaku
	unsigned char Mask;						 // maska pro rotaci pres jednotlivé bity v bytu 
	unsigned int Word0;						 // pomocná promenná pro získání barvy pozadí,n ebo  barvy znaku
	unsigned int Word1;						 // pomocná promenná pro získání barvy pozadí, nebo barvy znaku
	unsigned char *pFont;					 // ukazatel na char pro získání adresy tabulky znaku
	unsigned char *pChar;					 // ukazatel na char pro získání adresy posledního bytu znaku
	unsigned char temp;						 // pomocná promenná pro adresu tabulky
	unsigned char *FontTable[] = {(unsigned char *)FONT6x8,		// adresa malých fontu
		                          (unsigned char *)FONT8x8};	// adresa stredních fontu
//								  (unsigned char *)FONT8x16};   // adresa velkých fontu - nepouzito

   /*--prepocet z duvodu nepoužití všech znaku z tabulky--*/
   if(c==' ')temp = 0x1F;	// test zda-li se jedná o mezeru mezi znaky	adresa je 0x1F
   else temp = 0x2C;		// nejedn8 se o meyeru adresa je 0x2F

	pFont = (unsigned char *)FontTable[size];				// získání ukazatele na zacátek tabulky znaku
	nCols = *pFont;											// nactení poctu sloupcu pro znak z tabulky znaku
	nRows = *(pFont + 1);								    // nactení poctu radku pro znak z tabulky znaku
	nBytes = *(pFont + 2);								    // nactení poctu bytu pro znak z tabulky znaku
	pChar = (pFont + (nBytes * (c - temp)) + nBytes - 1);	// výpocet ukazatele na poslední byte zobrazovaného znaku


	SendLCD(LCDCommand,PASET);				// príkaz nastavení adresy rádku
	SendLCD(LCDData,x);						// data (start adresa) - od x
	SendLCD(LCDData,(x + nRows - 1));		// data (stop adresa) - do x + pocet radku -1

	SendLCD(LCDCommand,CASET);				// príkaz nastavení adresy sloupce
	SendLCD(LCDData,y);						// data (start adresa) - od y
	SendLCD(LCDData,(y + nCols - 1));		// data (stop adresa) - do y + pocet sloupcu -1

	SendLCD(LCDCommand,RAMWR);				// príkaz zápis do RAM LCD

	for (i = nRows - 1; i >= 0; i--) {		// cyklus pres vsechny rádky zobrazovaného znaku
		PixelRow = *pChar--;				// získání bytu z tabulky znaku
		Mask = 0x80;						// maska 10000000b
		for (j = 0; j < nCols; j += 2) {	// cyklus pres nactený byte zleva do prava(inkrementace o 2 z duvodu dvou zobrazovaných pixelu)
			if ((PixelRow & Mask) == 0)		// jestliže je na první pozici log. 0 jedná se o pozadí
				Word0 = bColor;				// barva pozadí
			else							// na první pozici je log. 1 jedná se o barvu znaku
				Word0 = fColor;				// barva znaku
			Mask = Mask >> 1;				// rotace masky o 1 do leva
			if ((PixelRow & Mask) == 0)		// jestliže je na druhé pozici je log. 0 jedná se o pozadí 
				Word1 = bColor;				// barva pozadí
			else						    // na druhé pozici je log. 1 jedná se o barvu znaku
				Word1 = fColor;			    // barva znaku
			Mask = Mask >> 1;				// rotace masky o 1 do leva

			SendLCD(LCDData,((Word0 >> 4) & 0x00FF));							// první byte dat barvy z promenné Word0(cervená a zelená) pro 1. pixel
			SendLCD(LCDData,(((Word0 & 0x0F) << 4) | ((Word1 >> 8) & 0x0F)));	// druhý byte barvy z promenné Word0(modrá) pro 1. pixel a první byte z promenné Word1(cervená) pro 2. pixel
			SendLCD(LCDData,(Word1 & 0x00FF));									// tretí byte barvy z promenné Word1(zelená a modrá) pro 2. pixel
		}
	}
}
/*---------------kreslení retezce znaku na LCD----------------*/
/*---------------s parametrem vykresleného retezce------------*/
/*---------------s parametrem souradnice x--------------------*/
/*---------------s parametrem souradnice y--------------------*/
/*---------------s parametrem velikost znaku------------------*/
/*---------------s parametrem barvy znaku---------------------*/
/*---------------s parametrem barvy pozadí znaku--------------*/
void LCDPutStr(char *pString, int x, int y, int Size, int fColor, int bColor) {
	while (*pString != 0x00) { 								// cyklus pres vsechny znaky v retezci
		LCDPutChar(*pString++, x, y, Size, fColor, bColor);	// volání funkce pro vykreslení znaku
		if (Size == SMALL)									// test zda-li se jedná o malý znak 
			y = y + 6;										// k souradnici y pripocítat 6 sloupcu (malý znak = 6sloupcu 8rádek)
		else if (Size == MEDIUM)							// test zda-li se jedná o strední znak
			y = y + 8;										// k souradnici y pripocítat 8 sloupcu (stredni znak = 8sloupcu 8rádek)
		else												// test zda-li se jedná o velký znak
			y = y + 8;										// k souradnici y pripocítat 8 sloupcu (stredni znak = 8sloupcu 16rádek)
		if (y > 131) break;									// test zda-li není prekrocena max souradnice LCD
	}
}

