/*
 * Copyright:      Fabian Maximilian Thiele  mailto:me@apetech.de
 * Author:         Fabian Maximilian Thiele
 * Remarks:        this Copyright must be included
 * known Problems: none
 * Version:        1.1
 * Description:    Graphic Library for LCD- (and compatible) based LCDs
 * 
 */

#include "lcd.h"


lcdCoord			LCDCoord;
uint8_t				LCDInverted=0;
LCDFontCallback	LCDFontRead;
uint8_t				LCDFontColor;
const uint8_t*		LCDFont;
uint8_t i,j=0xAA;
char rych_s[4];
uint32_t hornia,spodnia;
uint8_t u8CursorX, u8CursorY;


extern GPIO_InitTypeDef GPIO_InitStructure;
// Pro přepínaní směru na sběrnici se používá inicializační struktura,
// globálně definovaná v main.c

#define LCDportin()   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; \
  GPIO_Init(GPIOB, &GPIO_InitStructure);
// makro pro přepnutí sběrnice na čtení z displeje

#define LCDportout()   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; \
  GPIO_Init(GPIOB, &GPIO_InitStructure);
// makro pro přepnutí sběrnice na zápis do displeje

#define LCDout(data) spodnia=((data&0xFC)<<8)|(data&3);  \
		 hornia=((~spodnia)&0xFC03)<<16; \
		 GPIOB->BSRR=hornia|spodnia;  
//makro které převede 8bit hodnotu určenou pro displej
//na vývody portu (PB0,PB1,PB10-PB15) viz schéma


#define LCDEnable() for (i=0;i<1;i++);	ENport->BSRR = 1<<ENpin; \
for (i=0;i<2;i++); \
ENport->BRR = 1<<ENpin; \
//makro které vytvoří krátký enable pulz   


//následující makra se používají pro ovládání řídících signálů
#define RESup()	RESport->BSRR = 1<<RESpin;
#define RESdown() RESport->BRR = 1<<RESpin;


#define DIup()	for (i=0;i<3;i++); DIport->BSRR = 1<<DIpin;
#define DIdown() for (i=0;i<3;i++); DIport->BRR = 1<<DIpin;


#define RWup()	RWport->BSRR = 1<<RWpin;
#define RWdown() RWport->BRR = 1<<RWpin;



void LCDSetReset(uint8_t value) {
  if (value == 1)
  {
RESup()
  } 
    else 
  {
RESdown()
ENport->BRR = 1<<ENpin; //nastavení EN do zakladniho stavu L
  }      
}



void LcdPutchar (uint8_t, FONT_DEF*);

void LCDClearScreen() {
	
	uint8_t x = 0;
	uint8_t y = 0;
	uint8_t width = 127;
	uint8_t height = 63;
	uint8_t color = WHITE;
	
	uint8_t  pageOffset, h, i, data; 
	height++;
	
	pageOffset = 0;
	y -= pageOffset;


		h = 8-pageOffset;
	
	LCDGotoXY(x, y);
	for(i=0; i<=width; i++) {
	
		
		data = 0x00;
		
		LCDWriteData(data);
	}
	
	while(h+8 <= height) {
		h += 8;
		y += 8;
		LCDGotoXY(x, y);
		
		for(i=0; i<=width; i++) {
			LCDWriteData(color);
		}
	}
	

}

void LCDSetDot(uint8_t x, uint8_t y, uint8_t color) {
	uint8_t data;
	
	LCDGotoXY(x, y-y%8);					// read data from display memory
	data = LCDReadData();
	
	if(color == WHITE) {
		data &= ~(0x01 << (y%8));			// clear dot
	} else {
		data |= 0x01 << (y%8);				// set dot
	}
	
	LCDWriteData(data);					// write data back to display
}




