Homepage von Peter Rachow Startseite - Home

Der Selbstbau-Tauchcomputer

Allgemeines 

Forschung und Entwicklung: LCD-Anzeige | Mikrocontroller | Software | Sensoren | Gehäuse | Erfahrungsberichte

Geräte: SBTC3 | SBTC4 | Datenmodem zum PC | Simulator für Drucksensor

Quellcode der Version SBTC4:
///////////////////////////////////////////////////////////////////
//         Software fuer Selbstbau-Tauchcomputer (SBTC4)         //
///////////////////////////////////////////////////////////////////
//  Mikrocontroller:  ATMEL AVR ATmega128, 14.7456 MHz           //
//  Display           D072 von display3000.com                   //
//  Compiler:         GCC (GNU AVR C-Compiler) Release 20071221  //
//  Autor:            Peter Rachow (www.peter-rachow.de)         //
//  Letzte Aenderung: 19.07.2008  11:33                          //
///////////////////////////////////////////////////////////////////

/*
     _    _               _ 
    | |  | |             | |                       
    | |__| | __ _ _ __ __| |_      ____ _ _ __ ___ 
    |  __  |/ _` | '__/ _` \ \ /\ / / _` | '__/ _ \
    | |  | | (_| | | | (_| |\ V  V / (_| | | |  __/
    |_|  |_|\__,_|_|  \__,_| \_/\_/ \__,_|_|  \___|
*/

// Pinbelegung am ATMega128 ////////////////

//EINGÄNGE:
//F0 = Messverstärkerausgang (Drucksensorspannung)
//F2 = Temperatursensor (Spannungsteiler)
//F3 = Betriebsspannungsüberwachung (Spannungsteiler)
//D0, D1, D4...D7 = Taster, Reedkontakt an D6 für Gaswechsel während des TG

//BIDIREKTIONAL:
//D2, D3 = RS232 zum PC

//AUSGÄNGE
//A0= LED "Berechnung läuft"
//A6= LED "Dekotiefe nicht eingehalten!"
//B7= Displaybeleuchtung on/off
/////////////////////////////////////////////


// EEPROM: /////////////////////////////////

// Byte(s)    Inhalt
// Statische Werte
// 0,  1    Umgebungsluftdruck auf NN
// 2,  3    Höhe über NN
// 4,  5    Kabinendruck im Flugzeug
// 6        f.i.g N2 Gas1 (100facher Wert)
// 7        f.i.g N2 Gas2 (100facher Wert)
// 8        f.i.g N2 Gas3 (100facher Wert)
// 9        Konservativfaktor (10facher Wert)
// 10       ---
// 11       max. zulässiger ppO2 (10 facher Wert als Integer)
// 12       RTC: Tag
// 13       RTC: Monat
// 14       RTC: Jahr
// 15       RTC: Minute
// 16       RTC: Stunde
// 17       Lichtstärke des Displays (0=hell ..255=dunkel)
// 18       Zeit bis zum Ausschalten der Displaybeleuchtung in min.
// 19       Zeit bis die Peripherie abgeschaltet wird
// 20       Startbildschirm zeigen oder nicht.
// 21, 22, 23 Software Version 

// Tauchdatenspeicher statisch
// 24, 25    Anzahl TG mit diesem SBTC
// 26, 27    Tauchzeit mit diesem SBTC in Minuten
// 28, 29    Maximaltiefe mit diesem SBTC
// 30, 31    Letztes Byte des letzten TG im Ringspeicher

//Tauchprofilstruktur im EEPROM-Ringspeicher ab Byteadresse 50
//Byte        Wert    Bedeutung
//1            228    E4    Indikator für Beginn eines neuen Profiles
//2            xxx        Letzte OFP LoByte
//3            xxx        Letzte OFP HiByte
//4            xxx        Temp. zu Beginn des TG
//5            20        Aufzeichnungsintervall in Sekunden
//6            229    E5    Indikator für Tauchgangskoordinaten ab hier
//7...
//n                    Tiefe in Metern alle 20 sec. gemessen
//n+1        230    E6    Indikator für Ende der Profildaten
//n+2        xxx        Tauchzeit in Minuten LoByte
//n+3        xxx        Tauchzeit in Minuten HiByte
//n+4        xxx        Max. Tiefe in dm LoByte
//n+5        xxx        Max. Tiefe in dm HiByte
//n+6        xxx        Min. Temp. während des TG
//n+7        xxx        Temp. auf max. Tiefe
//n+8        231    E7    Indikator für Beginn der Dekostufen
//n+9        xxx        Dekozeiten beginnend ab der 3m-Stufe
//n+9+m     232    E8    Indikator für Ende der Dekostufen
//n+9+m+1    xxx        TG-Nummer LoByte
//n+9+m+2    xxx        TG-Nummer HiByte
//n+9+m+3    xxx        Tages-ZNS LoByte    
//n+9+m+4    xxx        Tages-ZNS HiByte
//n+9+m+5    xxx        TG-ZNS LoByte    
//n+9+m+6    xxx        TG-ZNS HiByte
//n+9+m+7    xxx        OTU LoByte    
//n+9+m+8    xxx        OTU HiByte
//n+9+m+9    233    E9    ENDE

/*
      _____        __ _              
     / ____|      / _| |                         
    | (___   ___ | |_| |___      ____ _ _ __ ___ 
     \___ \ / _ \|  _| __\ \ /\ / / _` | '__/ _ \
     ____) | (_) | | | |_ \ V  V / (_| | | |  __/
    |_____/ \___/|_|  \__| \_/\_/ \__,_|_|  \___|
*/

#include <stdio.h>
#include <avr/io.h>
#include <avr/iom128.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <math.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <string.h>
#include <avr/pgmspace.h>

// PINs von PORTB (SPI-Port)
#define SPI_CLOCK 1
#define SPI_DATA 2
#define SPI_RESET 4
#define SPI_SELECT 5
#define SPI_DC 6

// Farben
#define WHITE 65535
#define LIGHTBLUE 25873
#define BLUE 31
#define DARKBLUE 8
#define YELLOW 65504
#define ORANGE 64416
#define LIGHTRED 63968
#define RED 45056
#define DARKRED 32768
#define LIGHTGREEN 40947
#define GREEN 2016
#define DARKGREEN 704
#define DARKGREY 27335
#define LIGHTGREY 44271
#define BLACK 0
#define VIOLETT 63505
#define DARKCYAN 843
#define LIGHTCYAN 32372
#define DARKBROWN 14336
#define LIGHT 43968

//#define F_CPU 14.7456E6 => in delay.h !!

#define LCD_WIDTH 132
#define LCD_HEIGHT 176

//////////////
//  Misc   //
////////////

//Benutzereinstellungen
#define MENU_ITEMS 17

char menu_str[MENU_ITEMS][20]={ "LUFTDRUCK (NN)     ",
                                "HÖHE ÜBER NN       ",
                                "KABINENDRUCK FLUGZ.",
                                "MAXIMALER PPO2     ",
                                "GEWEBETOLERANZEN   ",
                                "GAS 1 (O2%)        ",
                                "GAS 2 (O2%)        ",
                                "GAS 3 (O2%)        ",
                                "STARTBILDSCHIRM    ",
                                "BELEUCHTUNG (ZEIT) ",
                                "AUSSCHALTEN (ZEIT) ",
                                "LICHTSTÄRKE        ",
                                "PROFILE ANSEHEN    ",
                                "PROFILE LÖSCHEN    ",
                                "EEPROM LÖSCHEN     ",
                                "PC-KOMMUNIKATION   ",
                                "FARBTEST           "};

char menu_unitstr[MENU_ITEMS][7]={ "MBAR",   //pLuft
                                   "M",      //Höhe
                                   "MBAR",   //Kabine
                                   "BAR",    // max. ppO2
                                   "",       //Toleranzen
                                   "%",      // O2 in Gas 1
                                   "%",      // O2 in Gas 2
                                   "%",      // O2 in Gas 3 
                                   "",       // Startbildschirm mit Parametern anzeigen?
                                   "MIN.",   // Zeit bis Displaybeleuchtung ausgeschaltet wird
                                   "MIN.",   // Zeit für PowerDown der Peripherie
                                   "",
                                   "",
                                   "",
                                   "",
                                   "",
                                   ""};

//                             pLuft  Höhe cabin po2  Tol  Gas1 Gas2 Gas3  SB  Li(t) Per. off  Lichtstä Pa  Pl El S2PC Color
int menu_digits[MENU_ITEMS] = {-1,   -1,   -1,   2,   2,   -1,  -1,  -1,   1,  -1,      -1,       -1,    0,  0, 0, 0,   0}; // Zahl der Ziffern 
int menu_dec[MENU_ITEMS] =    {-1,   -1,   -1,   1,   1,   -1,  -1,  -1,  -1,  -1,      -1,       -1,    0,  0, 0, 0,   0}; // Position des Dezimalpunktes 
int menu_sta[MENU_ITEMS] =    {900,  0,    400,  10,  5,   1,   1,   1,   0,   0,        5,       0,     0,  0, 0, 0,   0}; // Startwerte fuer Wertepektrum 
int menu_end[MENU_ITEMS] =    {1100, 4000, 1000, 20,  16,  100, 100, 100, 1,   120,     240,      255,   0,  0, 0, 0,   0}; // Endwerte fuer Wertepektrum   
int menu_step[MENU_ITEMS] =   {5,    50,   5,    1,   1,   1,   1,   1,   1,   5,        5,        5,    0,  0, 0, 0,   0}; // Inkrement                    

char show_start_screen = 0;

void menu(void);
int question(char*);
void headline(char*);
void message(char*, int);
int read_keys(void);
void progress_bar(int, int, int, char*);
void print_dive_profile(int, char, char);

//Warten n Millisekunden
void wait_ms(unsigned int);
//LED an A0 ein oder-aussschalten
void led(char, char);

void sbtc_power(char);
char peripherical_power = 1;

void colortest(void);

char *softwareversion = "V 4.2.9";  // Softwareversion 
int show_settings = 0;

double accu_voltage; //Akkuspannung

//////////////////////////////////

//////////////////////
// EEPROM-Speicher //
////////////////////
#define MAX_EEPROM_ADR 4096
#define EEPROM_PROF_START 50
void eeprom_store_byte(char);          // Byte ins EEPROM schreiben an Stelle...
int eeprom_byte_count;                 // ...Positionszeiger fuer EEPROM 
void erase_eeprom(int);

 ////////////////////
// SPI-Funktionen //
///////////////////
void spi_set_bit(char);
void spi_reset_bit(char);
void spi_send_byte_array(unsigned char [], unsigned int count) ;
void spi_send_int(unsigned int) ;
void spi_wait(void);
void spi_send_word_array(unsigned int [], unsigned int);

 ////////////////////
//   LCD-Display  //
///////////////////
void display_init(void);
void lcd_cls(int, unsigned int);
void lcd_set_window(int, int, int, int);
void lcd_draw_rectangle(int, int, int, int, unsigned int, char);
void lcd_draw_rectangle_filled(int, int, int, int, unsigned int);
void lcd_print_char(int, int, char, int, int, int);   //x, y, Char, Skalierung, VFarbe, BG
void lcd_put_string(int, int, char *, int, int, int); //x, y, String, Skalierung, Farbe, BG
void lcd_put_number(int, int, int, int, int, char*, char, int, int, int);

////////////////////////////////
// versch. Anzeigefunktionen //
//////////////////////////////
void lcd_print_cur_depth(void);    // Aktuelle Tiefe links oben ins LCD schreiben (x=50, y=50, scale=2 )
void lcd_print_max_depth(void);    // max. Tiefe re. klein daneben
void lcd_print_dive_time(void);    //Tauchzeit
void lcd_print_ppN2(int);          //ppN2 in den Geweben
void lcd_print_footer(void);       //Fußzeile mit Temp, Akkuspannung etc.
void lcd_print_ndt(int);           // Nullzeit aunsgeben
void lcd_print_ppo2_warning(void); //"ppO2 zu hoch" Warnmeldung
void lcd_print_decosteps(void);    //Dekoplan anzeigen
void show_temp(void);              //Temperatur anzeigen
void show_voltage(void);           //Akkuspannung anzeigen
void show_curgas(void);            //N2-Anteil akteulles Gas
void surface_info(void);           //OFP-Infost
void show_profiles(void);          //Gespeicherte Tauchprofile zur Auswahl bereitstellen
void put_profile_number(int, int, int, int, int); //Profilnummer in der Übersicht darstellen
void show_last_profile(void);

