/* LCD_GD-5360P
   Súbor definícií premenných a procedúr pre ovládanie LCD displeja GD-5360P na vývojovej doske
   TWR-S08LH64 s mikrokontrolérom MC9S08LH64.
   Autor: Miroslav Štibraný, FEKT VUT Brno 2014 */

#include "LCD_GD-5360P.h"
#include "STDIO.H"


const unsigned char table[] = {
/* Každá hodnota tohto poľa predstavuje jeden sedemsegmentový znak, ktorý sa dá zobraziť
   na jednej z piatich pozícií na displeji. Sedem bitov definovaných v každej hodnote
   predstavuje stavy jednotlivých segmentov sedemsegmentového znaku.
   Jednotka znamená zobrazený segment, nula nezobrazený segment.
   Hodnoty sú zoradené v poradí ASCII tabuľky od znaku pre medzeru po znak Z. */

 //  segment
 //  ABCDEFG      znak
   0b0000000,  // Space - zápisom znakov uvedených v zátvorkách sa vypíše medzera  
   0b0000000,  // (!)
   0b0100010,  //  " 
   0b0000000,  // (#) 
   0b0000000,  // ($)
   0b0000000,  // (%)
   0b0000000,  // (&) 
   0b0100000,  //  ' 
   0b0000000,  // (()
   0b0000000,  // ()) 
   0b0000000,  // (*) 
   0b0000000,  // (+)
   0b0000000,  // (,)
   0b0000001,  //  - 
   0b0000000,  // (.) 
   0b0000000,  // (/)
   0b1111110,  // 0
   0b0110000,  // 1
   0b1101101,  // 2
   0b1111001,  // 3
   0b0110011,  // 4
   0b1011011,  // 5
   0b1011111,  // 6
   0b1110000,  // 7 
   0b1111111,  // 8
   0b1111011,  // 9
   0b0000000,  // (:) 
   0b0000000,  // (;)
   0b0000000,  // (<) 
   0b0000000,  // (=) 
   0b0000000,  // (>)
   0b0000000,  // (?)
   0b0000000,  // (@) 
   0b1110111,  // A,a
   0b0011111,  // B,b
   0b1001110,  // C,c
   0b0111101,  // D,d
   0b1001111,  // E,e
   0b1000111,  // F,f 
   0b1111011,  // G,g
   0b0110111,  // H,h
   0b0000110,  // I,i
   0b0111100,  // J,j
   0b0110111,  // K,k
   0b0001110,  // L,l
   0b1010100,  // M,m
   0b0010101,  // N,n   
   0b1111110,  // O,o
   0b1100111,  // P,p
   0b1110011,  // Q,q
   0b0000101,  // R,r
   0b1011011,  // S,s
   0b0001111,  // T,t
   0b0111110,  // U,u
   0b0011100,  // V,v
   0b0101010,  // W,w
   0b0110111,  // X,x
   0b0111011,  // Y,y
   0b1101101   // Z,z
	  
};

const unsigned char table_special[] = {
/* Špeciálne znaky je možné dodefinovať užívateľom, je však potrebné upraviť hodnotu spec_char_max
   v hlavičkovom súbore LCD_GD-5360P.h na celkový počet definovaných znakov v tomto poli. */

//   ABCDEFG   kód znaku	 popis
   0b0001000,  // 0			 znak 	 (_)
   0b1100011,  // 1			 stupeň  (°)
   0b0000000   // 2			 prázdny znak

/* Označenie segmentov sedemsegmentového zobrazovacieho miesta
   __A__
  |     |
  F	    B
  |__G__|
  |	    |
  E	    C
  |__D__|
 
*/

};

const unsigned char bit_position[] = {
/* Každá hodnota v tomto poli je poradovým číslom bitu v registri LCDWF. Tento bit ovláda
   jeden zo segmentov A až G jedného z piatich sedemsegmentových znakov displeja. */
					 
// A B C D E F G
   1,0,0,0,1,1,1, 	// znak č.1
   1,0,0,0,1,1,1, 	// znak č.2
   1,0,0,0,1,1,1, 	// znak č.3
   0,1,1,0,0,0,1, 	// znak č.4
   0,1,1,1,0,0,1  	// znak č.5

};

