//***************************************************************//
//***************************************************************//
//  Dekompressionsprogramm fuer Selbstbau-Tauchcomputer (SBTC3)  //
//  ************************************************************ //
//  Mikrocontroller:  ATMEL AVR ATmega32, 8 MHz                  //
//                                                               //
//  Compiler:         GCC (GNU AVR C-Compiler)                   //
//  Autor:            Peter Rachow                               //
//  Letzte Aenderung: 21.11.2007                                 //
//***************************************************************//

// Vcc=5V stab.

// Pinbelegung am Mikrocontroller
//PIN    Funktion
// 1     Taster 1  Taster gegen Masse, 5.6kOhm gegen +VCC
// 2     Taster 2                   "
// 6     MOSI
// 7     MISO
// 8     SCK
// 9     REST
// 10    +Vcc
// 11    GND
// 14    Rx-Data
// 15    Tx-Data
// 18    Display 11
// 19    Display 12
// 20    Display 13
// 21    Display 14
// 22    Display 6
// 23    Display 4
// 24    LED
// 25    LED
// 28    X-TAL
// 29    X-TAL
// 30    Vcc ADC
// 31    GND
// 32    GND über 0.1uF
// 38    Spannungsteiler 1:5 für Akkuüberwachung
// 39    Temperatursensor KTY 81-210
// 40    Ausgang OP-Amp des Drucksensors

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/sleep.h>

//*****************
//  Benutzermenue  
//*****************
#define MENU_ITEMS 7
char menu_str[MENU_ITEMS][18]={"Luftdruck NN",
                                "Hoehe ueber NN",
                                "Kabinendruck",
                                "Max. ppO2",
                                "Toleranzen",
                        "ppN2 anzeigen",
                        "Einstellungen"};

char menu_unitstr[MENU_ITEMS][6]={"mbar",
                                   "m",
                                   "mbar",
                                   "bar",
                                   "",
                           "",
                           ""};

int menu_digits[MENU_ITEMS] = {-1, -1, -1, 2, 2,  -1, -1}; // Zahl der Ziffern 
int menu_dec[MENU_ITEMS] = {-1, -1, -1, 1, 1, -1, -1};     // Position des Dezimalpunktes 

int show_settings = 0;

//*****************
// EEPROM-Speicher 
//*****************
#include <avr/eeprom.h>
#define MAX_EEPROM_ADR 1023
#define EEPROM_PROF_START 50

void eeprom_store_byte(char);
void clear_flash(char);
void display_profile(void);
void display_rcd(void);
void display_log(void);
int eeprom_byte_count;                 // Positionszeiger fuer EEPROM 

//*******************
// Timer & Interrupt 
//*******************
#define SCLOCK 8             // Taktfrequenz im MHz                               
#define INITWAIT 750         // Wartezeit fuer Anzeigewechsel bei Programmstart   
unsigned long runseconds = 0, diveseconds = 0, surf_seconds = 0;

//*********
// M I S C 
//*********
int main(void);
void led(char, char);
int round_depth(int);
int get_keys(void);
void settings(void);

void showtemp(void);
double temp;                            // Aktuelle Temperatur                          
int temp_min;                        // niedrigste Temperatur                        
int temp_maxdepth;                   // Temperatur auf max Tiefe                     

void show_gas(int);
void set_curgas(void);

#define SWITCHDEPTH 10 // Tiefe, bei der von TG- in Oberflaechenmodus 
// gewechselt wird [dm].                        
#define SURF_SECONDS_MAX 120 // Oberflaechenzeit, nach der von TG- in OFP-Modus 
// gewechselt wird [s].                           

// Softwareversion 
unsigned char softwareversion[3] = {3, 02, 'k'};

void show_accu_voltage(void);
double accu_voltage = 0; //Akkuspannung

//*************
// LCD-Display 
//*************
#define LCD_INST 0x00
#define LCD_DATA 0x01

void lcd_write(char, unsigned char, int);
void set_rs(char);
void set_e(char);
void lcd_init(void);
void lcd_cls(void);
void lcd_linecls(int, int);
void lcd_putchar(int, int, unsigned char);
void lcd_putstring(int, int, unsigned char*);
int lcd_putnumber(int, int, int, int, int, char);
void wait_ms(int);
void lcd_printdiveinfo(int, int, int);

//*******
// USART 
//*******
#define RX_BUF_SIZE 32

void usart_init(void);
void usart_putc(char);
void clear_rx_buf(void);
char make_crc(int, int);
void sbtc2pc(void);

char rx_buf[RX_BUF_SIZE];
unsigned char rx_buf_cnt = 0;

//***********************
// Dekompressionrechnung 
//***********************
#define NCOMP 16   // Anzahl der Kompartimente des Modells 
#define FN2 0.78   // N2-Anteil im Atemgas                 
#define MAX_DECO_STEPS 10
#define MAXGASES 3

// Gewebekonstanten fuer 16 Kompartimente  
// STICKSTOFF                              
float t05N2[] = {4, 8, 12.5, 18.5, 27, 38.3, 54.3, 77, 109, 146, 187, 239, 305, 390, 498, 635};
float aN2[] = {1.2599, 1, 0.8618, 0.7562, 0.662, 0.5043, 0.441, 0.4,
    0.375, 0.35, 0.3295, 0.3065, 0.2835, 0.261, 0.248, 0.2327};
float bN2[] = {0.505, 0.6514, 0.7222, 0.7825, 0.8126, 0.8434, 0.8693, 0.891,
    0.9092, 0.9222, 0.9319, 0.9403, 0.9477, 0.9544, 0.9602, 0.9653};

// Kompartimentsaettigung 
float piN2[] = {0.72, 0.72, 0.72, 0.72, 0.72, 0.72, 0.72, 0.72,
    0.72, 0.72, 0.72, 0.72, 0.72, 0.72, 0.72, 0.72};

// 3 durch Anwender waehlbare Gasgemische aus O2 und N2 (Gas1 = Luft) 
unsigned char curgas = 0;
double figN2[MAXGASES] = {FN2, 0.36, 0};              // N2-Anteil in 3 Auswahlgasen    

float airp = 0.995;                  // Umgebungsluftdruck in bar am Tauchort        
float airp0 = 0.995;                 // Umgebungsluftdruck in bar auf NN             
float cabinp = 0.75;                 // Kabinendruck im Flugzeug in bar              
int altitude = 0;                    //Hoehe ueber NN                                
int depth = 0, maxdepth = 0;         // Akt. und max. Tiefe [dm]                     
int ldepth = 0;                      // Letzte aktuelle Tiefe fuer v.asc             
int deepest_decostep = 0;            // Tiefster Dekostopp in dm                     
int deco_minutes_total = 0;          // Gesamtdekozeit in min.                       
char dphase = 0;                     // TG-Phase: 1=tauchen 0=OFP                    
unsigned char f_cons;               // Faktor fuer ab-Modifikation (10facher Wert)  
char show_ppN2 = 0;                  // ppN2 nach TG anzeigen für 16 Kompartimente   

