#include "msp430x20x3.h"

//*** MSP430F2013 mit integriertem RC-Oszillator

#define delay_1us()     for (_counter1_Delay=0; _counter1_Delay<1; _counter1_Delay++);	//*** 9us
#define delay_5us()	    for (_counter1_Delay=0; _counter1_Delay<1; _counter1_Delay++);	//*** 9us
#define delay_50us()	for (_counter1_Delay=0; _counter1_Delay<4; _counter1_Delay++);  //*** 52us
#define delay_1ms()     for (_counter1_Delay=0; _counter1_Delay<90; _counter1_Delay++); //*** 1ms
#define delay_5ms()    	for (_counter2_Delay=0; _counter2_Delay<5; _counter2_Delay++){delay_1ms()}; //*** 5ms
#define delay_10ms()    for (_counter2_Delay=0; _counter2_Delay<10; _counter2_Delay++){delay_1ms()};
#define delay_50ms() 	for (_counter2_Delay=0; _counter2_Delay<50; _counter2_Delay++){delay_1ms()};
#define delay_100ms()	for (_counter2_Delay=0; _counter2_Delay<100; _counter2_Delay++){delay_1ms()};
#define delay_500ms()	for (_counter2_Delay=0; _counter2_Delay<500; _counter2_Delay++){delay_1ms()}; //*** 500ms


/*** Anschlussbelegung
CPU-Pin   LCD-Pin 1-16
P1.1	  D4	  11
P1.2      D5	  12
P1.3      D6	  13
P1.4      D7	  14
P1.5      RS	  4
P1.6      E       6
GND       R/W	  5
GND       GND	  1
          Vo 	  3	  Kontrast
VCC       VCC     2 ***/


#define LCDRS_ON  P1OUT |= 0x20 	//*** RS-Pin setzen
//#define LCDRS_ON  P1OUT |= (1<<5) 
#define LCDRS_OFF P1OUT &= 0xDF     //*** RS-Pin ruecksetzen
//#define LCDRS_OFF  P1OUT &= ~(1<<5)

#define LCDE_ON   P1OUT |= 0x40 	//*** E-Pin setzen
#define LCDE_OFF  P1OUT &= 0xBF     //*** E-Pin ruecksetzen
							   
#define LCDD4_ON  P1OUT |= 0x02     //*** D4-Pin setzen
#define LCDD4_OFF P1OUT &= 0xFD     //*** D4-Pin ruecksetzen

#define LCDD5_ON  P1OUT |= 0x04 	//*** D5-Pin setzen
#define LCDD5_OFF P1OUT &= 0xFB     //*** D5-Pin ruecksetzen

#define LCDD6_ON  P1OUT |= 0x08 	//*** D6-Pin setzen
#define LCDD6_OFF P1OUT &= 0xF7     //*** D6-Pin ruecksetzen

#define LCDD7_ON  P1OUT |= 0x10 	//*** D7-Pin setzen
#define LCDD7_OFF P1OUT &= 0xEF     //*** D7-Pin ruecksetzen



void lcd_init(void);
void lcd_clear(void);
void lcd_cmd(unsigned char cmd);
void lcd_pos(unsigned char zeile,unsigned char position);
void lcd_char(unsigned char data);
void lcd_string(const char *str);
void lcd_stringpos(char zeile, char pos,const char *str);

void lcd_ganzzahl(unsigned int zahl, unsigned int ziffern);
void lcd_kommazahl (unsigned int zahl, unsigned int vorkommastellen, unsigned int nachkommastellen);   

void lcd_e(void);

/*** Global Variables **/

unsigned int _counter1_Delay=0,_counter2_Delay=0; //*** fuer Delay


int main(void)
{
//  unsigned int temp=0;  //*** Hilfsvariable
  
  WDTCTL = WDTPW + WDTHOLD;  // Stop watchdog timer
  P1DIR |= 0xff;             // Set all ports to output direction

  
  //*** LCD initialisieren ***
  delay_500ms();
  
  lcd_init();
  delay_500ms();
  delay_500ms();
  
  
  lcd_clear(); //*** Display loeschen
  delay_500ms();
  delay_500ms();
  
  
  lcd_char('T');
  delay_500ms();
  delay_500ms();
  
  
  //*** Display-Meldung 
  lcd_stringpos(1,1,"Hello World"); 
 

  for (;;)
  {
    volatile unsigned int i;            // volatile to prevent optimization

    P1OUT ^= 0x01;                      // Toggle P1.0 using exclusive-OR

    i = 60000;                          // SW Delay
    do i--;
    while (i != 0);
  }
  return (0);
}