unsigned char * const LCDWF_Registers[] = { 
/* Každá hodnota v poli je adresa registra LCDWF, ktorý ovláda jeden zo segmentov A až G jedného
   z piatich sedemsegmentových znakov displeja. */

// segmenty
//    A         B         C         D         E         F         G 		// pozícia
   &Reg(4),  &Reg(5),  &Reg(6),  &Reg(7),  &Reg(7),  &Reg(5),  &Reg(6),  	// znak č.1
   &Reg(14), &Reg(15), &Reg(16), &Reg(17), &Reg(17), &Reg(15), &Reg(16), 	// znak č.2
   &Reg(18), &Reg(21), &Reg(22), &Reg(23), &Reg(23), &Reg(21), &Reg(22), 	// znak č.3
   &Reg(29), &Reg(29), &Reg(27), &Reg(26), &Reg(27), &Reg(28), &Reg(28), 	// znak č.4
   &Reg(39), &Reg(39), &Reg(30), &Reg(26), &Reg(30), &Reg(38), &Reg(38)  	// znak č.5

};

void Init_clock ( void ) {
/* Inicializácia hodinového signálu pre LCD radič */

  //ICS Control Register 2
  ICSC2 = 0x46;
  // Nastavenie a povolenie externého hodinového signálu z oscilítora (hodinový signál OSCOUT)   

}
void Init_LCD ( void ) {

/* Inicializácia registrov LCD radiča */
  
  //LCD Control Register 0
  LCDC0 	= 0b00100001;
  //			||||||||
  //			|||||+++ - DUTY[2:0] - LCD displej GD-5360P má 2 backplane vývody
  //			||+++--- - LCLK[2:0] - Nastavenie rámcovej frekvencie ( 32 Hz )
  //			|+------ - SOURCE 	 - Zdroj hodinového signálu bude OSCOUT
  //			+------- - LCDEN	 - Radič LCD displeja musí byť zastavený počas nastavovania bitov SOURCE, LCLK a DUTY.
    				
  
  //LCD Regulated Voltage Control Register 				
  LCDRVC 	= 0b10000010;
  //			|---||||
  //			|---++++ - RVTRIM[3:0] - Doladenie napätia VIREG o -9% od základnej hodnoty
  //			+------- - RVEN 	   - Povolenie činnosti tohto regulovateľného zdroja 
  
  
  //LCD Voltage Supply Register
  LCDSUPPLY = 0b10110111;
  //			||||-|||
  //			||||-|++ - VSUPPLY[1:0] - Používa sa vnútorný regulovateľný zdroj ( VIREG )
  // 			||||-+-- - BBYPASS		- Zapisuje sa iniciálna hodnota bitu BBYPASS po resete ( na voľbe v tomto prípade nezáleží ) 
  //			||++---- - LADJ[1:0]	- Možnosť upraviť hodinový signál pre nábojovú pumpu
  //			|+------ - HREFSEL		- Nastavenie základnej hodnoty napätia VIREG na 1V
  //			+------- - CPSEL		- Povolenie činnosti nábojovej pumpy
   
 
  All_seg_off();
  // Vynulovanie registrov LCDWF, ktoré ovládajú frontplane piny LCD displeja.
  // LCDWF registre nemajú po resete mikrokontroléra definovaný stav, preto je ich potrebné vymazať, aby po inicializácii 
  // displeja neboli niektoré segmenty viditeľné ( náhodnými hodnotami v registroch LCDWF po resete mikrokontroléra ).
  
  
  // LCD Pin Enable Registers 0-5
  LCDPEN0 = 0xff;
  LCDPEN1 = 0xe0;                       
  LCDPEN2 = 0xff;
  LCDPEN3 = 0x7f;
  LCDPEN4 = 0xc0;
  LCDPEN5 = 0x03;
  // Povolenie pinov mikrokontroléra fungovať ako vývody LCD radiča
  // (zapisované hodnoty vyplývajú z konkrétneho prepojenia mikrokontroléra a LCD displeja)
   
  
  // Backplane Enable Registers 0-5  
  LCDBPEN0 = 0x00;        								
  LCDBPEN1 = 0x00;
  LCDBPEN2 = 0x00;
  LCDBPEN3 = 0x00;
  LCDBPEN4 = 0x00;
  LCDBPEN5 = 0x03; 
  // Nastavenie funkcie LCD pinov mikrokontroléra (frontplane alebo backplane)
  // (zapisované hodnoty vyplývajú z konkrétneho prepojenia mikrokontroléra a LCD displeja) 


  // LCD Waveform Registers
  LCDWF40 =  0x11;        
  LCDWF41 =  0x22;
  // Priradenie backplane vývodov displeja k určitej fáze riadenia ( A - COM1 a B - COM2 ) a zároveň povolenie 
  // fázy E a F pri alternatívnom zobrazení segmentov displeja pri využití blikania segmentov  
  // (do ktorého registra sa zapisuje, vyplýva z konkrétneho prepojenia mikrokontroléra a LCD displeja)
  
  
  // LCD Blink Control Register
  LCDBCTL = 0b10001011;
  //		  ||| ||||
  //		  ||| |+++ - BRATE[2:0] - frekvencia blikania segmentov ( 4=0.5 Hz, 3=1 Hz  - pri použití kryštálu 32.768 kHz )
  //		  |++-+--- - nastavenie spôsobu blikania segmentov bitmi ALT, BLANK a BMODE
  //		  +------- - BLINK 		- povolenie blikania segmentov  
  
  
  //LCD Control Register 0
  LCDC0_LCDEN = 1;
  // Povolenie činnosti LCD radiča
 
}

