Homepage von Peter Rachow Startseite - Home
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;
}