/***********************************************************************
** lcd.c
**                
** 11.03.2009 li Umbau auf 10 Bit shift Register
** 12.03.2009 li Anpassung an 4 * 20 Display 
** 27.03.2009 li \n wird ausgewertet, dann neue Zeile 
** 04.04.2009 li 2 oder 4 Line-Displays werden unterstuetzt
** 07.09.2009 li Hintergrundbeleuchtung eigener Port
** 08.01.2010 li Schaltung mit HC4094 sollte gehen 
** 14.01.2010 li EA_DIP204_4 Electronic assembly display Erweiterungen 
** 13.03.2010 li \n Abfrage in lcd_data    
*/

#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include "basis.h"
#include "lcd.h"

#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME   0x02

S_LCD_ANZ lcdanz_s; 

void lcd_command( unsigned char temp1 );
void load_LCD( unsigned char temp1 , unsigned char rs );
void lcd_WaitBusy(void);


/********************************************************
** sendet ein Datenbyte an das LCD
*/ 
void lcd_data(unsigned char temp1)
{     
   if ( temp1 == '\n' )          // new line ??
   {
      while( lcdanz_s.xpos < LCD_CHARS )
      {
         lcdanz_s.xpos++;   
         load_LCD( ' ', 1 );       // RS auf 1
         lcd_WaitBusy();
      }  
   }
   else
   {
      if ( lcdanz_s.xpos > (LCD_CHARS-1) )
      {
         lcdanz_s.xpos = 0;
         lcdanz_s.zeile++;
         if ( lcdanz_s.zeile > LCD_LINES )
           lcdanz_s.zeile = 1;
         set_cursor( lcdanz_s.xpos, lcdanz_s.zeile);    
      }
      lcdanz_s.xpos++;   
      load_LCD( temp1 , 1 );       // RS auf 1
      lcd_WaitBusy();
   }
}


#if LCD_SHIFT == 10
/********************************************************
** 10 Bit Schift Register setzen
** danach Enable Puls
** von 10 Bits werden 9 genutzt  
*/  
void load_LCD( unsigned char temp1 , unsigned char rs )
{
   int n; 
   
   if ( rs )
      LCD_CNTL_PORT |= LCD_DA;     // RS auf 1 setzen = Data Mode
   else
      LCD_CNTL_PORT &= ~LCD_DA;    // RS auf 0 setzen = CMD Mode
   LCD_CNTL_PORT &= ~LCD_CP;       // CP auf 0 setzen
   _delay_us(2); 
   LCD_CNTL_PORT |= LCD_CP;        // CP auf 1 setzen
   _delay_us(2);
   
   for (n=0; n < 8; n++)           // 8 Datenbits
   {
      if ( temp1 & 0x80 )          // Bit high or low ?
         LCD_CNTL_PORT |= LCD_DA;  // DATA auf 1 
      else
         LCD_CNTL_PORT &= ~LCD_DA; // DATA auf 0
      temp1 <<= 1;                 // data shift left
      LCD_CNTL_PORT &= ~LCD_CP;    // CP auf 0 setzen
      _delay_us(2); 
      LCD_CNTL_PORT |= LCD_CP;     // CP auf 1 setzen
      _delay_us(2);
   }    
                                   // 10 Takt = Leertakt 
   LCD_CNTL_PORT &= ~LCD_DA;   
   LCD_CNTL_PORT &= ~LCD_CP;       // CP auf 0 setzen
   _delay_us(2); 
   LCD_CNTL_PORT |= LCD_CP;        // CP auf 1 setzen
   _delay_us(2);   
                                   // erzeugt den Enable-Puls
   LCD_CNTL_PORT |= LCD_EN;        // E auf 1
   _delay_us(2);                   // pulse Dauer
   LCD_CNTL_PORT &= ~LCD_EN;       // E auf 0   
}
#endif

#if LCD_SHIFT == 8
/********************************************************
** 8 Bit Schift Register setzen
** HC4094 schiebt wenn CP Flanke von 0 nach 1   
** danach Enable Puls mit RS
*/  
void load_LCD( unsigned char temp1 , unsigned char rs )
{
   int n; 
      
   for (n=0; n < 8; n++)            // 8 Datenbits
   {
      if ( temp1 & 0x80 )           // Bit high or low ?
         LCD_CNTL_PORT |= LCD_DA;  // DATA auf 1 
      else
         LCD_CNTL_PORT &= ~LCD_DA; // DATA auf 0
      temp1 <<= 1;                 // data shift left
      LCD_CNTL_PORT &= ~LCD_CP;    // CP auf 0 setzen
      _delay_us(2); 
      LCD_CNTL_PORT |= LCD_CP;     // CP auf 1 setzen
      _delay_us(2);
   }    
   
   if ( rs )
      LCD_CNTL_PORT |= LCD_DA;     // RS auf 1 setzen = Data Mode
   else
      LCD_CNTL_PORT &= ~LCD_DA;    // RS auf 0 setzen = CMD Mode
                                      // erzeugt den Enable-Puls
   LCD_CNTL_PORT |= LCD_EN;        // E auf 1
   _delay_us(2);                    // pulse Dauer
   LCD_CNTL_PORT &= ~LCD_EN;       // E auf 0   
}
#endif


/********************************************************
** sendet einen Befehl an das LCD
*/  
void lcd_command(unsigned char temp1)
{
   load_LCD( temp1 , 0 );   // RS auf 0 Setzen   
   lcd_WaitBusy();
}
 
 