void All_seg_on ( void ) {
/* Procedúra zobrazí všetky segmenty displeja */
  
   Reg(0)  =  seg_on;   	Reg(1)  =  seg_on;   	Reg(2)  =  seg_on;   	Reg(3)  =  seg_on;
   Reg(4)  =  seg_on;   	Reg(5)  =  seg_on;   	Reg(6)  =  seg_on;   	Reg(7)  =  seg_on;
   Reg(13) =  seg_on;   	Reg(14) =  seg_on;   	Reg(15) =  seg_on;   	Reg(16) =  seg_on;
   Reg(17) =  seg_on;   	Reg(18) =  seg_on;   	Reg(19) =  seg_on;   	Reg(20) =  seg_on;
   Reg(21) =  seg_on;   	Reg(22) =  seg_on;   	Reg(23) =  seg_on;   	Reg(24) =  seg_on;
   Reg(25) =  seg_on;   	Reg(26) =  seg_on;   	Reg(27) =  seg_on;   	Reg(28) =  seg_on;
   Reg(29) =  seg_on;   	Reg(30) =  seg_on;   	Reg(38) =  seg_on;   	Reg(39) =  seg_on;

}
  
void All_seg_off ( void ) { 
/* Procedúra vypne všetky segmenty LCD displeja GD-5360P
   (vynulovaním LCDWF registrov ovládajúcich frontplane piny LCD displeja) */

  Clr_reg(0);   Clr_reg(1);   Clr_reg(2);   Clr_reg(3);   Clr_reg(4);   Clr_reg(5);   Clr_reg(6);   
  Clr_reg(7);   Clr_reg(13);  Clr_reg(14);  Clr_reg(15);  Clr_reg(16);  Clr_reg(17);  Clr_reg(18); 
  Clr_reg(19);  Clr_reg(20);  Clr_reg(21);  Clr_reg(22);  Clr_reg(23);  Clr_reg(24);  Clr_reg(25);
  Clr_reg(26);  Clr_reg(27);  Clr_reg(28);  Clr_reg(29);  Clr_reg(30);  Clr_reg(38);  Clr_reg(39);

}

void Write_char ( char character, unsigned char position ) {
/* Procedúra zapíše zadaný znak na zadanú pozíciu, kontroluje sa, či taká pozícia existuje
   alebo či je taký znak definovaný v poli table[], pokiaľ neexistuje, výpis sa nevykoná. */
/* V registrov LCDWF sa zapisuje do bitov ovládajúcich segmenty oboch fází (normálnej - A,B  aj alternatívnej - E,F) */
 
  char i = 0;
  
  if (  !( ( position ) && ( position <= max_position ) )  ) return;	// Kontrola pozície, povolené pozície sú 1 2 3 4 5
  if ( ( character <= 'z' ) && ( character >='a' ) ) character -= ( 'a' - 'A' );  	  		// Malý znak sa zobrazí tak ako keby bol zadaný veľký znak
  if ( ( character > char_range_max ) || ( character < char_range_min ) )	return;		// Pokus zapísať znak, ktorý nie je v rozsahu

  character -= char_range_min;
  
  for ( i=0; i<7; i++ ) {  // Cyklus zapíše vo vhodných LCDWF registrov a do vhodných bitov v nich hodnotu log.1 alebo log.0 podľa poľa znakov table[].
      if (  ( table[character] ) & ( 1 << (6-i) )   ) 								   
          *LCDWF_Registers[(position-1)*7+i] |= ( 1 << bit_position[(position-1)*7+i] ) | (1 << (4+bit_position[(position-1)*7+i]) );
      else 
          *LCDWF_Registers[(position-1)*7+i] &= ~( ( 1 << bit_position[(position-1)*7+i] ) | (1 << (4+bit_position[(position-1)*7+i]) ) ); 
  }
 
}