void lcd_init(void)
{
   	unsigned int i;

 	//*** Schreibsignal vorbereiten

 	LCDRS_OFF;  //*** RS-Signal low (RS auf Kontrollregister statt Datenregister)
 	LCDE_OFF;   //*** E-Signal low

 	//*** >15ms warten nach anlegen von Vcc bzw. >40ms nach Erreichen von 2.7V: vgl. Datenblatt S.46 oben
 	//*** warten. nicht noetig, da uC-Reset lange genug dauert

 	for(i=0; i<3; i++)	 //*** 3x function set: vgl. Datenblatt S.46
  	{
   		//*** setze nur D4 und D5 auf H    
   		LCDD4_ON;    //*** D4
   		LCDD5_ON;	 //*** D5

   		lcd_e();     //*** E-Signal um zu schreiben
   		delay_5ms();
  	}
  	//*** Ende Software Reset


  	//*** der erste Schreibzugriff nach Reset ist ein 8 Bit-Zugriff !
  	//*** also nur einmal schreiben, nicht Low-High Nibble.
  	//*** um die 4Bit Operation einzuschalten

  	//*** Function set fuer 4-bit Interface: D4 auf L, D5 auf H
  	LCDD4_OFF; 	//*** D4 low, D5 noch high

  	lcd_e();    //*** E-Signal um zu schreiben
    delay_5ms();
  
  	lcd_cmd(0x28);    //4 Bit Operation,2 Line,5x7 Font  5
  	delay_5ms();

  	lcd_cmd(0x14); 	//Cursor Move,Right Shift
  	delay_5ms();

  	lcd_cmd(0x0C); 	//Display on, Cursor off ,Cursor no blink
  	delay_5ms();

  	lcd_cmd(0x06); 	//Increment, Display Freeze
  	delay_5ms();

  	lcd_cmd(0x02); 	//Cursor Home
  	delay_5ms();

  	LCDRS_ON;		//LCD-Datenregister

  	delay_10ms();  
}

//*****************************************
// E-Signal um Daten in LCD-Register zu schreiben
//*****************************************
void lcd_e(void)	//*** E-Signal
{
	LCDE_ON;
	delay_1us(); 
	LCDE_OFF;
}

//*****************************************
// loescht LCD Anzeige
// Cursor steht dann auf Position 1, Zeile 1
// auch bei mehrzeiligen Anzeigen
//*****************************************
void lcd_clear(void)
{
 	lcd_cmd(0x01); 	//*** CLS
 	delay_5ms();  	//*** 5ms
}

//*******************************************
// schreibt ein Byte in die LCD-Anzeige
// schreibt in zwei 4 Bit Nibbles, RS=0 !
//*******************************************
void lcd_cmd (unsigned char cmd)
{
	
	LCDRS_OFF;	  //*** RS=0 

 	//*** erst High Nibble bearbeiten: D4,D5,D6,D7
  	
 	if (cmd&(1>>4))   //*** bit D4 gesetzt?		
 		LCDD4_ON;     //*** setze D4 am Display H
 	else
 		LCDD4_OFF;    //*** setze D4 am Display L
 
 	if (cmd&(1>>5))   //*** bit D5 gesetzt?		
 		LCDD5_ON;     //*** setze D5 am Display H
 	else
 		LCDD5_OFF;    //*** setze D5 am Display L
 		
 	if (cmd&(1>>6))   //*** bit D6 gesetzt?		
 		LCDD6_ON;     //*** setze D6 am Display H
 	else
 		LCDD6_OFF;    //*** setze D6 am Display L
 		
 	if (cmd&(1>>7))   //*** bit D6 gesetzt?		
 		LCDD7_ON;     //*** setze D7 am Display H
 	else
 		LCDD7_OFF;    //*** setze D7 am Display L
 
 	lcd_e();	      //High Nibble nun schreiben
 
 	//*** nun Low Nibble bearbeiten: D0,D1,D2,D3

 	if (cmd&(1>>0))   //*** bit D0 gesetzt?		
 		LCDD4_ON;     //*** setze D4 am Display H
 	else
 		LCDD4_OFF;    //*** setze D4 am Display L
 
 	if (cmd&(1>>1))   //*** bit D0 gesetzt?		
 		LCDD5_ON;     //*** setze D5 am Display H
 	else
 		LCDD5_OFF;    //*** setze D5 am Display L
 
 	if (cmd&(1>>2))   //*** bit D0 gesetzt?		
 		LCDD6_ON;     //*** setze D6 am Display H
 	else
 		LCDD6_OFF;    //*** setze D6 am Display L
 
 	if (cmd&(1>>3))   //*** bit D0 gesetzt?		
 		LCDD7_ON;     //*** setze D7 am Display H
 	else
 		LCDD7_OFF;    //*** setze D7 am Display L
   
 	lcd_e();	      //Low Nibble nun schreiben
 	
 	delay_50us();     //etwas auf die Anzeige warten
}