unsigned char rcd_decotime[MAX_DECO_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Speicherdaten fuer die Decostufen, die 
// im EEPROM fuer den TG abgelegt werden   

unsigned char rcd_deco_minutes_total = 0;
unsigned char tmp_decotime_total = 0;

unsigned char surfaced = 0, ppo2_exceeded = 0;
unsigned char decostep_skipped = 0, ndt_runout = 0; // Flags fuer Ereignisaufzeichnung im Profilespeicher 
unsigned char temp_low = 0;

float get_water_pressure(int);
void calc_p_inert_gas(int);
float get_water_depth(float);
int calc_ndt(void);
void calc_deco(void);
unsigned int calc_no_fly_time(void);
void get_dsensor(void);
void get_tsensor(void);
void get_vsensor(void);
void set_ab_values(int, unsigned char);
void calc_airp_divesite(char);

//*********************
// ppO2-bezogene Werte 
//*********************
float cns_day = 0, cns_dive;
float otu = 0;
int maxppO2 = 16;  // 1.6 bar 

int calc_ppO2(char);
void calc_cns_otu(void);

//************
// AD-Wandler 
//************
#define ADWAITSTATE 3
int adc_val;
char adc_mode = 0; // 0= Druck, 1=Temperatur,2=Akkuspannung

//******************************************
// Funktionen und Prozeduren fuer DEKO-Teil 
//******************************************

// Wasserdruck p.amb aus Tiefe depth  
// berechnen                          
float get_water_pressure(int depth)
{
    return depth * 0.1 + airp;
}

// Inertgaspartialdruck im Gewebe berechnen 
// d: Tiefe in m  Intervall immer 10 sec.   
void calc_p_inert_gas(int d)
{
    unsigned char t1;
    float pamb = get_water_pressure(d) - 0.0627;

    for(t1 = 0; t1 < NCOMP; t1++)
        piN2[t1] += (pamb * figN2[curgas] - piN2[t1]) * (1 - exp((-0.1667 / t05N2[t1]) * log(2)));
}

// Wassertiefe depth aus p.amb berechnen 
float get_water_depth(float pamb)
{
    return (pamb - airp) * 10;
}

// Errechnen der Restnullzeit 
int calc_ndt()
{
    char calcok = 0;      // Flag, ob Rechnung OK ist 
    unsigned char t1;

    int dp = depth * 0.1; // Wassertiefe in m 
    int t0min = 999;

    float te, xN2;
    float piigN2, pamb = get_water_pressure(dp) - 0.0627;

    piigN2 = pamb * figN2[curgas];

    for(t1 = 0; t1 < NCOMP; t1++)
    {
        // Anwendung der Logarithmusgleichung 
        if(piigN2  - piN2[t1] && figN2[curgas])
        {
          xN2 = -1 * ((airp / bN2[t1] + aN2[t1] - piN2[t1]) / (piigN2 - piN2[t1]) - 1);

            if(xN2 > 0) // Ist Logarithmieren moeglich? 
            {
                te = -1 * log(xN2) / log(2) * t05N2[t1];
                if(te < t0min)
                    t0min = te;
                calcok = 1;
            }
        }
    }

    if(calcok && dp > 10)
    {
        if(t0min > 0)
            return (int) t0min;
        else
            return 0;
   }
    else
    {
        return -1;
    }
}


// Dekompressionsstufen berechnen 
void calc_deco()
{
    float piN2x[NCOMP];
    float pambtol, pambtolmax = 1.0;
    unsigned int decostep, deco_minutes1 = 0;
    unsigned char xpos = 0, t1, t2;
    unsigned char tmp_decotime[MAX_DECO_STEPS];
    unsigned int cnt = 0;
    int ndt;

    for(t1 = 0; t1 < MAX_DECO_STEPS; t1++)
        tmp_decotime[t1] = 0;

    deco_minutes_total = 0;

    // Signal LED ein 
    led(2, 1);

    // Aktuelle Gasspannungen in temporaeres eindimensionales Datenfeld uebertragen 
    for(t1 = 0; t1 < NCOMP; t1++)
    {
        piN2x[t1] = piN2[t1];
        //pigx[t1] = piN2x[t1] + piHex[t1];
    }

    // Erste Dekostufe 
    for(t1 = 0; t1 < NCOMP; t1++)
        if((piN2x[t1] - aN2[t1]) * bN2[t1] > pambtolmax)
            pambtolmax = (piN2x[t1] - aN2[t1]) * bN2[t1];

    decostep = get_water_depth(pambtolmax);
    decostep = ((decostep / 3) + 1) * 3;

    deepest_decostep = 0;

    if(dphase)
        lcd_linecls(1, 15);

    get_dsensor();

    // Nachfolgende Dekostufen bis 0 m Wassertiefe errechnen 
    while(decostep > 0)
    {
        pambtolmax = 0.0;

        for(t1 = 0; t1 < NCOMP; t1++)
        {
            piN2x[t1] += ((get_water_pressure(decostep) - 0.0627) * figN2[curgas]  - piN2x[t1]) * (1 - exp((-1 / t05N2[t1]) * log(2)));
            pambtol = (piN2x[t1] - aN2[t1]) * bN2[t1];
            if(pambtol > pambtolmax)
                pambtolmax = pambtol;
        }

        if(get_water_depth(pambtolmax) < decostep - 3)
        {
            if(deco_minutes1)
                xpos += lcd_putnumber(1, xpos, deco_minutes1, -1, -1, 'l') + 1;

            deco_minutes_total += deco_minutes1;

            // Werte im Datenfeld speichern fuer EEPROM-Aufzeichnung       
            cnt = (decostep / 3) - 1; // Nr. des Decostopp ermitteln 
            if(cnt >= 0 && cnt < MAX_DECO_STEPS)
                tmp_decotime[cnt] = deco_minutes1;

            decostep -= 3;
            deco_minutes1 = 0;
        }
        deco_minutes1 += 1;

        // Laengste gesamte Dekozeit speichern 
        if(deco_minutes_total > tmp_decotime_total)
        {
            for(t2 = 0; t2 < MAX_DECO_STEPS; t2++)
                rcd_decotime[t2] = tmp_decotime[t2];
            tmp_decotime_total = deco_minutes_total;
        }

        // Tiefsten errechneten Dekostopp speichern 
        if(decostep > deepest_decostep)
            deepest_decostep = decostep;
    }

    if(dphase || deco_minutes_total) // Restliche Anzeige (Gesamtdekozeit bzw. Nullzeit nur, wenn getaucht wird) 
    {
        if(!deco_minutes_total)      // Gesamte Dekozeit <= 0 also NZ-TG 
        {
            lcd_putstring(1, 0, "NZ: ");

            ndt = calc_ndt();

            if(ndt < 0)       // Unplausible NZ-Werte abfangen 
                lcd_putstring(1, 4, "-");
            else
            {
                xpos = lcd_putnumber(1, 4, ndt, -1, -1, 'l') + 4;
                lcd_putchar(1, xpos, 39);
            }
        }
        else  // Dekompressionsstopps sind erforderlich 
        {
            // Summe der Dekozeiten anzeigen 
            lcd_putchar(1, xpos++, 246);    // Sigma-Zeichen 
            lcd_putchar(1, xpos++, '=');
            xpos += lcd_putnumber(1, xpos, deco_minutes_total, -1, -1, 'l');
            lcd_putchar(1, xpos, 39);

            if(!ndt_runout) // Flag setzen fuer Profilaufzeichnung: Nullzeit zu Ende,  
            {               // PADIes muessen jetzt auftauchen! ;-P                     
                eeprom_store_byte(225);
                ndt_runout = 1;
            }

        }
    }

    if(xpos < 12)
        showtemp();

    led(2, 0);
}

// Flugverbotszeit für N2-Kompartimente berechnen 
// Aufloesung: 1 h              
unsigned int calc_no_fly_time()
{
    float piN2_b[NCOMP];
    float p_amb_tol;
    unsigned int nft = 0, flag_no_fly, t1;

    // Aktuelle Gasspannungen in temporaeres Datenfeld uebertragen 
    for(t1 = 0; t1 < NCOMP; t1++)
       piN2_b[t1] = piN2[t1];

    while(nft < 48)
    {
        flag_no_fly = 0;

        for(t1 = 0; t1 < NCOMP; t1++)
        {
            piN2_b[t1] += ((airp - 0.0627) * 0.78 - piN2_b[t1]) * (1 - exp((-60 / t05N2[t1]) * log(2)));

            p_amb_tol = (piN2_b[t1] - aN2[t1]) * bN2[t1];
            if(p_amb_tol > cabinp) // Kabinendruck in bar 
                flag_no_fly = 1;
        }

        if(!flag_no_fly)
            return nft;
        nft++;
    }
    return nft;
}

// Uebersaettigungstoleranzen veraendern 
void set_ab_values(int k, unsigned char showmode)
{
    unsigned char t1;
    double f = k * 0.1;

    for(t1 = 0; t1 < NCOMP; t1++)
    {
        aN2[t1] = 2 * exp(-0.33333333 * log(t05N2[t1]));
        //aHe[t1] = 2 * exp(-0.33333333 * log(t05He[t1]));
        bN2[t1] = 1.005 - exp(-0.5 * log(t05N2[t1]));
        //bHe[t1] = 1.005 - exp(-0.5 * log(t05He[t1]));
    }

    for(t1 = 0; t1 < NCOMP; t1++)
    {
        aN2[t1] /= f;
        bN2[t1] *= f;
    }

    if(!showmode)
        return;

    // A- und B-Werte anzeigen 
    lcd_putstring(0, 0, "a- und b-Werte:");
    wait_ms(1000);
    lcd_cls();

    for(t1 = 0; t1 < NCOMP; t1++)
    {
        lcd_putchar(0, 0, 'a');
        lcd_putnumber(0, 1, t1, -1, -1, 'l');
        lcd_putnumber(0, 4, aN2[t1 + 1] * 10000, 5, 4, 'l');
        lcd_putchar(1, 0, 'b');
        lcd_putnumber(1, 1, t1, -1, -1, 'l');
        lcd_putnumber(1, 4, bN2[t1 + 1] * 10000, 5, 4, 'l');
        wait_ms(1000);
        lcd_cls();
    }
}


// Drucksensor auslesen 
void get_dsensor()
{
    unsigned char xpos;

    adc_mode = 0;

    // AD-Wandler 
    outp(64 + 128, ADMUX);     // Interne Referenz auf 2,56V und Kanal 0 aktivieren PA0 PIN 40 
    wait_ms(ADWAITSTATE);
    outp(206, ADCSRA);   // AD-Wandler abfragen 
    wait_ms(ADWAITSTATE);

   if(depth > 999)
    {
        depth = 999;
    }

    if(depth > maxdepth)
    {
        maxdepth = depth;
        temp_maxdepth = temp;
    }


    lcd_printdiveinfo(depth, maxdepth, diveseconds * 0.0166666667);

    if(depth < (deepest_decostep - 1) * 10)
    {
        led(3, 1);
        lcd_putstring(0, 6, "!  ");
        xpos = lcd_putnumber(0, 7, deepest_decostep, -1, -1, 'l') + 7;
        lcd_putstring(0, xpos, "m! ");
        wait_ms(50);
        led(3, 0);

        if(!decostep_skipped) // Flag fuer Profilaufzeichnung setzen 
        {
            eeprom_store_byte(223);
            decostep_skipped = 1;
        }
    }
}

// Temperatursensor auslesen 
void get_tsensor()
{

    adc_mode = 1;

    // AD-Wandler 
    outp(64 + 128 + 1, ADMUX); // Interne Referenz 2,56V und Kanal 1 aktivieren PA PIN 39 
    wait_ms(ADWAITSTATE);
    outp(206, ADCSRA);   // AD-Wandler abfragen 
    wait_ms(ADWAITSTATE);
}

// Akkuspannung messen
void get_vsensor()
{
    adc_mode = 2;

    // AD-Wandler 
    outp(64 + 128 + 2, ADMUX); // Interne Referenz und Kanal 2 aktivieren PA PIN 38 
    wait_ms(ADWAITSTATE);
    outp(206, ADCSRA);   // AD-Wandler abfragen 
    wait_ms(ADWAITSTATE);
}

// ppO2 (Rueckgabe = 10facher Wert!) 
int calc_ppO2(char display_warning)
{
    int lppO2 = 10 * ((depth * 0.01) + airp) * (1 - figN2[curgas]);

    if(lppO2 > maxppO2 && display_warning)
    {
        lcd_putstring(1, 10, " ppO2!");
        if(!ppo2_exceeded)
        {
            eeprom_store_byte(224);
            ppo2_exceeded = 1;
        }
    }
    return lppO2;
}


// ZNS- und OTU-Werte berechnen (Aufruf 1 x pro Minute) 
void calc_cns_otu()
{
    // ZNS-Tabelle 
    unsigned int f_day[11] =  {720, 570, 450, 360, 300, 270, 240, 210, 180, 165, 150};
    unsigned int f_dive[11] = {720, 570, 450, 360, 300, 240, 210, 180, 150, 120, 45};

    // Index des Tabellenwertes zu geg. ppO2 
    int ndx = calc_ppO2(0) - 6;

    // ppO2-Rechnungen 
    float otu_ppO2 = calc_ppO2(0) * .1 - .5;
    float cns_ppO2 = calc_ppO2(0) * .1;

    // ZNS in Oberflaechenmodus t1/2 = 90 min. 
    if(!dphase)
    {
        cns_day *= 0.992327946262943;
        return;
    }

    if(ndx >= 0 && ndx <= 10) // Normaler ppO2 => Berechnung der Dosis auf Basis der Tabelle
    {
        cns_day += 100 * exp(-1 * log(f_day[ndx]));
        cns_dive += 100 * exp(-1 * log(f_dive[ndx]));
    }

    if(ndx > 10) // Sehr hoher ppO2 => Berechnung der Dosis auf funktionaler Basis 
    {
        cns_day +=   100 * exp(-1 * log((cns_ppO2 * 432) - (cns_ppO2 - .6) * 120));
        cns_dive += 100 * exp(-1 * log((cns_ppO2 * 432) - (cns_ppO2 - .6) * 120));
    }

    // OTU 
    if(otu_ppO2 > 0)
        otu += exp(0.83 * log(otu_ppO2 * 2));
}

//***************
// Ende Dekoteil 
//***************

//************************************
// Funktionen und Prozeduren fuer LCD 
//************************************
// Ein Byte (Befehl bzw. Zeichen) zum Display senden 
void lcd_write(char lcdmode, unsigned char value, int waitcycles)
{
    set_e(0);

    if(!lcdmode)
        set_rs(0);    // RS=0 => Befehl 
    else
        set_rs(1);    // RS=1 => Zeichen 

    wait_ms(waitcycles * 2);

    set_e(1);
    outp(value & 0xF0, PORTD);           // Hi byte 
    set_e(0);
    set_e(1);
    outp((value & 0x0F) * 0x10, PORTD);  // Lo byte 
    set_e(0);

}

// Ein Zeichen (Char) zum Display senden, dieses in 
// Zeile row und Spalte col positionieren           
void lcd_putchar(int row, int col, unsigned char ch)
{
    lcd_write(LCD_INST, col + 128 + row * 0x40, 1);
    lcd_write(LCD_DATA, ch, 1);
}


// Eine Zeichenkette direkt in das LCD schreiben 
// Parameter: Startposition, Zeile und Pointer   
void lcd_putstring(int row, int col, unsigned char *s)
{
    unsigned char t1;

    for(t1 = col; *(s); t1++)
        lcd_putchar(row, t1, *(s++));
}


// Display loeschen 
void lcd_cls(void)
{
    lcd_write(LCD_INST, 1, 5);
}


// Display loeschen (eine Zeile) 
void lcd_linecls(int displine, int chars)
{
    unsigned char t1;

    for(t1 = 0; t1 <= chars; t1++)
        lcd_putchar(displine, t1, 32);
}

// E setzen 
void set_e(char status)  // PORT C0 = Pin 6 am LCD 
{
    if(status)
        sbi(PORTC, 0);
    else
        cbi(PORTC, 0);
}

// RS setzen 
void set_rs(char status) // PORT C1 = Pin 4 am LCD 
{
    if(status)
        sbi(PORTC, 1);
    else
        cbi(PORTC, 1);
}

// LCD-Display initialisieren 
void lcd_init(void)
{
    // Grundeinstellungen: 2 Zeilen, 5x7 Matrix, 4 Bit 
    lcd_write(LCD_INST, 40, 5);
    lcd_write(LCD_INST, 40, 5);
    lcd_write(LCD_INST, 40, 5);

    lcd_write(LCD_INST, 2, 5);
    lcd_write(LCD_INST, 8, 5);

    // Display on, Cursor off, Blink off 
    lcd_write(LCD_INST, 12, 5);

    lcd_cls();

    // Entrymode !cursoincrease + !displayshifted 
    lcd_write(LCD_INST, 4, 5);
}


// Eine n-stellige Zahl direkt in das LCD schreiben 
// Parameter: Startposition und Zeile; Zahl,        
// darzustellende Ziffern, Position des Dezimalpunktes, (l)links- oder (r)echtsbuendig 
int lcd_putnumber(int row, int col, int num, int digits, int dec, char orientation)
{
    char cl = col, minusflag = 0;
    unsigned char cdigit[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, digitcnt = 0;
    int t1, t2, n = num, r, x = 1;

    if(num < 0)
    {
        minusflag = 1;
        n *= -1;
    }

    // Stellenzahl automatisch bestimmen 
    if(digits == -1)
    {
        for(t1 = 1; t1 < 10 && (n / x); t1++)
            x *= 10;
        digits = t1 - 1;
    }

    if(!digits)
        digits = 1;

    for(t1 = digits - 1; t1 >= 0; t1--)
    {
        x = 1;
        for(t2 = 0; t2 < t1; t2++)
            x *= 10;
        r = n / x;
        cdigit[digitcnt++] = r + 48;

        if(t1 == dec)
            cdigit[digitcnt++] = 46;
        n -= r * x;
    }

    digitcnt--;
    t1 = 0;

    // Ausgabe 
    switch(orientation)
    {
      case 'l':
        cl = col;
        if(minusflag)
        {
            lcd_putchar(row, cl++, '-');
            digitcnt++;
        }

        while(cl <= col + digitcnt)                       // Linksbuendig 
            lcd_putchar(row, cl++, cdigit[t1++]);

        break;

      case 'r':
        t1 = digitcnt;                              // Rechtsbuendig 
        for(cl = col; t1 >= 0; cl--)
            lcd_putchar(row, cl, cdigit[t1--]);
        if(minusflag)
            lcd_putchar(row, --cl, '-');
    }

    if(dec == -1)
        return digits;
    else
        return digits + 1;
}

// Alle Daten des laufenden TG an die richtigen Stellen des LCD  
// schreiben: Parameter: Tiefen in m, Zeit in sec.               
void lcd_printdiveinfo(int cdepth, int mdepth, int divetime)
{
    lcd_putnumber(0, 0, cdepth, 3, 1, 'l');
    lcd_putchar(0, 4, 'm');

    lcd_putnumber(0, 6, mdepth, 3, 1, 'l');
    lcd_putstring(0, 10, "m");

    lcd_putnumber(0, 14, divetime, -1, -1, 'r'); // Tauchzeit rechtsbuendig 1. Zeile 
    lcd_putchar(0, 15, 39);
}


// Temperatur anzeigen 
void showtemp()
{
    lcd_putstring(1, 12, "   ");
    lcd_putnumber(1, 14, temp * 10, 3, 1, 'r');

   lcd_putchar(1, 15, 223); // °-Zeichen 
}

void show_accu_voltage()
{
    get_vsensor();
    lcd_putstring(1, 0, "BAT:");
    lcd_putchar(1, 5 + lcd_putnumber(1, 5, accu_voltage * 10, 2, 1, 'l'), 'V');
}

// Aktuelles Atemgas anzeigen 
void show_gas(int gasnum)
{
    unsigned char xpos;

    lcd_cls();
    if(gasnum < MAXGASES)
    {
        lcd_putstring(0, 0, "Gas");
        lcd_putnumber(0, 4, gasnum + 1, -1, -1, 'l');

        lcd_putstring(1, 0, "N2:");
        xpos = lcd_putnumber(1, 4, figN2[gasnum] * 100, -1, -1, 'l') + 4;
        lcd_putchar(1, xpos, '%');
    }
}

// Aktuelles Atemgas einstellen 
void set_curgas()
{
    unsigned char lcurgas = curgas;
    unsigned long lseconds = runseconds;

    show_gas(lcurgas);

    while(get_keys());

    do
    {
        if(get_keys() == 3)
        {
            lseconds = runseconds;
            lcurgas++;
            if(lcurgas >= MAXGASES)
                lcurgas = 0;
            show_gas(lcurgas);
            while(get_keys() == 3);
        }
    }
    while(runseconds < lseconds  + 5);

    lcd_cls();

    if(curgas != lcurgas)
    {
        lcd_putstring(0, 0, "Wechsel zu Gas");
        lcd_putnumber(0, 15, lcurgas + 1, -1, -1, 'l');
        curgas = lcurgas;
        eeprom_store_byte(226);
        eeprom_store_byte(curgas);
    }

    wait_ms(2000);
    lcd_cls();
}


// Tiefenwert runden auf 1 Nachkommastelle u. /= 10 
int round_depth(int d)
{
    if(d - (d / 10) * 10 >= 5)
        return (d / 10 + 1);
    else
        return (d / 10);
}

void calc_airp_divesite(char show_mode)
{
    char xpos;
    int airp0_tmp = airp0 * 1000;

    airp0_tmp = airp0_tmp * exp(-1.2928 * 0.0981 * altitude / airp0_tmp);

    if(show_mode)
    {
        wait_ms(INITWAIT);
        lcd_cls();
        lcd_putstring(0, 0, "Luftdruck TP");
        xpos = lcd_putnumber(1, 0, airp0_tmp, -1, -1, 'l') + 1;
        lcd_putstring(1, xpos, "mbar");
    }

    airp = airp0_tmp * 0.001;
}
// Ende LCD-Teil 

//*******
// USART 
//*******
void usart_init()
{
    // 2.4 kBaud 
    UBRRL = 220; // Originaler Wert = 207 fuer 2.4k 215 
    UBRRH = 0;

    // RX Interrupt, RX und TX einschalten 
    UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);

    // 8 Datenbits, 1 Stopbit, keine Paritaet 
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);

    rx_buf_cnt = 0;
}