void show_rtc_time(void);              //Uhrzeit anzeigen
void show_rtc_date(void);              //Datum anzeigen
void update_rtc(void);            //RTC um 1 min. erhöhen

void display_light(int);
int idle = 0;  //Zeitmarker, wann der Benutzer die letzte Aktion durchgeüfhrt hat
               //wird auf 0 gesetzt, wenn  ein Taste gedrückt wurde. Ist immer 0 im Tauchmodus
int light_time = 10; //Zeit in min. bis Displaybeleuchtung abgeschaltet wird,
int powerdown_time = 25; //Zeit in min. bis die gesamte Peripherie abgeschaltet wird,
int power_status; //0=alle Systeme aktiviert, 1=Display-Licht aus, 2=Peripherie aus
int light_value; //Lichtstärke, 0=max., 255=min.

///////////////////////
///     P W M ////////
/////////////////////
void pwm_init(void);

 ////////////////
// AD-Wandler //
///////////////
// Ports für Analogdaten:
// F0: Betriebsspannungsüberwachung (Spannungsteiler)
// F1: Messverstärker für Drucksensor (Pin 8 an LM358) 
// F2: Temperatursensor (Spannungsteiler)

#define ADWAITSTATE 3
int  adc_val;
char adc_mode = 1; // 1=p.amb, 2=T, 3=Ub
void get_pressure_sensor(void); // Drucksensor auslesen
void get_temp_sensor(void); // Temperatursensor auslesen
void get_voltage_sensor(void); // Batteriespannung messen

/////////////
// 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;

 //////////////////////////////////////////////////
// Dekompressionsrechnung & verwandte Variablen //
/////////////////////////////////////////////////

#define NCOMP 16   // Anzahl der Kompartimente des Bühlmannalgorithmus
#define FN2 0.78   // N2-Anteil im Atemgas                 
#define MAX_DECO_STEPS 8  //Max. Dekotiefe der 1. Stufe = 24m
#define MAXGASES 3

#define DIVE 1        //Werte der Variablen 'phase' um kenntlich
#define SURFACE 0     // zu machen, ob getaucht wird oder nicht
char phase;

#define SWITCHDEPTH 10 // Tiefe, bei der von TG- in Oberflaechenmodus 

// 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}; //HWZ in min.
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};

// Kompartimente
float piN2[NCOMP];

// In dieses Array kommen die einzelnen Dekozeiten. Jedes Element 
// entspricht einer Dekostufe beginnend mit [0] = 3m
char decotime[MAX_DECO_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0};

// 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    

int curdepth = 0, maxdepth = 0;       // Akt. und max. Tiefe [dm]                     
int temp_min;                         // niedrigste Temperatur                        
int temp_maxdepth;                    // Temperatur auf max Tiefe                     

double airp = 0.995;                  // Umgebungsluftdruck in bar am Tauchort        
double airp0 = 0.995;                 // Umgebungsluftdruck in bar auf NN             
double cabinp = 0.58;                 // Kabinendruck im Flugzeug in bar              
int altitude = 0;                     //Hoehe ueber NN                                
double curtemp;                       // Aktuelle Temperatur                          
int deepest_decostep = 0;             // Tiefster Dekostopp in dm                     
int deco_minutes_total = 0;           // Gesamtdekozeit in min.                       
unsigned char f_cons;                 // Faktor fuer ab-Modifikation (10facher Wert)  
char show_ppN2 = 0;                   // ppN2 nach TG anzeigen für 16 Kompartimente   

//////////////////////////
// ppO2-bezogene Werte //
////////////////////////
void calc_cns_otu(void);

float cns_day = 0, cns_dive;
float otu = 0;
int maxppo2 = 16;  // 1.6 bar 

//// Speichervariablen

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

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

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

// Funktionen //
float get_water_pressure(int);
void calc_p_inert_gas(int);
int calc_ndt(void);
void calc_deco(void);
int calc_ceiling(void);
unsigned int calc_no_fly_time(void);
int calc_ppo2(void);
void set_ab_values(int);

////////////////////////////////////////
///////////// Laufzeit ////////////////
//////////////////////////////////////
unsigned long runseconds = 0;   // Zeit seit Einschalten des Gerätes [s] 
unsigned long diveseconds = 0;  // Dauer des TG  [s]
unsigned long surf_seconds = 0; //Dauer der OFP [s]
#define SURF_SECONDS_MAX 120    //   Oberflaechenzeit, nach der von TG- in OFP-Modus 
                                // umegschaltet wird [s]

//Für Real Time Clock
int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int rtc_day, rtc_month, rtc_year;
int rtc_hour, rtc_min;

int main(void);

 /////////////////////////////////////////////////
//  Zeichensatz H = 12 Pixel, Breite = 8 Pixel //
////////////////////////////////////////////////
int forecolor, backcolor;
#define MAXCHARS 91
char fchar[] PROGMEM = {
0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Leerzeichen
0, 56, 108, 198, 198, 198, 254, 254, 198, 198, 198, 0, //A
0, 252, 198, 198, 198, 252, 198, 198, 198, 198, 252, 0, //B
0, 124, 198, 192, 192, 192, 192, 192, 192, 198, 124, 0, //C
0, 252, 206, 198, 198, 198, 198, 198, 198, 206, 252, 0, //D
0, 254, 192, 192, 192, 248, 248, 192, 192, 192, 254, 0, //E
0, 254, 192, 192, 192, 248, 248, 192, 192, 192, 192, 0, //F
0, 124, 198, 192, 192, 192, 206, 198, 198, 206, 124, 0, //G
0, 198, 198, 198, 198, 254, 254, 198, 198, 198, 198, 0, //H
0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, //I
0, 252, 6, 6, 6, 6, 6, 6, 198, 102, 124, 0, //J
0, 198, 198, 204, 204, 240, 240, 204, 204, 198, 198, 0, //K
0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 254, 0, //L
0, 198, 238, 238, 214, 214, 214, 198, 198, 198, 198, 0, //M
0, 198, 230, 230, 214, 214, 214, 214, 206, 206, 198, 0, //N
0, 124, 238, 198, 198, 198, 198, 198, 198, 238, 124, 0, //O
0, 252, 198, 198, 198, 198, 252, 192, 192, 192, 192, 0, //P
0, 124, 198, 198, 198, 198, 214, 214, 206, 238, 124, 0, //Q
0, 252, 198, 198, 198, 198, 252, 248, 204, 198, 198, 0, //R
0, 124, 198, 192, 192, 252, 126, 6, 6, 198, 124, 0, //S
0, 126, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, //T
0, 198, 198, 198, 198, 198, 198, 198, 198, 238, 124, 0, //U
0, 198, 198, 198, 198, 198, 198, 198, 108, 56, 16, 0, //V
0, 198, 198, 198, 198, 214, 214, 214, 84, 124, 124, 0, //W
0, 198, 198, 198, 108, 56, 124, 238, 198, 198, 198, 0, //X
0, 102, 102, 102, 102, 60, 60, 24, 24, 24, 24, 0, //Y
0, 254, 6, 6, 12, 24, 48, 96, 192, 192, 254, 0, //Z
0, 214, 56, 124, 198, 198, 254, 254, 198, 198, 198, 0, //Ä
0, 198, 56, 108, 198, 198, 198, 198, 198, 238, 124, 0, //Ö
0, 198, 0, 198, 198, 198, 198, 198, 198, 238, 124, 0, //Ü
0, 124, 198, 206, 214, 214, 214, 214, 230, 198, 124, 0, //0
0, 12, 28, 124, 108, 12, 12, 12, 12, 12, 12, 0, //1
0, 124, 198, 6, 6, 12, 24, 48, 96, 192, 254, 0, //2
0, 124, 134, 6, 6, 60, 62, 6, 6, 134, 124, 0, //3
0, 28, 44, 76, 140, 140, 254, 12, 12, 12, 12, 0, //4
0, 254, 192, 192, 192, 124, 126, 6, 6, 134, 124, 0, //5
0, 124, 198, 192, 192, 124, 254, 198, 198, 198, 124, 0, //6
0, 254, 6, 6, 12, 12, 24, 24, 48, 48, 48, 0, //7
0, 124, 198, 198, 198, 124, 254, 198, 198, 198, 124, 0, //8
0, 124, 198, 198, 198, 124, 126, 6, 6, 134, 124, 0, //9
0,   0,   0,   0,   0,   0,   0,   0,   0,   48,  48,  0, //. Chr(46)
0,   0,   0,   0,   0,   0,   0,   24,  24,  48,  48,  0, //, Chr(44)
0,   0,   0,   0,   0,   48,  48,  0,   0,   48,  48,  0, //: Chr(58)
0,   0,   0,   0,   0,   24,  24,  0,   24,  48,  48,  0, //; Chr(59)
0,   24,  24,  24,  24,  24,  24,  24,  0,   24,  24,  0, //! Chr(33)
0,   100, 100, 8,   8,   16,  16,  32,  32,  76,  76,  0, //% Chr(37)
0,   56,  68,  68,  56,  48,  72,  136, 136, 76,  58,  0, //& Chr(38)
0,   28,  48,  112, 96,  96,  96,  96,  48,  48,  28,  0, //( Chr(40)
0,   56,  28,  6,   6,   6,   6,   6,   6,   28,  56,  0, //) Chr(41)
0,   0,   0,   0,   0,   126, 0,   126, 0,   0,   0,   0, //= Chr(61)
0,   0,   0,   24,  24,  126, 126, 24,  24,  0,   0,   0, //+ Chr(43)
0,   0,   0,   0,   0,   126, 126, 0,   0,   0,   0,   0, //- Chr(45)
0,   60,  102, 6,   28,  96,  102, 102, 60,  0,   24,  0, //? (Chr(63)
0,   0,   36,  36,  126, 36,  36,  126, 36,  36,  0,   0, //# Chr(35)
0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   126, 0, //_ Chr(95)
0,   0,   84,  84,  56,  124, 56,  84,  84,  0,   0,   0, //* Chr(42)
0,   96,  96,  48,  48,  24,  24,  12,  12,  6,   6,   0, //\ Chr(92)
0,   8,   30,  40,  40,  40,  28,  10,  10,  60,  8,   0, //$ Chr(36)
48,  72,  72,  48,  0,   0,   0,   0,   0,   0,   0,   0, // ° Grad Celsius Chr(167)
0,   24,  24,  24,  0,   0,   0,   0,   0,   0,   0,   0, // ' Chr(39)
0,   56,  56,  56,  56,  56,  254, 124, 56,  16,  0,   0, //   Pfeil nach unten (Chr200)
0,   6,   6,   12,  12,  24,  24,  48,  48,  96,  96,  0, // / Chr(47)
0,   0,   0,   0,   48,  24,  12,  6,   12,  24,  48,  0, // > Chr(62)
0,   0,   0,   0,   12,  24,  48,  96,  48,  24,  12,  0, // < Chr(60)
};

// Lookup-Tabelle fuer Zeichenposition
// ASCII-Wert => Arraypos
char arraypos[]={
32, // Leerzeichen
65, //A...
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,  //...Z
196, //AE
214, //OE
220, //UE
48,  //0...
49,
50,
51,
52,
53,
54,
55,
56,
57, //...9
46, //.
44, //,
58, //:
59, //;
33, //!
37, //%
38, //&
40, //(
41, //)
61, //=
43, //+
45, //-
63, //?
35, //#
95, //_
42, // *
92, // Backslash
36, //$
176, //°
39, // '
200, //Pfeil runter
47, //  /
62, // >
60 // <
};

//////////////////////// Funktionen ///////////////////////

    ///////////////////////////////////////////////////////////
   //////  Sättigung, Dekompression, ppO2 etc. etc.  ///////// 
  //////    (i. e.  Physik, Physik, Physik... ;-) ) /////////
 //////                                            /////////
///////////////////////////////////////////////////////////

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

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