//**************************************************
// schreibt ein Zeichen in die LCD-Anzeige, RS=1 !
//**************************************************
void lcd_char(unsigned char data)
{
	
	LCDRS_ON;	      //*** RS=1 

//*** erst High Nibble bearbeiten: D4,D5,D6,D7
  	
 	if (data&(1>>4))  //*** bit D4 gesetzt?		
 		LCDD4_ON;     //*** setze D4 am Display H
 	else
 		LCDD4_OFF;    //*** setze D4 am Display L
 
 	if (data&(1>>5))  //*** bit D5 gesetzt?		
 		LCDD5_ON;     //*** setze D5 am Display H
 	else
 		LCDD5_OFF;    //*** setze D5 am Display L
 		
 	if (data&(1>>6))  //*** bit D6 gesetzt?		
 		LCDD6_ON;     //*** setze D6 am Display H
 	else
 		LCDD6_OFF;    //*** setze D6 am Display L
 		
 	if (data&(1>>7))  //*** bit D6 gesetzt?		
 		LCDD7_ON;     //*** setze D7 am Display H
 	else
 		LCDD7_OFF;    //*** setze D7 am Display L
 
 	lcd_e();	      //High Nibble nun schreiben
 
 	//*** nun Low Nibble bearbeiten: D0,D1,D2,D3

 	if (data&(1>>0))  //*** bit D0 gesetzt?		
 		LCDD4_ON;     //*** setze D4 am Display H
 	else
 		LCDD4_OFF;    //*** setze D4 am Display L
 
 	if (data&(1>>1))  //*** bit D0 gesetzt?		
 		LCDD5_ON;     //*** setze D5 am Display H
 	else
 		LCDD5_OFF;    //*** setze D5 am Display L
 
 	if (data&(1>>2))  //*** bit D0 gesetzt?		
 		LCDD6_ON;     //*** setze D6 am Display H
 	else
 		LCDD6_OFF;    //*** setze D6 am Display L
 
 	if (data&(1>>3))  //*** bit D0 gesetzt?		
 		LCDD7_ON;     //*** setze D7 am Display H
 	else
 		LCDD7_OFF;    //*** setze D7 am Display L
   
 	lcd_e();	      //Low Nibble nun schreiben
 	
 	delay_50us();     //etwas auf die Anzeige warten
}

//**************************************************
//Schreibt String (Text) in die LCD-Anzeige
//**************************************************
void lcd_stringpos(char Zeile, char Pos, const char *str)
{
 	char ps;
 
	lcd_pos(Zeile,Pos);

	ps = *str;

  	while(ps>0)
   	{
    	str++;
    
		if (ps== 0) break;
    
		lcd_char(ps);
    	ps = *str;
   	}
}

//****************************************************
// springt auf Zeile x und Position y der LCD-Anzeige 
// gueltig für 2 Zeilen
//****************************************************
void lcd_pos(unsigned char zeile,unsigned char position)
{
 	unsigned char buff=0;

	// wenn Zeile null ist, dann keine neue Position ausgeben
	if(zeile)
	{

 		switch(zeile)
  		{
   			case 1: buff=0x7F+position; break;
   			case 2: buff=0xBF+position; break;
   			default : break;
  		}

 		lcd_cmd(buff); 
	}
}
