/****************************************************************************
* main.c
* Dispaly und Control nibobee
*
* Quarz mit 6144000 MHz Atmega8
*
*          ------------------------
*   Zeile1 ! 12345678901234567890 !
*   Zeile2 ! 12345678901234567890 !
*   Zeile3 ! 12345678901234567890 !
*   Zeile4 ! 12345678901234567890 !
*          !                      !
*          !                      !
*          !                      !
*          !                      !
*          !  S1      S2      S4  !
*          !                      !
*          !          S3          !
*          ------------------------
*
* Tasten Belegung:  S1 = Start/Stop  mit S2: Kurve rechts  mit S3: Kurve links 
*                   S2 = set                    
*                   S3 = menu++ (wrap around)
*                   S4 = Speed++ oder mit S2 Speed--
*
*
* Interupt Belegung                       wo              warum
*--------------------------------------------------------------------------
* INT0          external Int0
* INT1          external Int1
* TIMER2 COMP   compare match
* TIMER2 OVF    overflow                  main.c          10msek Timer
* TIMER1 CAPT   capture event
* TIMER1 COMPA  campare match
* TIMER1 COMPB  compare match
* TIMER1 OVF    overflow
* TIMER0 OVF    overflow                  abc/lcd.c       display backlight
* SPI           Serial Transfer
* USART RXC     RX complete               abc/avr_uart.c  receive Zeichen da
* USART UDRE    Data Empty                abc/avr_uart.c  transmit Zeichen buffer leer
* USART TXC     TX complete               abc/avr_uart.c  Transmit fertig
* ADC           ADC conversion complete   main.c          Batterie check
* EE_RDY        EE_PROM ready
* ANA_COMP      Analog comparator
* TWI           two wire Interface
* SPM_RDY       store pgm mem ready
*
* Prozessor:  ATmega8
* Frequenz:   6,144 MHz
*--------------------------------------------------------------------------
* Pin Belegung:
*                         +-------------------+
*  Reset sw  (RESET) PC6 -|1       XXX      28|- PC5 (ADC5/SCL)  ADC Input Batterie
*                         |                   |
*  IR Rec.     (RXD) PD0 -|2                27|- PC4 (ADC4/SDA)  frei
*                         |                   |
*  ATTiny24    (TXD) PD1 -|3                26|- PC3 (ADC3)      LCD Backlight
*                         |                   |
*             (INT0) PD2 -|4                25|- PC2 (ADC2)      LCD_DA shift data
*                         |                   |
*             (INT1) PD3 -|5                24|- PC1 (ADC1)      LCD_EN enable
*                         |                   |
* S1 Taster (XCK/T0) PD4 -|6                23|- PC0 (ADC0)      LCD_CP shift clock
*                         |                   |
*                    VCC -|7                22|- GND
*                         |                   |
*                    GND -|8                21|- AREF
*                         |                   |
*  6,144MHz  (XTAL1) PB6 -|9                20|- AVCC
*                         |                   |
*  6,144MHz  (XTAL2) PB7 -|10               19|- PB5 (SCK)       *****
*                         |                   |
* S2 Taster     (T1) PD5 -|11               18|- PB4 (MISO)      *****
*                         |                   |
* S3 Taster   (AIN0) PD6 -|12               17|- PB3 (MOSI/OC2)  *****
*                         |                   |
* S4 Taster   (AIN1) PD7 -|13               16|- PB2 (SS/OC1B)   frei
*                         |                   |
*             (ICP1) PB0 -|14               15|- PB1 (OC1A)
*                         +-------------------+
*--------------------------------------------------------------------------
* AVR GCC Compiler
*
* -------------------------------------------------------------------------------------------
* 02.11.2010 li display und control nibobee via IR-Interface
* 07.11.2010 li IR Interface funzt mit uart 8N1, Ausbau alle Werte
* 23.12.2010 li S3 und S4 Speed Control
* 02.07.2012 li V5: Display Helligkeit dazu, Definitionen in config.h
* 02.07.2012 li V6: Idle
* 02.07.2012 li V7: kein idle, dafür uart_ok und uart_err
* 06.08.2012 li V8: neue cmd Belegung, s2 und S4 = Speed, s1,s2,s3 = Start stopp..
* 07.08.2012 li V9: eigener Spannungswert auf Volt umgerechnet.
*                   beim nibobee funktioniert die Umschaltung auf die Interner Reverenz nicht.
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <math.h>
#include "../../abc/basis.h"
#include "../../abc/lcd.h"
#include "../../abc/avr_uart.h"
#include "config.h"

unsigned char tim0cnt = 0;      // alle  10 msek trigger
unsigned char tim0sek = 0;      // alle 200 msek trigger

unsigned char uart_trace = 0;
unsigned char uart_trace_pos;
unsigned char uart_trace_zeile;

unsigned char nb_req = 0;      // nibobee request
#define REQ_POWER 0x01         // .... ...1
#define REQ_ODOME 0x02         // .... ..1.
#define REQ_SENSO 0x04         // .... .1..

char receive_buffer[ RBUF_SIZE ];

unsigned char receive_ind = 0;     // receive Index

unsigned char display_flag = 0;
#define VOLT    0x01            // .... ...1
#define ODOM    0x02            // .... ..1.
#define SENS    0x04            // .... .1..
#define IDLE    0x08            // .... 1...
#define U_VOLT  0x10            // ...1 ....
#define U_ODOM  0x20            // ..1. ....
#define U_SENS  0x40            // .1.. ....
#define U_IDLE  0x80            // 1... ....


unsigned int xvolt;
unsigned int x_odo_left;
unsigned int x_odo_right;
unsigned int xsens[3];

unsigned int idle_min;
unsigned int idle_max;

unsigned char sflag = 0;
// fine TX_POWER 0x01            // .... ...1
//                               // .... ..1.
//                               // .... .1..
//                               // .... 1...
//                               // ...1 ....
//                               // ..1. ....
//                               // .1.. ....
#define T_ERROR  0x80            // 1... ....
unsigned char quitFlag = 0;

unsigned int  lcd_timer = 1;     // lcd Timer

unsigned char menu_aktiv = 0;    // menu aktiv
unsigned char mtime = 0;         // menu timer, nach 10 Sekunden weg
unsigned char menu1 = 0;         // mm3 sicherung text1
unsigned char mm3 = 0;           // s3: index auf texte
unsigned char mm2 = 0;           // s2: press counter

unsigned char backlight = 0;     // Hintergrundbeleuchtung LCD
unsigned char calibrate = 0;     // calibrierung

unsigned char uart_err_abs = 0;
unsigned char uartmproz=0;       // max Prozent Wert
unsigned char uartproz=0;        // current Prozent Wert
unsigned char uart_display_err=0;
int uarterr =0;                  // error count
int uart_ok =0;                  // erkannte Nachrichten
int uarttimer=0;

unsigned char adc_timer=0;

unsigned char mm3max[2];         // s3: max index text1
unsigned char mmxlen[2];
unsigned char mmxs1 = 1;

                   /* 123456789012345 */