void LCDGotoXY(uint8_t x, uint8_t y) {
	uint8_t chip = CHIP1, cmd;
	
	if(x > 127) x = 0;								// ensure that coordinates are legal
	if(y > 63)  y = 0;
	
	LCDCoord.x = x;								// save new coordinates
	LCDCoord.y = y;
	LCDCoord.page = y/8;
	
	if(x >= 64) {									    // select the right chip
		x -= 64;
		chip = CHIP2;
	}
	cmd = LCD_SET_ADD | x;
	LCDWriteCommand(cmd, chip);					  // set x address on active chip
	
	cmd = LCD_SET_PAGE | LCDCoord.page;		// set y address on both chips
	LCDWriteCommand(cmd, CHIP1);
	LCDWriteCommand(cmd, CHIP2);
}

void LCDInit(void) {
	LCDCoord.x = 0;
	LCDCoord.y = 0;
	LCDCoord.page = 0;
	

	LCDInverted=0;
	
	LCDWriteCommand(LCD_ON, CHIP1);				   
	
	LCDWriteCommand(LCD_ON, CHIP2);
	
	LCDWriteCommand(LCD_DISP_START, CHIP1);	 // display start line = 0
	LCDWriteCommand(LCD_DISP_START, CHIP2);

	  DIup()
	  RWdown()
	  LCDportout()

	LCDClearScreen();    // display clear
	LCDGotoXY(0,0);
}


uint8_t LCDDoReadData(uint8_t first) {
	uint8_t data;
	volatile uint8_t i;
	uint32_t temp;
	
	
	if(LCDCoord.x < 64) {

  CSport->BSRR = SELCHIP1;
	} else if(LCDCoord.x >= 64) {

  CSport->BSRR = SELCHIP2;
	}
	if(LCDCoord.x == 64 && first ) {				 
		LCDWriteCommand(LCD_SET_ADD, CHIP2); 	 
	}
	

LCDportin()
DIup()   
RWup()	
	

ENport->BSRR=1<<ENpin; //ENABLE nahoru
for (i=0;i<3;i++); // cca 500ns zpozdeni
			 
temp=GPIOB->IDR; //nacist data
data=((temp>>8)&0xFC)|(temp&3);	
// Složení hodnoty z portu do 8bit proměnné	

ENport->BRR=1<<ENpin;  //ENABLE dolů
for(i=0; i<3; i++);			
RWdown()	
LCDportout()
LCDGotoXY(LCDCoord.x, LCDCoord.y);
	
return data;
} 

uint8_t LCDReadData(void) {
	LCDDoReadData(1);							// dummy read
	return LCDDoReadData(0);				// "real" read
} 

void LCDWriteCommand(uint8_t cmd, uint8_t chip) {
	if(chip == CHIP1) {

CSport->BSRR = SELCHIP1;
	} else if(chip == CHIP2) {

CSport->BSRR = SELCHIP2;
	}	

DIdown()
RWdown()


LCDout(cmd)								      // write command
LCDEnable()									        // enable
LCDout(0x00)
}
									
void LCDWriteData(uint8_t data) {
	uint8_t displayData, yOffset; 

	if(LCDCoord.x >= 128)
		return;

	if(LCDCoord.x < 64) {

CSport->BSRR = SELCHIP1;
	} else if(LCDCoord.x >= 64) {

CSport->BSRR = SELCHIP2;
	}
	if(LCDCoord.x == 64)							    // chip2 X-address = 0
		LCDWriteCommand(LCD_SET_ADD, CHIP2);
	
DIup()
RWdown()	
LCDportout()
	
	yOffset = LCDCoord.y%8;
	if(yOffset != 0) {

		displayData = LCDReadData();
		

		LCDportout()
		
		displayData |= data << yOffset;
		if(LCDInverted)
			displayData = ~displayData;

		LCDout(displayData)					
		LCDEnable()								     
		
		// second page
		LCDGotoXY(LCDCoord.x, LCDCoord.y+8);
		
		displayData = LCDReadData();
		
		LCDportout()
		
		displayData |= data >> (8-yOffset);

		LCDout(displayData)
		LCDEnable()								        
		
		LCDGotoXY(LCDCoord.x+1, LCDCoord.y-8);
	} else {


		LCDout(data)
		LCDEnable()								     
		LCDCoord.x++;
	}

	LCDout(0x00)
}




//
// Font Functions
//

uint8_t LCDReadFontData(const uint8_t* ptr) {

	return *ptr;
}