void usart_putc(char tx_char)
{
    while(!(UCSRA & (1<<UDRE)));

    UDR = tx_char;
}

SIGNAL(SIG_UART_RECV)
{
    unsigned char rx_char = UDR, inputlen = 2;
    unsigned int t1, byte_adr, x = 0;;

    if(rx_buf_cnt < RX_BUF_SIZE)
    {
        rx_buf[rx_buf_cnt] = rx_char;

        switch(rx_buf[0])
        {
          case 100:
            inputlen = 2; // 1 Byte lesen 
            rx_buf_cnt++;
            break;

          case 101:
            inputlen = 4; // 1 Byte schreiben 
            rx_buf_cnt++;
            break;

          default:   clear_rx_buf();
        }
    }
    else
        clear_rx_buf();

    if(rx_buf_cnt > inputlen)
    {
        // Befehlsfolge quittieren 
        for(t1 = 0; t1 < rx_buf_cnt; t1++)
            usart_putc(rx_buf[t1]);

        byte_adr = rx_buf[1] + rx_buf[2] * 256;

        if(byte_adr <= MAX_EEPROM_ADR)
        {
            lcd_putnumber(1, 8, byte_adr, 4, -1, 'l');

            // Code auswerten 
            switch(rx_buf[0])
            {
              case 100:  // 1 Byte lesen 
                usart_putc(eeprom_rb(byte_adr));               // Byte senden 
                usart_putc(make_crc(3, eeprom_rb(byte_adr)));  // CRC anhaengen 
                lcd_putstring(1, 0, "Tx  ");
                lcd_putnumber(1, 13, eeprom_rb(byte_adr), 3, -1, 'l');
                break;

              case 101:  // 1 Byte schreiben 
                for(t1 = 0; t1 < 4; t1++)  // CRC berechnen 
                x = x ^ rx_buf[t1];

            if(x == rx_buf[4])   // CRC ist OK 
            {
                cli();
                    while(!eeprom_is_ready());
                    eeprom_wb(rx_buf[1] + rx_buf[2] * 256, rx_buf[3]);
                    sei();
                    lcd_putstring(1, 0, "Rx  ");
                    lcd_putnumber(1, 13, rx_buf[3], 3, -1, 'l');
            }
                else
                {
                lcd_putstring(1, 0, "CRC!");
            }
            }
        }
        clear_rx_buf();
    }
}