char zeile11[ 21 ] = "ralidzba.de" ;


                           // menu
                           //0-------9
const char m_t1[] PROGMEM = "retur V11"
                            "Displ.LED"
                            "UartTrace"
                            "Odometrie"  // Weg Werte links und rechts
                            "IRS Werte"  // Infrarot Sensoren Werte
                            "Calibrate"  // calibration
                            ;

#define LEN_M_T1   9    // Laenge String
#define ENT_M_T1   5    // Anzahl der Eintraege -1

                       //0--1--2--
char sm_t1[] PROGMEM  = "retausein";                  // standard submenu

                     // 01234   submenu Licht reale Leistung in %
char sm_t2[] PROGMEM = "ret"    // 0
                       "aus"    // 1
                       "12%"    // 2
                       "25%"    // 3
                       "50%"    // 4
                       "99%";   // 5
                       
                     // 01234   submenu calibration
char sm_t6[] PROGMEM = "ret  "  // 0  -1
                       "done "  // 1  0
                       "white"  // 2  1
                       "black"  // 3  2
                       "Save "; // 4  3
                       

#define LEN_SM_T1  3   // Laenge String
#define ENT_SM_T1  2   // Anzahl der Eintraege -1

#define LEN_SM_T2  3
#define ENT_SM_T2  5    // Anzahl der Eintraege -1

