//-------------------------------------------------------------------------------------------------
// Graphic LCD with Toshiba T6963 controller
// Copyright (c) Radoslaw Kwiecien, 2007r
// http://en.radzio.dxp.pl/t6963/
// Compiler : avr-gcc

// Angepasst Christoph Wostmann
// Datum: 06.09.2009
//-------------------------------------------------------------------------------------------------
#include "T6963C.h"

struct FONT_DEF {
   const unsigned int glyph_width;                 /* glyph width in pixels              */
                                             /*     0 for variable width fonts     */
   const unsigned int glyph_height;                /* glyph height for storage           */
   const unsigned char *glyph_table;         /* font table start address in memory */
   const unsigned char *mapping_table;       /* used to find the index of a glyph  */
   const unsigned char *width_table;         /* width table start adress           */
                                             /*        NULL for fixed width fonts  */
   const unsigned int *offset_table;         /* ks the offsets of the first byte*/
                                             /*                      of each glyph */
} ;


extern unsigned char pow2(unsigned char b);            
//extern void GLCD_lcd_print2_p(unsigned int x,unsigned int y, const char *in, const struct FONT_DEF *strcut1, unsigned char invers);
//extern void GLCD_lcd_print2(unsigned int x,unsigned int y, const char *in, const struct FONT_DEF *strcut1, unsigned char invers);

//#define GLCD_lcd_print2_P(__x, __y, __in, __s1, __i)       GLCD_lcd_print2_p(__x,__y,PSTR(__in),__s1,__i)

const unsigned char color = 1;

//-------------------------------------------------------------------------------------------------
//
// Delay function
//	
//-------------------------------------------------------------------------------------------------
void delay(void)
{
volatile unsigned char i;
for(i = 0; i < (F_CPU/1000000); i++)
  {
  asm("nop");
  }
}
//-------------------------------------------------------------------------------------------------
//
// Ports intalization
//
//-------------------------------------------------------------------------------------------------
void GLCD_InitalizeInterface(void)
{
GLCD_DATA_DDR = 0xFF;
GLCD_CTRL_DDR = ((1 << GLCD_WR) | (1 << GLCD_RD) | (1 << GLCD_CE) | (1 << GLCD_CD) | (1 << GLCD_RESET) | (1 << GLCD_FS));
GLCD_CTRL_PORT |= ((1 << GLCD_WR) | (1 << GLCD_RD) | (1 << GLCD_CE) | (1 << GLCD_CD) | (1 << GLCD_RESET) | (1 << GLCD_FS));
}
//-------------------------------------------------------------------------------------------------
//
// Reads dispay status
//
//-------------------------------------------------------------------------------------------------
unsigned char GLCD_ChceckStatus(void)
{
uint8_t tmp;
GLCD_DATA_DDR = 0x00;

GLCD_CTRL_PORT &= ~((1 << GLCD_RD) | (1 << GLCD_CE));
delay();
  
//  asm volatile ("nop"::);
//  asm volatile ("nop"::);


tmp = GLCD_DATA_PIN;
GLCD_DATA_DDR = 0xFF;
GLCD_CTRL_PORT |= ((1 << GLCD_RD) | (1 << GLCD_CE));
return tmp;
}
//-------------------------------------------------------------------------------------------------
//
// Writes instruction 
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteCommand(unsigned char command)
{
while(!(GLCD_ChceckStatus()&0x03));
GLCD_DATA_PORT = command;

GLCD_CTRL_PORT &= ~((1 << GLCD_WR) | (1 << GLCD_CE));
delay();

//  asm volatile ("nop"::);
//  asm volatile ("nop"::);


GLCD_CTRL_PORT |= ((1 << GLCD_WR) | (1 << GLCD_CE));
}