// CRC-Pruefsumme berechnen 
char make_crc(int buflen, int addchar)
{
    int t1, x = 0;

    for(t1 = 0; t1 < buflen; t1++) // Puffer bis dato 
        x = x ^ rx_buf[t1];
    x = x ^ addchar;                // Sendebyte 

    return x;
}

// Empfangspuffer loeschen 
void clear_rx_buf()
{
    int t1;

    for(t1 = 0; t1 < RX_BUF_SIZE; t1++)
        rx_buf[t1] = 0;
    rx_buf_cnt = 0;
}

// Hauptfunktion der Schnittstelle zwischen SBTC und PC 
void sbtc2pc()
{

    while(get_keys());
    lcd_cls();

    // Datenuebertragung zum PC starten? 
    lcd_putstring(0, 0, "SBTC <-> PC?");
    lcd_putstring(1, 0, "(j/n)");
    do
    {
        if(get_keys() == 3)
        {
            usart_init();
            lcd_cls();
            lcd_putstring(0, 0, "Modus");
            lcd_putstring(0, 8, "ADRS VAL");

            while(get_keys() != 2);
            UCSRB = 0;
            return;
        }
    }while(get_keys() != 2);
    while(get_keys());
    lcd_cls();

}

void display_profile()
{
    unsigned int t1;
   int startbyte = 55, endbyte;
   int xdepth = 0;
    unsigned char xpos, ok, p_cnt = 1;

    while(get_keys());
    lcd_cls();

    lcd_putstring(0, 0, "TG-Profil an-");
    lcd_putstring(1, 0, "zeigen? (j/n)");

    do
    {
        if(get_keys() == 3)
        {
          cli();
         while(1)
         {
            lcd_cls();

            // Startbyte finden 
            t1 = startbyte;
            endbyte = 0;
            ok = 0;
            while(t1 < MAX_EEPROM_ADR && !ok)
            {
               if(eeprom_rb(t1) == 229)
               {
                  startbyte = t1;
                  ok = 1;
               }
               else
                  t1++;
            }

            t1 = startbyte + 1;
            ok = 0;
            while(t1 < MAX_EEPROM_ADR && !ok)
            {
               if(eeprom_rb(t1) == 230)
               {
                  endbyte = t1;
                  ok = 1;
               }
               else
                  t1++;
            }

            if(endbyte)
            {
               lcd_cls();
               lcd_putstring(0, 0, "Profil");
               lcd_putnumber(0, 7, p_cnt++, -1, -1, 'l');
               wait_ms(1000);

               lcd_cls();
               lcd_putstring(0, 0, "Zeit");
               lcd_putstring(0, 8, "Tiefe");
               for(t1 = startbyte + 1; t1 < endbyte; t1++)
               {
                  xdepth = eeprom_rb(t1);
                  if(xdepth < 99)
                  {
                     lcd_linecls(1, 15);

                     xpos = lcd_putnumber(1, 0, (int)((t1 - startbyte) * 0.3333333), -1, -1, 'l') + 1;
                     lcd_putstring(1, xpos, "min.");


                     xpos = lcd_putnumber(1, 8, xdepth, -1, -1, 'l') + 9;
                     lcd_putchar(1, xpos, 'm');
                     wait_ms(500);

                     if(get_keys() == 2)
                     {
                        lcd_cls();
                        sei();
                        return;
                     }
                  }
               }
            }
            else
            {
               lcd_cls();
               lcd_putstring(0, 0, "Keine (weiteren)");
               lcd_putstring(1, 0, "Profile.");
               wait_ms(2000);
               lcd_cls();
               sei();
               return;
            }
            startbyte = endbyte + 1;
            endbyte = 0;
         }
      }
   }while(get_keys() != 2);
   while(get_keys());
   lcd_cls();
}