#define LEN_SM_T6  5
#define ENT_SM_T6  4    // Anzahl der Eintraege -1

#define INDEX_SM_T1  1
#define INDEX_SM_T2  2
#define INDEX_SM_T6  6

unsigned char volatile adc_event_flag = 0;  // 10 msec Trigger
unsigned int volatile  battery = 0;         // adc value

unsigned char volatile tim2_event_flag;     // 10 msec Trigger

   // ----------------------------------------------
   // INDEX test_switch_x()    0     1     2     3
   // ----------------------------------------------
unsigned char al_cc[4]  = {    0,    0,    0 ,   0 };
unsigned char al_bit[4] = { BIT5, BIT6, BIT4, BIT7 };
 //                           S1   S2    S3   S4

                                            // externe structuren
extern S_UART    volatile us;               // siehe ../../abc/avr_uart.h
extern S_LCD_ANZ volatile lcdanz_s;         // siehe ../../abc/lcd.h

                                            // prototypen
void timer2_init( void );
void start_adc( void );                     // analog digital converter starten
void display_x( void );
void s1_todo(void);
void s2_todo(void);
void s3_todo(void);
void s4_todo(void);
void display_menu(unsigned char m1);
void move2( char l, char pos, char index, unsigned char quelle );
void move1( char l, char index  );
void menu_U_sw2( void );
void decode_message( unsigned char );
void clear( char *);
unsigned char test_switch_D( unsigned char i );
void trace_uart( unsigned char c);
void send_cmd( char familycode, char c);
void clear_menu( void );
void clear34( unsigned char vier );