//-------------------------------------------------------------------------------------------------
//
// Writes data
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteData(unsigned char data)
{
while(!(GLCD_ChceckStatus()&0x03));
GLCD_DATA_PORT = data;

GLCD_CTRL_PORT &= ~((1 << GLCD_WR) | (1 << GLCD_CE) | (1 << GLCD_CD));

delay();

  asm volatile ("nop"::);
  asm volatile ("nop"::);

GLCD_CTRL_PORT |= ((1 << GLCD_WR) | (1 << GLCD_CE) | (1 << GLCD_CD));
}
//-------------------------------------------------------------------------------------------------
//
// Reads data
//
//-------------------------------------------------------------------------------------------------
unsigned char GLCD_ReadData(void)
{
uint8_t tmp;
while(!(GLCD_ChceckStatus()&0x03));
GLCD_DATA_DDR = 0x00;

GLCD_CTRL_PORT &= ~((1 << GLCD_RD) | (1 << GLCD_CE) | (1 << GLCD_CD));
delay();

  asm volatile ("nop"::);	// wait 150ns, sind das 150ns?						dies warten fehlte bisher!
  asm volatile ("nop"::);
  asm volatile ("nop"::);
  asm volatile ("nop"::);
  asm volatile ("nop"::);
  asm volatile ("nop"::);

tmp = GLCD_DATA_PIN;
GLCD_CTRL_PORT |= ((1 << GLCD_RD) | (1 << GLCD_CE) | (1 << GLCD_CD));
GLCD_DATA_DDR = 0xFF;
return tmp;
}
//-------------------------------------------------------------------------------------------------
//
// Sets address pointer for display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_SetAddressPointer(unsigned int address)
{
GLCD_WriteData(address & 0xFF);
GLCD_WriteData(address >> 8);
GLCD_WriteCommand(T6963_SET_ADDRESS_POINTER);
}
//-------------------------------------------------------------------------------------------------
//
// Clears text area of display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_ClearText(void)
{
int i;
GLCD_SetAddressPointer(GLCD_TEXT_HOME);

for(i = 0; i < GLCD_TEXT_SIZE; i++)
  {
  GLCD_WriteDisplayData(0);
  }
}
//-------------------------------------------------------------------------------------------------
//
// Clears characters generator area of display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_ClearCG(void)
{
unsigned int i;
GLCD_SetAddressPointer(GLCD_EXTERNAL_CG_HOME);

for(i = 0; i < 256 * 8; i++)
  {
  GLCD_WriteDisplayData(0);
  }
}
//-------------------------------------------------------------------------------------------------
//
// Clears graphics area of display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_ClearGraphic(void)
{
int i;
GLCD_SetAddressPointer(GLCD_GRAPHIC_HOME);
for(i = 0; i < GLCD_GRAPHIC_SIZE; i++)
  {
  GLCD_WriteDisplayData(0x00);
  }
}
//-------------------------------------------------------------------------------------------------
//
// Writes a single character (ASCII code) to display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteChar(char charCode)
{
GLCD_WriteDisplayData(charCode - 32);
}
//-------------------------------------------------------------------------------------------------
//
// Writes null-terminated string to display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteString(char * string)
{
while(*string)
  {
  GLCD_WriteChar(*string++);
  }
}
//-------------------------------------------------------------------------------------------------
//
// Writes null-terminated string from program memory to display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteStringPgm(prog_char * string)
{
char ch;
while((ch = pgm_read_byte(string++)))
  {
  GLCD_WriteChar(ch);
  }
}
//-------------------------------------------------------------------------------------------------
//
// Sets display coordinates
//
//-------------------------------------------------------------------------------------------------
void GLCD_TextGoTo(unsigned char x, unsigned char y)
{
unsigned int address;

address = GLCD_TEXT_HOME +  x + (GLCD_TEXT_AREA * y);

GLCD_SetAddressPointer(address);
}
//-------------------------------------------------------------------------------------------------
//
// Writes single char pattern to character generator area of display RAM memory
//
//-------------------------------------------------------------------------------------------------
void GLCD_DefineCharacter(unsigned char charCode, unsigned char * defChar)
{
unsigned int address;
unsigned char i; 

address = GLCD_EXTERNAL_CG_HOME + (8 * charCode);

GLCD_SetAddressPointer(address);

for(i = 0; i < 8 ; i++)
  {
  GLCD_WriteDisplayData(*(defChar + i));
  }
}