// 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)));
}


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

    int dp = curdepth * 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 t1, t2;
    unsigned int cnt = 0;
    int ndt;

    for(t1 = 0; t1 < MAX_DECO_STEPS; t1++)
    {
        decotime[t1] = 0;
    }

    deco_minutes_total = 0;

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

    // Aktuelle Gasspannungen in temporaeres eindimensionales Datenfeld uebertragen 
    for(t1 = 0; t1 < NCOMP; t1++)
    {
        piN2x[t1] = piN2[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;

    // 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)
        {
            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)
                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] = decotime[t2];
            tmp_decotime_total = deco_minutes_total;
        }

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

    if(phase || deco_minutes_total) // Restliche Anzeige (Gesamtdekozeit bzw. Nullzeit nur, wenn getaucht wird) 
    {
        if(!deco_minutes_total)      // Gesamte Dekozeit <= 0 also NZ-TG 
        {
            ndt = calc_ndt();
            if(ndt < 0)       // Unplausible NZ-Werte abfangen 
                lcd_print_ndt(-1);
            else
            {
                lcd_print_ndt(ndt);
            }

            //Dekobildschirm löschen
            lcd_draw_rectangle_filled(90, 34, LCD_HEIGHT - 1, LCD_WIDTH - 18, backcolor);
            lcd_draw_rectangle_filled(55, 46, 95, 58, backcolor);
        }
        else  // Dekompressionsstopps sind erforderlich 
        {
            // Summe der Dekozeiten anzeigen 
            lcd_print_decosteps();

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

        }
    }

    led(0, 0);
}

// "Ceiling" berechnen, Rückgabe = 10facher Wert!
int calc_ceiling()
{
    float pambtolmax = 1.0;
    unsigned char t1;

    // Minimaltiefe 
    for(t1 = 0; t1 < NCOMP; t1++)
    {
        if((piN2[t1] - aN2[t1]) * bN2[t1] > pambtolmax)
        {
            pambtolmax = (piN2[t1] - aN2[t1]) * bN2[t1];
        }
    }

    return (int) (10 * get_water_depth(pambtolmax));
}


// 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 t1;
    double f = k * 0.1;

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

// ppO2 (Rueckgabe = 10facher Wert!) 
int calc_ppo2(void)
{
    float fppO2    = (curdepth / 100 + airp) * (1 - figN2[curgas]);

    int ippO2 = (int) (fppO2 * 10);

    if(ippO2 > maxppo2)
    {
        lcd_print_ppo2_warning();
        if(!ppo2_exceeded)
        {
            eeprom_store_byte(224);
            ppo2_exceeded = 1;
        }
    }
    return ippO2;
}

// 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() - 6;

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

    // ZNS in Oberflaechenmodus t1/2 = 90 min. 
    if(!phase)
    {
        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));
}


//////////////
//  Misc   //
////////////

// LED an PA0 bzw PA6 ein- und ausschalten
void led(char led_num, char status)
{
    if(!status)
    {
        if(!led_num)
        {
            PORTA = PORTA | 0x01; //Ausschalten
        }
        else
        {
            PORTA = PORTA | 0x40; //Ausschalten
        }
    }
    else
    {
        if(!led_num)
        {
            PORTA &= ~(1<<PA0); //Einschalten
        }
        else
        {
            PORTA &= ~(1<<PA6); //Einschalten
        }
    }
}


//Warteschleife in Millisekunden
void wait_ms(unsigned int ms)
{
   unsigned int t1;

    for(t1 = 1; t1 < ms; t1++)
      _delay_ms(1);
}

////////////////////
// SPI-Routinen  //
//////////////////

//Ein Bit des Ports B auf HI setzen
void spi_set_bit(char bit)
{
    PORTB |= _BV(bit);
}

//Ein Bit des Port B auf LO setzen
void spi_reset_bit(char bit)
{
    PORTB &= ~_BV(bit);
}


//////////////////////////////
//        EEPROM        /////
////////////////////////////
//Ein Byte an laufender Zählerstelle speichern 
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_write_byte((uint8_t*)eeprom_byte_count++, eeprom_val);
    sei();
}


//Bestimmten Bereich im EEPROM löschen
//0: EEPROM komplett, 1: Nur Tauchprofile 
void erase_eeprom(int erasemode)
{
    unsigned int t1, startadr = 0;

    lcd_cls(1, backcolor);

    if(erasemode == 1)
        startadr = EEPROM_PROF_START;

    // TG-Profildaten loeschen? 
    if(startadr == EEPROM_PROF_START)
    {
        if(question("PROFILE LÖSCHEN?") != 1)
        {
            return;
        }
    }

    if(startadr == 0)
    {
        if(question("EEPROM LÖSCHEN?") != 1)
        {
            return;
        }
    }

    lcd_cls(1, backcolor);
    progress_bar(0, 0, 0, "LÖSCHE BYTE:");

    cli();

    for(t1 = startadr; t1 <= MAX_EEPROM_ADR; t1++)
    {
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)t1, 0);
        progress_bar(1, t1, MAX_EEPROM_ADR, "");
        lcd_put_number(5, 75, t1, -1, -1, "", 'l', 1, YELLOW, backcolor);
    }

    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)30, EEPROM_PROF_START);
    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)31, 0);

    sei();

    lcd_cls(1, backcolor);
    return;
}


/////////////////////////////////////
//// SPI-Funktionen für Display ////
///////////////////////////////////
// Ein einzelnes Byte oder ein Array senden
void spi_send_byte_array(unsigned char bytearray[], unsigned int bytecount)
{
    int t1;

    for(t1 = 0; t1 < bytecount; ++t1)    // Bytes sequentiell übertragen
    {
        SPCR |= _BV(SPE);
        SPDR = bytearray[t1];
        spi_wait();
    }
}

// Eine Integerzahl an SPI senden
void spi_send_int(unsigned int value)
{
    SPCR |= _BV(SPE);
    SPDR = (value >> 8) & 0xff;
    spi_wait();
    SPCR |= _BV(SPE);
    SPDR = value & 0xff;
    spi_wait();
}

// Ein Array aus Word an das LCD senden
// wird für die Farbdaten benötigt
void spi_send_word_array(unsigned int wordarray[], unsigned int bytecount)
{
    unsigned int t1;
    unsigned char msb;
    unsigned char lsb;

    for(t1 = 0; t1 < bytecount; ++t1)
    {
        spi_set_bit(SPI_DC);                // Befehlsmodus aktivieren
        spi_reset_bit(SPI_SELECT);            // Display auf Empfang schalten
        msb = (wordarray[t1] >> 8) & 0xff;
        lsb = wordarray[t1] & 0xff;
        SPCR |= _BV(SPE);                     //Bereit zum Übertragen
        SPDR = msb;                            //MSB senden

        spi_wait();
        SPCR |= _BV(SPE);
        SPDR = lsb;                            //LSB senden
        spi_wait();
        spi_reset_bit(SPI_DC);
        spi_set_bit(SPI_SELECT);
    }
}

//Warten bis SPI-Übertragung beendet ist
void spi_wait(void)
{
    while (SPCR & _BV(SPE))
    {
        while (!(SPSR & (_BV(SPIF))));
        SPCR &= ~(_BV(SPE));
    }
}

///////////////////////
// Display-Routinen //
/////////////////////
void display_init()
{
    //Initialisierungssequenz lt. Datenblatt, zwischen den 3 Blöcken
    //eine Pasue von 75ms einschalten!
    unsigned int init_data0[] = {0xFDFD, 0xFDFD};
    unsigned int init_data1[] = {0xEF00, 0xEE04, 0x1B04, 0xFEFE, 0xFEFE, 0xEF90, 0x4A04, 0x7F3F, 0xEE04, 0x4306};
    unsigned int init_data2[] = {0xEF90, 0x0983, 0x0800, 0x0BAF, 0x0A00, 0x0500, 0x0600, 0x0700, 0xEF00, 0xEE0C,
                                   0xEF90, 0x0080, 0xEFB0, 0x4902, 0xEF00, 0x7F01, 0xE181, 0xE202, 0xE276, 0xE183,
                                 0x8001, 0xEF90, 0x0000};

    SPSR |= _BV(SPI2X);
    SPCR  = _BV (SPE) | _BV(MSTR);

    wait_ms(300);
    spi_reset_bit(SPI_RESET);
    wait_ms(75);
    spi_set_bit(SPI_SELECT);
    wait_ms(75);
    spi_reset_bit(SPI_CLOCK);
    wait_ms(75);
    spi_set_bit(SPI_DC);
    wait_ms(75);
    spi_set_bit(SPI_RESET);
    wait_ms(75);

    spi_send_word_array(&init_data0[0], 2);
    wait_ms(75);
    spi_send_word_array(&init_data1[0], 10);
    wait_ms(75);
    spi_send_word_array(&init_data2[0], 23);
    spi_reset_bit(SPI_SELECT);

}

//Ausgabefenster setzen im Querformat-Modus
void lcd_set_window(int x0, int y0, int x1, int y1)
{
    unsigned char window_data[] = {0xEF, 0x08, 0x18, 0x05, 0x12, LCD_WIDTH - 1 - y0, 0x15,
                                   LCD_WIDTH - 1 - y1, 0x13, x0, 0x16, x1};
    spi_set_bit(SPI_DC);
    spi_reset_bit(SPI_SELECT);
    spi_send_byte_array(window_data, 12);
    spi_reset_bit(SPI_DC);
}


// CLS 
// area == 0: ganzer Bildschirm
// area == 1: oberer Teil des Bildschirms ohne Fußzeile
void lcd_cls(int area, unsigned int bgcolor)
{
    unsigned int t1, height, width;

    switch(area)
    {
        case 0:    height = LCD_HEIGHT; //Volles Fenster
                width = LCD_WIDTH;
                break;
        case 1: height = LCD_HEIGHT; //ohne Fußzeile
                width = LCD_WIDTH - 15;
                break;
        default: height = LCD_HEIGHT; //volles Fenster
                 width = LCD_WIDTH;
    }

    lcd_set_window(0, 0, height, width);
    for (t1 = 0; t1 < ((width + 1) * height); t1++)
    {
        spi_send_int(bgcolor);
    }
    spi_set_bit(SPI_SELECT);
}


// Ein Zeichen des Zeichenssatzes an x-y-Koordinate auf dem LCD ausgeben um
// den Faktor "scale" skaliert
void lcd_print_char(int x, int y, char asciicode, int scale, int fcolor, int bcolor)
{
    int t1, t2, t3, t4, t5;
    int xp;
    unsigned char ch;

    ch = 0;

    //Postion des Zeichens in der Lookup-Tabelle
    //finden
    t1 = 0;
    while(t1 < MAXCHARS)
    {
        if(arraypos[t1] == asciicode)
        {
            ch = t1 + 1;
            t1 = MAXCHARS;
        }
        t1++;
    }

    lcd_set_window(x, y, x + scale * 8 - 1, y + scale * 12 - 1);

    //Zeichen in Bits zerlegen
    for(t1 = (ch - 1) * 12; t1 < ch * 12; t1++)
    {
        for(t5 = 0; t5 < scale; t5++)
        {
            for(t2 = 7; t2 >= 0; t2--)
            {
                xp = 1;
                for(t3 = 0; t3 < t2; t3++)
                {
                    xp <<= 1;
                }

                for(t4 = 0; t4 < scale; t4++)
                {
                    if(pgm_read_byte(&fchar[t1]) & (int) xp)
                    {
                        spi_send_int(fcolor);
                    }
                    else
                    {
                        spi_send_int(bcolor);
                    }
                }
            }
        }
    }
    spi_set_bit(SPI_SELECT);//Zurücksetzen
}

//Eine Zeichenkette ausgeben, skaliert.
void lcd_put_string(int x, int y, char *s, int scale, int fcolor, int bcolor)
{
    int col = 0;
    while(*(s))
    {
        lcd_print_char(x + col++ * scale * 8, y, *(s++), scale, fcolor, bcolor);
    }
}