/*****************************************************************************
*  Main, 10msek vorbei routinen und uart receive scannen
*/
int main(void)
{
   int ic;
   unsigned char aktion, tc ;
   char vmsg[6];
   float fadc;
                                                          // static schutz
   PORTB |=  (BIT0+BIT1+BIT2+BIT3+BIT4+BIT5);             // mit pull up Widerstand
   PORTC |=  (BIT0+BIT1+BIT2+BIT3+BIT4);                  // mit pull up Widerstand
   PORTD |=  (BIT0+BIT1+BIT2+BIT3+BIT4+BIT5+BIT6+BIT7);   // mit pull up Widerstand


   lcd_init(1);                       // Initialize LC-Display
   timer2_init();                     // 10 msec Int
   uart_init();

   sei();                             // enable int
   set_cursor( 0, 1 );
   lcd_string( zeile11 );

   DDRD  &=  ~(BIT4+BIT5+BIT6+BIT7);  // Input S1 S2 S3 S4
   PORTD &=  ~(BIT4+BIT5+BIT6+BIT7);  // ohne pull up Widerstand


   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   for(;;)
   {
      if ( adc_event_flag )           // eigene Spannung gemessen ?
      {
         adc_event_flag = 0;
         fadc = 0.5065 *  battery;    // ergibt vvv.nnnn  
         edit99( fadc, vmsg+1, 3 );   // float wird hier nach int gewandelt  
         vmsg[0] = vmsg[1];
         vmsg[1] = ',' ;
         vmsg[4] = 'V' ;
         vmsg[5] = '\0';
         set_cursor( 15, 1 );         // eigener Werte zeile 1 rechts
         lcd_string( vmsg );          // spannungswert anzeigen
      }

      if ( tim2_event_flag )          // 10 msek int ******************
      {
         tim0cnt += tim2_event_flag;  // merke ticks
         tim2_event_flag = 0;


         if ( tim0cnt >= 20  )        // ticks >= 20  = 200 msek ??
         {
            tim0cnt = 0;
            tim0sek++;

            if ( tim0sek >= 5 )       // Sekunden interupt da ??
            {
               tim0sek = 0;

               if ( lcd_timer )
               {
                  if ( lcd_timer++ > 15  )          // 15 Sek. vorbei ?
                  {
                     lcd_timer = 0;
                     lcd_beleuchtung( backlight );  // Beleuchtung aus
                  }
               }

               if ( menu_aktiv )        // reset menu nach 10Sek
               {
                  if ( mtime++ > 10 )
                  {
                     menu_aktiv = 0;
                     mtime = 0;
                     mm3 = 0;
                     mm2 = 0;
                     display_menu( 0xFF );   // clear menu Zeile
                  }
               }

               if ( ++adc_timer > 5 )       // alle 5 sekunden die Sapnnung abfragen
               {
                  adc_timer=0;
                  start_adc();
               }
            }
         }
         if ( test_switch_D(0) == 1 )  // ++ S1 +++
            s1_todo();
         if ( test_switch_D(1) == 1 )  // ++ S2 +++
            s2_todo();
         if ( test_switch_D(2) == 1 )  // ++ S3 +++
            s3_todo();
         if ( test_switch_D(3) == 1 )  // ++ S4 +++
            s4_todo();

      }              // ende timer 10msek int *******************

      // ++++++++++++++++ UART Abfrage auf Zeichen empfangen ++++++

      ic = uart_getc();               // Zeichen von nibobee via IR

      if ( ic != -1 )                 // ungleich -1: Zeichen da
      {
         aktion = (unsigned char)ic & 0xFF ;

         if ( uart_trace )
         {
            if ( us.rx_error )
               tc = 'x' ;
            else if ( aktion == 0x02 )
               tc = '<' ;
            else if ( aktion == 0x03 )
               tc = '>' ;
            else
               tc = aktion ;         // display char
            trace_uart( tc );
         }

         if ( us.rx_error )           // transfer fehler ??
         {
            us.rx_error = 0;
         }
         else
         {
            if ( aktion == 0x02 )     // start of txt = reset error
            {
               receive_ind = 0;       // receive Index auf 0
               sflag &= ~T_ERROR;               
            }

            if ( receive_ind >= RBUF_SIZE )
            {
               sflag |= T_ERROR;
               receive_ind = 0;
               uarterr++;
            }
            else
               receive_buffer[ receive_ind++ ] = aktion;

            if ( aktion == 0x03 && !(sflag & T_ERROR) )
               decode_message(receive_ind);       // <<<<<<<<<<<<<<<<<<<<<<
         }
      }
   }   // MAIN LOOP
}