//-------------------------------------------------------------------------------------------------
//
// Set (if color==1) or clear (if color==0) pixel on screen
//
//-------------------------------------------------------------------------------------------------
void GLCD_SetPixel(unsigned char x, unsigned char y, unsigned char color)
{
unsigned char tmp;
unsigned int address;

address = GLCD_GRAPHIC_HOME + (x / GLCD_FONT_WIDTH) + (GLCD_GRAPHIC_AREA * y);

GLCD_SetAddressPointer(address);

GLCD_WriteCommand(T6963_DATA_READ_AND_NONVARIABLE);
tmp = GLCD_ReadData();

if(color)
  tmp |= (1 <<  (GLCD_FONT_WIDTH - 1 - (x % GLCD_FONT_WIDTH)));
else
 tmp &= ~(1 <<  (GLCD_FONT_WIDTH - 1 - (x % GLCD_FONT_WIDTH)));

GLCD_WriteDisplayData(tmp);

}
//-------------------------------------------------------------------------------------------------
//
// Writes display data and increment address pointer
//
//-------------------------------------------------------------------------------------------------
void GLCD_WriteDisplayData(unsigned char x)
{
GLCD_WriteData(x);
GLCD_WriteCommand(T6963_DATA_WRITE_AND_INCREMENT);
}
//-------------------------------------------------------------------------------------------------
//
// Sets graphics coordinates
//
//-------------------------------------------------------------------------------------------------
void GLCD_GraphicGoTo(unsigned char x, unsigned char y)
{
unsigned int address;
address = GLCD_GRAPHIC_HOME + (x / GLCD_FONT_WIDTH) + (GLCD_GRAPHIC_AREA * y);
GLCD_SetAddressPointer(address);
}
//-------------------------------------------------------------------------------------------------
//
// Displays bitmap from program memory
//
//-------------------------------------------------------------------------------------------------

void GLCD_Bitmap(unsigned char * bitmap, unsigned char x, unsigned char y, unsigned char width, unsigned char height)
{
unsigned char i, j;

for(j = 0; j < height; j++)
{
GLCD_GraphicGoTo(x, y + j);
for(i = 0; i < width/GLCD_FONT_WIDTH; i++)
  {
 //  GLCD_WriteDisplayData(pgm_read_byte(bitmap + i + (GLCD_GRAPHIC_AREA  * j)));
     GLCD_WriteDisplayData(pgm_read_byte(bitmap + i + ((width / GLCD_FONT_WIDTH) * j)));

  }
}
}

//-------------------------------------------------------------------------------------------------
//
// Draws an Rectangle
//
//-------------------------------------------------------------------------------------------------
void GLCD_Rectangle(unsigned char x, unsigned char y, unsigned char b, unsigned char a)
{
  unsigned char j; // zmienna pomocnicza
  // rysowanie linii pionowych (boki)
  for (j = 0; j < a; j++) {
		GLCD_SetPixel(x, y + j, color);
		GLCD_SetPixel(x + b - 1, y + j, color);
	}
  // rysowanie linii poziomych (podstawy)
  for (j = 0; j < b; j++)	{
		GLCD_SetPixel(x + j, y, color);
		GLCD_SetPixel(x + j, y + a - 1, color);
	}
}