// Eine n-stellige INTEGER-Zahl an eine Koordinate in das LCD schreiben 
// Parameter: x, y; Zahl, darzustellende Ziffern, Position des 
// Dezimalpunktes, (l)links- oder (r)echtsbuendig 
void lcd_put_number(int x, int y, int num, int digits, int dec, char *unit, char orientation, int scale, int fcolor, int bcolor)
{
    int xcur, col;
    char minusflag = 0;
    unsigned char cdigit[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, digitcnt = 0;
    int t1, t2, n = num, r, z = 1;

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

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

    if(!digits)
        digits = 1;

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

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

    //ggf. Maßeinheit anhängen
    t1 = digitcnt;
    t2 = 0;
    while (unit[t2])
    {
        cdigit[t1++] = unit[t2++];
    }
    // In cdigit[] ist nun die Zahl als String eingespeichert ggf. mit Maßeinheit am Ende

    // Ausgabe am LCD =>
    // Übergabe an lcd_print_char(int x, int y, char asciicode, int scale, int color)        
    t1 = 0;
    col = 0;
    switch(orientation)
    {
        case 'l':

            while(cdigit[t1])                       // Linksbuendig 
            {
                xcur = x + col * scale * 8;
                lcd_print_char(xcur, y, cdigit[t1++], scale, fcolor, bcolor);
                col++;
            }
            break;

        case 'r':                                 // Rechtsbuendig 
            digits = 0;                          // Stringende finden    
            while(cdigit[digits++]);

            t1 = 0;
            while(cdigit[t1])
            {
                xcur = x - (digits - t1 - 1) * scale * 8;
                lcd_print_char(xcur, y, cdigit[t1++], scale, fcolor, bcolor);
            }
            break;
    }
}

// Ein unausgefülltes  Rechteck zeichnen
void lcd_draw_rectangle(int x0, int y0, int x1, int y1, unsigned int linecolor, char thickness)
{
    //Hor. Linie oben
    lcd_draw_rectangle_filled(x0, y0, x1, y0 + thickness, linecolor);

    //Hor. Linie unten
    lcd_draw_rectangle_filled(x0, y1, x1, y1 + thickness, linecolor);

    //Vert. Linie links
    lcd_draw_rectangle_filled(x0, y0, x0 + thickness, y1, linecolor);

    //Vert. Linie rechts
    lcd_draw_rectangle_filled(x1, y0, x1 + thickness, y1 + thickness, linecolor);
}

// Ein ausgefülltes Rechteck zeichnen
void lcd_draw_rectangle_filled(int x0, int y0, int x1, int y1, unsigned int color)
{
    int t1, t2;

    lcd_set_window(x0, y0, x1, y1);

    for(t1 = x0; t1 <= x1; t1++)
    {
        for (t2 = y0; t2 <= y1; t2++)
        {
            spi_send_int(color);
        }
    }
    spi_set_bit(SPI_SELECT); //Zurücksetzen
}

 /////////////////////////////////////////
//      Hoehere Anzeigefunktionen      //
////////////////////////////////////////
// Aktuelle Tiefe links oben im Display ausgeben (5, 5)
void lcd_print_cur_depth(void)
{
    lcd_put_number(5, 5, curdepth, 3, 1, "", 'l', 2, YELLOW, backcolor);
    lcd_put_string(70, 16, "M", 1, YELLOW, backcolor);
}

// Max. Tiefe im Display ausgeben
void lcd_print_max_depth(void)
{
    lcd_put_number(5, 34, maxdepth, 3, 1, "M", 'l', 1, WHITE, backcolor);
}

void lcd_print_dive_time(void)
{
    lcd_put_number(LCD_HEIGHT, 5, diveseconds * 0.0166666667, -1, -1, "'", 'r', 2, WHITE, backcolor);
}

void lcd_print_footer(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    //Fußzeile 
    lcd_draw_rectangle_filled(0, LCD_WIDTH - 16, LCD_HEIGHT - 1, LCD_WIDTH - 1, color);
    show_voltage();
    show_temp();
    show_curgas();
}

//ppN2 in den Geweben als Balkengrafik
//p.amb = max. 7 bar => ppN2: max. 5,5 bar 
//t max. = 60 min.
void lcd_print_ppN2(int pos)
{
    int t1, x0, y0;
    int color[16] = {RED, BLUE, DARKGREEN, ORANGE, BLACK, LIGHTRED, BLUE,
                     LIGHTBLUE, DARKGREY, GREEN, RED, VIOLETT, DARKBROWN, DARKCYAN,
                     DARKRED, DARKGREEN};

    if(!pos) //Position der Grafik auf dem LCD während des Tauchgangs
    {
        x0 = 2;
        y0 = 60;
        lcd_draw_rectangle_filled(x0, y0, x0 + 79, y0 + 52, YELLOW);
    }
    else //Position der Grafik auf dem LCD während der OFP
    {
        x0 = 89;
        y0 = 48;
        lcd_draw_rectangle_filled(x0, 48, x0 + 79, 105, YELLOW);
    }

    for(t1 = 0; t1 < 16; t1++)
    {
        if((int) (y0 + 52 - piN2[t1] * 10) >= 48)
        {
            lcd_draw_rectangle_filled(t1 * 5 + x0, (int) (y0 + 52 - piN2[t1] * 10), t1 * 5 + x0 + 4, y0 + 52, color[t1]);
        }
    }
    lcd_put_string(x0 + 24, y0 + 2, "PPN2", 1, DARKGREEN, YELLOW);
}

//Nz ausgeben (gaaaaanz wichtig für PADI-Taucher ;-) )
void lcd_print_ndt(int ndt)
{
    lcd_draw_rectangle_filled(50, 34, 84, 48, backcolor);

    if(ndt > 0)
    {
        lcd_put_number(50, 34, ndt, -1, -1, "'", 'l', 1, GREEN, backcolor);
    }

    if(ndt < 0) //Unplausibler Wert
    {
        lcd_put_string(50, 34, "---", 1, WHITE, backcolor);
    }

    if(ndt == 0)
    {
        lcd_put_string(50, 34, "DEKO", 1, ORANGE, backcolor);
    }

}

//Dekoplan anzeigen
void lcd_print_decosteps(void)
{
    int t1, ypos;
    int decosteps = 0;

    lcd_draw_rectangle_filled(90, 34, LCD_HEIGHT - 1, LCD_WIDTH - 20, backcolor);

    //Anzahl der Dekostufen ermitteln, weil nur 3 dargestellt werden sollen
    decosteps = 0;
    while(decotime[decosteps++]);

    t1 = 0;
    ypos = 0;
    while(decotime[t1])
    {
        if(t1 >= decosteps - 4) //NUr die letzten 3 Stufen ausgeben
        {
            lcd_put_number(90, 34 + ypos * 24, (t1 + 1) * 3, -1, -1, "M", 'l', 2, WHITE, backcolor);
            lcd_put_number(LCD_HEIGHT - 1, 34 + ypos * 24, decotime[t1], -1, -1, "'", 'r', 2, WHITE, backcolor);
            ypos++;
        }
        t1++;
    }

    //Gesamtdekozeit ausgeben
    lcd_draw_rectangle_filled(50, 46, 89, 58, backcolor);
    lcd_put_number(50, 48, deco_minutes_total, -1, -1, "'", 'l', 1, ORANGE, backcolor);

}

//Temp. ausgeben in Fußzeile des Fensters
void show_temp(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    if(curtemp < -20 || curtemp > 100) //Fehlmessungen zu Programmstart abfangen
    {
        return;
    }

    lcd_draw_rectangle_filled(2, LCD_WIDTH - 16, 29, LCD_WIDTH - 1, color);
    lcd_put_number(2, LCD_WIDTH - 14, curtemp, -1, -1, "°", 'l', 1, BLACK, color);
}

//dito für Akkuspannung
void show_voltage(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    lcd_draw_rectangle_filled(30, LCD_WIDTH - 16, 69, LCD_WIDTH - 1, color);
    lcd_put_number(30, LCD_WIDTH - 14, accu_voltage * 10, 2, 1, "V", 'l', 1, DARKRED, color);
}

//Aktuelles Gas in Fußzeile anzeigen
void show_curgas(void)
{
    unsigned int back = (phase)? WHITE:GREEN;

    lcd_draw_rectangle_filled(72, LCD_WIDTH - 16, 104, LCD_WIDTH - 1, back);

    lcd_put_number(72, LCD_WIDTH - 14, 99 - figN2[curgas] * 100, -1, -1, "%", 'l', 1, DARKBLUE, back);
}

//Infos für die Oberflächenpause insbesondere nach dem Tauchen
void surface_info(void)
{
    unsigned long surf_hrs, surf_mins;

    lcd_draw_rectangle_filled(2, 48, 81, 105, WHITE);

    //Flugverbot
    lcd_put_string(7, 50, "FVB:", 1, BLUE, WHITE);
    lcd_put_number(40, 50, calc_no_fly_time(), -1, -1, " H", 'l', 1, BLUE, WHITE);

    //ZNS
    lcd_put_string(5, 64, "ZNS:", 1, BLUE, WHITE);
    lcd_put_number(40, 64, cns_day, -1, -1, "%", 'l', 1, BLUE, WHITE);

    //OTU
    lcd_put_string(5, 78, "OTU:", 1, BLUE, WHITE);
    lcd_put_number(40, 78, otu, -1, -1, "%", 'l', 1, BLUE, WHITE);

    //OFP
    lcd_put_string(5, 92, "OFP:", 1, BLUE, WHITE);
    surf_hrs = surf_seconds / 3600; // (1/60)²     
    surf_mins = (surf_seconds - surf_hrs * 3600) / 60;
    lcd_put_number(40, 92, surf_hrs, 2, -1, ":", 'l', 1, BLUE, WHITE);
    lcd_put_number(64, 92, surf_mins, 2, -1, "", 'l', 1, BLUE, WHITE);
}


//Anzeigen, dass ppO2 zu hoch ist
void lcd_print_ppo2_warning(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    lcd_put_string(122, LCD_WIDTH - 14, "PPO2!", 1, LIGHTRED, color);
}

//Den User ein Tauchprofil auswählen lassen
//und dieses grafisch darstellen
void show_profiles(void)
{
    unsigned int divenum[50], startbyte[50], endbyte[50];
    int t1, i = 0, row, col;
    char ch;

    for(t1 = 0; t1 < 50; t1++)
    {
        divenum[t1] = 0;
        startbyte[t1] = 0;
        endbyte[t1] = 0;
    }

    //Alle TG mit erstem, letztem Byte und TG-Nummer in Liste eintragen
    for(t1 = EEPROM_PROF_START; t1 < MAX_EEPROM_ADR; t1++)
    {
        if(eeprom_read_byte((uint8_t*)t1) == 229) //Indikator für Profilkoordinaten START
        {
            startbyte[i] = t1 + 2;
        }

        if(eeprom_read_byte((uint8_t*)t1) == 230) //Indikator für Profilkoordinaten ENDE
        {
            endbyte[i] = t1 - 1;
        }

        if(eeprom_read_byte((uint8_t*)t1) == 232) //Indikator für Sequenzende
        {
            divenum[i] = eeprom_read_byte((uint8_t*)(t1 + 1)) + 256 * eeprom_read_byte((uint8_t*)(t1 + 2)) ;
            i++;
        }
    }

    //So lange auswählen + anzeigen, bis Taste 1 gedrückt wurde
    do
    {
        lcd_cls(0, backcolor);
        headline("TAUCHPROFILE");

        lcd_put_string(5, 30, "IM SPEICHER:", 1, YELLOW, backcolor);
        lcd_put_number(111, 30, i, -1, -1, "", 'l', 1, YELLOW, backcolor);

        if(!i)
        {
            return;
        }

        //Übersicht ausgeben 5x5
        row = 0; col = 0;
        for(t1 = 0; t1 < i; t1++)
        {
            put_profile_number(divenum[t1], row, col, RED, YELLOW);
            col++;
            if(col > 4)
            {
                col = 0;
                if(row < 5)
                {
                    row++;
                }
            }
        }


        //Auswahl ermöglichen durch Tastensteuerung im x-y-Raster
        //   O              254 (1)
        //   0              223 (32)
        //  O 0      127(128)     239(16)
        //   O              191(64)
        //   O              253(2)

        row = 0;
        col = 0;
        t1 = 0;

        do
        {
            row = t1 / 5;
            col = t1 - row * 5;

            //Highlight setzen
            put_profile_number(divenum[t1], row, col, YELLOW, RED);

            ch = read_keys();
            while(!ch)
            {
                ch = read_keys();
            }

            wait_ms(150);

            //Highlight entfernen
            put_profile_number(divenum[t1], row, col, RED, YELLOW);

            //Tastencode auswerten
            switch(ch)
            {
                case 64:    if(t1 + 5 < i - 1)
                            {
                                t1 += 5;
                            }
                            break;

                case 16:    if(t1 < i - 1)
                            {
                                t1++;
                            }
                            break;

                case 32:    if(t1 - 5 > 0)
                            {
                                t1 -= 5;
                            }
                            break;

                case 128:   if(t1 > 0)
                            {
                                t1--;
                            }
                            break;
            };

            //wait_ms(100);            

        }while(ch != 1 && ch != 2);

        //Gewähltes Profil aufrufen
        if(ch == 1)
        {
            return;
        }

        print_dive_profile(divenum[t1], 0, 0);

        ch = read_keys();
        while(!ch)
        {
            ch = read_keys();
        }
    }
    while(ch != 1);
}

//TG-Nummerliste 5 in x-, 5 in y-Richtung
void put_profile_number(int num, int row, int col, int fore, int back)
{
    int x, y, n = num, digits = 0;

    x = 5 + col * 32;
    y = 45 + row * 15;

    //Zentrierung finden: Ziffern zählen
    while(n)
    {
        n = n / 10;
        digits++;
    }

    lcd_draw_rectangle_filled(x, y, x + 30, y + 14, back);
    //Zahl im Kästchen zentrieren: x + (15 - (digits * 8) / 2)
    lcd_put_number(x + (15 - (digits * 8) / 2), y + 2, num, -1, -1, "", 'l', 1, fore, back);
}

//Tauchprofil grafisch ausgeben, wenn "num == 0" wird
//letzter TG gesucht
//num = TG-Nummer im EEPROM, wait=Warten auf Tastendruck?
// mode = Art der Zusatzinfos: 0: max. Tiefe, mittl. Tiefe, Tauchzeit
//                             1: Dekostufen
void print_dive_profile(int num, char wait, char mode)
{
    int t1, t2;
    int x, y, x0, y0; //, lastdive = 0;
    int dv[3], dvmax;
    int av_dive_depth = 0, max_dive_depth = 0;
    long sum_depth = 0;
    int startadr = 0, endadr = 0;
    char end_of_profile;

    if(!num) //Letzten TG finden
    {
        for(t1 = EEPROM_PROF_START; t1 < MAX_EEPROM_ADR; t1++)
        {
            //Nach dem Marker 229 suchen, hier beginnt das TG-Profil
            if(eeprom_read_byte((uint8_t*)t1) == 229)
            {
                startadr = t1 + 1;
            }
        }
    }
    else
    {
        lcd_put_number(0, LCD_WIDTH - 12, num, -1, -1, "", 'l', 1, YELLOW, RED);
        t1 = EEPROM_PROF_START;
        while(t1 < MAX_EEPROM_ADR)
        {
            //Nach dem Marker 232 suchen, hier steht die TG-Nummer
            if(eeprom_read_byte((uint8_t*)t1) == 232)
            {
                if(eeprom_read_byte((uint8_t*)(t1 + 1)) + 256 * eeprom_read_byte((uint8_t*)(t1 + 2)) == num)
                {
                    t1 = MAX_EEPROM_ADR; //Schleife verlassen, TG-Nummer wurde gefunden
                }
            }

            //Nach dem Marker 229 suchen, hier beginnt das TG-Profil
            if(eeprom_read_byte((uint8_t*)t1) == 229)  //merken...
            {
                startadr = t1 + 1;
            }

            //Nach Marker 230 suchen, hier enden die Profilpunkte            
            if(eeprom_read_byte((uint8_t*)t1) == 230) //merken...
            {
                endadr = t1;
            }

            t1++;
        }
    }

    if(!startadr)
    {
        return;
    }

    //Bildschirm löschen
    lcd_cls(0, backcolor);

    //Achsenkreuz zeichnen
    //Y-Achse auf x = 25
    x0 = 25;
    lcd_draw_rectangle_filled(x0, 5, x0, LCD_WIDTH - 10, WHITE);
    for(t1 = 0; t1 < 6; t1++) //Achsenskalierung
    {
        lcd_draw_rectangle_filled(x0 - 2, 5 + t1 * 20, x0 + 2, 5 + t1 * 20, WHITE);
        lcd_put_number(2, t1 * 20, t1 * 10, -1, -1, "", 'l', 1, WHITE, backcolor);
    }

    //X-Achse auf LCD_WIDTH - 20
    y0 = LCD_WIDTH - 20;
    lcd_draw_rectangle_filled(x0, y0, 170, y0, WHITE);

    for(t1 = 0; t1 < 8; t1++) //Achsenskalierung
    {
        lcd_draw_rectangle_filled(t1 * 20 + x0, y0 - 2, t1 * 20 + x0, y0 + 2, WHITE);
        lcd_put_number(t1 * 20 + x0 - 8, LCD_WIDTH - 14, t1 * 10, -1, -1, "", 'l', 1, WHITE, backcolor);
    }
    lcd_put_string(140, y0 - 14, "MIN.", 1, WHITE, backcolor);

    //TG-Koordinaten als Pixel schreiben
    t1 = startadr + 1;
    x = 0; //von (30 bis 170) / 2 = 70 Minuten

    end_of_profile = 0;
    while(x < 156 && !end_of_profile)
    {
        dvmax = 0;
        //Den größten Wert aus den 3 Tiefenangaben einer TG-Minute ermitteln
        for(t2 = 0; t2 < 3; t2++)
        {
            dv[t2] = eeprom_read_byte((uint8_t*)(t1 + t2));

            if(dv[t2] == 230)
            {
                end_of_profile = 1;
            }

            if(dv[t2] < 200) //Alle TG-Marker ignorieren!
            {
                if(dv[t2] > dvmax)
                {
                    dvmax = dv[t2];
                }
            }

        }

        if(!end_of_profile)
        {
            y =  dvmax * 2; //y von (5 bis 122) / 2 = 58,5 m max.

            if(dvmax > max_dive_depth)
            {
                max_dive_depth = dvmax;
            }

            sum_depth += dvmax;

            if(y > 132)
            {
                y = 132;
            }
            lcd_draw_rectangle_filled(x * 2 + 30, 5 + y, x * 2 + 31, 6 + y, GREEN);
            t1 += 3;
            x++;
        }
    }

    switch(mode)
    {
        case 0: //Durchschnittstiefe ausrechnen
                av_dive_depth = (int) sum_depth / x;
                lcd_put_string(100, 45, "MAX:", 1, YELLOW, backcolor);
                lcd_put_number(132, 45, max_dive_depth, -1, -1, "M", 'l', 1, WHITE, backcolor);
                lcd_put_string(100, 60, "MW:", 1, YELLOW, backcolor);
                lcd_put_number(132, 60, av_dive_depth, -1, -1, "M", 'l', 1, WHITE, backcolor);
                lcd_put_string(100, 75, "T:", 1, YELLOW, backcolor);
                lcd_put_number(120, 75, x, -1, -1, "MIN.", 'l', 1, WHITE, backcolor);

                if(av_dive_depth <= 0 || max_dive_depth <= 0)
                {
                    headline("FEHLER!");
                }
                break;

        case 1: t1 = endadr + 1;
                while(eeprom_read_byte((uint8_t*)t1++) != 231); // 231 = Anfang der Dekostufen

                lcd_put_string(120, 40, "DEKO:", 1, YELLOW, backcolor);
                if(!eeprom_read_byte((uint8_t*)t1)) //Keine 3m-Stufe => Keine Deko => PADIes finden's geil! ;-)
                {
                    lcd_put_string(120, 55, "---", 1, YELLOW, backcolor);
                }
                else //Deko
                {
                    y = 55;
                    while(eeprom_read_byte((uint8_t*)t1) > 0 && eeprom_read_byte((uint8_t*)t1) != 232) //232 = Ende der Dekostufen
                    {
                        if(y < LCD_WIDTH - 20)
                        {
                            lcd_put_number(120, y, eeprom_read_byte((uint8_t*)t1), -1, -1, "MIN.", 'l', 1, GREEN, backcolor);
                            y += 15;
                        }
                        t1++;
                    }
                }


    }

    if(wait)
    {
        while(read_keys());
        while(!read_keys());
        lcd_cls(0, backcolor);
    }
}

//Letztes Tauchprofil anzeigen
void show_last_profile(void)
{
    print_dive_profile(0, 1, 0);
}

//Beluchtungsstärke einstellen
void display_light(int value)
{
    OCR1C = value;
}

//Gesamte Periphere (LCD, MAX232) abschalten
void sbtc_power(char pwr)
{
    if(!pwr)
    {
        PORTG |= _BV(PG1);  //Ausschalten
        wait_ms(1000);
        peripherical_power = 0;
    }
    else
    {
        PORTG &= ~_BV(PG1); //Einschalten
        wait_ms(1000);
        display_init();
        peripherical_power = 1;
    }
}

//Uhrzeit anzeigen
void show_rtc_time(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    lcd_put_number(122, LCD_WIDTH - 14, rtc_hour, 2, -1, ":", 'l', 1, DARKBROWN, color);
    lcd_put_number(146, LCD_WIDTH - 14, rtc_min, 2, -1, "", 'l', 1, DARKBROWN, color);
}

//Uhrzeit anzeigen
void show_rtc_date(void)
{
    unsigned int color = (phase)? WHITE:GREEN;

    lcd_put_number(110, LCD_WIDTH - 14, rtc_day, 2, -1, ".", 'l', 1, DARKBROWN, color);
    lcd_put_number(134, LCD_WIDTH - 14, rtc_month, 2, -1, ".", 'l', 1, DARKBROWN, color);
    lcd_put_number(158, LCD_WIDTH - 14, rtc_year, 2, -1, "", 'l', 1, DARKBROWN, color);
}

// RTC um eine Minute erhöhen 
void update_rtc(void)
{
    rtc_min++;
    if(rtc_min > 59)
    {
        rtc_min = 0;
        rtc_hour++;
        if(rtc_hour > 23)
        {
            rtc_hour = 0;
            rtc_day++;

            if((rtc_year / 4) * 4 == rtc_year)  //Schaltjahr
            {
                mdays[1] = 29;
            }

              if(rtc_day > mdays[rtc_month - 1])
            {
                rtc_day = 1;
                rtc_month++;
            }

            if(rtc_month > 12)
            {
                rtc_month = 1;
                rtc_year++;
            }
        }
    }
}


//Colortest
//Auswahl ermöglichen durch Tastensteuerung im x-y-Raster
    //   O              254 (1)
    //   0              223 (32)
    //  O 0      127(128)     239(16)
    //   O              191(64)
    //   O              253(2)
void colortest(void)
{

    unsigned char r = 0, g = 0, b = 0, byte1, byte2, dir = 0;
    unsigned int c = 0;

    char ch;

    lcd_cls(0, backcolor);

    while(read_keys());

    lcd_put_string(10, 40, "+", 2, YELLOW, backcolor);
    lcd_put_number(10, 10, r, -1, -1, "", 'l', 2, RED, YELLOW);
    lcd_put_number(60, 10, g, -1, -1, "", 'l', 2, DARKGREEN, YELLOW);
    lcd_put_number(110, 10, b, -1, -1, "", 'l', 2, BLUE, YELLOW);

    ch = read_keys();

    lcd_draw_rectangle_filled(90, 50, 150, 110, WHITE);

    while(ch != 1)
    {
        if(ch)
        {
            switch(ch)
            {
                case 32:    if(!dir)
                            {
                                if(g < 63)
                                {
                                    g++;
                                }
                            }
                            else
                            {
                                if(g > 0)
                                {
                                    g--;;
                                }
                            }
                            lcd_put_string(60, 10, "  ", 2, DARKGREEN, YELLOW);
                            lcd_put_number(60, 10, g, -1, -1, "", 'l', 2, DARKGREEN, YELLOW);
                            break;

                case 128:   if(!dir)
                            {
                                if(r < 31)
                                {
                                    r++;;
                                }
                            }
                            else
                            {
                                if(r > 0)
                                {
                                    r--;;
                                }
                            }
                            lcd_put_string(10, 10, "  ", 2, RED, YELLOW);
                            lcd_put_number(10, 10, r, -1, -1, "", 'l', 2, RED, YELLOW);

                            break;

                case 64:    dir = ~dir;
                            if(!dir)
                            {
                                lcd_put_string(10, 40, "+", 2, YELLOW, backcolor);
                            }
                            else
                            {
                                lcd_put_string(10, 40, "-", 2, YELLOW, backcolor);
                            }
                            break;

                case 16:    if(!dir)
                            {
                                if(b < 31)
                                {
                                    b++;
                                }
                            }
                            else
                            {
                                if(b > 0)
                                {
                                    b--;
                                }
                            }
                            lcd_put_string(110, 10, "  ", 2, BLUE, YELLOW);

                            lcd_put_number(110, 10, b, -1, -1, "", 'l', 2, BLUE, YELLOW);
                            break;
            }

            wait_ms(100);

            byte1 = ((r << 3) + (g >> 3));
            byte2 = (g << 5) + b;

            lcd_draw_rectangle_filled(10, 60, 60, 120, backcolor);
            lcd_put_number(10, 70, byte1, -1, -1, "", 'l', 1, YELLOW, backcolor);
            lcd_put_number(10, 90, byte2, -1, -1, "", 'l', 1, YELLOW, backcolor);

            c = byte1 * 256 + byte2;

            lcd_draw_rectangle_filled(100, 60, 140, 100, c);
         }
        ch = read_keys();
    }

    lcd_cls(0, backcolor);
}

 /////////////////////////////////////////
// AD-Wandler & zugeordnete Funktionen //
////////////////////////////////////////
// Drucksensor auslesen 
void get_pressure_sensor()
{
    adc_mode = 1;

    // AD-Wandler konfigurieren
    ADMUX = 193;   // Interne Referenz des ADC auf 2,56V und Kanal ADC1 (PINF1) aktivieren binär 11000001
    wait_ms(ADWAITSTATE);
    ADCSRA = 206; // AD-Wandler abfragen 11001110
    wait_ms(ADWAITSTATE);

    // => ab zur SIGNAL-Routine
    //.....
    // return from interrupt

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

    if(!phase)
    {
        return;
    }

    if(curdepth > maxdepth)
    {
        maxdepth = curdepth;
        lcd_print_max_depth();
        temp_maxdepth = curtemp;
    }

    //Prüfen ob die Dekotiefe eingehalten wird 
    if(curdepth < (deepest_decostep - 1) * 10)
    {
        lcd_put_string(90, 5, "  ", 2, backcolor, backcolor);
        lcd_print_char(90, 5, 200, 2, LIGHTRED, backcolor);
        led(1, 1);

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

}

// Temperatursensor auslesen 
void get_temp_sensor()
{
    adc_mode = 2;

    // AD-Wandler konfigurieren
    ADMUX = 194;   // Interne Referenz des ADC auf 2,56V und 
                   // Kanal ADC2 (PINF2) aktivieren 11000010
    wait_ms(ADWAITSTATE);
    ADCSRA = 206; // AD-Wandler abfragen 11001110
    wait_ms(ADWAITSTATE);
}

// Spannung messen
void get_voltage_sensor()
{
    adc_mode = 3;

    // AD-Wandler konfigurieren
    ADMUX = 195;   // Interne Referenz des ADC auf 2,56V und 
                   // Kanal ADC7 (PINF7) aktivieren 11000011
    wait_ms(ADWAITSTATE);
    ADCSRA = 206; // AD-Wandler abfragen 11001110
    wait_ms(ADWAITSTATE);
}

////////////
// USART //
//////////
void usart_init()
{
    cli();

    // 9.6 kBaud 
    UBRR1H = 0;
    UBRR1L = 95;

    UCSR1A &=  ~(1<<U2X1);
    UCSR1B = (1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1); //Einschalten von TX und RX
    UCSR1C = 0x06;

    rx_buf_cnt = 0;

    sei();

}

// Ein Zeichen an den USART senden
void usart_putc(char tx_char)
{
    while(!(UCSR1A & (1<<UDRE1)));

    UDR1 = tx_char;
}

//Auswerten, was der USART empfangen hat
SIGNAL(SIG_USART1_RECV)
{
    unsigned char rx_char = UDR1, inputlen = 2;
    unsigned char curval = 0;
    unsigned int t1, byte_adr, x = 0, last_adr = 0;

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

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

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

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

    if(rx_buf_cnt > inputlen)
    {
        byte_adr = rx_buf[1] + rx_buf[2] * 256;

        if(byte_adr <= MAX_EEPROM_ADR)
        {
            if(byte_adr != last_adr)
            {
                lcd_put_string(5, 80, "    ", 1, forecolor, backcolor);
            }
            lcd_put_number(5, 80, byte_adr, -1, -1, "", 'l', 1, YELLOW, backcolor);
            last_adr = byte_adr;

            // Code auswerten 
            switch(rx_buf[0])
            {
                case 200:  // 1 Byte aus EEPROM senden 
                    curval = eeprom_read_byte((uint8_t*)byte_adr);
                    usart_putc(curval);               // Byte senden 
                    usart_putc(make_crc(3, curval));  // CRC anhaengen 
                    lcd_put_string(80, 50, "SENDE...   ", 1, GREEN, backcolor);
                    lcd_put_number(80, 80, curval, -1, -1, "   ", 'l', 1, GREEN, backcolor);
                    break;

                case 201:  // 1 Byte in EEPROM schreiben
                    for(t1 = 0; t1 < 4; t1++)  // CRC berechnen 
                    {
                        x = x ^ rx_buf[t1];
                    }

                    if(x == rx_buf[4])    // CRC ist OK 
                    {
                        while(!eeprom_is_ready());
                        cli();
                        //                           Adresse Lo, Hi                 Wert
                        eeprom_write_byte((uint8_t*)(rx_buf[1] + rx_buf[2] * 256), rx_buf[3]);

                        lcd_put_string(80, 50, "EMPFANGE...", 1, RED, backcolor);
                        sei();

                        lcd_put_number(80, 80, rx_buf[3], -1, -1, "   ", 'l', 1, RED, backcolor);

                        //"CRC!" löschen
                        lcd_put_string(120, 64, "    ", 1, WHITE, backcolor);
                    }
                    else
                    {
                        lcd_put_string(120, 64, "CRC!", 1, RED, YELLOW);
                    }
            }
        }
        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;
}

// Startfunktion der Schnittstelle zwischen SBTC und PC 
void sbtc2pc(void)
{
    while(read_keys());

    //Bildschirm    
    lcd_cls(0, backcolor);
    lcd_draw_rectangle_filled(0, 1, LCD_HEIGHT - 1, 21, WHITE);
    headline("SBTC <=> PC");

    // Datenuebertragung zum PC starten.
    usart_init();

    lcd_put_string(5, 50, "TX-RX", 1, YELLOW, backcolor);
    lcd_put_string(5, 64, "ADRESSE", 1, LIGHTGREEN, backcolor);
    lcd_put_string(80, 64, "WERT", 1, LIGHTGREEN, backcolor);

    while(!read_keys());

    //USART deaktivieren
    UCSR1B &= ~(1<<RXEN1); //RX aus
    UCSR1B &= ~(1<<TXEN1); //TX aus
    UCSR1B &= ~(1<<RXCIE1); //Interrupt abschalten
}


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

    lo = ADCL; //Reihenfolge beachten!
    hi = ADCH; // erst Lo, dann Hi

    adc_val = hi * 256 + lo;

    switch(adc_mode) //Was wurde gemessen? Tiefe, Temp, Akkuspannung?
    {
      case 1: curdepth = adc_val;
              break;

      case 2: v = (double) adc_val / 1024 * 2.56;
              curtemp = (((15000 * v) / (5.00 - v)) - 1630) / 14.3 - 5; // -5 = Korrekturfaktor
              break;

      case 3: accu_voltage  = (5.6666 * (double) adc_val * 2.56) / 1024;
              break;
    }

}

// Timer 0 Ereignisroutine (autom. Aufruf 1/s) 
ISR(TIMER0_OVF_vect)
{
    runseconds++;

    TCNT0 = 0;       // Timerregister auf 0 
}

////////////////////////////////////////

//////////////////////
//  Benutzermenue  //
////////////////////

void menu(void)
{
    int t1;
    int menupos = 0;
    int intv;
    int airp0_tmp;
    unsigned char ch;
    int ystart = 0, yend = 5;

    //Temporäre Werte zum Verändern innerhalb der Funktion
    int menu_tmpval[MENU_ITEMS];
    int menu_pointer = 0;

    //Zuweisung
    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] = 100 - figN2[0] * 100; //O2-Anteil!
    menu_tmpval[6] = 100 - figN2[1] * 100;
    menu_tmpval[7] = 100 - figN2[2] * 100;
    menu_tmpval[8] = show_start_screen;
    menu_tmpval[9] = light_time;
    menu_tmpval[10] = powerdown_time;
    menu_tmpval[11] = light_value;

    //Warten bis User Taste losgelassen hat
    while(read_keys());

    //Bildschirm aufbauen
    lcd_cls(0, backcolor);
    headline("EINSTELLUNGEN");

    //Menue aufbauen
    lcd_draw_rectangle(8, 25, 165, 100, YELLOW, 2);

    for(t1 = ystart; t1 < yend; t1++)
    {
        if(t1 == menupos)
        {
            lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, backcolor, YELLOW);
        }
        else
        {
            lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, YELLOW, backcolor);
        }
    }

    menu_pointer = 0;

    ch = 0;
    while(ch != 1)
    {

        //Up
        if(ch == 32)
        {
            if(menupos > 0)
            {
                menupos--;

                if(menupos < ystart && ystart > 0)
                {
                    ystart--;
                    yend--;
                }

                for(t1 = ystart; t1 < yend; t1++)
                {
                    if(t1 == menupos)
                    {
                        lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, backcolor, YELLOW);
                    }
                    else
                    {
                        lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, YELLOW, backcolor);
                    }
                }
            }
        }

        //Down
        if(ch == 64)
        {
            if(menupos < MENU_ITEMS - 1)
            {
                menupos++;
                if(menupos > 4 && yend < MENU_ITEMS)
                {
                    ystart++;
                    yend++;
                }

                for(t1 = ystart; t1 < yend; t1++)
                {
                    if(t1 == menupos)
                    {
                        lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, backcolor, YELLOW);
                    }
                    else
                    {
                        lcd_put_string(12, (t1 - ystart) * 14 + 30, menu_str[t1], 1, YELLOW, backcolor);
                    }
                }
            }
        }

        if(ch == 16) //Taste "rechts" => Wert soll geändert werden
        {
            if(ch == 16 && menu_step[menupos]) //Wert initial anzeigen, abernicht bei Funktionen
            {
                //Unteren LCD-Teil löschen
                lcd_draw_rectangle_filled(0, 108, LCD_HEIGHT - 1, LCD_WIDTH - 1, backcolor);
                //Wert anzeigen
                lcd_put_number(80, 110, menu_tmpval[menupos], menu_digits[menupos], menu_dec[menupos], 0, 'r', 1, forecolor, backcolor);
                lcd_put_string(89, 110, menu_unitstr[menupos], 1, WHITE, backcolor);

                while(read_keys());
            }
            ch = read_keys();

            //Wert verändern, bis Taste 3 ("links") gedrückt
            while(ch != 128)
            {
                if(menu_step[menupos]) //Nur für Zahlenwerte, nicht für Funktionsaufrufe
                {
                    intv =  menu_tmpval[menupos] / menu_step[menupos];

                    switch(ch)
                    {
                        case 32:
                            menu_tmpval[menupos] = intv * menu_step[menupos] + menu_step[menupos];
                            if(menu_tmpval[menupos] > menu_end[menupos])
                            {
                                menu_tmpval[menupos] = menu_sta[menupos] ;
                            }
                            //Wert anzeigen
                            lcd_draw_rectangle_filled(0, 108, LCD_HEIGHT - 1, LCD_WIDTH - 1, backcolor);
                            lcd_put_number(80, 110, menu_tmpval[menupos], menu_digits[menupos], menu_dec[menupos], 0, 'r', 1, forecolor, backcolor);
                            lcd_put_string(89, 110, menu_unitstr[menupos], 1, WHITE, backcolor);

                            //Lichtstärke einstellen, wenn Funktion aufgerufen
                            if(menupos == 11)
                            {
                                display_light(menu_tmpval[11]);
                            }
                            break;

                        case 64:
                            menu_tmpval[menupos]  = intv *  menu_step[menupos] - menu_step[menupos];
                            if(menu_tmpval[menupos] < menu_sta[menupos])
                            {
                                menu_tmpval[menupos] = menu_end[menupos];
                            }

                            //Wert anzeigen
                            lcd_draw_rectangle_filled(0, 108, LCD_HEIGHT - 1, LCD_WIDTH - 1, backcolor);
                            lcd_put_number(80, 110, menu_tmpval[menupos], menu_digits[menupos], menu_dec[menupos], 0, 'r', 1, forecolor, backcolor);
                            lcd_put_string(89, 110, menu_unitstr[menupos], 1, WHITE, backcolor);

                            //Lichtstärke einstellen, wenn Funktion aufgerufen
                            if(menupos == 11)
                            {
                                display_light(menu_tmpval[11]);
                            }

                            break;
                    }
                }
                else
                {   //Funktionen
                    switch(menupos)
                    {
                        case 12:    show_profiles();
                                    return;

                        case 13:    erase_eeprom(1);
                                    return;

                        case 14:    erase_eeprom(0);
                                    return;

                        case 15:    sbtc2pc();
                                    return;

                        case 16:    colortest();
                                    return;
                    }
                    return;
                }

                ch = read_keys();
                wait_ms(100);
            }

            while(read_keys());

            lcd_draw_rectangle_filled(0, 108, LCD_HEIGHT - 1, LCD_WIDTH - 1, backcolor);
        }
        wait_ms(100);
        ch = read_keys();
    }

    if(question("SPEICHERN?") == 1)
    {
        // Luftdruck am Tauchort 
        airp0 = menu_tmpval[0] * 0.001;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)0, menu_tmpval[0] & 0x00FF);          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)1, (menu_tmpval[0] & 0xFF00) / 256);  // HiByte 

        // Hoehe ueber NN 
        altitude = menu_tmpval[1];
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)2, menu_tmpval[1] & 0x00FF);          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)3, (menu_tmpval[1] & 0xFF00) / 256);  // HiByte 

        // Kabinendruck im Flugzeug 
        cabinp = menu_tmpval[2] * 0.001;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)4, menu_tmpval[2] & 0x00FF);          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)5, (menu_tmpval[2] & 0xFF00) / 256);  // HiByte 

        // max. ppO2 
        maxppo2 = menu_tmpval[3];
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)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]);
            while(!eeprom_is_ready());
            eeprom_write_byte((uint8_t*)9, menu_tmpval[4]);
            f_cons = menu_tmpval[4];
        }

        //f.i.g. N2 3 Gase
        for(t1 = 5; t1 < 8; t1++)
        {
            while(!eeprom_is_ready());
            eeprom_write_byte((uint8_t*)(t1 + 1), 100 - menu_tmpval[t1]);  // Nur 1 Byte 
            figN2[t1 - 5] = 1 - menu_tmpval[t1] * 0.01;
        }

        //Lichtstärke
        light_value = menu_tmpval[11];
        display_light(light_value);
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)17, menu_tmpval[11]);

        //Displaylicht Zeit bis zum aut. Abschalten
        light_time = menu_tmpval[9];
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)18, light_time);

        //Peripherie Zeit bis zum aut. Abschalten
        powerdown_time = menu_tmpval[10];
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)19, powerdown_time);

        //Startbildschirm zeigen oder nicht?        
        show_start_screen = menu_tmpval[8];
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)20, menu_tmpval[8]);

        sei();

        lcd_cls(0, backcolor);

        message("GESPEICHERT.", 2000);

        //Luftdruck am TP berechnen 
        if(altitude == 0)
        {
            airp = airp0;
        }
        else
        {
            airp0_tmp = airp0 * 1000;
            airp0_tmp = airp0_tmp * exp(-1.2928 * 0.0981 * altitude / airp0_tmp);
            airp = airp0_tmp * 0.001;
        }

        message("LUFTDRUCK TP", 1);
        airp0_tmp = airp0 * 1000;
        lcd_put_number(54, 70, airp * 1000, -1, -1, " MBAR", 'l', 1, DARKCYAN, WHITE);
        airp = airp0_tmp * 0.001;
        wait_ms(2000);
    }
    else
    {
        message("ABBRUCH", 1000);
    }
}