/*****************************************************************************
**  Message von nibobee decodieren
**
**           1         2
** 012345678901234567890123456
**  10000p                     = Volt Messwert
**  2000000p                   = Odometrie Counter L  R
**  4000000000000000000000000p = Linien Messwerte  L  C  R ohne IR Beleuchtung
**  5.p                        = Echo CMD
**  6.p                        = Echo CMD
**  90000000000p               = idle_min und idle_max
*/
void decode_message( unsigned char len)
{
   unsigned char p;
                   // 01234567890
   char echo[ 11 ] = "Echo: 6789";
   char msgbuffer[ RBUF_SIZE ];
   
   for ( p=0; p < len; p++)
      msgbuffer[p] = receive_buffer[p];
   
   msgbuffer[ len-1] = '\0' ;        // clear end of text   

   switch (  msgbuffer[ 1 ] )        // 2. Zeichen
   {
     case '1':                            // Volt
      p = berechne_PZ( msgbuffer+1, 5 ) + 0x30 ;    // Pruefzeichen
      if ( p == msgbuffer[6] )                      // check transfer
      {
         xvolt = a_to_i( msgbuffer+2, 4 );
         display_flag |= VOLT;
         display_flag ^= U_VOLT;
         display_x();
      }
      else
         uarterr++;
      break;

     case '2':                            // Oodometrie
      p = berechne_PZ( msgbuffer+1, 7 ) + 0x30 ;  // Pruefzeichen
      if ( p == msgbuffer[8] )                    // check transfer
      {
       x_odo_left  = a_to_i( msgbuffer+2, 3 );
       x_odo_right = a_to_i( msgbuffer+5, 3 );
       display_flag |= ODOM;
       display_flag ^= U_ODOM;
       display_x();
      }
      else
       uarterr++;
      break;
      
     case '3':                            // message
      set_cursor( 0, 4 );
      lcd_string( msgbuffer+2 );
      break;

     case '4':                            // Sensoren ohne IR
      p = berechne_PZ( msgbuffer+1, 13 ) + 0x30 ;  // Pruefzeichen
      if ( p == msgbuffer[14] )                    // check transfer
      {
         xsens[0] = a_to_i( msgbuffer+2, 4 );
         xsens[1] = a_to_i( msgbuffer+6, 4 );
         xsens[2] = a_to_i( msgbuffer+10, 4 );
         display_flag |= SENS;
         display_flag ^= U_SENS;
         display_x();
      }
      else
         uarterr++;
      break;

     case '5':                        // IR: eigener CMD als Input
     case '6':
      echo[6] = msgbuffer[ 1 ];
      echo[7] = msgbuffer[ 2 ];
      echo[8] = msgbuffer[ 3 ];
      echo[9] = msgbuffer[ 4 ];
      if ( echo[9] == 0x03 )
         echo[9] = 0x1C ;
      set_cursor( 0, 3 );
      lcd_string( echo );
      break;

     case '9':                                          // s9mmmmmMMMMMpe
      p = berechne_PZ( msgbuffer+1, 11 ) + 0x30 ;  // Pruefzeichen
      if ( p == msgbuffer[12] )                    // check transfer
      {
         idle_min = a_to_i( msgbuffer+2, 5 );
         idle_max = a_to_i( msgbuffer+7, 5 );
         display_flag |= IDLE;
         display_flag ^= U_IDLE;
         display_x();
      }
      else
         uarterr++;
      break;

     default:
      uarterr++;
      break;
   }
}


/*****************************************************************************
** uart trace bereich zeile 1 bis 2
*/
void trace_uart( unsigned char c)
{
   if ( uart_trace )
   {
      if ( uart_trace_pos > 19 )
      {
         uart_trace_pos = 0;
         uart_trace_zeile++;
      }
      if ( uart_trace_zeile > 2 || ( menu_aktiv && (uart_trace_zeile > 1) ))
      {
         uart_trace_pos = 0;
         uart_trace_zeile =1 ;
      }
      set_cursor( uart_trace_pos++, uart_trace_zeile );
      lcd_data( c );
   }
}



/*****************************************************************************
** S1 Taster pressed, Start / Stop  und back
*/
void s1_todo(void)
{
   if ( al_cc[1] == 128 )     // S2 pressed ???
      send_cmd( '5','2' );    // neu am 6.8.2012
   if ( al_cc[2] == 128 )     // S3 pressed ???
      send_cmd( '5','3' );    // neu am 6.8.2012
   else
      send_cmd( '5','9' );    // neu am 6.8.2012
   clear_menu();
}



/*****************************************************************************
** S4 Taster pressed, speed
*/
void s4_todo(void)
{
   if ( al_cc[1] == 128 )     // S2 pressed ???
      send_cmd( '5','0' );    // speed minus
   else
      send_cmd( '5','1' );    // speed plus
   clear_menu();
}



/*****************************************************************************
** send cmd, Start / Stop
**
** 01234
**  61p     = request Volt
**  62p     = request odometrie
**  64p     = request sensoren
**
**  67p     = clear request
**
**  50p     = speed minus
**  51p     = speed plus
**  52p     = back and turn right
**  53p     = back and turn left
**
**  59p     = start / stop
*/
void send_cmd( char familycode, char c)
{
   unsigned char p;
   char msg[ 8 ];

   msg[0] = 0x02;               // start of txt
   msg[1] = familycode;         // neue kennung
   msg[2] = c;
   p = berechne_PZ( msg+1, 2 ) + 0x30 ;    // Pruefzeichen
   msg[3] = p;
   msg[4] = 0x03;               // end of txt
   msg[5] = 0x00;
   usart_write_str( msg );       // senden

   trace_uart( familycode );
   trace_uart( c );
   trace_uart( p );
   trace_uart( ' ' );
}