void LCDSelectFont(const uint8_t* font, LCDFontCallback callback, uint8_t color) {
	LCDFont = font;
	LCDFontRead = callback;
	LCDFontColor = color;
}

int LCDPutChar(unsigned char c) {
       uint8_t width = 0;
	  uint8_t i,j;
       uint8_t height = LCDFontRead(LCDFont+FONT_HEIGHT);
       uint8_t bytes = (height+7)/8;
       uint8_t tooMuch = height % 8;
       
       uint8_t firstChar = LCDFontRead(LCDFont+FONT_FIRST_CHAR);
       uint8_t charCount = LCDFontRead(LCDFont+FONT_CHAR_COUNT);
       
       uint16_t index = 0;
       uint8_t x = LCDCoord.x, y = LCDCoord.y;
       
       if(c < firstChar || c >= (firstChar+charCount)) {
               return 1;
       }
       c-= firstChar;
       
       // read width data, to get the index
       for(i=0; i<c; i++)
               index += LCDFontRead(LCDFont+FONT_WIDTH_TABLE+i);
       index = index*bytes+charCount+FONT_WIDTH_TABLE;
       width = LCDFontRead(LCDFont+FONT_WIDTH_TABLE+c);
       
       // last but not least, draw the character
       for(i=0; i<bytes; i++)
       {
               uint8_t page = i*width;
               for(j=0; j<width; j++)
               {
                       uint8_t data = LCDFontRead(LCDFont+index+page+j);
                       
                       if (i + 1 == bytes)
                       {
                               uint8_t pageOld = (i - 1) * width;
                               uint8_t dataOld = LCDFontRead(LCDFont+index+pageOld+j); //get last byte
                               dataOld >>= tooMuch;
                               data |= dataOld;
                       }
                       
                       if(LCDFontColor == BLACK)
                       {
                               LCDWriteData(data);
                       } else
                       {
                               LCDWriteData(~data);
                       }
               }
               // 1px gap between chars
               if(LCDFontColor == BLACK)
               {
                       LCDWriteData(0x00);
               }
               else
               {
                       LCDWriteData(0xFF);
               }
               if (i + 2 < bytes)
                       LCDGotoXY(x, LCDCoord.y+8);
               else
                       LCDGotoXY(x, LCDCoord.y + tooMuch);
       }
       LCDGotoXY(x+width+1, y);
       
       return 0;
}


void LCDPuts(char* str) {
	int x = LCDCoord.x;
	while(*str != 0) {
		if(*str == '\n') {
			LCDGotoXY(x, LCDCoord.y+LCDFontRead(LCDFont+FONT_HEIGHT));
		} else {
			LCDPutChar(*str);
		}
		str++;
	}
}

void LCDPuts_P(PGM_P str) {
	int x = LCDCoord.x;

	while(*str != 0) {
		if(*str == '\n') {
			LCDGotoXY(x, LCDCoord.y+LCDFontRead(LCDFont+FONT_HEIGHT));
		} else {

			LCDPutChar(*str);
			
		}
		str++;
	}
}

uint8_t LCDCharWidth(char c) {
	uint8_t width = 0;
	uint8_t firstChar = LCDFontRead(LCDFont+FONT_FIRST_CHAR);
	uint8_t charCount = LCDFontRead(LCDFont+FONT_CHAR_COUNT);
	
	// read width data
	if(c >= firstChar && c < (firstChar+charCount)) {
		c -= firstChar;
		width = LCDFontRead(LCDFont+FONT_WIDTH_TABLE+c)+1;
	}
	
	return width;
}

uint16_t LCDStringWidth(char* str) {
	uint16_t width = 0;
	
	while(*str != 0) {
		width += LCDCharWidth(*str++);
	}
	
	return width;
}

uint16_t LCDStringWidth_P(PGM_P str) {
	uint16_t width = 0;
	

   while(*str != 0) {

 		width += LCDCharWidth(*str++);
	}
	
	return width;
}

//---------------------------------------------------------
//LCD