//-------------------------------------------------------------------------------------------------
//
// Draws a Circle
//
//-------------------------------------------------------------------------------------------------
void GLCD_Circle(unsigned char cx, unsigned char cy ,unsigned char radius, unsigned char colorCircle)
{
int x, y, xchange, ychange, radiusError;
x = radius;
y = 0;
xchange = 1 - 2 * radius;
ychange = 1;
radiusError = 0;
while(x >= y)
  {
  GLCD_SetPixel(cx+x, cy+y, colorCircle); 
  GLCD_SetPixel(cx-x, cy+y, colorCircle); 
  GLCD_SetPixel(cx-x, cy-y, colorCircle);
  GLCD_SetPixel(cx+x, cy-y, colorCircle); 
  GLCD_SetPixel(cx+y, cy+x, colorCircle); 
  GLCD_SetPixel(cx-y, cy+x, colorCircle); 
  GLCD_SetPixel(cx-y, cy-x, colorCircle); 
  GLCD_SetPixel(cx+y, cy-x, colorCircle); 
  y++;
  radiusError += ychange;
  ychange += 2;
  if ( 2*radiusError + xchange > 0 )
    {
    x--;
	radiusError += xchange;
	xchange += 2;
	}
  }
}
//-------------------------------------------------------------------------------------------------
//
// Draws a line
//
//-------------------------------------------------------------------------------------------------
void GLCD_Line(int X1, int Y1,int X2,int Y2, int farbe)
{
int CurrentX, CurrentY, Xinc, Yinc, 
    Dx, Dy, TwoDx, TwoDy, 
	TwoDxAccumulatedError, TwoDyAccumulatedError;

Dx = (X2-X1); // obliczenie składowej poziomej
Dy = (Y2-Y1); // obliczenie składowej pionowej

TwoDx = Dx + Dx; // podwojona składowa pozioma
TwoDy = Dy + Dy; // podwojona składowa pionowa

CurrentX = X1; // zaczynamy od X1
CurrentY = Y1; // oraz Y1

Xinc = 1; // ustalamy krok zwiększania pozycji w poziomie 
Yinc = 1; // ustalamy krok zwiększania pozycji w pionie

if(Dx < 0) // jesli składowa pozioma jest ujemna 
  {
  Xinc = -1; // to będziemy się "cofać" (krok ujemny)
  Dx = -Dx;  // zmieniamy znak składowej na dodatni
  TwoDx = -TwoDx; // jak również podwojonej składowej
  }

if (Dy < 0) // jeśli składowa pionowa jest ujemna
  {
  Yinc = -1; // to będziemy się "cofać" (krok ujemny)
  Dy = -Dy; // zmieniamy znak składowej na dodatki
  TwoDy = -TwoDy; // jak równiez podwojonej składowej
  }

GLCD_SetPixel(X1,Y1, farbe); // stawiamy pierwszy krok (zapalamy pierwszy piksel)

if ((Dx != 0) || (Dy != 0)) // sprawdzamy czy linia składa się z więcej niż jednego punktu ;)
  {
  // sprawdzamy czy składowa pionowa jest mniejsza lub równa składowej poziomej
  if (Dy <= Dx) // jeśli tak, to idziemy "po iksach"
    { 
    TwoDxAccumulatedError = 0; // zerujemy zmienną 
    do // ruszamy w drogę
	  {
      CurrentX += Xinc; // do aktualnej pozycji dodajemy krok 
      TwoDxAccumulatedError += TwoDy; // a tu dodajemy podwojoną składową pionową
      if(TwoDxAccumulatedError > Dx)  // jeśli TwoDxAccumulatedError jest większy od Dx
        {
        CurrentY += Yinc; // zwiększamy aktualną pozycję w pionie
        TwoDxAccumulatedError -= TwoDx; // i odejmujemy TwoDx
        }
       GLCD_SetPixel(CurrentX,CurrentY, farbe);// stawiamy następny krok (zapalamy piksel)
       }while (CurrentX != X2); // idziemy tak długo, aż osiągniemy punkt docelowy
     }
   else // w przeciwnym razie idziemy "po igrekach" 
      {
      TwoDyAccumulatedError = 0; 
      do 
	    {
        CurrentY += Yinc; 
        TwoDyAccumulatedError += TwoDx;
        if(TwoDyAccumulatedError>Dy) 
          {
          CurrentX += Xinc;
          TwoDyAccumulatedError -= TwoDy;
          }
         GLCD_SetPixel(CurrentX,CurrentY, farbe); 
         }while (CurrentY != Y2);
    }
  }
}