// Werte für Gesamttauchzeit, Max. Tiefe etc. 
void display_rcd()
{
    unsigned char xpos;
   unsigned long dminutes_t;
   unsigned int dminutes, dhours;

    lcd_putstring(0, 0, "Logwerte zeigen?");
   lcd_putstring(1, 0, "(j/n)");

    do
    {
        if(get_keys() == 3)
        {
            lcd_cls();
         lcd_putstring(0, 0, "Anzahl TG:");
         lcd_putnumber(1, 0, eeprom_rb(24) + eeprom_rb(25) * 256, -1, -1, 'l') + 1;
         while(get_keys() != 2);
         while(get_keys());

         lcd_cls();
         lcd_putstring(0, 0, "Ges. Tauchzeit:");

         dminutes_t = eeprom_rb(26) + eeprom_rb(27) * 256;
         dhours = dminutes_t / 60;
         dminutes = dminutes_t - dhours * 60;

         xpos = lcd_putnumber(1, 0, dhours, -1, -1, 'l') + 1;
         lcd_putstring(1, xpos, "Std.");
         xpos = lcd_putnumber(1, 8, dminutes, -1, -1, 'l') + 9;
         lcd_putstring(1, xpos, "Min.");
            while(get_keys() != 2);
         while(get_keys());

         lcd_cls();
         lcd_putstring(0, 0, "Max. Tiefe:");
         xpos = lcd_putnumber(1, 0, eeprom_rb(28) + eeprom_rb(29) * 256, 3, 1, 'l') + 1;
         lcd_putstring(1, xpos, "m");
      }
   }while(get_keys() != 2);
   while(get_keys());
   lcd_cls();
}


void display_log()
{

    unsigned int t1;
   int startbyte = 55;
    unsigned char xpos, ok, p_cnt = 1;

    while(get_keys());
    lcd_cls();

    lcd_putstring(0, 0, "TG-Daten an-");
    lcd_putstring(1, 0, "zeigen? (j/n)");

    do
    {
        if(get_keys() == 3)
        {
          cli();
         while(1)
         {
            lcd_cls();

            // Anfang eines Datensatzes im EEPROM-Speicher suchen 
            t1 = startbyte;
            ok = 0;
            while(t1 < MAX_EEPROM_ADR && !ok)
            {
               if(eeprom_rb(t1) == 230)
               {
                  startbyte = t1;
                  ok = 1;
               }
               else
                  t1++;
            }

            if(startbyte && ok)
            {
               lcd_cls();
               lcd_putstring(0, 0, "TG Nr.");
               lcd_putnumber(0, 7, p_cnt++, -1, -1, 'l');
               wait_ms(1000);

               lcd_cls();

               lcd_putstring(0, 0, "Tauchzeit in Min.");
               lcd_putnumber(1, 0, eeprom_rb(startbyte + 1) + eeprom_rb(startbyte + 2) * 256, -1, -1, 'l');
               wait_ms(1000);
               lcd_cls();

                  lcd_putstring(0, 0, "Max. Tiefe in m");
               lcd_putnumber(1, 0, (eeprom_rb(startbyte + 3) + eeprom_rb(startbyte + 4) * 256) / 10, -1, -1, 'l');
               wait_ms(1000);
               lcd_cls();

               lcd_putstring(0, 0, "Dekostufen");

               ok = 0;
               t1 = startbyte + 5;
               xpos = 0;
               while(t1 < MAX_EEPROM_ADR && t1 < startbyte + 10 && !ok)
                {
                   if(eeprom_rb(t1++) == 231)
                   {
                      ok = 1;

                     while(t1 < MAX_EEPROM_ADR && ok && eeprom_rb(t1++) != 232)
                        xpos = lcd_putnumber(1, xpos, eeprom_rb(t1), -1, -1, 'l') + 2;
                   }
                }
            }
          }
      }
    }while(get_keys() != 2);
   while(get_keys());
   lcd_cls();

}

// Flash-Speicher löschen 
// Parameter: 1: Nur Profilspeicher löschen, 2: kompletten Speicher löschen 
void clear_flash(char erasemode)
{
    unsigned int t1, startadr = 0;

    while(get_keys());
    lcd_cls();

   if(erasemode == 1)
       startadr = EEPROM_PROF_START;

    // TG-Profildaten loeschen? 
   if(startadr == EEPROM_PROF_START)
   {
        lcd_putstring(0, 0, "TG-Profile loe-");
        lcd_putstring(1, 0, "schen? (j/n)");
    }
   else
   {
        lcd_putstring(0, 0, "Flashspeicher");
        lcd_putstring(1, 0, "loeschen? (j/n)");
   }

    do
    {
        if(get_keys() == 3)
        {
            lcd_cls();
            lcd_putstring(0, 0, "Loesche Byte:");
            cli();
            for(t1 = startadr; t1 <= MAX_EEPROM_ADR; t1++)
            {
                while(!eeprom_is_ready());
                eeprom_wb(t1, 0);
                lcd_putnumber(1, 0, t1, -1, -1, 'l');
            }
            while(!eeprom_is_ready());
            eeprom_wb(30, EEPROM_PROF_START);
            while(!eeprom_is_ready());
            eeprom_wb(31, 0);
            sei();
            lcd_cls();
            return;
        }
    }while(get_keys() != 2);
    while(get_keys());
    lcd_cls();

}

// Wartezeit in Millisekunden bei fck = 8.000 MHz 
void wait_ms(int ms)
{
    int t1, t2;

    for(t1 = 0; t1 < ms; t1++)
        for(t2 = 0; t2 < 137 * SCLOCK; t2++)
            asm volatile ("nop" ::);
}



// LEDs an PC2, 3 und 4 
void led(char lednum, char status)
{
    if(status)
        cbi(PORTC, lednum);
    else
        sbi(PORTC, lednum);
}


// Einstellungen durch Benutzer                
// Tastenentprellung per Hardware erforderlich 
int get_keys(void)
{
    int t1;
   for(t1 = 0; t1 < 3;t1++)
     if(!bit_is_set(PINB, t1))
        return (t1 + 1);
   return 0;
}