void LcdPutchar (uint8_t u8Char, FONT_DEF *fonttype)
{
	uint8_t u8CharColumn=0,u8RightSide=0;
	uint8_t u8UpperCharPointer=1;
	uint8_t chip;
	
	/* test for carrier return */
	if (u8CursorX > 128 - (fonttype->u8Width) ) 
	{	
		u8CursorX = 0;
		u8CursorY++;
		if (u8CursorY == 8) u8CursorY = 0;
	}
	
	/* Select the side of the LCD */
	if (u8CursorX < 64)
	{	
		/* Select left side */
		chip=LEFT;
		LcdInstructionWrite (Y_ADRESS + u8CursorX,chip);
	}
	else 
	{
		/* Select right side */
		chip=RIGHT;
		LcdInstructionWrite (Y_ADRESS + u8CursorX - 64,chip);
	}
	    	
    	/* Draw a char */
	while (u8CharColumn < (fonttype->u8Width) )
	{
		if ((fonttype->u8Height) > 8)
		{
			u8UpperCharPointer = 2;
			if (u8CursorX > 64)
				u8RightSide=64;
			LcdInstructionWrite (X_ADRESS + u8CursorY -1,chip); 
			LcdInstructionWrite (Y_ADRESS + u8CursorX -u8RightSide,chip);
			LcdDataWrite ((fonttype->au8FontTable)[( (u8Char - 32) * u8UpperCharPointer * (fonttype->u8Width) ) + ((u8CharColumn * u8UpperCharPointer) + 1)]);
			LcdInstructionWrite (Y_ADRESS + u8CursorX -u8RightSide,chip);
			
		}
		LcdInstructionWrite (X_ADRESS + u8CursorY,chip); 
		LcdDataWrite ((fonttype->au8FontTable)[( (u8Char - 32) * u8UpperCharPointer * (fonttype->u8Width) ) + (u8CharColumn * u8UpperCharPointer)]);		
		u8CharColumn++;
		u8CursorX++;
		
		/* test if a char is wrote on both sides of LCD */
		if (u8CursorX == 64)
		{
			u8RightSide=64;

			chip=RIGHT;


			LcdInstructionWrite (X_ADRESS + u8CursorY,chip);
			LcdInstructionWrite (Y_ADRESS + u8CursorX -u8RightSide,chip);
		}
	}
	
	/* Insert a space after a char */	
	if (u8CursorX < 128) 		/* Check if this is the last char of the line */
	{
		if ((fonttype->u8Height) > 8)
		{
			LcdInstructionWrite (X_ADRESS + u8CursorY - 1,chip); /* Select the page of the LCD */
			LcdInstructionWrite (Y_ADRESS + u8CursorX,chip);
			LcdDataWrite(0x00);
			LcdInstructionWrite (Y_ADRESS + u8CursorX -u8RightSide,chip);
		}
		LcdInstructionWrite (X_ADRESS + u8CursorY,chip); /* Select the page of the LCD */
		LcdInstructionWrite (Y_ADRESS + u8CursorX ,chip);	
		LcdDataWrite(0x00); 	/* if not then insert a space before next letter */
	}
			
	u8CharColumn++;
	u8CursorX++;
}

void LCD_Printf (uint8_t *au8Text, FONT_DEF *fonttype) 
{
	while(*au8Text != 0)
	{
		LcdPutchar (*au8Text,fonttype);
		au8Text++;
	}
}

void LCD_Setpos (uint8_t u8Column, uint8_t u8Line)
{
LCDGotoXY(u8Column,u8Line*8);
	u8CursorX = u8Column;
	u8CursorY = u8Line;
}


void bargraf(uint8_t yline,uint8_t start, uint8_t stop, uint8_t value)
{  uint8_t delka,i;
delka=stop-start;
LCDGotoXY(start,yline*8);
LCDWriteData(0x7F); //zacatek
for (i=start+1;i<stop;i++)
	{
	 if (i==64) {	 //prepnout na druhy cip
	 LCDGotoXY(64,yline*8);
	 }

	 if (i>(value*delka/255)+start)
	 	{
		LCDWriteData(0x41);
		}else
		{
		LCDWriteData(0x7F);
		}
	 
	}
LCDWriteData(0x7F); //konec
}