//-------------------------------------------------------------------------------------------------
//
// Display initalization
//
//-------------------------------------------------------------------------------------------------
void GLCD_Initalize(void)
{
GLCD_InitalizeInterface();

GLCD_CTRL_PORT &= ~(1 << GLCD_RESET);
_delay_ms(5);
GLCD_CTRL_PORT |= (1 << GLCD_RESET);

#if (GLCD_FONT_WIDTH == 8)
GLCD_CTRL_PORT &= ~(1 << GLCD_FS);
#endif

GLCD_WriteData(GLCD_GRAPHIC_HOME & 0xFF);
GLCD_WriteData(GLCD_GRAPHIC_HOME >> 8);
GLCD_WriteCommand(T6963_SET_GRAPHIC_HOME_ADDRESS);

GLCD_WriteData(GLCD_GRAPHIC_AREA);
GLCD_WriteData(0x00);
GLCD_WriteCommand(T6963_SET_GRAPHIC_AREA);

GLCD_WriteData(GLCD_TEXT_HOME);
GLCD_WriteData(GLCD_TEXT_HOME >> 8);
GLCD_WriteCommand(T6963_SET_TEXT_HOME_ADDRESS);

GLCD_WriteData(GLCD_TEXT_AREA);
GLCD_WriteData(0x00);
GLCD_WriteCommand(T6963_SET_TEXT_AREA);

GLCD_WriteData(GLCD_OFFSET_REGISTER);
GLCD_WriteData(0x00);
GLCD_WriteCommand(T6963_SET_OFFSET_REGISTER);

GLCD_WriteCommand(T6963_DISPLAY_MODE  | T6963_GRAPHIC_DISPLAY_ON   | T6963_TEXT_DISPLAY_ON /*| T6963_CURSOR_DISPLAY_ON*/);

GLCD_WriteCommand(T6963_MODE_SET | T6963_MODE_XOR);

}

//
// TEST //

void GLCD_lcd_print2_p(unsigned int x,unsigned int y, const char *in, const struct FONT_DEF *strcut1, unsigned char invers)
{
	register unsigned int offset,width;
	register unsigned char i,j,map,ertefah,allwidth=0;

	while((map = pgm_read_byte(in++))) 
	{
		map = pgm_read_byte(&strcut1->mapping_table[map]);

		width = strcut1->glyph_width;
		if(width == 0)
			width = pgm_read_byte(&strcut1->width_table[map]);

		offset = pgm_read_word(&strcut1->offset_table[map]);
		ertefah = strcut1->glyph_height;

	
		for(j=0 ; j<ertefah * (((width-1)/8)+1) ; j+=(((width-1)/8)+1)    )
		{   // ertefah
			for(i=0 ; i<width  ; i++)
			{   //  width
				if( pgm_read_byte(&strcut1->glyph_table[ offset+j+(i/8) ]) & (1 << ( 7 - ( i % 8 ) ) ) )
					GLCD_SetPixel(  x+i+allwidth , y+j/ (((width-1)/8)+1), !invers  );
	 			else
	 				GLCD_SetPixel(  x+i+allwidth , y+j/ (((width-1)/8)+1), invers  );
				}//End i
			}// End j
		allwidth+=width;
	}// End K
}

void GLCD_lcd_print2(unsigned int x,unsigned int y, const char *in, const struct FONT_DEF *strcut1, unsigned char invers)
{
	register unsigned int offset,width;
	register unsigned char i,j,map,ertefah,allwidth=0;

	while((map = *in++)) 
	{
		map = pgm_read_byte(&strcut1->mapping_table[map]);

		width = strcut1->glyph_width;
		if(width == 0)
			width = pgm_read_byte(&strcut1->width_table[map]);

		offset = pgm_read_word(&strcut1->offset_table[map]);
		ertefah = strcut1->glyph_height;

	
		for(j=0 ; j<ertefah * (((width-1)/8)+1) ; j+=(((width-1)/8)+1)    )
		{   // ertefah
			for(i=0 ; i<width  ; i++)
			{   //  width
				if( pgm_read_byte(&strcut1->glyph_table[ offset+j+(i/8) ]) & (1 << ( 7 - ( i % 8 ) ) ) )
					GLCD_SetPixel(  x+i+allwidth , y+j/ (((width-1)/8)+1), !invers  );
	 			else
	 				GLCD_SetPixel(  x+i+allwidth , y+j/ (((width-1)/8)+1), invers  );
				}//End i
			}// End j
		allwidth+=width;
	}// End K
}