//Message-Box mit Mitteilung an Anwender
void message(char *m, int wait)
{
    unsigned char ch;
    int xpos = (LCD_HEIGHT - strlen(m) * 8) / 2;

    while(read_keys());

    lcd_cls(1, backcolor);

    lcd_draw_rectangle_filled(20, 30, LCD_HEIGHT - 20, 100, WHITE);
    lcd_put_string(xpos, 40, m, 1, BLUE, WHITE); //Zentrieren

    lcd_put_string(70, 70, " OK ", 1, WHITE, DARKGREY);

    if(wait)
    {
        wait_ms(wait);
    }
    else
    {
        while(!read_keys());
        ch = read_keys();
    }
}

//Frage an User
int question(char *q)
{
    unsigned char ch;
    int xpos = (LCD_HEIGHT - strlen(q) * 8) / 2;

    while(read_keys());

    lcd_cls(1, backcolor);

    lcd_draw_rectangle_filled(20, 30, LCD_HEIGHT - 20, 100, WHITE);
    lcd_put_string(xpos, 40, q, 1, BLUE, WHITE); //Zentrieren

    lcd_put_string(40, 80, " JA ", 1, DARKCYAN, WHITE);
    lcd_put_string(90, 80, " NEIN ", 1, RED, WHITE);

    while(!read_keys());
    ch = read_keys();

    switch(ch)
    {
        case 32: return 1;
                  break;
        case 64: return 0;
                  break;
        default:  return -1;
    }
}