/********************************************************
** Warten 100 µ da read nicht moeglich
*/  
void lcd_WaitBusy(void)
{
   _delay_us(100);                
}



/********************************************************
** LCD Initialisierung
*/   
void lcd_init(void)
{
   LCD_CNTL_PORT |=  LCD_CP;   // CP auf 1 setzen
   LCD_CNTL_PORT &= ~LCD_DA;   // D  auf 0 setzen
   LCD_CNTL_PORT &= ~LCD_EN;   // E  auf 0 setzen
   LCD_CNTL_PORT &= ~LCD_LED;  // Beleuchtung aus
   lcdanz_s.lcd_backlight = 0;
   LCD_CNTL_DDR |= ( LCD_DA + LCD_EN + LCD_CP + LCD_LED );  // Ports auf OUTPUT 
   _delay_ms(500);      // delay nach Power ON

#ifdef EA_DIP204_4      // Electronic assembly display  

   lcd_command(0x34);	// 8 Bit Modus einschalten, RE=1 
      
   lcd_command(0x09);	// 4 Zeilen Modus 
      
   lcd_command(0x30);	// 8 Bit Modus einschalten, RE=0 
      
   lcd_command(0x0C);	// Display ein, Cursor aus, Cursor blinken aus
      
   lcd_command(0x01);	// Clear Display 
   _delay_us(2000);     // wait 
   
   lcd_command(0x06);	// Cursor auto increment 
      
#else
                
   lcd_command(0x30);   // 8 Bit Modus einschalten     
   _delay_ms(20);       // wait 
                                
   lcd_command(0x38);   // 8 Bit Modus und mehr als 1 Zeile  
    _delay_us(200);     // wait
    
   lcd_command(0x38);   // 8 Bit Modus und mehr als 1 Zeile 
    _delay_us(200);     // wait     
          
   cursor_view( 0, 0 ); // Display ein / Cursor aus / kein Blinken
   
   lcd_command(0x06);   // inkrement / kein Scrollen
   
   lcd_clear();
#endif
  
   lcd_home();       
}
 
 
 
/******************************************************** 
** display ein und cursor sichtweise 
*/ 
void cursor_view( unsigned char on, unsigned char blinken )
{
   unsigned char cmd = 0x0C ;
   
   if ( on )
      cmd |= 0x02;
   if ( blinken )
      cmd |= 0x01;       
   lcd_command( cmd );   // Display ein / Cursor control / blink control
} 
 
 
 
/******************************************************** 
** Hintergrundbeleuchtung des Panels ein/aus
*/ 
void lcd_beleuchtung( unsigned char on)
{
   if ( on )                     // einschalten ??
   {
      lcdanz_s.lcd_backlight = 1;         // merker fuer menu 
      LCD_CNTL_PORT |= LCD_LED;  // Beleuchtung ein
   }
   else
   {
      lcdanz_s.lcd_backlight = 0;         // merker fuer menu
      LCD_CNTL_PORT &= ~LCD_LED; // Beleuchtung aus
   }      
} 


 
/******************************************************** 
** Loeschung des Displays
** Dauer 1,64ms 
*/ 
void lcd_clear(void)
{
   lcd_command(CLEAR_DISPLAY);
   _delay_ms(2);   
}
 
 
 
/********************************************************  
** Cursor auf Home Position setzen
** Dauer 1,64ms 
*/ 
void lcd_home(void)
{
   lcd_command(CURSOR_HOME);
   _delay_ms(2); 
   lcdanz_s.xpos  = 0;
   lcdanz_s.zeile = 1;
   lcdanz_s.lcd_backlight = 0;

#if LCD_CHARS == 16   
   lcdanz_s.chars_pl = 16;
#endif
#if LCD_CHARS == 20   
   lcdanz_s.chars_pl = 20;
#endif      
   
#if LCD_LINES == 4
   lcdanz_s.zeilen = 4;
#else
   lcdanz_s.zeilen = 2;   
#endif     
}



/********************************************************  
** setzt den Cursor in Zeile y (1..4) Spalte x (0..15)
*/ 
void set_cursor(uint8_t x, uint8_t y)
{
   register uint8_t tmp;

   lcdanz_s.xpos  = x;
   lcdanz_s.zeile = y;  
   
   tmp = 0x80;           // command

#ifdef EA_DIP204_4      // Electronic assembly display 

   if ( y == 1 )
      tmp += x;          // 1. Zeile     
   else if (y == 2) 
      tmp += 0x20 + x;   // 2. Zeile
   else if (y == 3)
      tmp += 0x40 + x;   // 3. Zeile
   else if ( y == 4) 
      tmp += 0x60 + x;   // 4. Zeile 

#else     

   if ( y == 1 )
      tmp += x;          // 1. Zeile     
   else if (y == 2) 
      tmp += 0x40 + x;   // 2. Zeile
#if LCD_LINES == 4
   else if (y == 3)
      tmp += 0x14 + x;   // 3. Zeile
   else if ( y == 4) 
      tmp += 0x54 + x;   // 4. Zeile
#endif

#endif                   // /EA_DIP204_4      Electronic assembly display  
   lcd_command( tmp );
}



/******************************************************** 
** String auf das LCD ausgeben, \n Auswertung in lcd_data
*/ 
void lcd_string(char *data)
{
   while( *data )
   {
      lcd_data( *data );
      data++;
   }
}