void Write_spec_char ( unsigned char char_code, unsigned char position ) {
/* Procedúra zapíše špeciálny znak ( nadefinovaný v poli table_special[] ) na zadanú pozíciu, kontroluje sa,
   či taká pozícia existuje alebo či je taký znak nadefinovaný, pokiaľ neexistuje, výpis sa nevykoná. */
/* V registrov LCDWF sa zapisuje do bitov ovládajúcich segmenty oboch fází (normálnej - A,B  aj alternatívnej - E,F) */     
   
   char i = 0;
   
   if (  !( ( position ) && ( position <= max_position ) )  ) return;
   if ( char_code > spec_char_max-1 ) return;
   
   for ( i=0; i<7; i++ ) {  // Cyklus zapíše vo vhodných registrov LCDWF do vhodných bitov log.1 alebo log.0 podľa poľa table_special[].
      if (  ( table_special[char_code] ) & ( 1 << (6-i) )   ) 								   
          *LCDWF_Registers[(position-1)*7+i] |= ( 1 << bit_position[(position-1)*7+i] ) | (1 << (4+bit_position[(position-1)*7+i]) );
      else 
          *LCDWF_Registers[(position-1)*7+i] &= ~( ( 1 << bit_position[(position-1)*7+i] ) | (1 << (4+bit_position[(position-1)*7+i]) ) );  
   }

}

void Write_string ( char *str, unsigned char init_position ) {
/* Procedúra, ktorá zapisuje znakový reťazec str od pozície init_postition */
	
	while( *str ) Write_char( *str++, init_position++ );

}

void Num_aux ( int num ) { 
/* Výpis čísla v rozsahu -9 až 99 na sedemsegmentových pozíciach 4 a 5.
   Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */
    
    char new_string[3];
    
    if( num > 99 || num < -9 ) {
	    Write_string("OL",4);  // Range overload
	    return;
	}

	if ( sprintf( new_string ,"%2d" , num ) ) Write_string(new_string,4);

}

void Num_aux_hex ( unsigned int num ) { 
/* Výpis čísla v rozsahu 0 až 0xff na sedemsegmentových pozíciach 4 a 5.
   Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */
    
    char new_string[3];
    
    if ( num > 0xff ) {
	    Write_string("OL",4);  // Range overload
	    return;
	}

	if ( sprintf( new_string ,"%2x" , num ) ) Write_string(new_string,4);

}

void Num_small ( int num ) { 
/* Výpis čísla v rozsahu -99 až 1 999, s využitím segmentu T1 (znak jedna) a sedemsegmentových znakov 1 až 3.
   Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */
    
    char new_string[4];
    
    COL_off;
    
    if( num > 1999 || num < -99 ) {
	    Write_string(" OL",1);  // Range overload
	    T1_off;
		return;
	}
	if( num >= 1000 ) {
		T1_on;
		num-= 1000;
		if ( sprintf( new_string ,"%03d" , num ) ) Write_string(new_string,1);
	}
	else {
		T1_off;
	    if ( sprintf( new_string ,"%3d" , num ) ) Write_string(new_string,1);
	}

}

void Num_small_hex ( unsigned int num ) { 
/* Výpis čísla v rozsahu 0 až 0x1fff, s využitím segmentu T1 (znak jedna) a sedemsegmentových znakov 1 až 3.
   Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */
    
    char new_string[4];
    
    COL_off;
    
	if( num > 0x1fff ) {
		Write_string(" OL",1);  // Range overload
	    T1_off;
		return;
	}
	if( num >= 0x1000 ) {
		T1_on;
		num-= 0x1000;
		if ( sprintf( new_string ,"%03x" , num ) ) Write_string(new_string,1);
	}
	else {
		T1_off;
	    if ( sprintf( new_string ,"%3x" , num ) ) Write_string(new_string,1);
	}

}

void Num_big ( long int num ) {
/* Výpis čísla v rozsahu -9999 až 199 999, s využitím segmentu T1 (znak jedna) a sedemsegmentových znakov 1 až 5.
   Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */

	char new_string[6];
	
	COL_off;

	if( num > 199999 || num < -9999 ) {
	    Write_string(" OL  ",1);  // Range overload
	    T1_off;
		return;
	}
	if( num >= 100000 ) {
		T1_on;
		num-= 100000;
	    if ( sprintf( new_string ,"%05ld" , num ) ) Write_string(new_string,1);
	}
	else {
		T1_off; 
	    if ( sprintf( new_string ,"%5ld" , num ) ) Write_string(new_string,1);
	}
	
}

