/////////////////////////////////////////////////////////////////// // 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: 12.10.2008 11:15 // /////////////////////////////////////////////////////////////////// /* _ _ _ | | | | | | | |__| | __ _ _ __ __| |_ ____ _ _ __ ___ | __ |/ _` | '__/ _` \ \ /\ / / _` | '__/ _ \ | | | | (_| | | | (_| |\ 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 #include #include #include #include #include #include #include #include #include #include // 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*, int fc, int bc); void message(char*, int); int read_keys(void); void progress_bar(int, int, int, char*); void print_dive_profile(int, char, char); void delete_error_profil(int , int); //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.3.7"; // 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; // 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 int switchdepth = 20; //Tiefe bei der zwischen OFP- und Tauchmodus umgeschaltet wird. ////////////////////////// // 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 300 // 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); } } 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< 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; } //TG-Zähler auf 0 setzen while(!eeprom_is_ready()); eeprom_write_byte((uint8_t*)24, 0); // LoByte while(!eeprom_is_ready()); eeprom_write_byte((uint8_t*)25, 0); // HiByte } 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); lcd_draw_rectangle_filled(90, 34, LCD_HEIGHT - 1, LCD_WIDTH - 20, backcolor); if(ndt == 0) { lcd_draw_rectangle_filled(50, 34, 84, 48, backcolor); lcd_put_string(50, 34, "DEKO", 1, ORANGE, backcolor); lcd_put_number(LCD_HEIGHT, 34, ndt, -1, -1, "'", 'r', 2, LIGHTRED, backcolor); return; } if(ndt > 0) { lcd_put_number(LCD_HEIGHT, 34, ndt, -1, -1, "'", 'r', 2, GREEN, backcolor); } if(ndt < 0) //Unplausibler Wert { lcd_put_string(100, 34, "---", 2, LIGHTGREEN, 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", DARKGREEN, WHITE); 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; t1 = EEPROM_PROF_START; while(t1 < MAX_EEPROM_ADR) //Letzten TG oder bestimmten TG finden { //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; } //Nach dem Marker 232 suchen, hier steht die TG-Nummer, wenn best. TG gesucht wird if(num && 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 } } 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(max_dive_depth < 2) //Profil fehlerhaft { headline("FEHLER!", DARKRED, WHITE); //Fehlerhafte Daten löschen delete_error_profil(startadr, endadr); return; } break; case 1: t1 = endadr + 1; //lcd_put_number(60, 60, t1, -1, -1, "", 'l', 1, LIGHTRED, backcolor); //wait_ms(1000); while(eeprom_read_byte((uint8_t*)t1) != 231 && t1 < MAX_EEPROM_ADR) // 231 = Anfang der Dekostufen { //lcd_put_number(60, 60, t1, -1, -1, "", 'l', 1, WHITE, backcolor); t1++; } t1++; 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, "'", 'l', 1, GREEN, backcolor); y += 15; } t1++; } } if(max_dive_depth < 2) //Profil fehlerhaft { headline("FEHLER!", DARKRED, WHITE); //Fehlerhafte Daten löschen delete_error_profil(startadr, endadr); return; } } if(wait) { while(read_keys()); while(!read_keys()); lcd_cls(0, backcolor); } } //Fehlerhafte Daten löschen void delete_error_profil(int sadr, int eadr) { int t1 = sadr; lcd_cls(0, backcolor); headline("LÖSCHE PROFIL", GREEN, backcolor); lcd_put_string(20, 50, "START:", 1, WHITE, backcolor); lcd_put_number(75, 50, sadr, -1, -1, "", 'l', 1, YELLOW, backcolor); lcd_put_string(20, 70, "ENDE:", 1, WHITE, backcolor); lcd_put_number(75, 70, eadr, -1, -1, "", 'l', 1, YELLOW, backcolor); while(t1 <= eadr) { lcd_put_number(20, 90, t1, -1, -1, "", 'l', 1, GREEN, backcolor); eeprom_write_byte((uint8_t*)t1++, 0); } } //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< 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", DARKGREEN, WHITE); // 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< 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 forecol, int backcol) { int xpos = (LCD_HEIGHT - strlen(s) * 8) / 2; lcd_draw_rectangle_filled(0, 0, LCD_HEIGHT - 1, 21, backcol); lcd_put_string(xpos, 6, s, 1, forecol, backcol); } //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< 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", DARKGREEN, WHITE); //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< 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 { switchdepth = 10; //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; switchdepth = 20; } } } 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; }