/*****************************************************************************
** S3 Taster pressed
*/
void s3_todo(void)
{
   lcd_beleuchtung( 1 );     // 10.9.2009 Beleuchtung einnb_req + 0x30
   lcd_timer = 1;            // timer starten
   menu_aktiv = 1;
   mtime = 0;
   mm3++;
   if ( mm2 == 0 )            // kein s2 switch da
   {                          // default werte einstellen
      mmxlen[0] = LEN_M_T1;
      mm3max[0] = ENT_M_T1;
      mmxlen[1] = LEN_SM_T1;
      mm3max[1] = ENT_SM_T1;
      mmxs1 = INDEX_SM_T1;
   }
   if ( mm3 > mm3max[mm2] )
      mm3 = 0;
   if ( mm2 == 0 )
   {
      switch( mm3 )
      {
        case 1:
         mmxlen[1] = LEN_SM_T2;
         mm3max[1] = ENT_SM_T2;
         mmxs1 = INDEX_SM_T2;   // sm_t2;
         break;
         
        case 5:
         mmxlen[1] = LEN_SM_T6;
         mm3max[1] = ENT_SM_T6;
         mmxs1 = INDEX_SM_T6;   // sm_t6;
         break; 

        default:
         mmxlen[1] = LEN_SM_T1;
         mm3max[1] = ENT_SM_T1;
         mmxs1 = INDEX_SM_T1;       //  sm_t1;
         break;
      }
      display_menu( mm3 );     // text1
   }
   else
      display_menu( menu1 );   // text 1 und text2
}



/*****************************************************************************
** S2 Taster pressed
*/
void s2_todo(void)
{
   lcd_beleuchtung( 1 );     // Beleuchtung ein
   lcd_timer = 1;            // timer starten
   if ( menu_aktiv )         // s3 da gewesen  ??
   {
      mtime = 0;             // restart mtime
      mm2++;
      if ( mm2 == 1 )        // erster text da
      {
         menu1 = mm3;        // save level 1
                             // das was da ist anzeigen
         switch( menu1 )     // Anzeige submenu mit currend Einstellung
         {
           case 0: mm3 = 0; menu_U_sw2(); break;          // DCF77 Menu break;

           case 1: mm3 = backlight+1; break;              // licht

           case 2: mm3 = uart_trace +1; break;            // uart trace

           case 3:
            mm3 =1;
            if ( nb_req == '1')
               mm3 = 2;
            break;   // odometrie request
            
           case 4:
            mm3 =1;
            if ( nb_req == '2')
               mm3 = 2;
            break;   // sensor request 

           case 5:
            mm3 = calibrate + 1;
            break;   // sensoren request
         }

         if ( mm3 )
         {
            display_menu( menu1 );   // text1 und text2
         }
      }
      else
      {
         mm2 = 1;            // mm2 auf 1 halten
         menu_U_sw2();       // set
      }
   }
}