//Überschrift anzeigen
void headline(char *s)
{
    int xpos = (LCD_HEIGHT - strlen(s) * 8) / 2;

    lcd_draw_rectangle_filled(0, 0, LCD_HEIGHT - 1, 21, WHITE);
    lcd_put_string(xpos, 6, s, 1, LIGHTRED, WHITE);
}

//Tasten abfragen
//Wert ergibt für Taste
//   O              254 (1)
//   0              223 (32)
//  O 0      127(128)     239(16)
//   O              191(64)
//   O              253(2)
//PIND    Wert

int read_keys(void)
{
    int t1, z = 1, key0 = 0, key1 = 0;

    //1. Durchlauf
    for(t1 = 0; t1 < 8; t1++)
    {
        if(t1 != 2 && t1 != 3)
        {
            if(!(PIND & z))
             {
                key0 = z;
            }

        }
        z <<= 1;
    }

    z = 1;

    //2. Durchlauf
    for(t1 = 0; t1 < 8; t1++)
    {
        if(t1 != 2 && t1 != 3)
        {
            if(!(PIND & z))
             {
                key1 = z;
            }

        }
        z <<= 1;
    }

    if(key0 == key1)
    {
        if(key0)
        {
            idle = 0; //Alle Systeme "on"
            power_status = 0;

            display_light(light_value);
            if(!peripherical_power) //Falls SBTC im "PowerDown"-Modus ist, wieder aktivieren
            {
                sbtc_power(1);
            }
        }
        return key0;
    }
    else
    {
        return 0;
    }

}