// NEUER VERSUCH DER DARSTELLUNG
// *****************************
uint8_t GLCD_lcd_char(unsigned char x,unsigned char y, char in, const struct FONT_DEF *strcut1, unsigned char invers)
{
  uint8_t map;
  uint8_t width;
  uint16_t offset;
  uint8_t height;
  unsigned char buffer[GLCD_GRAPHIC_AREA];
  
  uint8_t spalte,zeile;
  uint8_t counter=0;
  uint8_t buffer_counter=0;
  uint8_t pixel=0,bc;
  uint8_t o;
  
  map = in;                  // Index des Zeichens
  map = pgm_read_byte(&strcut1->mapping_table[map]);
  width = pgm_read_byte(&strcut1->width_table[map]);        // Breite des Zeichens
  offset = pgm_read_word(&strcut1->offset_table[map]);        // Die Startadresse, wo das Zeichen anfaengt
  height = strcut1->glyph_height;            // Die Hoehe des Fonts
  
  for(zeile=0;zeile<=height;zeile++)            // Zeilenweise lesen wir das Zeichen
  {
    GLCD_GraphicGoTo(x,y+zeile);              // Zur Position auf dem Screen gehen
    for(spalte=0;spalte<((width-1)/8)+1;spalte++)        // Wir lesen pro Spalte nur soviel bytes, wie benoetigt
    {
      buffer[buffer_counter]= pgm_read_byte(&strcut1->glyph_table[offset+counter]);// Ein Byte lesen
    //if(invers > 0) buffer[buffer_counter]= ~buffer[buffer_counter];//bitweises invertieren des Blocks
	  counter++;                // Zaehler erhoehen
      buffer_counter++;
      buffer[buffer_counter]=0;              // das naechste buffer byte loeschen, sonst steht muell drin
    }
    
    for(pixel=0;pixel<x-((x/6)*6);pixel++)          // Um soviel bits muessen wir das Zeichen nach rechts verschieben
    {
      for(bc=buffer_counter;bc>0;bc--)            // Rueckwaerts zaehlen, bis die Anzahl der gelesenen Bytes um 1 bit verschoben ist
      {
        buffer[bc]>>=1;                // Buffer um 1 bit nach rechts shiften
        buffer[bc]|=(buffer[bc-1] & 1<<0)<<5;          // Ueberlauf bit vom vorherigen byte holen
      }
      buffer[0]>>=1;                // Buffer 0 muss nur geshiftet werden, hier gibt's keinen Ueberlauf
    }
  
    GLCD_GraphicGoTo(x,y+zeile);              // Zur Position auf dem Screen gehen
    for(o=0;o<=buffer_counter;o++)            // Alle bytes + 1 durchzaehlen
    {
      
      GLCD_WriteCommand(T6963_DATA_READ_AND_NONVARIABLE);        // 0xC5 = Lese Display ohne adresse zu aendern
  //    Das ist auch noch nicht der Weisheit letzter Schluss
	  buffer[o] |= GLCD_ReadData();              // Hintergrund-byte mit buffer-byte zusammenfuegen
      GLCD_WriteData(buffer[o]);              // Den buffer ausgeben
      GLCD_WriteCommand(T6963_DATA_WRITE_AND_INCREMENT);           // 0xC0 = Schreibe und erhoehe die adresse +1
    }

    buffer_counter=0;

  }
  
  return width;                  // Rueckgabewert ist die Breite des Zeichens
}
/****************************************************************************
*
* Gives lenght of string
*
****************************************************************************/
uint8_t strlenght(const char *string) {
  const char *s;

  s = string;
  while (*s)
    s++;
  return s - string-1;
}

void GLCD_lcd_print(unsigned char x, unsigned char y, const char *in,  const struct FONT_DEF *strcut1, unsigned char invers)
{
  unsigned int z;
  int width=x;
  for(z=0;z<=strlenght(in);z++)
  {
    width+=GLCD_lcd_char(width+2,y,in[z],strcut1,invers);
      width--;
  }
    //glcd_box(s,y+2,s+width,y+h-1,TRUE);
}