// Benutzereinstellungen 
void settings(void)
{
    int menu_sta[MENU_ITEMS] = {900, 0, 400, 10, 3, 0, 0};         // Startwerte fuer Wertepektrum 
    int menu_end[MENU_ITEMS] = {1100, 4000, 1000, 20, 20, 1, 1};   // Endwerte fuer Wertepektrum   
    int menu_step[MENU_ITEMS] = {5, 100, 5, 1, 1, 1, 1};           // Inkrement                    

    int menu_N2[3]; // Temporaere Werte fuer Stickstoff 

    int intv, t1;

    char ch, xpos;

    int menu_tmpval[MENU_ITEMS];
    menu_tmpval[0] = airp0 * 1000;   // Luftdruck                                                     
    menu_tmpval[1] = altitude;       // Hoehe ueber NN                                                
    menu_tmpval[2] = cabinp * 1000;  // Kabinendruck Flugzeug                                         
    menu_tmpval[3] = maxppO2;        // Max. zul. Sauerstoffpartialdruck (10facher Wert!)             
    menu_tmpval[4] = f_cons;         // Multiplikationsfaktor fuer Übersaettigungstoleranzen          
    menu_tmpval[5] = eeprom_rb(18);  // ppN2 nach TG-Ende anzeigen
   menu_tmpval[6] = eeprom_rb(19);  // Beim Starten Einstellungen anzeigen?

    for(t1 = 0; t1 < MAXGASES; t1++)
    {
        menu_N2[t1] = figN2[t1] * 100;
    }

    while(get_keys());
    lcd_cls();

    for(t1 = 0; t1 < MENU_ITEMS; t1++)
    {
        lcd_putstring(0, 0, menu_str[t1]);
        xpos = lcd_putnumber(1, 0, menu_tmpval[t1], menu_digits[t1], menu_dec[t1], 'l') + 1;
        lcd_putstring(1, xpos, menu_unitstr[t1]);

        do
        {
            ch = get_keys();
            if(ch == 1 || ch == 3)
            {
                lcd_linecls(1, 15);
                intv =  menu_tmpval[t1] / menu_step[t1];
                switch(ch)
                {
                  case 3:
                    menu_tmpval[t1] = intv * menu_step[t1] + menu_step[t1];
                    if(menu_tmpval[t1] > menu_end[t1])
                        menu_tmpval[t1] = menu_sta[t1] ;
                    break;

                  case 1:
                    menu_tmpval[t1]  = intv *  menu_step[t1] - menu_step[t1];
                    if(menu_tmpval[t1] < menu_sta[t1])
                        menu_tmpval[t1] = menu_end[t1];
                }
                xpos = lcd_putnumber(1, 0, menu_tmpval[t1], menu_digits[t1], menu_dec[t1], 'l') + 1;
                lcd_putstring(1, xpos, menu_unitstr[t1]);
                wait_ms(100);
            }
        }while(ch != 2);
        while(get_keys());
        lcd_cls();
    }

    // 3 Atemgase 
    for(t1 = 0; t1 < MAXGASES; t1++)
    {
        // N2 
        lcd_putstring(0, 0, "Gas   N2-Anteil");
        lcd_putnumber(0, 4, t1 + 1, -1, -1, 'l');
        xpos = lcd_putnumber(1, 0, menu_N2[t1], -1, -1, 'l');
        lcd_putstring(1, xpos, "%  ");
        do
        {
            ch = get_keys();
            if(ch == 1 || ch == 3)
            {
                lcd_linecls(1, 15);
                switch(ch)
                {
                  case 3:
                    menu_N2[t1]++;
                    if(menu_N2[t1] > 79)
                        menu_N2[t1] = 0;
                    break;

                  case 1:
                    menu_N2[t1]--;
                    if(menu_N2[t1] < 0)
                        menu_N2[t1] = 79;
                }
                xpos = lcd_putnumber(1, 0, menu_N2[t1], -1, -1, 'l');
                lcd_putstring(1, xpos, "%  ");
                lcd_putstring(1, 4, "(Nitrox");
                xpos = lcd_putnumber(1, 12, 100 - menu_N2[t1], -1, -1, 'l') + 12;
            lcd_putstring(1, xpos, ")");
            }
            wait_ms(100);
        }while(ch != 2);
        while(get_keys());
        lcd_cls();
    }

    // Speichern? 
    lcd_putstring(0, 0, "Sichern? (j/n)");
    do
    {
        if(get_keys() == 3)
        {
            cli();
            // Luftdruck am Tauchort 
            airp0 = menu_tmpval[0] * 0.001;
            while(!eeprom_is_ready());
            eeprom_wb(0, menu_tmpval[0] & 0x00FF);          // LoByte 
            while(!eeprom_is_ready());
            eeprom_wb(1, (menu_tmpval[0] & 0xFF00) / 256);  // HiByte 

            // Hoehe ueber NN 
            altitude = menu_tmpval[1];
            while(!eeprom_is_ready());
            eeprom_wb(16, menu_tmpval[1] & 0x00FF);          // LoByte 
            while(!eeprom_is_ready());
            eeprom_wb(17, (menu_tmpval[1] & 0xFF00) / 256);  // HiByte 

            calc_airp_divesite(1); // Luftdruck am Tauchort nachberechnen 
            wait_ms(2000);
            lcd_cls();

            // Kabinendruck im Flugzeug 
            cabinp = menu_tmpval[2]* 0.001;
            while(!eeprom_is_ready());
            eeprom_wb(14, menu_tmpval[2] & 0x00FF);          // LoByte 
            while(!eeprom_is_ready());
            eeprom_wb(15, (menu_tmpval[2] & 0xFF00) / 256);  // HiByte 

            // max. ppO2 
            maxppO2 = menu_tmpval[3];
            while(!eeprom_is_ready());
            eeprom_wb(11, menu_tmpval[3]);

            // Konservativfaktor 
         // Anzeigen der a- und b-Werte wenn geändert 
            if(menu_tmpval[4] != f_cons)
            {
                set_ab_values(menu_tmpval[4], 1);
                while(!eeprom_is_ready());
                eeprom_wb(10, menu_tmpval[4]);
                f_cons = menu_tmpval[4];
            }

            // ppN2-Anzeige nach TG 
            show_ppN2 = menu_tmpval[5];
            while(!eeprom_is_ready());
            eeprom_wb(18, menu_tmpval[5]);

         // Einstellungen beim Starten anzeigen?
            show_ppN2 = menu_tmpval[6];
            while(!eeprom_is_ready());
            eeprom_wb(19, menu_tmpval[6]);

            // Gase 
            for(t1 = 0; t1 < MAXGASES; t1++)
            {
                figN2[t1] = menu_N2[t1] * 0.01;
                while(!eeprom_is_ready());
                eeprom_wb(t1 * 2 + 2, menu_N2[t1]);
            }

            sei();

            lcd_putstring(0, 2, "Gespeichert.");
         wait_ms(1000);
         lcd_cls();
         return;
        }
    }while(get_keys() != 2);
    while(get_keys());
    lcd_cls();
}

// Timer 2 Ereignisroutine (autom. Aufruf 1/s) 
INTERRUPT(SIG_OVERFLOW2)
{
    runseconds++;

    outp(0, TCNT2);       // Timerregister auf 0 
}

// AD-Wandler Ereignisroutine 
SIGNAL(SIG_ADC)
{
    unsigned char lo, hi;

    lo = inp(ADCL);
    hi = inp(ADCH);

    adc_val = hi * 256 + lo;
   switch(adc_mode)
   {
      case 0: depth = adc_val;
     break;
     case 1: temp = (adc_val - 396.28) / 2.7714;
     break;
     case 2: accu_voltage = (adc_val - 1) / 58.5;
     break;
   }
}

void eeprom_store_byte(char eeprom_val)
{
    if(eeprom_byte_count < EEPROM_PROF_START || eeprom_byte_count > MAX_EEPROM_ADR)
        eeprom_byte_count = EEPROM_PROF_START;

    cli();
    while(!eeprom_is_ready());
    eeprom_wb(eeprom_byte_count++, eeprom_val);
    sei();
}