//Fortschrittsbalken
// action == 0: Init, action == 1: Update)
void progress_bar(int action, int done, int full, char *msg)
{
    double pgb_end;
    int xpos = (LCD_HEIGHT - strlen(msg) * 8) / 2;

    if(!action)
    {
        lcd_cls(1, backcolor);
        lcd_put_string(xpos, 50, msg, 1, forecolor, backcolor);
        lcd_draw_rectangle_filled(50, 75, 150, 90, WHITE);
    }
    else //Update
    {
        if(full)
        {
            pgb_end = 100 * (double) done / (double) full + 50;
            lcd_draw_rectangle_filled(50, 75, (int) pgb_end, 90, BLACK);
        }
    }
}

//////////////////
// PWM-Routinen //
//////////////////

// Den PWM-Controller konfigurieren
void pwm_init(void)
{
    TCNT1 = 0x00FF;   //TimerCounter1 setzen
    TCCR1A = 13;     //Einstellen nur für OC1C auf "PWM, Phase Correct, 8-bit"
    TCCR1B = 11;    //Prescaler CLK/64
    OCR1C = 0x00;  // Duty-cycles auf 100% 
    TCCR1C = 32;  // PWM Outport=OC1C (PORT B7)
}

int main()
{
    int t1;
    int airp0_tmp, cabinptmp;
    unsigned long seconds_old1, seconds_old2, seconds_old3, seconds_old4;  //versch. Sekundenmessungen
    unsigned char store_seconds = 0; // Intervallschalter fuer Profilaufzeichnung 
    unsigned char surf_screen = 0, surf_screen_old = 1; //Bildschirmanzeige wechseln
    unsigned char switcher_date_time = 0; //Wechelsn zwischen Datums- und Uhrzeitanzeige

    DDRA = (1 << DDA0) | (1 << DDA6); //Port A0 und A6 auf "Output" schalten für LEDs
    DDRB = 255; //Alle PINs von PORTB auf "output"
    DDRD = 0; //Port D als Input (D2, D3 = RS232, wird bei Aufruf der Funktionen vom AVR geschaltet)
    PORTD = 243; //Pull-up Widerstände an Port D aktivieren bis auf PIN 2 und 3
    DDRF = 0; //Port A als Input (für ADC)
    DDRG = 2; // PIN 1 auf "Output". Wird benötigt, um 
              // die Peripherie von der Energieversorung zu trennen um Strom zu sparen
              // ACHTUNG: Option muss auf Modul "D072" vorhanden sein, Brücke "JP" muss getrennt sein!!!!

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

    //LCD initialisieren
    display_init();

    //Grundfarben setzen für VG und HG
    forecolor = WHITE;
    backcolor = DARKBLUE;

    lcd_cls(0,backcolor);

    //LEDs aus
    led(0, 0);
    led(1, 0);

    //SBTC Version anzeigen
    lcd_draw_rectangle_filled(1, 25, LCD_HEIGHT - 1, 30, RED);
    lcd_draw_rectangle_filled(1, 90, LCD_HEIGHT - 1, 95, RED);
    lcd_put_string(40, 40, "SBTC 4", 2, YELLOW, backcolor);
    lcd_put_string(20, 70, "PETER RACHOW 2008", 1, GREEN, backcolor);
    lcd_put_string(60, 105, softwareversion, 1, LIGHTGREEN, backcolor);

    //Displaybeleuchtung an
    light_value = eeprom_read_byte((uint8_t*)17);
    if(light_value > 255 || light_value < 0)
    {
        light_value = 127;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)17, light_value);
    }

    //PWM-Funktion initialisieren
    pwm_init();

    //Lichtstärke einstellen        
    for(t1 = 255; t1 > 0; t1--)
    {
        display_light(t1);
        wait_ms(10);
    }
    for(t1 = 0; t1 < light_value; t1++)
    {
        display_light(t1);
        wait_ms(10);
    }
    power_status = 0;

    wait_ms(2000);

    //Softwareversion in EEPROM ablegen, wenn geändert 
    for(t1 = 21; t1 < 24; t1++)
    {
        if(eeprom_read_byte((uint8_t*)t1) != softwareversion[(t1 - 20) * 2])
        {
            eeprom_write_byte((uint8_t*)t1, softwareversion[(t1 - 20) * 2]);
        }
    }

    lcd_cls(0, backcolor);

    //Kompartimente initialisieren        
    for(t1 = 0; t1 < NCOMP; t1++)
    {
        piN2[t1] = 0.72;
    }

    // Konservativ-Faktor auf 5 setzen (i. e. *= 1.2) 
    f_cons = 12;
    if(eeprom_read_byte((uint8_t*)9) != f_cons)
    {
        eeprom_write_byte((uint8_t*)9, f_cons);
    }
    set_ab_values(f_cons);

    //Oberflächenmodus setzen
    phase = SURFACE;

    //Atemgas = Luft
    curgas = 0;

    // Startparameter aus EEPROM lesen    
    // Umgebungsluftdruck 
    airp0 = (eeprom_read_byte((uint8_t*)0) + eeprom_read_byte((uint8_t*)1) * 256) * 0.001;  // Luftdruck 
    if(airp0 < 0.66 || airp0 > 1.2)
    {
        airp0 = 1;
        airp0_tmp = airp0 * 1000;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)0, (airp0_tmp & 0x00FF));          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)1, (airp0_tmp & 0xFF00) / 256);  // HiByte 
    }

    // Hoehe ueber NN in m 
    altitude = (eeprom_read_byte((uint8_t*)2) + eeprom_read_byte((uint8_t*)3) * 256);
    if(altitude < 0 || altitude > 2000)
    {
        altitude = 116; //Karlsruhe
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)2, altitude & 0x00FF);          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)3, (altitude & 0xFF00) / 256);  // HiByte 
    }

    cabinp = (eeprom_read_byte((uint8_t*)4) + eeprom_read_byte((uint8_t*)5) * 256) * 0.001;
    if(cabinp < 0.3 || cabinp > 1)
    {
        cabinp = 0.65;
        cabinptmp = cabinp * 1000;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)4, cabinptmp & 0x00FF);          // LoByte 
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)5, (cabinptmp & 0xFF00) / 256);  // HiByte 
    }

    // Luftdruck am Tauchort 
    if(altitude == 0)
    {
        airp = airp0;
    }
    else
    {
        airp0_tmp = airp0 * 1000;
        airp0_tmp = airp0_tmp * exp(-1.2928 * 0.0981 * altitude / airp0_tmp);
        airp = airp0_tmp * 0.001;
    }

    // maxppO2 
    maxppo2 = eeprom_read_byte((uint8_t*)11);
    if(maxppo2 > 20 || maxppo2 < 10)
    {
        maxppo2 = 16; // = 1.6 bar
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)11, maxppo2);
     }

    //f.i.g 2er weiterer Gase laden, aber 1. Gas ist immer Luft
    for(t1 = 7; t1 < 9; t1++)
    {
        figN2[t1 - 6] = (double) eeprom_read_byte((uint8_t*)t1) / 100;
        if((figN2[t1 - 6] < 0) || (figN2[t1 - 6] > 1))
        {
            figN2[t1 - 6] = 0.78;
            while(!eeprom_is_ready());
            eeprom_write_byte((uint8_t*)t1, 100 - figN2[t1 - 6] * 100);  // Nur 1 Byte 
        }
    }

    // Energiespareinstellungen ////////////////////////////////////
    // Zeit bis zum Ausschalten der Displaybeleuchtung in Minuten
    light_time = eeprom_read_byte((uint8_t*)18);
    if(light_time > 60 || light_time < 5)
    {
        light_time = 10;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)18, light_time);
    }

    // Zeit bis zum Ausschalten der Peripherie in Minuten
    powerdown_time = eeprom_read_byte((uint8_t*)19);
    if(powerdown_time > 120 || powerdown_time < 5)
    {
        powerdown_time = 15;
        while(!eeprom_is_ready());
        eeprom_write_byte((uint8_t*)19, powerdown_time);
    }
    ////////////////////////////////////////////////////////////////

    //Startbildschirm mit Parametern anzeigen?
    show_start_screen = eeprom_read_byte((uint8_t*)20);

    //YES!
    if(show_start_screen)
    {
        //Bildschirm aufbauen
        lcd_cls(0, backcolor);
        headline("PARAMETER");

        //Umgebungsluftdruck 
        lcd_put_string(5, 25, "LUFTDRUCK", 1, forecolor, backcolor);
        lcd_put_number(110, 25, airp0 * 1000, menu_digits[0], menu_dec[0], menu_unitstr[0], 'l', 1, forecolor, backcolor);

        // Hoehe ueber NN 
        lcd_put_string(5, 40, menu_str[1], 1, forecolor, backcolor);
        lcd_put_number(110, 40, altitude, menu_digits[1], menu_dec[1], menu_unitstr[1], 'l', 1, forecolor, backcolor);

        // Kabinendruck im Flugzeug 
        lcd_put_string(5, 55, "KABINENDRUCK", 1, forecolor, backcolor);
        lcd_put_number(110, 55, cabinp  * 1000, menu_digits[2], menu_dec[2], menu_unitstr[2], 'l', 1, forecolor, backcolor);

        // Luftdruck am Tauchort
        lcd_put_string(5, 70, "LUFTDRUCK TP", 1, YELLOW, backcolor);
        lcd_put_number(110, 70, airp * 1000, -1, -1, "MBAR", 'l', 1, YELLOW, backcolor);

        // maxppO2 
        lcd_put_string(5, 85, "MAX. PPO2:", 1, YELLOW, backcolor);
        lcd_put_number(110,85, maxppo2, 2, 1, "BAR", 'l', 1, YELLOW, backcolor);

        //Anzeigen Gase
        for(t1 = 5; t1 < 8; t1++)
        {
            lcd_put_number(5 + (t1 - 5) * 50, 100, figN2[t1 - 5] * 100, -1, -1, "%N2", 'l', 1, GREEN, backcolor);
        }

        wait_ms(6000);
        lcd_cls(1, backcolor);
        //////////////////////////////////////////////
    }

    // Timer 0 fuer Sekundenzaehlung initialisieren, wird getaktet vom 32.768 kHz-Uhrenqurz 
    TIMSK &=~((1<<TOIE0)|(1<<OCIE0));     //TC0 Interrupt unterbinden
    ASSR |= (1<<AS0);           //Timer/Counter0 im Asynchronmodus mit Uhrenquarz.  
    TCNT0 = 0x00;
    TCCR0 = 0x05;                //Abgeleitet von f.xtal (CLK / 128) Taktrate
                                 //für f=1/s definieren
    while(ASSR&0x07);            //Warten bis REgister Update durchlaufen hat
    TIMSK |= (1<<TOIE0);        //8-bit Timer/Counter0 Overflow Interrupt einschalten 

    //Startwerte für RTC laden
    rtc_day = eeprom_read_byte((uint8_t*)12);
    rtc_month = eeprom_read_byte((uint8_t*)13);
    rtc_year = eeprom_read_byte((uint8_t*)14);
       rtc_min = eeprom_read_byte((uint8_t*)15);
    rtc_hour = eeprom_read_byte((uint8_t*)16);

    sei();                      // Interrupts ermoeglichen 

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

    get_temp_sensor();
    wait_ms(20);
    get_voltage_sensor();
    wait_ms(20);


    for(;;) // Endlosschleife fuer period. Aufgaben (Druckmessung, Dekorechnung, etc.) Periode: 1/s 
    {
        //Oberste Taste
        if(read_keys() == 1)
        {
            cli();

            menu();

            //Bildschirm löschen
            lcd_cls(0, backcolor);

            //surf_screen = 0 erzwingen
            seconds_old3 = runseconds;

            sei();
        }

        //Unterste Taste
        if(read_keys() == 2)
        {
            sbtc2pc();
        }

        if(read_keys() == 64) //Gaswechsel (Reedkontakt an D6 gegen Masse parallel zum Taster)
        {
            curgas++;
            if(curgas > 2)
            {
                curgas = 0;
            }

            if(surf_screen != 2) //Anzeigen wenn nicht gerade das Tauchprofil eingeblendet wird
            {
                show_curgas();
            }
            while(read_keys());
        }

        get_pressure_sensor();   // Aktuelle Tiefe erfassen

        // Akt. Tiefe, max. Tiefe und Tauchzeit anzeigen
        if(!surf_screen) //Nicht anzeigen, wenn TG-Grafik eingeblendet wird
        {
            lcd_print_cur_depth();
        }

        // Akt. ppO2 pruefen
        calc_ppo2();

        if(curtemp < temp_min)
        {
            temp_min = curtemp;
        }

        //Tauchmodus oder OFP-Modus?    
        if(curdepth > SWITCHDEPTH)
        {
            diveseconds++;

            if(!phase) //TG beginnt, auf Tauchphase umschalten 
            {
                //Dauerlicht
                display_light(light_value);

                if(!peripherical_power) //Falls "PowerDown" => Gerät reaktivieren
                {
                    sbtc_power(1);
                }

                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;

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

                // 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(curtemp);

                // Aufzeichnungsintervall 
                eeprom_store_byte(20);

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

                surf_seconds = 0;
                surf_screen = 0;

                lcd_cls(0, backcolor);

                lcd_print_dive_time(); //Tauchzeit anzeigen
                lcd_print_footer();   //Fußzeile mit T und U ausgeben 
                lcd_print_ppN2(0);   //ppN2 ausgeben

                phase = DIVE;
            }
        }
        else // Oberflaechenmodus 
        {
            surf_seconds++;

            if(surf_seconds > SURF_SECONDS_MAX && !deco_minutes_total)    // TG beendet, keine Restdeko
            {
                if(phase)//TG-Ende => Daten sichern
                {
                    // EEPROM aktualisieren... 
                    // TG-Zaehler um 1 erhoehen 
                    t1 = eeprom_read_byte((uint8_t*)24) + 256 * eeprom_read_byte((uint8_t*)25) + 1; // Alten Wert holen 
                    cli();
                    while(!eeprom_is_ready());
                    eeprom_write_byte((uint8_t*)24, t1 & 0x00FF);         // LoByte 
                    while(!eeprom_is_ready());
                    eeprom_write_byte((uint8_t*)25, (t1 & 0xFF00) / 256); // HiByte 

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

                    // Maximaltiefe evtl. erhoehen 
                    if(maxdepth > eeprom_read_byte((uint8_t*)28) + 256 * eeprom_read_byte((uint8_t*)29)) // Alten Wert holen 
                    {
                        while(!eeprom_is_ready());
                        eeprom_write_byte((uint8_t*)28, maxdepth & 0x00FF);         // LoByte 
                        while(!eeprom_is_ready());
                        eeprom_write_byte((uint8_t*)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_read_byte((uint8_t*)24));
                    eeprom_store_byte(256 * eeprom_read_byte((uint8_t*)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_write_byte((uint8_t*)30, eeprom_byte_count & 0x00FF);            // LoByte 
                    while(!eeprom_is_ready());
                    eeprom_write_byte((uint8_t*)31, (eeprom_byte_count & 0xFF00) / 256);   // HiByte 
                    sei();

                    lcd_cls(1, backcolor);
                    phase = SURFACE;
                }

            }
        }

        if(phase)
        {
            // TG-Profilpunkt speichern als Absolutwert in [m] in 1 Byte alle 20s 
            if(store_seconds > 19)
            {
                eeprom_store_byte((unsigned char) (curdepth * .1));
                store_seconds = 0;
            }
            else
            {
                store_seconds++;
            }
        }
        else
        {
            //Bildschirmanzeige für die OFP schalten
            //TG-werte wie ZNS, OTU, FVB etc.
            if((runseconds - seconds_old3) < 5)
            {
                surf_screen = 0;
            }

            //Tauchprofil mit TG-Daten
            if((runseconds - seconds_old3) >= 5 && (runseconds - seconds_old3) < 10)
            {
                surf_screen = 1;
            }

            //Tauchprofil mit Dekostufen
            if((runseconds - seconds_old3) >= 11 && (runseconds - seconds_old3) < 15)
            {
                surf_screen = 2;
            }

            //Und wieder von vorne...            
            if((runseconds - seconds_old3) >= 16)
            {
                seconds_old3 = runseconds;
            }

            if(surf_screen != surf_screen_old)
            {
                switch(surf_screen) //Wechsel zwischen Zahlendaten und Tauchprofilgrafik
                {
                    case 0: lcd_cls(1, backcolor);
                            lcd_print_cur_depth();
                            lcd_print_dive_time();
                            lcd_print_max_depth();
                            surface_info();        //FVB, OFP, ZNS, OTU
                            lcd_print_ppN2(1);     // ppN2 in den Geweben
                            lcd_print_footer();
                            if(switcher_date_time)
                            {
                                show_rtc_time();
                                switcher_date_time = 0;
                            }
                            else
                            {
                                show_rtc_date();
                                switcher_date_time = 1;
                            }
                            break;

                    case 1: print_dive_profile(0, 0, 0); //Letztes Profil einblenden mit TG-Daten
                            break;

                    case 2: print_dive_profile(0, 0, 1); //Letztes Profil einblenden mit Dekostufen
                            break;

                }
                surf_screen_old = surf_screen;
            }
        }

        //  Unabhängig vom Tauchstatus (Tauchen vs. Oberfläche?)
        //  alle 10 sec. Gewebesaettigung, Dekorechnung, Verschiedenes
        if(runseconds >= seconds_old1 + 10)
        {
            get_temp_sensor();
            get_voltage_sensor();

            if(phase)
            {
                lcd_print_footer(); //Fußzeile mit T und U ausgeben 
                lcd_print_ppN2(0);  //ppN2-Balkengrafik ausgeben

                //"Ceiling" ausgeben
                lcd_put_number(5, 48,  calc_ceiling(), 3, 1, "M", 'l', 1, YELLOW, backcolor);

                //Pfeil "runter" wieder löschen
                lcd_draw_rectangle_filled(90, 4, 106, 28, backcolor);

                idle = 0;
                power_status = 0;

                //Uhrzeit anzeigen
                show_rtc_time();
            }
            else
            {                            //x Minuten nach letzter Aktion das Licht abschalten
                if(idle >= light_time) //aber nicht im Tauchmodus! ;-))
                {
                    display_light(255);
                    power_status = 1;
                }

                //dito für Peripherie (i. e. komplett abschalten!)
                if(idle >= powerdown_time)
                {
                    sbtc_power(0);
                    power_status  = 2;
                }

                if(curtemp <= 8 && !temp_low)
                {
                    temp_low = 1;
                    set_ab_values(f_cons + 1);
                }
            }

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

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

        // Jede Minute..  
        if(runseconds > seconds_old2 + 60)
        {
            //Zeitmsswert für Displaybeleuchtung um 1 min. erhöhen
            idle++;

            //Tauchzeit
            if(phase)
            {
                lcd_print_dive_time();
            }

            //...ZNS und OTU berechnen
            calc_cns_otu();

            seconds_old2 = runseconds;

            //Real Time Clock erhöhen
            update_rtc();

        }

        //Warten auf Interrupt TIMER0-Overflow
        if(power_status < 2)
        {
            seconds_old4 = runseconds;
            while(seconds_old4 == runseconds)
            {
                if(seconds_old4 != runseconds)
                {
                    led(1, 1);
                }
                else
                {
                    led(1, 0);
                }
            };
        }
           else
        {
            // AD-Wandler aus 
            ADCSRA = 0;

            // In Sleep-Mode gehen 
            set_sleep_mode (SLEEP_MODE_PWR_SAVE);
            sleep_mode();
        }

    }
    return 0;
}