void Num_big_hex ( unsigned long int num ) { 
/* Výpis čísla v rozsahu 0 až 0x1fffff, s využitím segmentu T1 (znak jedna) a sedemsegmentových znakov 1 až 5.
	Pri pokuse vypísať číslo mimo rozsah funkcia vypíše na displej OL ( overload ) */
    
    char new_string[6];
    
    COL_off;
    
	if( num > 0x1fffff ) {
		Write_string(" OL  ",1);  //range overload
	    T1_off;
		return;
	}
	if( num >= 0x100000 ) {
		T1_on;
		num-= 0x100000;
		if ( sprintf( new_string ,"%05lx" , num ) ) Write_string(new_string,1);
	}
	else {
		T1_off;
	    if ( sprintf( new_string ,"%5lx" , num ) ) Write_string(new_string,1);
	}

}

void Set_week_day ( enum days day ) {
/* Procedúra sa stará o vypisovanie zvoleného dňa v týždni. */  
  
  Clr_week_day();
  
  switch ( day ) {
  	case MO: MO_on; break;
  	case TU: TU_on; break;
  	case WE: WE_on; break;
  	case TH: TH_on; break;
  	case FR: FR_on; break;
  	case SA: SA_on; break;
  	case SU: SU_on; break;
  }

}

void Clr_week_day ( void ) {
/* Procedúra zmaže segmenty dní v týždni z displeja */
  
  MO_off; TU_off; WE_off; TH_off; FR_off; SA_off; SU_off;

}

void Battery ( unsigned char ctrl ) {
/* Procedúra zobrazuje stav batérie. */
  
  P0_off; P1_off; P2_off; P3_off; P4_off;

  switch ( ctrl ) {
  	case 0: break;
  	case 1: P0_on; break; 
  	case 2: P0_on; P1_on; break;
  	case 3: P0_on; P1_on; P2_on; break;
	case 4: P0_on; P1_on; P2_on; P3_on; break;	
	case 5: P0_on; P1_on; P2_on; P3_on; P4_on; break;
	default: P0_off; P1_off; P2_off; P3_off; P4_off; break;	
  }

}

void Time_HM ( unsigned int hours, unsigned int minutes ) {
/* Výpis hodín - vstupom je počet hodín od 0 do 23, počet minút od 0 do 59. 
   Na displeji sa zobrazujú v 12 hodinovom formáte ( AM / PM ). Znak dvojbodky bliká. */ 	

	if ( (hours > 23) || (minutes > 59) ) return;
	if ( hours > 11 ) {		/* oprava kodu, 11AM -> 12PM --->11PM - > 12AM */
		if( hours > 12 ) hours -= 12;
	    AM_off;
	    PM_on;
	}
	else {
		if ( !hours ) hours = 12;
		AM_on;
		PM_off;
	}
	
	
    Num_small( hours*100 + minutes );
	if ( !hours ) Write_char( '0', 1 );
	if ( minutes < 10 ) Write_char( '0', 2 );
	   
    COL_blink_on;

}

void Time_HMS ( unsigned int hours, unsigned int minutes, unsigned int seconds ) {
/* Procedúra pre laboratórnu úlohu */
/* Výpis hodín - vstupom je počet hodín od 0 do 23, počet minút od 0 do 59 a počet sekúnd od 0 do 59. 
   Na displeji sa zobrazujú v 12 hodinovom formáte ( AM / PM ). Znak dvojbodky bliká. */	

	char new_string[3];
	
	if ( (hours > 24) || (minutes > 59) || (seconds > 59) ) return;
	Time_HM ( hours, minutes );
	if ( sprintf( new_string ,"%02d" , seconds ) ) Write_string( new_string, 4 );
	
}

void Stopwatch ( unsigned int minutes, unsigned int seconds, unsigned int ticks ) {
/* Procedúra pre laboratórnu úlohu */
/* Výpis času pri stopkách. Vstupom je počet minút od 0 do 19, počet sekúnd od 0 do 59 a počet stotín sekundy od 0 do 99. */
	
	char new_string[3];

	if ( (minutes > 19) || (seconds > 59) || (ticks > 99) ) return;
	
	Num_small( minutes*100 + seconds );
	if ( !minutes ) Write_char( '0', 1 );
	if ( seconds < 10 ) Write_char( '0', 2 );	
	if ( sprintf( new_string ,"%02d" , ticks ) ) Write_string( new_string, 4 );
	
	COL_blink_on;
	
}