/*****************************************************************************
** die text elemente sind so angeordnet, dass bei "ein" todo=1 ist, "aus" todo=0
**  61   = req Odometrie
**  62   = req Sensoren
**  63   = req CalibrateWhite
**  64   = req CalibrateBlack
**  65   = req Write Calibrate
**  66   =
**  67   = reset 
**
** menu1 = 0  und mm3 = 0
*/
void menu_U_sw2( void )
{
   unsigned char rc=1;
   unsigned char todo;

   mm2 = 0;                           // mm2 immer auf 0
   if ( menu1 )                       // menu 1
   {
      todo = mm3 - 1;
      if ( todo == 0xff )             // retur ausgewaehlt ?
      {
         display_menu( menu1 );
         rc=0;
      }
      else
      {
         switch ( menu1 )
         {
           case 1:                    // backlight
            backlight = todo;
            lcd_beleuchtung( backlight );
            break;

           case 2:                    // uart Trace
            uart_trace = todo;
            uart_trace_pos = 0;
            uart_trace_zeile = 1;
            break;

           case 3:                    // odo  request
            if ( todo == 1 )
               nb_req = '1';
            else
               nb_req = '7';
            send_cmd( '6', nb_req );
            break;

           case 4:                   // Line sensor request
            if ( todo == 1 )
               nb_req = '2';
            else
               nb_req = '7';
            send_cmd( '6', nb_req );            
            break;
            
           case 5:                   // Calibrate request
            calibrate = todo;
            nb_req = '7';
            if ( calibrate == 1 )
               nb_req = '3';
            else if ( calibrate == 2 )
               nb_req = '4';
            else if ( calibrate == 3 )
            {
               nb_req = '5';
               calibrate = 0;               
            }
            send_cmd( '6', nb_req );            
            break; 
         }
      }
   }
   if ( rc )
   {
      clear_menu();
   }
}


void clear_menu( void )
{
   mm3 = 0;            // mm3 wieder auf 0
   mtime = 0;
   menu_aktiv = 0;
   display_menu( 0xFF );   // clear menu Zeile
}


/*****************************************************************************
** clear Zeile
*/
void clear( char *s )
{
   unsigned char i;

   for( i=0; i < 20 ; i++ )
   {
      *s = ' ' ;
      s++;
   }
   *s = '\0' ;
}



/*****************************************************************************
** clear display Zeile 3 und 4
*/
void clear34( unsigned char vier )
{
   clear( zeile11 );
   set_cursor( 0, 3 );
   lcd_string( zeile11 );
   if ( vier )
   {
      set_cursor( 0, 4 );
      lcd_string( zeile11 );
   }
}



/*****************************************************************************
** Display empfangene Werte
*/
void display_x( void)
{
   char vmsg[6];
   
   if ( display_flag & IDLE )
   {
      clear34(0);
      edit99( idle_min, zeile11+2, 5 );
      edit99( idle_max, zeile11+8, 5 );
      if ( display_flag & U_IDLE )      // update Bit
         zeile11[0] = '*' ;
      else
         zeile11[0] = ':' ;
      zeile11[15] = '\0' ;
      set_cursor( 0, 1 );
      lcd_string( zeile11 );
   }

   if ( display_flag & VOLT )    // immer zeile 2 rechts 
   {
      xvolt *= 2;
      edit99( xvolt, vmsg+1, 3 );
      vmsg[0] = vmsg[1];
      vmsg[1] = ',';
      vmsg[4] = 'V';
      vmsg[5] = '\0';
      set_cursor( 15, 2 );
      lcd_string( vmsg );
   }

   if ( display_flag & ODOM )
   {
      clear34(0);
      edit99( x_odo_left,  zeile11+2, 4 );
      edit99( x_odo_right, zeile11+8, 4 );
      if ( display_flag & U_ODOM )      // update Bit
         zeile11[0] = '*' ;
      else
         zeile11[0] = ':' ;
      set_cursor( 0, 3 );
      lcd_string( zeile11 );
   }

   if ( display_flag & SENS )
   {
      clear34(0);
      edit99( xsens[0], zeile11+2, 4 );
      edit99( xsens[1], zeile11+7, 4 );
      edit99( xsens[2], zeile11+12, 4 );
      if ( display_flag & U_SENS )      // update Bit
         zeile11[0] = '*' ;
      else
         zeile11[0] = ':' ;
      set_cursor( 0, 3 );
      lcd_string( zeile11 );
   }
   display_flag &= 0xF0 ;                // update bits lassen
}