int main()
{
    unsigned long seconds_old1, seconds_old2, seconds_old3;
    unsigned char do_record_depth = 0; // Intervallschalter fuer Profilaufzeichnung 
   unsigned char info_mode = 0;       // Definieren, was angezeigt werden soll     
    unsigned char nft = 0;             // Flugverbotszeit                           
   unsigned char is_deco;
    char xpos;
   unsigned int cur_comp = 0;
   char max_info_mode;
   unsigned long surf_hrs, surf_mins;

    int t1;

    // Ports einrichten 
    // OUTPUT 
    // Port D Bit 4-7 auf "Output" schalten (=Display=DB4-DB7) 
    outp(0xF0, DDRD);
    // Port C Bit 0 bis 4 auf "Output" schalten 
    // LCD RS, E, LEDs und BIAS Drucksensor bei niedr. Temp) 
    outp(0x3F, DDRC);

    outp(0x00, DDRB);  // Port B auf Input schalten für taster 1-3

    // Alle LEDs aus 
    for(t1 = 2; t1 < 4; t1++)
        led(t1, 0);

    // Mit LCD-Initialisierung 0.2 s warten bis PowerUp von MC OK 
    wait_ms(200);
    lcd_init();
    lcd_putstring(0, 1, "SBTC 3 (O2/N2)");

    // Softwareversion 
    for(t1 = 0; t1 < 3; t1++)
    {
        if(softwareversion[t1] != eeprom_rb(21 + t1))
        {
            while(!eeprom_is_ready());
            eeprom_wb(21 + t1, softwareversion[t1]);
        }
    }

    lcd_putstring(1, 5, "V .");
    lcd_putnumber(1, 6, softwareversion[0], -1, -1, 'l');
    lcd_putnumber(1, 8, softwareversion[1], 2, -1, 'l');
    lcd_putchar(1, 10, softwareversion[2]);

    // Watchdog aus, wird nicht gebraucht, da Software zuverlaessig ist ;-)) 
    // Logisch '1' in WDTOE und WDE schreiben 
    WDTCR = (1<<WDTOE) | (1<<WDE);
    // WDT abschalten 
    WDTCR = 0x00;
    wait_ms(INITWAIT * 2);

   // ppN2 anzeigen 
    show_settings = eeprom_rb(19);
    if (show_settings   != 1 && show_settings != 0)
        show_settings = 0;

   // Konservativ-Faktor auf 5 setzen (^= *= 1.2) 
    f_cons = 12;
    set_ab_values(f_cons, 0);

    if(show_settings)
   {
      // Umgebungsluftdruck 
      lcd_cls();
      airp0 = (eeprom_rb(0) + eeprom_rb(1) * 256) * 0.001;  // Luftdruck 
      if(airp0 < 0.66 || airp0 > 1.2)
         airp0 = 1;
      lcd_putstring(0, 0, menu_str[0]);
      xpos = lcd_putnumber(1, 0, airp0 * 1000, -1, -1, 'l') + 1;
      lcd_putstring(1, xpos, menu_unitstr[0]);

      // Hoehe ueber NN 
      wait_ms(INITWAIT);
      lcd_cls();
      altitude = (eeprom_rb(16) + eeprom_rb(17) * 256);  // Hoehe ueber NN in m 
      if(altitude < 0 || altitude > 6000)
         altitude = 0;
      lcd_putstring(0, 0, menu_str[1]);
      xpos = lcd_putnumber(1, 0, altitude , -1, -1, 'l') + 1;
      lcd_putstring(1, xpos, menu_unitstr[1]);

      // Luftdruck am Tauchort 
      calc_airp_divesite(1);

      // Kabinendruck im Flugzeug 
      wait_ms(INITWAIT);
      lcd_cls();
      cabinp = (eeprom_rb(14) + eeprom_rb(15) * 256) * 0.001;  // Wert aus EEPROM lesen 
      if(cabinp < 0.55 || cabinp > 1)
         cabinp = 0.75;
      lcd_putstring(0, 0, menu_str[2]);
      xpos = lcd_putnumber(1, 0, cabinp * 1000, -1, -1, 'l') + 1;
      lcd_putstring(1, xpos, menu_unitstr[2]);

      // N2- und He-Anteile in den 4 Gasen 
      wait_ms(INITWAIT);
      figN2[0] = FN2;
      show_gas(0);
      wait_ms(INITWAIT);

      for(t1 = 1; t1 < MAXGASES; t1++)
      {
         figN2[t1] = eeprom_rb(t1 * 2 + 2) * 0.01;
         if(figN2[t1] < 0 || figN2[t1] > 0.78)
            figN2[t1] = 0.78;

         show_gas(t1);
         wait_ms(INITWAIT);
      }

      // maxppO2 
      maxppO2 = eeprom_rb(11);
      if(maxppO2 > 20 || maxppO2 < 10)
         maxppO2 = 16;

      lcd_putstring(0, 0, menu_str[3]);
      lcd_putnumber(1, 0, maxppO2, 2, 1, 'l');
      lcd_putstring(1, 4, menu_unitstr[3]);

      wait_ms(INITWAIT);
      lcd_cls();

      // ppN2 anzeigen 
      show_ppN2 = eeprom_rb(18);
      if (show_ppN2  != 1 && show_ppN2 != 0)
         show_ppN2  = 0;
      lcd_putstring(0, 0, menu_str[5]);
      if(show_ppN2 )
         lcd_putstring(1, 0, "an");
      else
         lcd_putstring(1, 0, "aus");

      wait_ms(INITWAIT);
      lcd_cls();

      //Konservativfaktor
      lcd_putstring(0, 0, menu_str[4]);
      lcd_putnumber(1, 0, f_cons, menu_digits[4], menu_dec[4], 'l');
      wait_ms(INITWAIT);
   }

    curgas = 0;

    lcd_cls();

    // Timer 2 fuer Sekundenzaehlung initialisieren 
    // (asynchron getaktet durch 32.768 kHz-Quarz)  
    TIMSK &=~((1<<TOIE2)|(1<<OCIE2));  // Disable TC2 interrupt 
    ASSR |= (1<<AS2);                   // Timer/Counter2 auf asynchronen Betrieb mit quarz 32,768kHz schalten 
    TCNT2 = 0x00;                        // Startwert fuer Timer2 
    TCCR2 = 0x05;                        // Teiler ck/128 
    while(ASSR & 0x07);                 // Warten bis ASSR-Register neu geschrieben wurde 
    TIMSK |= (1<<TOIE2);                // Interrupt ermoeglichen 

    sei();

    seconds_old1 = runseconds - 10;
    seconds_old2 = runseconds;
    seconds_old3 = runseconds;


    for(;;) // Endlosschleife fuer period. Aufgaben (Druckmessung, Dekorechnung, etc.) Periode: 1/s 
    {
        get_dsensor();   // Sensorabfrage Drucksensor          

        calc_ppO2(1);    // ppO2 pruefen                       
        ldepth = depth;  // Zusatzwert fuer Deltatiefe         

        if(!depth && !surfaced && dphase)
        {
            eeprom_store_byte(227); // "Aufgetaucht" ins Log schreiben 
            surfaced = 1;
        }

        if(temp < temp_min)
            temp_min = temp;

        if(depth > SWITCHDEPTH)
        {
            surfaced = 0;

            if(!dphase) // TG beginnt, auf Tauchphase umschalten 
            {
                maxdepth = 0;
                diveseconds = 0;
                temp_min = 100;
                deco_minutes_total = 0;
                rcd_deco_minutes_total = 0;

            for(t1= 0; t1 < MAX_DECO_STEPS; t1++)
                rcd_decotime[t1] = 0;
                ndt_runout = 0;
                cns_dive = 0;
                temp_low = 0;

                lcd_cls();

                // Neues TG-Profil im Ringspeicher anlegen, Startpunkt suchen 
                eeprom_byte_count = eeprom_rb(30) + 256 * eeprom_rb(31);

                // Startsignal 
                eeprom_store_byte(228);

                // Oberflaechenpause speichern 
                eeprom_store_byte((surf_seconds / 60) & 0x00FF);          // Lo 
                eeprom_store_byte(((surf_seconds / 60) & 0xFF00) / 256);  // Hi 

                // Temperatur zu TG-Beginn 
                eeprom_store_byte(temp);

                // Aufzeichnungsintervall 
                eeprom_store_byte(20);

                // Indikator fuer den Beginn des TG-Profiles 
                eeprom_store_byte(229);

                surf_seconds = 0;
            }
            dphase = 1;

            diveseconds++;
        }
        else // Oberflaechenmodus 
        {
            if(surf_seconds > SURF_SECONDS_MAX && !deco_minutes_total)    // TG beendet 
            {
                if(dphase)
                {
                    // EEPROM aktualisieren... 
                    // TG-Zaehler um 1 erhoehen 
                    t1 = eeprom_rb(24) + 256 * eeprom_rb(25) + 1; // Alten Wert holen 
                    cli();
                    while(!eeprom_is_ready());
                    eeprom_wb(24, t1 & 0x00FF);         // LoByte 
                    while(!eeprom_is_ready());
                    eeprom_wb(25, (t1 & 0xFF00) / 256); // HiByte 

                    // Gesamttauchzeit erhoehen 
                    t1 = eeprom_rb(26) + 256 * eeprom_rb(27) + (int)(diveseconds / 60); // Alten Wert holen und veraendern 
                    while(!eeprom_is_ready());
                    eeprom_wb(26, t1 & 0x00FF);           // LoByte 
                    while(!eeprom_is_ready());
                    eeprom_wb(27, (t1 & 0xFF00) / 256);  // HiByte 

                    // Maximaltiefe evtl. erhoehen 
                    if(maxdepth > eeprom_rb(28) + 256 * eeprom_rb(29)) // Alten Wert holen 
                    {
                        while(!eeprom_is_ready());
                        eeprom_wb(28, maxdepth & 0x00FF);         // LoByte 
                        while(!eeprom_is_ready());
                        eeprom_wb(29, (maxdepth & 0xFF00) / 256); // HiByte 
                    }
                    sei();

                    // Indikator fuer Profilende 
                    eeprom_store_byte(230);

                    // Tauchzeit in [min] 
                    eeprom_store_byte((diveseconds / 60) & 0x00FF);          // Lo 
                    eeprom_store_byte(((diveseconds / 60) & 0xFF00) / 256);  // Hi 

                    // Max. Tiefe in [dm] 
                    eeprom_store_byte(maxdepth  & 0x00FF);          // Lo 
                    eeprom_store_byte((maxdepth  & 0xFF00) / 256); // Hi 

                    // Min. Temperatur 
                    eeprom_store_byte(temp_min);

                    // Temperatur auf max. Tauchtiefe 
                    eeprom_store_byte(temp_maxdepth);

                    // Dekostufen 
                    eeprom_store_byte(231);
                    for(t1 = 0; t1 < MAX_DECO_STEPS; t1++)
                        eeprom_store_byte(rcd_decotime[t1]);
                    eeprom_store_byte(232);

                    // Nr. des TG 
                    eeprom_store_byte(eeprom_rb(24) + 256 * eeprom_rb(25));

                    // Tages ZNS 
                    eeprom_store_byte((int)cns_day  & 0x00FF);          // Lo 
                    eeprom_store_byte(((int)cns_day & 0xFF00) / 256);  // Hi 

                    // Tauchgangs-ZNS 
                    eeprom_store_byte((int)cns_dive  & 0x00FF);        // Lo 
                    eeprom_store_byte(((int)cns_dive & 0xFF00) / 256); // Hi 

                    // OTU 
                    eeprom_store_byte((int)otu  & 0x00FF);         // Lo  
                    eeprom_store_byte(((int)otu & 0xFF00) / 256);  // Hi 

                    // Sequenzende 
                    eeprom_store_byte(233);

                    // Speichern der letzten Adresse bei Offset 30 & 31 
                    cli();
                    while(!eeprom_is_ready());
                    eeprom_wb(30, eeprom_byte_count & 0x00FF);            // LoByte 
                    while(!eeprom_is_ready());
                    eeprom_wb(31, (eeprom_byte_count & 0xFF00) / 256);   // HiByte 
                    sei();
                }
                dphase = 0;
            }
            surf_seconds++;
        }

        // Informationen der OFP alle 2 sec. wechseln 
        if(!dphase && !deco_minutes_total) // Bei WT = 0 m, 0 Deco und 5 min. ausgetaucht umschalten 
        {                                   // auf Anzeige der TG-Daten in der Zeile 1 
            if(runseconds > seconds_old3 + 2)
            {
                switch(info_mode)
                {
                  case 0:
                    lcd_linecls(1, 10);
               lcd_putstring(1, 0, "OFP: ");
               surf_hrs = surf_seconds / 3600; // (1/60)²     
               surf_mins = (surf_seconds - surf_hrs * 3600) / 60;
                    xpos = lcd_putnumber(1, 5, surf_hrs, 2, -1, 'l') + 5;
                    lcd_putstring(1, xpos++, ":");
               xpos = lcd_putnumber(1, xpos, surf_mins, 2, -1, 'l') + 5;
                    break;

                  case 1:
                    nft = calc_no_fly_time();
                    if(nft)
                    {
                        lcd_linecls(1, 10);
                        lcd_putstring(1, 0, "FVB: ");
                        xpos = lcd_putnumber(1, 5, nft, -1, -1, 'l') + 5;
                        lcd_putstring(1, xpos, "h");
                    }
                    break;

                  case 2:
                    if(cns_dive)
                    {
                        lcd_linecls(1, 10);
                        lcd_putstring(1, 0, "ZNS TG: ");
                        xpos = lcd_putnumber(1, 8, cns_dive, -1, -1, 'l') + 8;
                        lcd_putstring(1, xpos, "%");
                    }
                    break;

                  case 3:
                    if(cns_day)
                    {
                        lcd_linecls(1, 10);
                        lcd_putstring(1, 0, "ZNS D: ");
                        xpos = lcd_putnumber(1, 7, cns_day, -1, -1, 'l') + 7;
                        lcd_putstring(1, xpos, "%");
                    }
                    break;

                  case 4:
                    if(otu)
                    {
                        lcd_linecls(1, 10);
                        lcd_putstring(1, 0, "OTU: ");
                        xpos = lcd_putnumber(1, 5, otu, -1, -1, 'l') + 5;
                        lcd_putstring(1, xpos, "%");
                    }
                    break;

              case 5: // Dekostufen anzeigen 
                    {
                   is_deco = 0;
                   for(t1 = MAX_DECO_STEPS - 1; t1 >= 0; t1--)
                   {
                     if(rcd_decotime[t1] > 0)
                      is_deco = 1;
                    }

                   if(is_deco)
                   {
                     lcd_linecls(1, 15);
                     lcd_putstring(1, 0, "DEC:");
                     xpos = 5;
                          for(t1 = MAX_DECO_STEPS - 1; t1 >= 0; t1--)
                     {
                       if(rcd_decotime[t1] > 0)
                        xpos += lcd_putnumber(1, xpos, rcd_decotime[t1], -1, -1, 'l') + 1;
                     }
                       }
               }
             }

            if(info_mode == 5)
            {
                lcd_linecls(1, 10);
                show_accu_voltage();
            }

            // Anzeige ppN2 nach TG 
            if(info_mode > 6 && show_ppN2)
            {
                lcd_linecls(1, 15);
               lcd_putstring(1, 0, "ppIg");
               xpos = lcd_putnumber(1, 4, cur_comp + 1, -1, -1, 'l') + 4;
               lcd_putstring(1, xpos, ":");

               lcd_putnumber(1, xpos + 2, piN2[cur_comp++] * 1000, 4, 3, 'l');
               if(cur_comp > 15)
                 cur_comp = 0;
            }

                if(show_ppN2)
                max_info_mode = 22;
            else
                    max_info_mode = 6;

                if(info_mode < max_info_mode)
                    info_mode++;
                else
                    info_mode = 0;

                seconds_old3 = runseconds;
            }
        }

        //  Alle 10 sec. Gewebesaettigung & Dekorechnung 
        if(runseconds >= seconds_old1 + 10)
        {

         if(dphase)
         {
              lcd_cls();
         }

            get_tsensor();

            if(temp <= 8 && !temp_low && dphase)
            {
                temp_low = 1;
                set_ab_values(f_cons + 1, 0);
            }

            // Saettigungsrechnung 
            calc_p_inert_gas(depth * 0.1);
            calc_deco();

            // TG-Profilpunkt speichern als Absolutwert in [m] in 1 Byte alle 20s 
            if(do_record_depth >= 1)
            {
             eeprom_store_byte((unsigned char) (depth * .1));
                do_record_depth = 0;
            }
            else
                do_record_depth++;

            ppo2_exceeded = 0;
            decostep_skipped = 0;
            seconds_old1 = runseconds;
        }

        // Jede Minute ZNS und OTU berechnen 
        if(runseconds > seconds_old2 + 60)
        {
            calc_cns_otu();
            seconds_old2 = runseconds;
        }

        //  Tastaturabfrage ob Einstellungen gesetzt werden sollen 
        switch(get_keys())
        {
          case 1: // Abfrage ob verschiedene Extrafunktionen ausgeführt werden sollen 
          sbtc2pc();
         display_profile();
            clear_flash(1); // TG Profile loeschen ?
         clear_flash(2); // kompletten Flash loeschen ?
         display_rcd();
         display_log();
         break;


          case 2: // Einstellungen 
            settings();
            break;

          case 3:  // Gaswechsel 
            set_curgas();
        }

        // Mikrocontroller fuer den Rest der Sekunde in Energiesparmodus schalten 
        // AD-Wandler aus 
        outp(0, ADCSRA);
        // In Sleep-Mode gehen 
        set_sleep_mode (SLEEP_MODE_PWR_SAVE);
        sleep_mode();
    }
    return 0;
}