/*****************************************************************************
**  Menu Zeile erzeugen und anzeigen
**  7.11.2010 li Sonderfall m1 = 0xFF = clear menu Zeile
*/
void display_menu( unsigned char m1 )
{
   clear( zeile11 );

   if ( m1 != 0xff )
   {
      move1( mmxlen[0], m1  );      // move text1
      if ( mm2 )
      {
         move2( mmxlen[1], mmxlen[0]+1, mm3, mmxs1 ); // move text2
      }
   }
   set_cursor( 0, 2 );
   lcd_string( zeile11 );
}



/*****************************************************************************
**  Text nach zeile11 kopieren
*/
void move1( char l, char index  )
{
   unsigned char n,q;
   prog_char *addr;

   addr = (prog_char*)&m_t1;      // DCF77 Menu
   q = index * l;                 // quell Position
   addr += q;
   for( n = 0; n < l ; n++)
   {
      zeile11[n] = pgm_read_byte( addr++ );
   }
}



/*****************************************************************************
**  Text2 nach zeile11 kopieren
**  9.3.2010 pgmspace
*/
void move2( char l, char pos, char index, unsigned char quelle )
{
   char n,q;
   prog_char *addr;

   addr = (prog_char*)&sm_t1;

   switch ( quelle )
   {
     case INDEX_SM_T2:  addr = (prog_char*)&sm_t2; break;
     case INDEX_SM_T6:  addr = (prog_char*)&sm_t6; break;
   }

   q = index * l;            // quell Position
   addr += q;
   for( n = 0; n < l ; n++)
   {
      zeile11[pos+n] = pgm_read_byte( addr++ );
   }
}


/*****************************************************************************
**  Timer2 overflow interupt fuer 10 msec setzen
*/
void timer2_init( void )
{
   TCNT2 = 0;                     // reset
#if F_CPU == 6144000
   TCCR2 =  (1 << CS22) + (1 << CS21) + (1 << WGM21) ; // prescaler=256 + CTC
   OCR2  = 239;                   // 240-1
#endif
#if F_CPU == 12000000
   TCCR2 =  (1 << CS22) + (1 << CS21) + (1 << CS20) + (1 << WGM21); // prescaler=1024 + CTC
   OCR2  = 116;                   // 117-1
#endif
   tim2_event_flag = 0;           // flag
   TIMSK |= (1 << OCIE2);         // enable compare match interrupt
}



/*****************************************************************************
*  8 Bit TIMER2 Compare Match
*  alle ca. 10 msek
*/
ISR ( TIMER2_COMP_vect )
{
   tim2_event_flag++;             // timer2 flag
}



/*****************************************************************************
** Schalter Abfrage PORTD
** funktionieren nur wenn Aufruf alle 10 msek
*/
unsigned char test_switch_D( unsigned char i )
{
   unsigned char bit;

   bit = al_bit[i];           // Bit holen

   if ( !(PIND & bit) )       // pressed = 0 ??
   {
      if ( !(PIND & bit) && al_cc[i] < 128 )
      {
         if ( al_cc[i]++ > 3 )
         {
            al_cc[i] = 128;    // pressed
            return 1;
         }
      }
   }
   else
   {
      if ( al_cc[i] >= 128 )
      {
         if ( PIND & bit )
         {
            if ( al_cc[i]++ > 131 )
            {
               al_cc[i] = 0;  // no pressed
               return 2;
            }
         }
      }
   }
   return 0;
}



/*****************************************************************************
** Wandlung fertig
*/
ISR ( ADC_vect )
{   
   battery = ADC;
   adc_event_flag = 1;         // flag setzen
}



/*****************************************************************************
** Analog digital wandler initialisieren
** wird alle 5 sek gestartet um die Batterie Spannung zu messen
*/
void start_adc( void )
{
   ADMUX = (1 << REFS0) + (1 << REFS1);     // interne Spannung 2,56V benutzen
   ADMUX |= (1 << MUX2) + (1 << MUX0);      // Port ADC5 auswaehlen
   ADCSRA |=  (1 << ADPS2) + (1 << ADPS1)    // ADC Takt einstellen auf 96kHz
            + (1 << ADEN)                    // ENABLE
            + (1 << ADIE)                    // Interrupt enable
            + (1 << ADSC);                   // Start Conversion
}