From afc41f082e610bd5ccc5a0ed6c6c32446b02074a Mon Sep 17 00:00:00 2001 From: Damjan Adamic Date: Thu, 29 Jan 2015 19:26:54 +0100 Subject: [PATCH] Fixes #2009: Timer Voice minute call not working for persistent timers Timers moved to timer.cpp Added timer overflow protection Added timer gtests THt mode now triggers on THR>10% --- companion/src/file.cpp | 8 +- .../opentx/opentxGruvin9xsimulator.cpp | 1 + .../firmwares/opentx/opentxM128simulator.cpp | 1 + .../firmwares/opentx/opentxM64simulator.cpp | 1 + .../firmwares/opentx/opentxSky9xsimulator.cpp | 1 + .../opentx/opentxTaranisSimulator.cpp | 1 + .../opentx/opentxTaranisX9ESimulator.cpp | 1 + radio/src/Makefile | 2 +- radio/src/debug.cpp | 2 +- radio/src/eeprom_common.cpp | 5 +- radio/src/eeprom_raw.cpp | 11 +- radio/src/eeprom_rlc.cpp | 13 +- radio/src/functions.cpp | 6 +- radio/src/gui/9X/view_main.cpp | 1149 ++-- radio/src/gui/9X/view_statistics.cpp | 449 +- radio/src/gui/Taranis/lcd.cpp | 1 + radio/src/gui/Taranis/view_main.cpp | 1 + radio/src/gui/Taranis/view_statistics.cpp | 1 + radio/src/lua_api.cpp | 1 + radio/src/mixer.cpp | 1 + radio/src/opentx.cpp | 5310 ++++++++--------- radio/src/opentx.h | 31 +- radio/src/tests/timers.cpp | 284 + radio/src/timers.cpp | 209 + radio/src/timers.h | 74 + 25 files changed, 4001 insertions(+), 3563 deletions(-) create mode 100644 radio/src/tests/timers.cpp create mode 100644 radio/src/timers.cpp create mode 100644 radio/src/timers.h diff --git a/companion/src/file.cpp b/companion/src/file.cpp index f2cb1f980..a9f617d43 100644 --- a/companion/src/file.cpp +++ b/companion/src/file.cpp @@ -15,13 +15,13 @@ */ -#include "stdio.h" -#include "inttypes.h" -#include "string.h" -#include "file.h" +#include +#include +#include #include #include #include "eeprominterface.h" +#include "file.h" RleFile::RleFile(): eeprom(NULL), diff --git a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp index e1cc45fbb..50e9bde68 100644 --- a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp @@ -71,6 +71,7 @@ namespace Open9xGruvin9x { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxM128simulator.cpp b/companion/src/firmwares/opentx/opentxM128simulator.cpp index c7ff8a680..8e7e039d4 100644 --- a/companion/src/firmwares/opentx/opentxM128simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM128simulator.cpp @@ -72,6 +72,7 @@ namespace OpenTxM128 { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxM64simulator.cpp b/companion/src/firmwares/opentx/opentxM64simulator.cpp index a37cf3bc0..9a67157fa 100644 --- a/companion/src/firmwares/opentx/opentxM64simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM64simulator.cpp @@ -75,6 +75,7 @@ namespace OpenTxM64 { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp index 004ff68f9..fa3a1e996 100644 --- a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp @@ -83,6 +83,7 @@ namespace Open9xSky9x { #include "radio/src/switches.cpp" #include "radio/src/functions.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/curves.cpp" #include "radio/src/targets/sky9x/pulses_driver.cpp" #include "radio/src/pulses/pulses_arm.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp index aaa72c240..f47e14fd0 100644 --- a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp @@ -87,6 +87,7 @@ inline int geteepromsize() { #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" #include "radio/src/sdcard.cpp" +#include "radio/src/timers.cpp" #include "radio/src/targets/taranis/pulses_driver.cpp" #include "radio/src/targets/taranis/rtc_driver.cpp" #include "radio/src/targets/taranis/trainer_driver.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp index e41c38c50..8dd52bf82 100755 --- a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp @@ -88,6 +88,7 @@ inline int geteepromsize() { #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" #include "radio/src/sdcard.cpp" +#include "radio/src/timers.cpp" #include "radio/src/targets/taranis/pulses_driver.cpp" #include "radio/src/targets/taranis/rtc_driver.cpp" #include "radio/src/targets/taranis/rotenc_driver.cpp" diff --git a/radio/src/Makefile b/radio/src/Makefile index d9ea261d8..4aa4c4ed7 100644 --- a/radio/src/Makefile +++ b/radio/src/Makefile @@ -954,7 +954,7 @@ ifeq ($(GUI), YES) CPPDEFS += -DGUI endif -CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) +CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) timers.cpp ifeq ($(GUI), YES) GUISRC += gui/$(GUIDIRECTORY)/lcd.cpp gui/$(GUIDIRECTORY)/splash.cpp gui/$(GUIDIRECTORY)/fonts.cpp diff --git a/radio/src/debug.cpp b/radio/src/debug.cpp index b60631207..44a62a9e5 100644 --- a/radio/src/debug.cpp +++ b/radio/src/debug.cpp @@ -285,7 +285,7 @@ const struct TraceElement * getTraceElement(uint16_t idx) void dumpTraceBuffer() { - TRACE("Dump of Trace Buffer ("VERS_STR " " DATE_STR " " TIME_STR "):"); + TRACE("Dump of Trace Buffer (" VERS_STR " " DATE_STR " " TIME_STR "):"); TRACE("# Time Event Data"); for(int n = 0; n < TRACE_BUFFER_LEN; ++n) { struct gtm tp; diff --git a/radio/src/eeprom_common.cpp b/radio/src/eeprom_common.cpp index acf36ab30..ced8703a4 100644 --- a/radio/src/eeprom_common.cpp +++ b/radio/src/eeprom_common.cpp @@ -35,9 +35,10 @@ */ #include +#include +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" uint8_t s_eeDirtyMsk; tmr10ms_t s_eeDirtyTime10ms; diff --git a/radio/src/eeprom_raw.cpp b/radio/src/eeprom_raw.cpp index dafde85ef..c4a3664d8 100644 --- a/radio/src/eeprom_raw.cpp +++ b/radio/src/eeprom_raw.cpp @@ -35,9 +35,10 @@ */ #include +#include +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" volatile uint32_t Spi_complete; // TODO in the driver ? @@ -396,11 +397,7 @@ void eeLoadModel(uint8_t id) customFunctionsReset(); - for (uint8_t i=0; i +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" uint8_t s_write_err = 0; // error reasons RlcFile theFile; //used for any file operation @@ -985,13 +986,7 @@ void eeLoadModel(uint8_t id) customFunctionsReset(); -#if !defined(PCBSTD) - for (uint8_t i=0; i - * - Andreas Weitl - * - Bertrand Songis - * - Bryan J. Rentoul (Gruvin) - * - Cameron Weeks - * - Erez Raviv - * - Gabriel Birkus - * - Jean-Pierre Parisy - * - Karl Szmutny - * - Michael Blandford - * - Michal Hlavinka - * - Pat Mackenzie - * - Philip Moss - * - Rob Thomson - * - Romolo Manfredini - * - Thomas Husterer - * - * opentx is based on code named - * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, - * er9x by Erez Raviv: http://code.google.com/p/er9x/, - * and the original (and ongoing) project by - * Thomas Husterer, th9x: http://code.google.com/p/th9x/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "../../opentx.h" - -#define BIGSIZE DBLSIZE -#define LBOX_CENTERX (LCD_W/4 + 10) -#define RBOX_CENTERX (3*LCD_W/4 - 10) -#define MODELNAME_X (2*FW-2) -#define MODELNAME_Y (0) -#define PHASE_X (6*FW) -#define PHASE_Y (2*FH) -#define PHASE_FLAGS 0 -#define VBATT_X (6*FW) -#define VBATT_Y (2*FH) -#define VBATTUNIT_X (VBATT_X-1) -#define VBATTUNIT_Y (3*FH) -#define REBOOT_X (20*FW-3) -#define VSWITCH_X(i) (16 + 3*i) -#define VSWITCH_Y (LCD_H-8) -#define BAR_HEIGHT (BOX_WIDTH-1l) -#define TRIM_LH_X (LCD_W*1/4+2) -#define TRIM_LV_X 3 -#define TRIM_RV_X (LCD_W-4) -#define TRIM_RH_X (LCD_W*3/4-2) - -#define TRIM_LEN 27 - -void drawPotsBars() -{ - // Optimization by Mike Blandford - uint8_t x, i, len ; // declare temporary variables - for (x=LCD_W/2-5, i=NUM_STICKS; i TRIM_MAX) { - exttrim = true; - } -#endif - if (val < -(TRIM_LEN+1)*4) { - val = -(TRIM_LEN+1); - } - else if (val > (TRIM_LEN+1)*4) { - val = TRIM_LEN+1; - } - else { - val /= 4; - } - - if (vert[i]) { - ym = 31; - lcd_vline(xm, ym-TRIM_LEN, TRIM_LEN*2); - if (i!=2 || !g_model.thrTrim) { - lcd_vline(xm-1, ym-1, 3); - lcd_vline(xm+1, ym-1, 3); - } - ym -= val; -#if !defined(CPUM64) || !defined(FRSKY) - drawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE); - if (dir >= 0) { - lcd_hline(xm-1, ym-1, 3); - } - if (dir <= 0) { - lcd_hline(xm-1, ym+1, 3); - } - if (exttrim) { - lcd_hline(xm-1, ym, 3); - } -#endif - } - else { - ym = 60; - lcd_hline(xm-TRIM_LEN, ym, TRIM_LEN*2); - lcd_hline(xm-1, ym-1, 3); - lcd_hline(xm-1, ym+1, 3); - xm += val; -#if !defined(CPUM64) || !defined(FRSKY) - drawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE); - if (dir >= 0) { - lcd_vline(xm+1, ym-1, 3); - } - if (dir <= 0) { - lcd_vline(xm-1, ym-1, 3); - } - if (exttrim) { - lcd_vline(xm, ym-1, 3); - } -#endif - } - lcd_square(xm-3, ym-3, 7, att); - } -} - - -void displayTimers() -{ -#if defined(TRANSLATIONS_CZ) - #define MAINTMR_LBL_COL (9*FW-FW/2-1) -#else - #define MAINTMR_LBL_COL (9*FW-FW/2+3) -#endif - // Main timer - if (g_model.timers[0].mode) { - TimerState & timerState = timersStates[0]; - uint8_t att = DBLSIZE | (timerState.val<0 ? BLINK|INVERS : 0); - putsTimer(12*FW+2+10*FWNUM-4, FH*2, timerState.val, att, att); - uint8_t xLabel = (timerState.val >= 0 ? MAINTMR_LBL_COL : MAINTMR_LBL_COL-7); -#if defined(CPUARM) - uint8_t len = zlen(g_model.timers[0].name, LEN_TIMER_NAME); - if (len > 0) { - xLabel += (LEN_TIMER_NAME-len)*FW; - lcd_putsnAtt(xLabel, FH*3, g_model.timers[0].name, len, ZCHAR); - } - else { - putsTimerMode(xLabel, FH*3, g_model.timers[0].mode); - } -#else - putsTimerMode(xLabel, FH*3, g_model.timers[0].mode); -#endif - } -} - -void displayBattVoltage() -{ -#if defined(BATTGRAPH) - putsVBat(VBATT_X-8, VBATT_Y+1, 0); - drawFilledRect(VBATT_X-25, VBATT_Y+9, 22, 5); - lcd_vline(VBATT_X-3, VBATT_Y+10, 3); - uint8_t count = limit(2, 20 * (g_vbat100mV - g_eeGeneral.vBatMin - 90) / (30 + g_eeGeneral.vBatMax - g_eeGeneral.vBatMin), 20); - for (uint8_t i=0; i g_eeGeneral.vBatWarn || BLINK_ON_PHASE) - drawFilledRect(VBATT_X-26, VBATT_Y, 25, 15); -#else - LcdFlags att = (g_vbat100mV <= g_eeGeneral.vBatWarn ? BLINK|INVERS : 0) | BIGSIZE; - putsVBat(VBATT_X-1, VBATT_Y, att|NO_UNIT); - lcd_putc(VBATT_X, VBATTUNIT_Y, 'V'); -#endif -} - -#if defined(PCBSKY9X) -void displayVoltageOrAlarm() -{ - if (g_vbat100mV > g_eeGeneral.vBatWarn && g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { - putsValueWithUnit(6*FW-1, 2*FH, getTemperature(), UNIT_TEMPERATURE, BLINK|INVERS|DBLSIZE); - } - else if (g_vbat100mV > g_eeGeneral.vBatWarn && g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.currentCalib)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { - putsValueWithUnit(7*FW-1, 2*FH, (g_eeGeneral.mAhUsed + Current_used*(488 + g_eeGeneral.currentCalib)/8192/36)/10, UNIT_MAH, BLINK|INVERS|DBLSIZE); - } - else { - displayBattVoltage(); - } -} -#else - #define displayVoltageOrAlarm() displayBattVoltage() -#endif - -#define EVT_KEY_MODEL_MENU EVT_KEY_LONG(KEY_RIGHT) -#define EVT_KEY_GENERAL_MENU EVT_KEY_LONG(KEY_LEFT) -#define EVT_KEY_TELEMETRY EVT_KEY_LONG(KEY_DOWN) -#define EVT_KEY_STATISTICS EVT_KEY_LONG(KEY_UP) -#define EVT_KEY_CONTEXT_MENU EVT_KEY_BREAK(KEY_MENU) - -#if defined(NAVIGATION_MENUS) -void onMainViewMenu(const char *result) -{ - if (result == STR_RESET_TIMER1) { - timerReset(0); - } - else if (result == STR_RESET_TIMER2) { - timerReset(1); - } -#if defined(CPUARM) - else if (result == STR_RESET_TIMER3) { - timerReset(2); - } - else if (result == STR_VIEW_NOTES) { - pushModelNotes(); - } - else if (result == STR_RESET_SUBMENU) { - MENU_ADD_ITEM(STR_RESET_FLIGHT); - MENU_ADD_ITEM(STR_RESET_TIMER1); - MENU_ADD_ITEM(STR_RESET_TIMER2); - MENU_ADD_ITEM(STR_RESET_TIMER3); -#if defined(FRSKY) - MENU_ADD_ITEM(STR_RESET_TELEMETRY); -#endif - } -#endif -#if defined(FRSKY) - else if (result == STR_RESET_TELEMETRY) { - telemetryReset(); - } -#endif - else if (result == STR_RESET_FLIGHT) { - flightReset(); - } - else if (result == STR_STATISTICS) { - chainMenu(menuStatisticsView); - } -#if defined(CPUARM) - else if (result == STR_ABOUT_US) { - chainMenu(menuAboutView); - } -#endif -} -#endif - -void menuMainView(uint8_t event) -{ - STICK_SCROLL_DISABLE(); - - uint8_t view = g_eeGeneral.view; - uint8_t view_base = view & 0x0f; - - switch(event) { - - case EVT_ENTRY: - killEvents(KEY_EXIT); - killEvents(KEY_UP); - killEvents(KEY_DOWN); - break; - - /* TODO if timer2 is OFF, it's possible to use this timer2 as in er9x... - case EVT_KEY_BREAK(KEY_MENU): - if (view_base == VIEW_TIMER2) { - Timer2_running = !Timer2_running; - AUDIO_KEYPAD_UP(); - } - break; - */ - - case EVT_KEY_BREAK(KEY_RIGHT): - case EVT_KEY_BREAK(KEY_LEFT): - if (view_base <= VIEW_INPUTS) { -#if defined(PCBSKY9X) - if (view_base == VIEW_INPUTS) - g_eeGeneral.view ^= ALTERNATE_VIEW; - else - g_eeGeneral.view = (g_eeGeneral.view + (4*ALTERNATE_VIEW) + ((event==EVT_KEY_BREAK(KEY_LEFT)) ? -ALTERNATE_VIEW : ALTERNATE_VIEW)) % (4*ALTERNATE_VIEW); -#else - g_eeGeneral.view ^= ALTERNATE_VIEW; -#endif - eeDirty(EE_GENERAL); - AUDIO_KEYPAD_UP(); - } - break; - -#if defined(NAVIGATION_MENUS) - case EVT_KEY_CONTEXT_MENU: - killEvents(event); - -#if defined(CPUARM) - if (modelHasNotes()) { - MENU_ADD_ITEM(STR_VIEW_NOTES); - } -#endif - -#if defined(CPUARM) - MENU_ADD_ITEM(STR_RESET_SUBMENU); -#else - MENU_ADD_ITEM(STR_RESET_TIMER1); - MENU_ADD_ITEM(STR_RESET_TIMER2); -#if defined(FRSKY) - MENU_ADD_ITEM(STR_RESET_TELEMETRY); -#endif - MENU_ADD_ITEM(STR_RESET_FLIGHT); -#endif - - MENU_ADD_ITEM(STR_STATISTICS); -#if defined(CPUARM) - MENU_ADD_ITEM(STR_ABOUT_US); -#endif - menuHandler = onMainViewMenu; - break; -#endif - -#if MENUS_LOCK != 2/*no menus*/ - case EVT_KEY_LONG(KEY_MENU):// go to last menu - pushMenu(lastPopMenu()); - killEvents(event); - break; - - CASE_EVT_ROTARY_BREAK - case EVT_KEY_MODEL_MENU: - pushMenu(menuModelSelect); - killEvents(event); - break; - - CASE_EVT_ROTARY_LONG - case EVT_KEY_GENERAL_MENU: - pushMenu(menuGeneralSetup); - killEvents(event); - break; -#endif - - case EVT_KEY_BREAK(KEY_UP): - case EVT_KEY_BREAK(KEY_DOWN): - g_eeGeneral.view = (event == EVT_KEY_BREAK(KEY_UP) ? (view_base == VIEW_COUNT-1 ? 0 : view_base+1) : (view_base == 0 ? VIEW_COUNT-1 : view_base-1)); - eeDirty(EE_GENERAL); - AUDIO_KEYPAD_UP(); - break; - - case EVT_KEY_STATISTICS: - chainMenu(menuStatisticsView); - killEvents(event); - break; - - case EVT_KEY_TELEMETRY: -#if defined(FRSKY) - if (!IS_FAI_ENABLED()) - chainMenu(menuTelemetryFrsky); -#elif defined(JETI) - JETI_EnableRXD(); // enable JETI-Telemetry reception - chainMenu(menuTelemetryJeti); -#elif defined(ARDUPILOT) - ARDUPILOT_EnableRXD(); // enable ArduPilot-Telemetry reception - chainMenu(menuTelemetryArduPilot); -#elif defined(NMEA) - NMEA_EnableRXD(); // enable NMEA-Telemetry reception - chainMenu(menuTelemetryNMEA); -#elif defined(MAVLINK) - chainMenu(menuTelemetryMavlink); -#else - chainMenu(menuStatisticsDebug); -#endif - killEvents(event); - break; - - case EVT_KEY_FIRST(KEY_EXIT): -#if defined(GVARS) && !defined(PCBSTD) - if (s_gvar_timer > 0) { - s_gvar_timer = 0; - } -#endif - if (view == VIEW_TIMER2) { - timerReset(1); - } - AUDIO_KEYPAD_UP(); - break; - -#if !defined(NAVIGATION_MENUS) - case EVT_KEY_LONG(KEY_EXIT): - flightReset(); - AUDIO_KEYPAD_UP(); - break; -#endif - } - - { - // Flight Phase Name - uint8_t phase = mixerCurrentFlightMode; - lcd_putsnAtt(PHASE_X, PHASE_Y, g_model.flightModeData[phase].name, sizeof(g_model.flightModeData[phase].name), ZCHAR|PHASE_FLAGS); - - // Model Name - putsModelName(MODELNAME_X, MODELNAME_Y, g_model.header.name, g_eeGeneral.currModel, BIGSIZE); - - // Main Voltage (or alarm if any) - displayVoltageOrAlarm(); - - // Timers - displayTimers(); - - // Trims sliders - displayTrims(phase); - } - - if (view_base < VIEW_INPUTS) { - // scroll bar - lcd_hlineStip(38, 34, 54, DOTTED); -#if defined(PCBSKY9X) - lcd_hline(38 + (g_eeGeneral.view / ALTERNATE_VIEW) * 13, 34, 13, SOLID); -#else - lcd_hline((g_eeGeneral.view & ALTERNATE_VIEW) ? 64 : 38, 34, 26, SOLID); -#endif - - for (uint8_t i=0; i<8; i++) { - uint8_t x0,y0; -#if defined(PCBSKY9X) - uint8_t chan = 8*(g_eeGeneral.view / ALTERNATE_VIEW) + i; -#else - uint8_t chan = (g_eeGeneral.view & ALTERNATE_VIEW) ? 8+i : i; -#endif - - int16_t val = channelOutputs[chan]; - - switch(view_base) - { - case VIEW_OUTPUTS_VALUES: - x0 = (i%4*9+3)*FW/2; - y0 = i/4*FH+40; -#if defined(PPM_UNIT_US) - lcd_outdezAtt(x0+4*FW , y0, PPM_CH_CENTER(chan)+val/2, 0); -#elif defined(PPM_UNIT_PERCENT_PREC1) - lcd_outdezAtt(x0+4*FW , y0, calcRESXto1000(val), PREC1); -#else - lcd_outdezAtt(x0+4*FW , y0, calcRESXto1000(val)/10, 0); // G: Don't like the decimal part* -#endif - break; - - case VIEW_OUTPUTS_BARS: -#define WBAR2 (50/2) - x0 = i<4 ? LCD_W/4+2 : LCD_W*3/4-2; - y0 = 38+(i%4)*5; - - uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; - int8_t len = (abs(val) * WBAR2 + lim/2) / lim; - - if(len>WBAR2) len = WBAR2; // prevent bars from going over the end - comment for debugging - lcd_hlineStip(x0-WBAR2, y0, WBAR2*2+1, DOTTED); - lcd_vline(x0,y0-2,5); - if (val>0) - x0+=1; - else - x0-=len; - lcd_hline(x0,y0+1,len); - lcd_hline(x0,y0-1,len); - break; - } - } - } - else if (view_base == VIEW_INPUTS) { - if (view == VIEW_INPUTS) { - // Sticks + Pots - doMainScreenGraphics(); - - // Switches - for (uint8_t i=SWSRC_THR; i<=SWSRC_TRN; i++) { - int8_t sw = (i == SWSRC_TRN ? (switchState(SW_ID0) ? SWSRC_ID0 : (switchState(SW_ID1) ? SWSRC_ID1 : SWSRC_ID2)) : i); - uint8_t x = 2*FW-2, y = i*FH+1; - if (i>=SWSRC_AIL) { - x = 17*FW-1; - y -= 3*FH; - } - putsSwitches(x, y, sw, getSwitch(i) ? INVERS : 0); - } - } - else { -#if defined(PCBMEGA2560) && defined(ROTARY_ENCODERS) - for (uint8_t i=0; i 2 -#define V_BAR_W 5 - V_BAR(LCD_W/2-8+V_BAR_W*i, LCD_H-8, len) -#else -#define V_BAR_W 5 - V_BAR(LCD_W/2-3+V_BAR_W*i, LCD_H-8, len) -#endif - } -#endif // PCBGRUVIN9X && ROTARY_ENCODERS - - // Logical Switches -#if defined(PCBSKY9X) - for (uint8_t i=0; i2 ? 3*FW+2 : (3*FW-1)) + (i/3>2 ? 2*FW : 0), 4*FH+1 + (i%3)*FH, SWSRC_SW1+i, getSwitch(SWSRC_SW1+i) ? INVERS : 0); -#elif !defined(PCBSTD) - for (uint8_t i=0; i= 0 ? TMR2_LBL_COL : TMR2_LBL_COL-7, FH*6, g_model.timers[1].mode); - // lcd_outdezNAtt(33+11*FW, FH*6, s_timerVal_10ms[1], LEADING0, 2); // 1/100s - } - - // And ! in case of unexpected shutdown - if (unexpectedShutdown) { - lcd_putcAtt(REBOOT_X, 0*FH, '!', INVERS); - } - -#if defined(GVARS) && !defined(PCBSTD) - if (s_gvar_timer > 0) { - s_gvar_timer--; - s_warning = STR_GLOBAL_VAR; - displayBox(); - lcd_putsnAtt(16, 5*FH, g_model.gvars[s_gvar_last].name, LEN_GVAR_NAME, ZCHAR); - lcd_putsAtt(16+7*FW, 5*FH, PSTR("[\010]"), BOLD); - lcd_outdezAtt(16+7*FW+4*FW+FW/2, 5*FH, GVAR_VALUE(s_gvar_last, getGVarFlightPhase(mixerCurrentFlightMode, s_gvar_last)), BOLD); - s_warning = NULL; - } -#endif - -#if defined(DSM2) - if (moduleFlag[0] == MODULE_BIND) { - // Issue 98 - lcd_putsAtt(15*FW, 0, PSTR("BIND"), 0); - } -#endif -} +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../../opentx.h" +#include "../../timers.h" + +#define BIGSIZE DBLSIZE +#define LBOX_CENTERX (LCD_W/4 + 10) +#define RBOX_CENTERX (3*LCD_W/4 - 10) +#define MODELNAME_X (2*FW-2) +#define MODELNAME_Y (0) +#define PHASE_X (6*FW) +#define PHASE_Y (2*FH) +#define PHASE_FLAGS 0 +#define VBATT_X (6*FW) +#define VBATT_Y (2*FH) +#define VBATTUNIT_X (VBATT_X-1) +#define VBATTUNIT_Y (3*FH) +#define REBOOT_X (20*FW-3) +#define VSWITCH_X(i) (16 + 3*i) +#define VSWITCH_Y (LCD_H-8) +#define BAR_HEIGHT (BOX_WIDTH-1l) +#define TRIM_LH_X (LCD_W*1/4+2) +#define TRIM_LV_X 3 +#define TRIM_RV_X (LCD_W-4) +#define TRIM_RH_X (LCD_W*3/4-2) + +#define TRIM_LEN 27 + +void drawPotsBars() +{ + // Optimization by Mike Blandford + uint8_t x, i, len ; // declare temporary variables + for (x=LCD_W/2-5, i=NUM_STICKS; i TRIM_MAX) { + exttrim = true; + } +#endif + if (val < -(TRIM_LEN+1)*4) { + val = -(TRIM_LEN+1); + } + else if (val > (TRIM_LEN+1)*4) { + val = TRIM_LEN+1; + } + else { + val /= 4; + } + + if (vert[i]) { + ym = 31; + lcd_vline(xm, ym-TRIM_LEN, TRIM_LEN*2); + if (i!=2 || !g_model.thrTrim) { + lcd_vline(xm-1, ym-1, 3); + lcd_vline(xm+1, ym-1, 3); + } + ym -= val; +#if !defined(CPUM64) || !defined(FRSKY) + drawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE); + if (dir >= 0) { + lcd_hline(xm-1, ym-1, 3); + } + if (dir <= 0) { + lcd_hline(xm-1, ym+1, 3); + } + if (exttrim) { + lcd_hline(xm-1, ym, 3); + } +#endif + } + else { + ym = 60; + lcd_hline(xm-TRIM_LEN, ym, TRIM_LEN*2); + lcd_hline(xm-1, ym-1, 3); + lcd_hline(xm-1, ym+1, 3); + xm += val; +#if !defined(CPUM64) || !defined(FRSKY) + drawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE); + if (dir >= 0) { + lcd_vline(xm+1, ym-1, 3); + } + if (dir <= 0) { + lcd_vline(xm-1, ym-1, 3); + } + if (exttrim) { + lcd_vline(xm, ym-1, 3); + } +#endif + } + lcd_square(xm-3, ym-3, 7, att); + } +} + + +void displayTimers() +{ +#if defined(TRANSLATIONS_CZ) + #define MAINTMR_LBL_COL (9*FW-FW/2-1) +#else + #define MAINTMR_LBL_COL (9*FW-FW/2+3) +#endif + // Main timer + if (g_model.timers[0].mode) { + TimerState & timerState = timersStates[0]; + uint8_t att = DBLSIZE | (timerState.val<0 ? BLINK|INVERS : 0); + putsTimer(12*FW+2+10*FWNUM-4, FH*2, timerState.val, att, att); + uint8_t xLabel = (timerState.val >= 0 ? MAINTMR_LBL_COL : MAINTMR_LBL_COL-7); +#if defined(CPUARM) + uint8_t len = zlen(g_model.timers[0].name, LEN_TIMER_NAME); + if (len > 0) { + xLabel += (LEN_TIMER_NAME-len)*FW; + lcd_putsnAtt(xLabel, FH*3, g_model.timers[0].name, len, ZCHAR); + } + else { + putsTimerMode(xLabel, FH*3, g_model.timers[0].mode); + } +#else + putsTimerMode(xLabel, FH*3, g_model.timers[0].mode); +#endif + } +} + +void displayBattVoltage() +{ +#if defined(BATTGRAPH) + putsVBat(VBATT_X-8, VBATT_Y+1, 0); + drawFilledRect(VBATT_X-25, VBATT_Y+9, 22, 5); + lcd_vline(VBATT_X-3, VBATT_Y+10, 3); + uint8_t count = limit(2, 20 * (g_vbat100mV - g_eeGeneral.vBatMin - 90) / (30 + g_eeGeneral.vBatMax - g_eeGeneral.vBatMin), 20); + for (uint8_t i=0; i g_eeGeneral.vBatWarn || BLINK_ON_PHASE) + drawFilledRect(VBATT_X-26, VBATT_Y, 25, 15); +#else + LcdFlags att = (g_vbat100mV <= g_eeGeneral.vBatWarn ? BLINK|INVERS : 0) | BIGSIZE; + putsVBat(VBATT_X-1, VBATT_Y, att|NO_UNIT); + lcd_putc(VBATT_X, VBATTUNIT_Y, 'V'); +#endif +} + +#if defined(PCBSKY9X) +void displayVoltageOrAlarm() +{ + if (g_vbat100mV > g_eeGeneral.vBatWarn && g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { + putsValueWithUnit(6*FW-1, 2*FH, getTemperature(), UNIT_TEMPERATURE, BLINK|INVERS|DBLSIZE); + } + else if (g_vbat100mV > g_eeGeneral.vBatWarn && g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.currentCalib)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { + putsValueWithUnit(7*FW-1, 2*FH, (g_eeGeneral.mAhUsed + Current_used*(488 + g_eeGeneral.currentCalib)/8192/36)/10, UNIT_MAH, BLINK|INVERS|DBLSIZE); + } + else { + displayBattVoltage(); + } +} +#else + #define displayVoltageOrAlarm() displayBattVoltage() +#endif + +#define EVT_KEY_MODEL_MENU EVT_KEY_LONG(KEY_RIGHT) +#define EVT_KEY_GENERAL_MENU EVT_KEY_LONG(KEY_LEFT) +#define EVT_KEY_TELEMETRY EVT_KEY_LONG(KEY_DOWN) +#define EVT_KEY_STATISTICS EVT_KEY_LONG(KEY_UP) +#define EVT_KEY_CONTEXT_MENU EVT_KEY_BREAK(KEY_MENU) + +#if defined(NAVIGATION_MENUS) +void onMainViewMenu(const char *result) +{ + if (result == STR_RESET_TIMER1) { + timerReset(0); + } + else if (result == STR_RESET_TIMER2) { + timerReset(1); + } +#if defined(CPUARM) + else if (result == STR_RESET_TIMER3) { + timerReset(2); + } + else if (result == STR_VIEW_NOTES) { + pushModelNotes(); + } + else if (result == STR_RESET_SUBMENU) { + MENU_ADD_ITEM(STR_RESET_FLIGHT); + MENU_ADD_ITEM(STR_RESET_TIMER1); + MENU_ADD_ITEM(STR_RESET_TIMER2); + MENU_ADD_ITEM(STR_RESET_TIMER3); +#if defined(FRSKY) + MENU_ADD_ITEM(STR_RESET_TELEMETRY); +#endif + } +#endif +#if defined(FRSKY) + else if (result == STR_RESET_TELEMETRY) { + telemetryReset(); + } +#endif + else if (result == STR_RESET_FLIGHT) { + flightReset(); + } + else if (result == STR_STATISTICS) { + chainMenu(menuStatisticsView); + } +#if defined(CPUARM) + else if (result == STR_ABOUT_US) { + chainMenu(menuAboutView); + } +#endif +} +#endif + +void menuMainView(uint8_t event) +{ + STICK_SCROLL_DISABLE(); + + uint8_t view = g_eeGeneral.view; + uint8_t view_base = view & 0x0f; + + switch(event) { + + case EVT_ENTRY: + killEvents(KEY_EXIT); + killEvents(KEY_UP); + killEvents(KEY_DOWN); + break; + + /* TODO if timer2 is OFF, it's possible to use this timer2 as in er9x... + case EVT_KEY_BREAK(KEY_MENU): + if (view_base == VIEW_TIMER2) { + Timer2_running = !Timer2_running; + AUDIO_KEYPAD_UP(); + } + break; + */ + + case EVT_KEY_BREAK(KEY_RIGHT): + case EVT_KEY_BREAK(KEY_LEFT): + if (view_base <= VIEW_INPUTS) { +#if defined(PCBSKY9X) + if (view_base == VIEW_INPUTS) + g_eeGeneral.view ^= ALTERNATE_VIEW; + else + g_eeGeneral.view = (g_eeGeneral.view + (4*ALTERNATE_VIEW) + ((event==EVT_KEY_BREAK(KEY_LEFT)) ? -ALTERNATE_VIEW : ALTERNATE_VIEW)) % (4*ALTERNATE_VIEW); +#else + g_eeGeneral.view ^= ALTERNATE_VIEW; +#endif + eeDirty(EE_GENERAL); + AUDIO_KEYPAD_UP(); + } + break; + +#if defined(NAVIGATION_MENUS) + case EVT_KEY_CONTEXT_MENU: + killEvents(event); + +#if defined(CPUARM) + if (modelHasNotes()) { + MENU_ADD_ITEM(STR_VIEW_NOTES); + } +#endif + +#if defined(CPUARM) + MENU_ADD_ITEM(STR_RESET_SUBMENU); +#else + MENU_ADD_ITEM(STR_RESET_TIMER1); + MENU_ADD_ITEM(STR_RESET_TIMER2); +#if defined(FRSKY) + MENU_ADD_ITEM(STR_RESET_TELEMETRY); +#endif + MENU_ADD_ITEM(STR_RESET_FLIGHT); +#endif + + MENU_ADD_ITEM(STR_STATISTICS); +#if defined(CPUARM) + MENU_ADD_ITEM(STR_ABOUT_US); +#endif + menuHandler = onMainViewMenu; + break; +#endif + +#if MENUS_LOCK != 2/*no menus*/ + case EVT_KEY_LONG(KEY_MENU):// go to last menu + pushMenu(lastPopMenu()); + killEvents(event); + break; + + CASE_EVT_ROTARY_BREAK + case EVT_KEY_MODEL_MENU: + pushMenu(menuModelSelect); + killEvents(event); + break; + + CASE_EVT_ROTARY_LONG + case EVT_KEY_GENERAL_MENU: + pushMenu(menuGeneralSetup); + killEvents(event); + break; +#endif + + case EVT_KEY_BREAK(KEY_UP): + case EVT_KEY_BREAK(KEY_DOWN): + g_eeGeneral.view = (event == EVT_KEY_BREAK(KEY_UP) ? (view_base == VIEW_COUNT-1 ? 0 : view_base+1) : (view_base == 0 ? VIEW_COUNT-1 : view_base-1)); + eeDirty(EE_GENERAL); + AUDIO_KEYPAD_UP(); + break; + + case EVT_KEY_STATISTICS: + chainMenu(menuStatisticsView); + killEvents(event); + break; + + case EVT_KEY_TELEMETRY: +#if defined(FRSKY) + if (!IS_FAI_ENABLED()) + chainMenu(menuTelemetryFrsky); +#elif defined(JETI) + JETI_EnableRXD(); // enable JETI-Telemetry reception + chainMenu(menuTelemetryJeti); +#elif defined(ARDUPILOT) + ARDUPILOT_EnableRXD(); // enable ArduPilot-Telemetry reception + chainMenu(menuTelemetryArduPilot); +#elif defined(NMEA) + NMEA_EnableRXD(); // enable NMEA-Telemetry reception + chainMenu(menuTelemetryNMEA); +#elif defined(MAVLINK) + chainMenu(menuTelemetryMavlink); +#else + chainMenu(menuStatisticsDebug); +#endif + killEvents(event); + break; + + case EVT_KEY_FIRST(KEY_EXIT): +#if defined(GVARS) && !defined(PCBSTD) + if (s_gvar_timer > 0) { + s_gvar_timer = 0; + } +#endif + if (view == VIEW_TIMER2) { + timerReset(1); + } + AUDIO_KEYPAD_UP(); + break; + +#if !defined(NAVIGATION_MENUS) + case EVT_KEY_LONG(KEY_EXIT): + flightReset(); + AUDIO_KEYPAD_UP(); + break; +#endif + } + + { + // Flight Phase Name + uint8_t phase = mixerCurrentFlightMode; + lcd_putsnAtt(PHASE_X, PHASE_Y, g_model.flightModeData[phase].name, sizeof(g_model.flightModeData[phase].name), ZCHAR|PHASE_FLAGS); + + // Model Name + putsModelName(MODELNAME_X, MODELNAME_Y, g_model.header.name, g_eeGeneral.currModel, BIGSIZE); + + // Main Voltage (or alarm if any) + displayVoltageOrAlarm(); + + // Timers + displayTimers(); + + // Trims sliders + displayTrims(phase); + } + + if (view_base < VIEW_INPUTS) { + // scroll bar + lcd_hlineStip(38, 34, 54, DOTTED); +#if defined(PCBSKY9X) + lcd_hline(38 + (g_eeGeneral.view / ALTERNATE_VIEW) * 13, 34, 13, SOLID); +#else + lcd_hline((g_eeGeneral.view & ALTERNATE_VIEW) ? 64 : 38, 34, 26, SOLID); +#endif + + for (uint8_t i=0; i<8; i++) { + uint8_t x0,y0; +#if defined(PCBSKY9X) + uint8_t chan = 8*(g_eeGeneral.view / ALTERNATE_VIEW) + i; +#else + uint8_t chan = (g_eeGeneral.view & ALTERNATE_VIEW) ? 8+i : i; +#endif + + int16_t val = channelOutputs[chan]; + + switch(view_base) + { + case VIEW_OUTPUTS_VALUES: + x0 = (i%4*9+3)*FW/2; + y0 = i/4*FH+40; +#if defined(PPM_UNIT_US) + lcd_outdezAtt(x0+4*FW , y0, PPM_CH_CENTER(chan)+val/2, 0); +#elif defined(PPM_UNIT_PERCENT_PREC1) + lcd_outdezAtt(x0+4*FW , y0, calcRESXto1000(val), PREC1); +#else + lcd_outdezAtt(x0+4*FW , y0, calcRESXto1000(val)/10, 0); // G: Don't like the decimal part* +#endif + break; + + case VIEW_OUTPUTS_BARS: +#define WBAR2 (50/2) + x0 = i<4 ? LCD_W/4+2 : LCD_W*3/4-2; + y0 = 38+(i%4)*5; + + uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; + int8_t len = (abs(val) * WBAR2 + lim/2) / lim; + + if(len>WBAR2) len = WBAR2; // prevent bars from going over the end - comment for debugging + lcd_hlineStip(x0-WBAR2, y0, WBAR2*2+1, DOTTED); + lcd_vline(x0,y0-2,5); + if (val>0) + x0+=1; + else + x0-=len; + lcd_hline(x0,y0+1,len); + lcd_hline(x0,y0-1,len); + break; + } + } + } + else if (view_base == VIEW_INPUTS) { + if (view == VIEW_INPUTS) { + // Sticks + Pots + doMainScreenGraphics(); + + // Switches + for (uint8_t i=SWSRC_THR; i<=SWSRC_TRN; i++) { + int8_t sw = (i == SWSRC_TRN ? (switchState(SW_ID0) ? SWSRC_ID0 : (switchState(SW_ID1) ? SWSRC_ID1 : SWSRC_ID2)) : i); + uint8_t x = 2*FW-2, y = i*FH+1; + if (i>=SWSRC_AIL) { + x = 17*FW-1; + y -= 3*FH; + } + putsSwitches(x, y, sw, getSwitch(i) ? INVERS : 0); + } + } + else { +#if defined(PCBMEGA2560) && defined(ROTARY_ENCODERS) + for (uint8_t i=0; i 2 +#define V_BAR_W 5 + V_BAR(LCD_W/2-8+V_BAR_W*i, LCD_H-8, len) +#else +#define V_BAR_W 5 + V_BAR(LCD_W/2-3+V_BAR_W*i, LCD_H-8, len) +#endif + } +#endif // PCBGRUVIN9X && ROTARY_ENCODERS + + // Logical Switches +#if defined(PCBSKY9X) + for (uint8_t i=0; i2 ? 3*FW+2 : (3*FW-1)) + (i/3>2 ? 2*FW : 0), 4*FH+1 + (i%3)*FH, SWSRC_SW1+i, getSwitch(SWSRC_SW1+i) ? INVERS : 0); +#elif !defined(PCBSTD) + for (uint8_t i=0; i= 0 ? TMR2_LBL_COL : TMR2_LBL_COL-7, FH*6, g_model.timers[1].mode); + // lcd_outdezNAtt(33+11*FW, FH*6, s_timerVal_10ms[1], LEADING0, 2); // 1/100s + } + + // And ! in case of unexpected shutdown + if (unexpectedShutdown) { + lcd_putcAtt(REBOOT_X, 0*FH, '!', INVERS); + } + +#if defined(GVARS) && !defined(PCBSTD) + if (s_gvar_timer > 0) { + s_gvar_timer--; + s_warning = STR_GLOBAL_VAR; + displayBox(); + lcd_putsnAtt(16, 5*FH, g_model.gvars[s_gvar_last].name, LEN_GVAR_NAME, ZCHAR); + lcd_putsAtt(16+7*FW, 5*FH, PSTR("[\010]"), BOLD); + lcd_outdezAtt(16+7*FW+4*FW+FW/2, 5*FH, GVAR_VALUE(s_gvar_last, getGVarFlightPhase(mixerCurrentFlightMode, s_gvar_last)), BOLD); + s_warning = NULL; + } +#endif + +#if defined(DSM2) + if (moduleFlag[0] == MODULE_BIND) { + // Issue 98 + lcd_putsAtt(15*FW, 0, PSTR("BIND"), 0); + } +#endif +} diff --git a/radio/src/gui/9X/view_statistics.cpp b/radio/src/gui/9X/view_statistics.cpp index f7b80cfd4..b48ccbe65 100755 --- a/radio/src/gui/9X/view_statistics.cpp +++ b/radio/src/gui/9X/view_statistics.cpp @@ -1,224 +1,225 @@ -/* - * Authors (alphabetical order) - * - Andre Bernet - * - Andreas Weitl - * - Bertrand Songis - * - Bryan J. Rentoul (Gruvin) - * - Cameron Weeks - * - Erez Raviv - * - Gabriel Birkus - * - Jean-Pierre Parisy - * - Karl Szmutny - * - Michael Blandford - * - Michal Hlavinka - * - Pat Mackenzie - * - Philip Moss - * - Rob Thomson - * - Romolo Manfredini - * - Thomas Husterer - * - * opentx is based on code named - * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, - * er9x by Erez Raviv: http://code.google.com/p/er9x/, - * and the original (and ongoing) project by - * Thomas Husterer, th9x: http://code.google.com/p/th9x/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "../../opentx.h" - -void menuStatisticsView(uint8_t event) -{ - TITLE(STR_MENUSTAT); - - switch(event) - { - case EVT_KEY_FIRST(KEY_UP): - chainMenu(menuStatisticsDebug); - break; - -#if defined(CPUARM) - case EVT_KEY_LONG(KEY_MENU): - g_eeGeneral.globalTimer = 0; - eeDirty(EE_GENERAL); - sessionTimer = 0; - break; -#endif - case EVT_KEY_FIRST(KEY_EXIT): - chainMenu(menuMainView); - break; - } - - lcd_puts( 1*FW, FH*0, STR_TOTTM1TM2THRTHP); - putsTimer( 5*FW+5*FWNUM+1, FH*1, timersStates[0].val, 0, 0); - putsTimer( 12*FW+5*FWNUM+1, FH*1, timersStates[1].val, 0, 0); - - putsTimer( 5*FW+5*FWNUM+1, FH*2, s_timeCumThr, 0, 0); - putsTimer( 12*FW+5*FWNUM+1, FH*2, s_timeCum16ThrP/16, 0, 0); - - putsTimer( 12*FW+5*FWNUM+1, FH*0, sessionTimer, 0, 0); - -#if defined(CPUARM) - putsTimer(21*FW+5*FWNUM+1, 0*FH, g_eeGeneral.globalTimer + sessionTimer, TIMEHOUR, 0); -#endif - -#if defined(THRTRACE) - coord_t traceRd = (s_traceCnt < 0 ? s_traceWr : 0); - const coord_t x = 5; - const coord_t y = 60; - lcd_hline(x-3, y, MAXTRACE+3+3); - lcd_vline(x, y-32, 32+3); - - for (coord_t i=0; i=MAXTRACE) traceRd = 0; - if (traceRd==s_traceWr) break; - } -#endif -} - -#if defined(PCBSKY9X) - #define MENU_DEBUG_COL1_OFS (11*FW-3) - #define MENU_DEBUG_COL2_OFS (17*FW) - #define MENU_DEBUG_Y_CURRENT (1*FH) - #define MENU_DEBUG_Y_MAH (2*FH) - #define MENU_DEBUG_Y_CPU_TEMP (3*FH) - #define MENU_DEBUG_Y_COPROC (4*FH) - #define MENU_DEBUG_Y_MIXMAX (5*FH) - #define MENU_DEBUG_Y_RTOS (6*FH) -#else - #define MENU_DEBUG_COL1_OFS (14*FW) -#endif - -void menuStatisticsDebug(uint8_t event) -{ - TITLE(STR_MENUDEBUG); - - switch(event) - { -#if defined(CPUARM) - case EVT_KEY_LONG(KEY_ENTER): - g_eeGeneral.mAhUsed = 0; - g_eeGeneral.globalTimer = 0; - eeDirty(EE_GENERAL); -#if defined(PCBSKY9X) - Current_used = 0; -#endif - sessionTimer = 0; - killEvents(event); - AUDIO_KEYPAD_UP(); - break; -#endif - case EVT_KEY_FIRST(KEY_ENTER): -#if !defined(CPUARM) - g_tmr1Latency_min = 0xff; - g_tmr1Latency_max = 0; -#endif - maxMixerDuration = 0; - AUDIO_KEYPAD_UP(); - break; - -#if defined(DEBUG_TRACE_BUFFER) - case EVT_KEY_FIRST(KEY_UP): - pushMenu(menuTraceBuffer); - return; -#endif - - case EVT_KEY_FIRST(KEY_DOWN): - chainMenu(menuStatisticsView); - break; - case EVT_KEY_FIRST(KEY_EXIT): - chainMenu(menuMainView); - break; - } - -#if defined(PCBSKY9X) - if ((ResetReason&RSTC_SR_RSTTYP) == (2<<8)) { - lcd_puts(LCD_W-8*FW, 0*FH, "WATCHDOG"); - } - else if (unexpectedShutdown) { - lcd_puts(LCD_W-13*FW, 0*FH, "UNEXP.SHTDOWN"); - } -#endif - -#if defined(PCBSKY9X) && !defined(REVA) - // current - lcd_putsLeft(MENU_DEBUG_Y_CURRENT, STR_CPU_CURRENT); - putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_CURRENT, getCurrent(), UNIT_MILLIAMPS, LEFT); - uint32_t current_scale = 488 + g_eeGeneral.currentCalib; - lcd_putc(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_CURRENT, '>'); - putsValueWithUnit(MENU_DEBUG_COL2_OFS+FW+1, MENU_DEBUG_Y_CURRENT, Current_max*10*current_scale/8192, UNIT_RAW, LEFT); - // consumption - lcd_putsLeft(MENU_DEBUG_Y_MAH, STR_CPU_MAH); - putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_MAH, g_eeGeneral.mAhUsed + Current_used*current_scale/8192/36, UNIT_MAH, LEFT|PREC1); -#endif - -#if defined(PCBSKY9X) - lcd_putsLeft(MENU_DEBUG_Y_CPU_TEMP, STR_CPU_TEMP); - putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_CPU_TEMP, getTemperature(), UNIT_TEMPERATURE, LEFT); - lcd_putc(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_CPU_TEMP, '>'); - putsValueWithUnit(MENU_DEBUG_COL2_OFS+FW+1, MENU_DEBUG_Y_CPU_TEMP, maxTemperature+g_eeGeneral.temperatureCalib, UNIT_TEMPERATURE, LEFT); -#endif - -#if defined(COPROCESSOR) - lcd_putsLeft(MENU_DEBUG_Y_COPROC, STR_COPROC_TEMP); - - if (Coproc_read==0) { - lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Co Proc NACK"),INVERS); - } - else if (Coproc_read==0x81) { - lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Inst.TinyApp"),INVERS); - } - else if (Coproc_read<3) { - lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Upgr.TinyApp"),INVERS); - } - else { - putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, Coproc_temp, UNIT_TEMPERATURE, LEFT); - putsValueWithUnit(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_COPROC, Coproc_maxtemp, UNIT_TEMPERATURE, LEFT); - } -#endif - -#if defined(CPUARM) - lcd_putsLeft(MENU_DEBUG_Y_MIXMAX, STR_TMIXMAXMS); - lcd_outdezAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_MIXMAX, DURATION_MS_PREC2(maxMixerDuration), PREC2|LEFT); - lcd_puts(lcdLastPos, MENU_DEBUG_Y_MIXMAX, "ms"); -#endif - -#if defined(PCBSKY9X) - lcd_putsLeft(MENU_DEBUG_Y_RTOS, STR_FREESTACKMINB); - lcd_outdezAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_RTOS, stack_free(0), UNSIGN|LEFT); - lcd_puts(lcdLastPos, MENU_DEBUG_Y_RTOS, "/"); - lcd_outdezAtt(lcdLastPos, MENU_DEBUG_Y_RTOS, stack_free(1), UNSIGN|LEFT); - lcd_puts(lcdLastPos, MENU_DEBUG_Y_RTOS, "/"); - lcd_outdezAtt(lcdLastPos, MENU_DEBUG_Y_RTOS, stack_free(2), UNSIGN|LEFT); -#endif - -#if !defined(CPUARM) - lcd_putsLeft(1*FH, STR_TMR1LATMAXUS); - lcd_outdez8(MENU_DEBUG_COL1_OFS , 1*FH, g_tmr1Latency_max/2 ); - lcd_putsLeft(2*FH, STR_TMR1LATMINUS); - lcd_outdez8(MENU_DEBUG_COL1_OFS , 2*FH, g_tmr1Latency_min/2 ); - lcd_putsLeft(3*FH, STR_TMR1JITTERUS); - lcd_outdez8(MENU_DEBUG_COL1_OFS , 3*FH, (g_tmr1Latency_max - g_tmr1Latency_min) /2 ); - lcd_putsLeft(4*FH, STR_TMIXMAXMS); - lcd_outdezAtt(MENU_DEBUG_COL1_OFS, 4*FH, DURATION_MS_PREC2(maxMixerDuration), PREC2); - lcd_putsLeft(5*FH, STR_FREESTACKMINB); - lcd_outdezAtt(14*FW, 5*FH, stack_free(), UNSIGN) ; -#endif - - lcd_puts(3*FW, 7*FH+1, STR_MENUTORESET); - lcd_status_line(); -} +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "../../opentx.h" +#include "../../timers.h" + +void menuStatisticsView(uint8_t event) +{ + TITLE(STR_MENUSTAT); + + switch(event) + { + case EVT_KEY_FIRST(KEY_UP): + chainMenu(menuStatisticsDebug); + break; + +#if defined(CPUARM) + case EVT_KEY_LONG(KEY_MENU): + g_eeGeneral.globalTimer = 0; + eeDirty(EE_GENERAL); + sessionTimer = 0; + break; +#endif + case EVT_KEY_FIRST(KEY_EXIT): + chainMenu(menuMainView); + break; + } + + lcd_puts( 1*FW, FH*0, STR_TOTTM1TM2THRTHP); + putsTimer( 5*FW+5*FWNUM+1, FH*1, timersStates[0].val, 0, 0); + putsTimer( 12*FW+5*FWNUM+1, FH*1, timersStates[1].val, 0, 0); + + putsTimer( 5*FW+5*FWNUM+1, FH*2, s_timeCumThr, 0, 0); + putsTimer( 12*FW+5*FWNUM+1, FH*2, s_timeCum16ThrP/16, 0, 0); + + putsTimer( 12*FW+5*FWNUM+1, FH*0, sessionTimer, 0, 0); + +#if defined(CPUARM) + putsTimer(21*FW+5*FWNUM+1, 0*FH, g_eeGeneral.globalTimer + sessionTimer, TIMEHOUR, 0); +#endif + +#if defined(THRTRACE) + coord_t traceRd = (s_traceCnt < 0 ? s_traceWr : 0); + const coord_t x = 5; + const coord_t y = 60; + lcd_hline(x-3, y, MAXTRACE+3+3); + lcd_vline(x, y-32, 32+3); + + for (coord_t i=0; i=MAXTRACE) traceRd = 0; + if (traceRd==s_traceWr) break; + } +#endif +} + +#if defined(PCBSKY9X) + #define MENU_DEBUG_COL1_OFS (11*FW-3) + #define MENU_DEBUG_COL2_OFS (17*FW) + #define MENU_DEBUG_Y_CURRENT (1*FH) + #define MENU_DEBUG_Y_MAH (2*FH) + #define MENU_DEBUG_Y_CPU_TEMP (3*FH) + #define MENU_DEBUG_Y_COPROC (4*FH) + #define MENU_DEBUG_Y_MIXMAX (5*FH) + #define MENU_DEBUG_Y_RTOS (6*FH) +#else + #define MENU_DEBUG_COL1_OFS (14*FW) +#endif + +void menuStatisticsDebug(uint8_t event) +{ + TITLE(STR_MENUDEBUG); + + switch(event) + { +#if defined(CPUARM) + case EVT_KEY_LONG(KEY_ENTER): + g_eeGeneral.mAhUsed = 0; + g_eeGeneral.globalTimer = 0; + eeDirty(EE_GENERAL); +#if defined(PCBSKY9X) + Current_used = 0; +#endif + sessionTimer = 0; + killEvents(event); + AUDIO_KEYPAD_UP(); + break; +#endif + case EVT_KEY_FIRST(KEY_ENTER): +#if !defined(CPUARM) + g_tmr1Latency_min = 0xff; + g_tmr1Latency_max = 0; +#endif + maxMixerDuration = 0; + AUDIO_KEYPAD_UP(); + break; + +#if defined(DEBUG_TRACE_BUFFER) + case EVT_KEY_FIRST(KEY_UP): + pushMenu(menuTraceBuffer); + return; +#endif + + case EVT_KEY_FIRST(KEY_DOWN): + chainMenu(menuStatisticsView); + break; + case EVT_KEY_FIRST(KEY_EXIT): + chainMenu(menuMainView); + break; + } + +#if defined(PCBSKY9X) + if ((ResetReason&RSTC_SR_RSTTYP) == (2<<8)) { + lcd_puts(LCD_W-8*FW, 0*FH, "WATCHDOG"); + } + else if (unexpectedShutdown) { + lcd_puts(LCD_W-13*FW, 0*FH, "UNEXP.SHTDOWN"); + } +#endif + +#if defined(PCBSKY9X) && !defined(REVA) + // current + lcd_putsLeft(MENU_DEBUG_Y_CURRENT, STR_CPU_CURRENT); + putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_CURRENT, getCurrent(), UNIT_MILLIAMPS, LEFT); + uint32_t current_scale = 488 + g_eeGeneral.currentCalib; + lcd_putc(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_CURRENT, '>'); + putsValueWithUnit(MENU_DEBUG_COL2_OFS+FW+1, MENU_DEBUG_Y_CURRENT, Current_max*10*current_scale/8192, UNIT_RAW, LEFT); + // consumption + lcd_putsLeft(MENU_DEBUG_Y_MAH, STR_CPU_MAH); + putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_MAH, g_eeGeneral.mAhUsed + Current_used*current_scale/8192/36, UNIT_MAH, LEFT|PREC1); +#endif + +#if defined(PCBSKY9X) + lcd_putsLeft(MENU_DEBUG_Y_CPU_TEMP, STR_CPU_TEMP); + putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_CPU_TEMP, getTemperature(), UNIT_TEMPERATURE, LEFT); + lcd_putc(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_CPU_TEMP, '>'); + putsValueWithUnit(MENU_DEBUG_COL2_OFS+FW+1, MENU_DEBUG_Y_CPU_TEMP, maxTemperature+g_eeGeneral.temperatureCalib, UNIT_TEMPERATURE, LEFT); +#endif + +#if defined(COPROCESSOR) + lcd_putsLeft(MENU_DEBUG_Y_COPROC, STR_COPROC_TEMP); + + if (Coproc_read==0) { + lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Co Proc NACK"),INVERS); + } + else if (Coproc_read==0x81) { + lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Inst.TinyApp"),INVERS); + } + else if (Coproc_read<3) { + lcd_putsAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, PSTR("Upgr.TinyApp"),INVERS); + } + else { + putsValueWithUnit(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_COPROC, Coproc_temp, UNIT_TEMPERATURE, LEFT); + putsValueWithUnit(MENU_DEBUG_COL2_OFS, MENU_DEBUG_Y_COPROC, Coproc_maxtemp, UNIT_TEMPERATURE, LEFT); + } +#endif + +#if defined(CPUARM) + lcd_putsLeft(MENU_DEBUG_Y_MIXMAX, STR_TMIXMAXMS); + lcd_outdezAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_MIXMAX, DURATION_MS_PREC2(maxMixerDuration), PREC2|LEFT); + lcd_puts(lcdLastPos, MENU_DEBUG_Y_MIXMAX, "ms"); +#endif + +#if defined(PCBSKY9X) + lcd_putsLeft(MENU_DEBUG_Y_RTOS, STR_FREESTACKMINB); + lcd_outdezAtt(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_RTOS, stack_free(0), UNSIGN|LEFT); + lcd_puts(lcdLastPos, MENU_DEBUG_Y_RTOS, "/"); + lcd_outdezAtt(lcdLastPos, MENU_DEBUG_Y_RTOS, stack_free(1), UNSIGN|LEFT); + lcd_puts(lcdLastPos, MENU_DEBUG_Y_RTOS, "/"); + lcd_outdezAtt(lcdLastPos, MENU_DEBUG_Y_RTOS, stack_free(2), UNSIGN|LEFT); +#endif + +#if !defined(CPUARM) + lcd_putsLeft(1*FH, STR_TMR1LATMAXUS); + lcd_outdez8(MENU_DEBUG_COL1_OFS , 1*FH, g_tmr1Latency_max/2 ); + lcd_putsLeft(2*FH, STR_TMR1LATMINUS); + lcd_outdez8(MENU_DEBUG_COL1_OFS , 2*FH, g_tmr1Latency_min/2 ); + lcd_putsLeft(3*FH, STR_TMR1JITTERUS); + lcd_outdez8(MENU_DEBUG_COL1_OFS , 3*FH, (g_tmr1Latency_max - g_tmr1Latency_min) /2 ); + lcd_putsLeft(4*FH, STR_TMIXMAXMS); + lcd_outdezAtt(MENU_DEBUG_COL1_OFS, 4*FH, DURATION_MS_PREC2(maxMixerDuration), PREC2); + lcd_putsLeft(5*FH, STR_FREESTACKMINB); + lcd_outdezAtt(14*FW, 5*FH, stack_free(), UNSIGN) ; +#endif + + lcd_puts(3*FW, 7*FH+1, STR_MENUTORESET); + lcd_status_line(); +} diff --git a/radio/src/gui/Taranis/lcd.cpp b/radio/src/gui/Taranis/lcd.cpp index deb71aac6..a6279dc36 100755 --- a/radio/src/gui/Taranis/lcd.cpp +++ b/radio/src/gui/Taranis/lcd.cpp @@ -35,6 +35,7 @@ */ #include "opentx.h" +#include "timers.h" #if defined(REVPLUS) && defined(LCD_DUAL_BUFFER) display_t displayBuf1[DISPLAY_BUF_SIZE]; diff --git a/radio/src/gui/Taranis/view_main.cpp b/radio/src/gui/Taranis/view_main.cpp index 64fad251a..e846fad8f 100755 --- a/radio/src/gui/Taranis/view_main.cpp +++ b/radio/src/gui/Taranis/view_main.cpp @@ -35,6 +35,7 @@ */ #include "../../opentx.h" +#include "../../timers.h" #define BIGSIZE MIDSIZE #define LBOX_CENTERX (BOX_WIDTH/2 + 17) diff --git a/radio/src/gui/Taranis/view_statistics.cpp b/radio/src/gui/Taranis/view_statistics.cpp index 3c050f05c..99da45a65 100755 --- a/radio/src/gui/Taranis/view_statistics.cpp +++ b/radio/src/gui/Taranis/view_statistics.cpp @@ -35,6 +35,7 @@ */ #include "../../opentx.h" +#include "../../timers.h" void menuStatisticsView(uint8_t event) { diff --git a/radio/src/lua_api.cpp b/radio/src/lua_api.cpp index 4a1af9222..5f8b06e09 100644 --- a/radio/src/lua_api.cpp +++ b/radio/src/lua_api.cpp @@ -39,6 +39,7 @@ #include "opentx.h" #include "stamp-opentx.h" #include "bin_allocator.h" +#include "timers.h" #if !defined(SIMU) extern "C" { diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index 2444c6061..01b264c3b 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -35,6 +35,7 @@ */ #include "opentx.h" +#include "timers.h" #if defined(PCBTARANIS) int8_t virtualInputsTrims[NUM_INPUTS]; diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 073bbf47f..ed92d44ce 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1,2715 +1,2595 @@ -/* - * Authors (alphabetical order) - * - Andre Bernet - * - Andreas Weitl - * - Bertrand Songis - * - Bryan J. Rentoul (Gruvin) - * - Cameron Weeks - * - Erez Raviv - * - Gabriel Birkus - * - Jean-Pierre Parisy - * - Karl Szmutny - * - Michael Blandford - * - Michal Hlavinka - * - Pat Mackenzie - * - Philip Moss - * - Rob Thomson - * - Romolo Manfredini - * - Thomas Husterer - * - * opentx is based on code named - * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, - * er9x by Erez Raviv: http://code.google.com/p/er9x/, - * and the original (and ongoing) project by - * Thomas Husterer, th9x: http://code.google.com/p/th9x/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "opentx.h" - -#if defined(COLORLCD) -#elif defined(PCBTARANIS) - const pm_uchar asterisk_lbm[] PROGMEM = { - #include "bitmaps/Taranis/asterisk.lbm" - }; -#else - const pm_uchar asterisk_lbm[] PROGMEM = { - #include "bitmaps/9X/asterisk.lbm" - }; -#endif - -EEGeneral g_eeGeneral; -ModelData g_model; - -#if defined(SDCARD) -Clipboard clipboard; -#endif - -#if defined(PCBTARANIS) && defined(SDCARD) -uint8_t modelBitmap[MODEL_BITMAP_SIZE]; -void loadModelBitmap(char *name, uint8_t *bitmap) -{ - uint8_t len = zlen(name, LEN_BITMAP_NAME); - if (len > 0) { - char lfn[] = BITMAPS_PATH "/xxxxxxxxxx.bmp"; - strncpy(lfn+sizeof(BITMAPS_PATH), name, len); - strcpy(lfn+sizeof(BITMAPS_PATH)+len, BITMAPS_EXT); - if (bmpLoad(bitmap, lfn, MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT) == 0) { - return; - } - } - -#if !defined(COLORLCD) - // In all error cases, we set the default logo - memcpy(bitmap, logo_taranis, MODEL_BITMAP_SIZE); -#endif -} -#endif - -#if !defined(CPUARM) -uint8_t g_tmr1Latency_max; -uint8_t g_tmr1Latency_min; -uint16_t lastMixerDuration; -#endif - -uint8_t unexpectedShutdown = 0; - -/* AVR: mixer duration in 1/16ms */ -/* ARM: mixer duration in 0.5us */ -uint16_t maxMixerDuration; - -#if defined(AUDIO) && !defined(CPUARM) -audioQueue audio; -#endif - -uint8_t heartbeat; - -uint8_t stickMode; - -#if defined(OVERRIDE_CHANNEL_FUNCTION) -safetych_t safetyCh[NUM_CHNOUT]; -#endif - -union ReusableBuffer reusableBuffer; - -const pm_uint8_t bchout_ar[] PROGMEM = { - 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39, - 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78, - 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4, - 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 }; - -uint8_t channel_order(uint8_t x) -{ - return ( ((pgm_read_byte(bchout_ar + g_eeGeneral.templateSetup) >> (6-(x-1) * 2)) & 3 ) + 1 ); -} - -/* -mode1 rud ele thr ail -mode2 rud thr ele ail -mode3 ail ele thr rud -mode4 ail thr ele rud -*/ -const pm_uint8_t modn12x3[] PROGMEM = { - 0, 1, 2, 3, - 0, 2, 1, 3, - 3, 1, 2, 0, - 3, 2, 1, 0 }; - -volatile tmr10ms_t g_tmr10ms; - -#if defined(CPUARM) -volatile uint8_t rtc_count = 0; -uint32_t watchdogTimeout = 0; - -void watchdogSetTimeout(uint32_t timeout) -{ - watchdogTimeout = timeout; -} -#endif - -void per10ms() -{ - g_tmr10ms++; - -#if defined(CPUARM) - if (watchdogTimeout) { - watchdogTimeout -= 1; - wdt_reset(); // Retrigger hardware watchdog - } -#endif - -#if defined(GUI) - if (lightOffCounter) lightOffCounter--; - if (flashCounter) flashCounter--; - if (s_noHi) s_noHi--; -#endif - - if (trimsCheckTimer) trimsCheckTimer--; - if (ppmInValid) ppmInValid--; - -#if defined(CPUARM) - if (trimsDisplayTimer) - trimsDisplayTimer--; - else - trimsDisplayMask = 0; -#endif - -#if defined(RTCLOCK) - /* Update global Date/Time every 100 per10ms cycles */ - if (++g_ms100 == 100) { - g_rtcTime++; // inc global unix timestamp one second -#if defined(COPROCESSOR) - if (g_rtcTime < 60 || rtc_count<5) { - rtcInit(); - rtc_count++; - } - else { - coprocReadData(true); - } -#endif - g_ms100 = 0; - } -#endif - - readKeysAndTrims(); - -#if defined(ROTARY_ENCODER_NAVIGATION) - if (IS_RE_NAVIGATION_ENABLE()) { - static rotenc_t rePreviousValue; - rotenc_t reNewValue = (g_rotenc[NAVIGATION_RE_IDX()] / ROTARY_ENCODER_GRANULARITY); - int8_t scrollRE = reNewValue - rePreviousValue; - if (scrollRE) { - rePreviousValue = reNewValue; - putEvent(scrollRE < 0 ? EVT_ROTARY_LEFT : EVT_ROTARY_RIGHT); - } - uint8_t evt = s_evt; - if (EVT_KEY_MASK(evt) == BTN_REa + NAVIGATION_RE_IDX()) { - if (IS_KEY_BREAK(evt)) { - putEvent(EVT_ROTARY_BREAK); - } - else if (IS_KEY_LONG(evt)) { - putEvent(EVT_ROTARY_LONG); - } - } - } -#endif - -#if defined(FRSKY) || defined(JETI) - if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol[0])) - telemetryInterrupt10ms(); -#endif - - // These moved here from evalFlightModeMixes() to improve beep trigger reliability. -#if defined(PWM_BACKLIGHT) - if ((g_tmr10ms&0x03) == 0x00) - backlightFade(); // increment or decrement brightness until target brightness is reached -#endif - -#if !defined(AUDIO) - if (mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) AUDIO_MIX_WARNING(1); - if (mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) AUDIO_MIX_WARNING(2); - if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3); -#endif - -#if defined(SDCARD) - sdPoll10ms(); -#endif - - heartbeat |= HEART_TIMER_10MS; -} - -FlightModeData *flightModeAddress(uint8_t idx) -{ - return &g_model.flightModeData[idx]; -} - -ExpoData *expoAddress(uint8_t idx ) -{ - return &g_model.expoData[idx]; -} - -MixData *mixAddress(uint8_t idx) -{ - return &g_model.mixData[idx]; -} - -LimitData *limitAddress(uint8_t idx) -{ - return &g_model.limitData[idx]; -} - -#if defined(CPUM64) -void memclear(void *ptr, uint8_t size) -{ - memset(ptr, 0, size); -} -#endif - -void generalDefault() -{ - memclear(&g_eeGeneral, sizeof(g_eeGeneral)); - g_eeGeneral.version = EEPROM_VER; - g_eeGeneral.variant = EEPROM_VARIANT; - g_eeGeneral.contrast = 25; - -#if defined(PCBTARANIS) - g_eeGeneral.vBatWarn = 65; - g_eeGeneral.vBatMin = -30; - g_eeGeneral.vBatMax = -40; -#else - g_eeGeneral.vBatWarn = 90; -#endif - -#if defined(DEFAULT_MODE) - g_eeGeneral.stickMode = DEFAULT_MODE-1; -#endif - -#if defined(PCBTARANIS) - g_eeGeneral.templateSetup = 17; /* TAER */ -#endif - -#if !defined(CPUM64) - g_eeGeneral.backlightMode = e_backlight_mode_all; - g_eeGeneral.lightAutoOff = 2; - g_eeGeneral.inactivityTimer = 10; -#endif - -#if defined(CPUARM) - g_eeGeneral.wavVolume = 2; - g_eeGeneral.backgroundVolume = 1; -#endif - - g_eeGeneral.chkSum = 0xFFFF; -} - -uint16_t evalChkSum() -{ - uint16_t sum = 0; - const int16_t *calibValues = (const int16_t *) &g_eeGeneral.calib[0]; - for (int i=0; i<12; i++) - sum += calibValues[i]; - return sum; -} - -#if defined(PCBTARANIS) -void clearInputs() -{ - memset(g_model.expoData, 0, sizeof(g_model.expoData)); // clear all expos -} - -void defaultInputs() -{ - clearInputs(); - - for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; - expo->curve.type = CURVE_REF_EXPO; - expo->chn = i; - expo->weight = 100; - expo->mode = 3; // TODO constant - for (int c=0; c<4; c++) { -#if defined(TRANSLATIONS_CZ) && defined(CPUARM) - g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+STR_INPUTNAMES[0]*(stick_index-1)+c]); -#else - g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[1+STR_VSRCRAW[0]*stick_index+c]); -#endif - } - } - eeDirty(EE_MODEL); -} -#endif - -#if defined(TEMPLATES) -inline void applyDefaultTemplate() -{ - applyTemplate(TMPL_SIMPLE_4CH); -} -#else -void applyDefaultTemplate() -{ - for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; - expo->curve.type = CURVE_REF_EXPO; - expo->chn = i; - expo->weight = 100; - expo->mode = 3; // TODO constant - for (int c=0; c<4; c++) { -#if defined(TRANSLATIONS_CZ) && defined(CPUARM) - g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+STR_INPUTNAMES[0]*(stick_index-1)+c]); -#else - g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[1+STR_VSRCRAW[0]*stick_index+c]); -#endif - } -#endif - - MixData *mix = mixAddress(i); - mix->destCh = i; - mix->weight = 100; - -#if defined(PCBTARANIS) - mix->srcRaw = i+1; -#else - mix->srcRaw = MIXSRC_Rud - 1 + channel_order(i+1); -#endif - } - eeDirty(EE_MODEL); -} -#endif - -#if defined(CPUARM) -void checkModelIdUnique(uint8_t id) -{ - for (uint8_t i=0; ichn > index) - break; - else if (line->chn < index) - continue; - else if (line->srcRaw >= MIXSRC_FIRST_LOGICAL_SWITCH) - return true; - } - return false; -} -#endif - -#if defined(AUTOSOURCE) -int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) -{ - int8_t result = 0; - static tmr10ms_t s_move_last_time = 0; - -#if defined(PCBTARANIS) - static int16_t inputsStates[MAX_INPUTS]; - if (min <= MIXSRC_FIRST_INPUT) { - for (uint8_t i=0; i 512) { - if (!isInputRecursive(i)) { - result = MIXSRC_FIRST_INPUT+i; - break; - } - } - } - } -#endif - - static int16_t sourcesStates[NUM_STICKS+NUM_POTS]; - if (result == 0) { - for (uint8_t i=0; i 512) { - result = MIXSRC_Rud+i; - break; - } - } - } - - bool recent = ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10); - if (recent) { - result = 0; - } - - if (result || recent) { -#if defined(PCBTARANIS) - memcpy(inputsStates, anas, sizeof(inputsStates)); -#endif - memcpy(sourcesStates, calibratedStick, sizeof(sourcesStates)); - } - - s_move_last_time = get_tmr10ms(); - return result; -} -#endif - -#if defined(FLIGHT_MODES) -uint8_t getFlightMode() -{ - for (uint8_t i=1; iswtch && getSwitch(phase->swtch)) { - return i; - } - } - return 0; -} -#endif - -trim_t getRawTrimValue(uint8_t phase, uint8_t idx) -{ - FlightModeData *p = flightModeAddress(phase); -#if defined(PCBSTD) - return (((trim_t)p->trim[idx]) << 2) + ((p->trim_ext >> (2*idx)) & 0x03); -#else - return p->trim[idx]; -#endif -} - -int getTrimValue(uint8_t phase, uint8_t idx) -{ -#if defined(PCBTARANIS) - int result = 0; - for (uint8_t i=0; i> 1; - if (p == phase || phase == 0) { - return result + v.value; - } - else { - phase = p; - if (v.mode % 2 != 0) { - result += v.value; - } - } - } - } - return 0; -#else - return getRawTrimValue(getTrimFlightPhase(phase, idx), idx); -#endif -} - -#if defined(PCBTARANIS) -bool setTrimValue(uint8_t phase, uint8_t idx, int trim) -{ - for (uint8_t i=0; itrim[idx]; - if (v.mode == TRIM_MODE_NONE) - return false; - unsigned int p = v.mode >> 1; - if (p == phase || phase == 0) { - v.value = trim; - break; - } - else if (v.mode % 2 == 0) { - phase = p; - } - else { - v.value = limit(TRIM_EXTENDED_MIN, trim - getTrimValue(p, idx), TRIM_EXTENDED_MAX); - break; - } - } - eeDirty(EE_MODEL); - return true; -} -#else -void setTrimValue(uint8_t phase, uint8_t idx, int trim) -{ -#if defined(PCBSTD) - FlightModeData *p = flightModeAddress(phase); - p->trim[idx] = (int8_t)(trim >> 2); - idx <<= 1; - p->trim_ext = (p->trim_ext & ~(0x03 << idx)) + (((trim & 0x03) << idx)); -#else - FlightModeData *p = flightModeAddress(phase); - p->trim[idx] = trim; -#endif - eeDirty(EE_MODEL); -} -#endif - -#if !defined(PCBTARANIS) -uint8_t getTrimFlightPhase(uint8_t phase, uint8_t idx) -{ - for (uint8_t i=0; i= phase) result++; - phase = result; - } - return 0; -} -#endif - -#if defined(ROTARY_ENCODERS) -uint8_t getRotaryEncoderFlightPhase(uint8_t idx) -{ - uint8_t phase = mixerCurrentFlightMode; - for (uint8_t i=0; irotaryEncoders[idx]; - if (value <= ROTARY_ENCODER_MAX) return phase; - uint8_t result = value-ROTARY_ENCODER_MAX-1; - if (result >= phase) result++; - phase = result; - } - return 0; -} - -int16_t getRotaryEncoder(uint8_t idx) -{ - return flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]; -} - -void incRotaryEncoder(uint8_t idx, int8_t inc) -{ - g_rotenc[idx] += inc; - int16_t *value = &(flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]); - *value = limit((int16_t)-1024, (int16_t)(*value + (inc * 8)), (int16_t)+1024); - eeDirty(EE_MODEL); -} -#endif - -#if defined(GVARS) - -#if defined(PCBSTD) - #define SET_GVAR_VALUE(idx, phase, value) \ - (GVAR_VALUE(idx, phase) = value, eeDirty(EE_MODEL)) -#else - #define SET_GVAR_VALUE(idx, phase, value) \ - GVAR_VALUE(idx, phase) = value; \ - eeDirty(EE_MODEL); \ - if (g_model.gvars[idx].popup) { \ - s_gvar_last = idx; \ - s_gvar_timer = GVAR_DISPLAY_TIME; \ - } -#endif - -#if defined(PCBSTD) -int16_t getGVarValue(int16_t x, int16_t min, int16_t max) -{ - if (GV_IS_GV_VALUE(x, min, max)) { - int8_t idx = GV_INDEX_CALCULATION(x, max); - int8_t mul = 1; - - if (idx < 0) { - idx = -1-idx; - mul = -1; - } - - x = GVAR_VALUE(idx, -1) * mul; - } - - return limit(min, x, max); -} - -void setGVarValue(uint8_t idx, int8_t value) -{ - if (GVAR_VALUE(idx, -1) != value) { - SET_GVAR_VALUE(idx, -1, value); - } -} -#else -uint8_t s_gvar_timer = 0; -uint8_t s_gvar_last = 0; - -uint8_t getGVarFlightPhase(uint8_t phase, uint8_t idx) -{ - for (uint8_t i=0; i= phase) result++; - phase = result; - } - return 0; -} - -int16_t getGVarValue(int16_t x, int16_t min, int16_t max, int8_t phase) -{ - if (GV_IS_GV_VALUE(x, min, max)) { - int8_t idx = GV_INDEX_CALCULATION(x, max); - int8_t mul = 1; - - if (idx < 0) { - idx = -1-idx; - mul = -1; - } - - x = GVAR_VALUE(idx, getGVarFlightPhase(phase, idx)) * mul; - } - return limit(min, x, max); -} - -void setGVarValue(uint8_t idx, int16_t value, int8_t phase) -{ - phase = getGVarFlightPhase(phase, idx); - if (GVAR_VALUE(idx, phase) != value) { - SET_GVAR_VALUE(idx, phase, value); - } -} -#endif - -#endif - -#if defined(CPUARM) -getvalue_t convert16bitsTelemValue(source_t channel, ls_telemetry_value_t value) -{ - return value; -} - -getvalue_t convert8bitsTelemValue(source_t channel, ls_telemetry_value_t value) -{ - return value; -} - -#if defined(FRSKY) -ls_telemetry_value_t minTelemValue(source_t channel) -{ - return 0; -} - -ls_telemetry_value_t maxTelemValue(source_t channel) -{ - return 30000; -} -#endif - -ls_telemetry_value_t max8bitsTelemValue(source_t channel) -{ - return 30000; -} - -#elif defined(FRSKY) - -/* -ls_telemetry_value_t minTelemValue(uint8_t channel) -{ - switch (channel) { - case TELEM_TIMER1: - case TELEM_TIMER2: - return -3600; - case TELEM_ALT: - case TELEM_MIN_ALT: - case TELEM_MAX_ALT: - case TELEM_GPSALT: - return -500; - case TELEM_T1: - case TELEM_MAX_T1: - case TELEM_T2: - case TELEM_MAX_T2: - return -30; - case TELEM_ACCx: - case TELEM_ACCy: - case TELEM_ACCz: - return -1000; - case TELEM_VSPEED: - return -3000; - default: - return 0; - } -} -*/ -ls_telemetry_value_t maxTelemValue(uint8_t channel) -{ - switch (channel) { - case TELEM_FUEL: - case TELEM_RSSI_TX: - case TELEM_RSSI_RX: - return 100; - case TELEM_HDG: - return 180; - default: - return 255; - } -} -#endif - -#if !defined(CPUARM) -getvalue_t convert8bitsTelemValue(uint8_t channel, ls_telemetry_value_t value) -{ - getvalue_t result; - switch (channel) { - case TELEM_TIMER1: - case TELEM_TIMER2: - result = value * 5; - break; -#if defined(FRSKY) - case TELEM_ALT: - case TELEM_GPSALT: - case TELEM_MAX_ALT: - case TELEM_MIN_ALT: - result = value * 8 - 500; - break; - case TELEM_RPM: - case TELEM_MAX_RPM: - result = value * 50; - break; - case TELEM_T1: - case TELEM_T2: - case TELEM_MAX_T1: - case TELEM_MAX_T2: - result = (getvalue_t)value - 30; - break; - case TELEM_CELL: - case TELEM_HDG: - case TELEM_SPEED: - case TELEM_MAX_SPEED: - result = value * 2; - break; - case TELEM_ASPEED: - case TELEM_MAX_ASPEED: - result = value * 20; - break; - case TELEM_DIST: - case TELEM_MAX_DIST: - result = value * 8; - break; - case TELEM_CURRENT: - case TELEM_POWER: - case TELEM_MAX_CURRENT: - case TELEM_MAX_POWER: - result = value * 5; - break; - case TELEM_CONSUMPTION: - result = value * 100; - break; - case TELEM_VSPEED: - result = ((getvalue_t)value - 125) * 10; - break; -#endif - default: - result = value; - break; - } - return result; -} -#endif - -#if defined(FRSKY)&& !defined(CPUARM) -FORCEINLINE void convertUnit(getvalue_t & val, uint8_t & unit) -{ - if (IS_IMPERIAL_ENABLE()) { - if (unit == UNIT_TEMPERATURE) { - val += 18; - val *= 115; - val >>= 6; - } - if (unit == UNIT_DIST) { - // m to ft *105/32 - val = val * 3 + (val >> 2) + (val >> 5); - } - if (unit == UNIT_FEET) { - unit = UNIT_DIST; - } - if (unit == UNIT_KTS) { - // kts to mph - unit = UNIT_SPEED; - val = (val * 23) / 20; - } - } - else { - if (unit == UNIT_KTS) { - // kts to km/h - unit = UNIT_SPEED; -#if defined(CPUARM) - val = (val * 1852) / 1000; -#else - val = (val * 50) / 27; -#endif - } - } - - if (unit == UNIT_HDG) { - unit = UNIT_TEMPERATURE; - } -} -#endif - -#define INAC_STICKS_SHIFT 6 -#define INAC_SWITCHES_SHIFT 8 -bool inputsMoved() -{ - uint8_t sum = 0; - for (uint8_t i=0; i> INAC_STICKS_SHIFT; - for (uint8_t i=0; i> INAC_SWITCHES_SHIFT; - - if (abs((int8_t)(sum-inactivity.sum)) > 1) { - inactivity.sum = sum; - return true; - } - else { - return false; - } -} - -void checkBacklight() -{ - static uint8_t tmr10ms ; - -#if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION) - rotencPoll(); -#endif - - uint8_t x = g_blinkTmr10ms; - if (tmr10ms != x) { - tmr10ms = x; - if (inputsMoved()) { - inactivity.counter = 0; - if (g_eeGeneral.backlightMode & e_backlight_mode_sticks) - backlightOn(); - } - - bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || lightOffCounter || isFunctionActive(FUNCTION_BACKLIGHT)); - if (flashCounter) backlightOn = !backlightOn; - if (backlightOn) - BACKLIGHT_ON(); - else - BACKLIGHT_OFF(); - -#if defined(PCBSTD) && defined(VOICE) && !defined(SIMU) - Voice.voice_process() ; -#endif - } -} - -void backlightOn() -{ - lightOffCounter = ((uint16_t)g_eeGeneral.lightAutoOff*250) << 1; -} - -#if MENUS_LOCK == 1 -bool readonly = true; -bool readonlyUnlocked() -{ - if (readonly) { - POPUP_WARNING(STR_MODS_FORBIDDEN); - return false; - } - else { - return true; - } -} -#endif - -#if defined(SPLASH) -void doSplash() -{ - if (SPLASH_NEEDED()) { - displaySplash(); - -#if !defined(CPUARM) - AUDIO_TADA(); -#endif - -#if defined(PCBSTD) - lcdSetContrast(); -#elif !defined(PCBTARANIS) - tmr10ms_t curTime = get_tmr10ms() + 10; - uint8_t contrast = 10; - lcdSetRefVolt(contrast); -#endif - - getADC(); // init ADC array - - inputsMoved(); - - tmr10ms_t tgtime = get_tmr10ms() + SPLASH_TIMEOUT; - while (tgtime != get_tmr10ms()) { -#if defined(SIMU) - SIMU_SLEEP(1); -#elif defined(CPUARM) - CoTickDelay(1); -#endif - - getADC(); - -#if defined(FSPLASH) - // Splash is forced, we can't skip it - if (!(g_eeGeneral.splashMode & 0x04)) { -#endif - - if (keyDown() || inputsMoved()) return; - -#if defined(FSPLASH) - } -#endif - - if (pwrCheck()==e_power_off) return; - -#if !defined(PCBTARANIS) && !defined(PCBSTD) - if (curTime < get_tmr10ms()) { - curTime += 10; - if (contrast < g_eeGeneral.contrast) { - contrast += 1; - lcdSetRefVolt(contrast); - } - } -#endif - - checkBacklight(); - } - } -} -#else -#define Splash() -#define doSplash() -#endif - -#if defined(GUI) -void checkAll() -{ -#if !defined(PCBSKY9X) - checkLowEEPROM(); -#endif - -#if defined(MODULE_ALWAYS_SEND_PULSES) - startupWarningState = STARTUP_WARNING_THROTTLE; -#else - checkTHR(); - checkSwitches(); -#endif - -#if defined(CPUARM) - if (g_model.displayChecklist && modelHasNotes()) { - pushModelNotes(); - } -#endif - -#if defined(CPUARM) - if (!clearKeyEvents()) { - displayPopup(STR_KEYSTUCK); - tmr10ms_t tgtime = get_tmr10ms() + 500; - while (tgtime != get_tmr10ms()) { -#if defined(SIMU) - SIMU_SLEEP(1); -#elif defined(CPUARM) - CoTickDelay(1); -#endif - wdt_reset(); - } - } -#else // #if defined(CPUARM) - clearKeyEvents(); -#endif // #if defined(CPUARM) - - START_SILENCE_PERIOD(); -} -#endif // GUI - -#if defined(MODULE_ALWAYS_SEND_PULSES) -void checkStartupWarnings() -{ - if (startupWarningState < STARTUP_WARNING_DONE) { - if (startupWarningState == STARTUP_WARNING_THROTTLE) - checkTHR(); - else - checkSwitches(); - } -} -#endif - -#if !defined(PCBSKY9X) -void checkLowEEPROM() -{ - if (g_eeGeneral.disableMemoryWarning) return; - if (EeFsGetFree() < 100) { - ALERT(STR_EEPROMWARN, STR_EEPROMLOWMEM, AU_ERROR); - } -} -#endif - -void checkTHR() -{ - uint8_t thrchn = ((g_model.thrTraceSrc==0) || (g_model.thrTraceSrc>NUM_POTS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1; - // throttle channel is either the stick according stick mode (already handled in evalInputs) - // or P1 to P3; - // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS) we assume the throttle stick is the input - // no other information available at the moment, and good enough to my option (otherwise too much exceptions...) - -#if defined(MODULE_ALWAYS_SEND_PULSES) - int16_t v = calibratedStick[thrchn]; - if (v<=THRCHK_DEADBAND-1024 || g_model.disableThrottleWarning || pwrCheck()==e_power_off || keyDown()) { - startupWarningState = STARTUP_WARNING_THROTTLE+1; - } - else { - calibratedStick[thrchn] = -1024; -#if !defined(PCBTARANIS) - if (thrchn < NUM_STICKS) { - rawAnas[thrchn] = anas[thrchn] = calibratedStick[thrchn]; - } -#endif - MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); - } -#else - if (g_model.disableThrottleWarning) return; - getADC(); - evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job - - int16_t v = calibratedStick[thrchn]; - if (v<=(THRCHK_DEADBAND-1024)) return; // prevent warning if throttle input OK - - // first - display warning; also deletes inputs if any have been before - MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); - - while (1) { - - SIMU_SLEEP(1); - - getADC(); - - evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job - v = calibratedStick[thrchn]; - - if (pwrCheck()==e_power_off || keyDown() || v<=(THRCHK_DEADBAND-1024)) - break; - - checkBacklight(); - - wdt_reset(); - } -#endif -} - -void checkAlarm() // added by Gohst -{ - if (g_eeGeneral.disableAlarmWarning) - return; - - if (IS_SOUND_OFF()) - ALERT(STR_ALARMSWARN, STR_ALARMSDISABLED, AU_ERROR); -} - -void alert(const pm_char * t, const pm_char *s MESSAGE_SOUND_ARG) -{ - MESSAGE(t, s, STR_PRESSANYKEY, sound); - - while(1) - { - SIMU_SLEEP(1); - - if (pwrCheck() == e_power_off) { - // the radio has been powered off during the ALERT - pwrOff(); // turn power off now - } - - if (keyDown()) return; // wait for key release - - checkBacklight(); - - wdt_reset(); - } -} - -#if defined(GVARS) -int8_t trimGvar[NUM_STICKS] = { -1, -1, -1, -1 }; -#endif - -#if defined(CPUARM) -void checkTrims() -{ - uint8_t event = getEvent(true); - if (event && !IS_KEY_BREAK(event)) { - int8_t k = EVT_KEY_MASK(event) - TRM_BASE; -#else -uint8_t checkTrim(uint8_t event) -{ - int8_t k = EVT_KEY_MASK(event) - TRM_BASE; - if (k>=0 && k<8 && !IS_KEY_BREAK(event)) { -#endif - // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP - uint8_t idx = CONVERT_MODE((uint8_t)k/2); - uint8_t phase; - int before; - bool thro; - -#if defined(CPUARM) - trimsDisplayTimer = 200; // 2 seconds - trimsDisplayMask |= (1<=mark && beforemark))) { - after = mark; - beepTrim = (mark == 0 ? 1 : 2); - } - } - - if ((beforeTRIM_MAX) || (before>after && after TRIM_EXTENDED_MAX) { - after = TRIM_EXTENDED_MAX; - } - -#if defined(GVARS) - if (TRIM_REUSED(idx)) { - SET_GVAR_VALUE(trimGvar[idx], phase, after); - } - else -#endif - { -#if defined(PCBTARANIS) - if (!setTrimValue(phase, idx, after)) { - // we don't play a beep, so we exit now the function - return; - } -#else - setTrimValue(phase, idx, after); -#endif - } - -#if defined(AUDIO) - // toneFreq higher/lower according to trim position - // limit the frequency, range -125 to 125 = toneFreq: 19 to 101 - if (after > TRIM_MAX) - after = TRIM_MAX; - if (after < TRIM_MIN) - after = TRIM_MIN; -#if defined(CPUARM) - after <<= 3; - after += 120*16; -#else - after >>= 2; - after += 60; -#endif -#endif - - if (beepTrim) { - if (beepTrim == 1) { - AUDIO_TRIM_MIDDLE(after); - pauseEvents(event); - } - else { - AUDIO_TRIM_END(after); - killEvents(event); - } - } - else { - AUDIO_TRIM(event, after); - } -#if !defined(CPUARM) - return 0; -#endif - } -#if !defined(CPUARM) - return event; -#endif -} - -#if !defined(SIMU) -static uint16_t s_anaFilt[NUMBER_ANALOG]; -#endif - -#if defined(SIMU) -uint16_t BandGap = 225; -#elif defined(CPUM2560) -// #define STARTADCONV (ADCSRA = (1<> 3; -#if defined(PCBTARANIS) - if (calibrationState) v = temp[x] >> 1; - StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x]; - if (!calibrationState && IS_POT_MULTIPOS(x) && calib->count>0 && calib->count> 4); - s_anaFilt[x] = 2*RESX; - for (int i=0; icount; i++) { - if (vShifted < calib->steps[i]) { - s_anaFilt[x] = i*2*RESX/calib->count; - break; - } - } - } - else -#endif - s_anaFilt[x] = v; - } -} -#else - -/** - * Read ADC using 10 bits - */ -inline uint16_t read_adc10(uint8_t adc_input) -{ - uint16_t temp_ana; - ADMUX = adc_input|ADC_VREF_TYPE; -#if defined(TELEMETRY_MOD_14051) - ADCSRA &= 0x87; -#endif - ADCSRA |= 1 << ADSC; // Start the AD conversion - while (ADCSRA & (1 << ADSC)); // Wait for the AD conversion to complete - temp_ana = ADC; - ADCSRA |= 1 << ADSC; // Start the second AD conversion - while (ADCSRA & (1 << ADSC)); // Wait for the AD conversion to complete - temp_ana += ADC; - return temp_ana; -} - -#if defined(TELEMETRY_MOD_14051) -enum MuxInput { - MUX_BATT, - MUX_THR, - MUX_AIL, - MUX_MAX = MUX_AIL -}; - -uint8_t pf7_digital[2]; -/** - * Update ADC PF7 using 14051 multiplexer - * X0 : Battery voltage - * X1 : THR SW - * X2 : AIL SW - */ -void readMultiplexAna() -{ - static uint8_t muxNum = MUX_BATT; - uint16_t temp_ana; - uint8_t nextMuxNum = muxNum-1; - - DDRC |= 0xC1; - temp_ana = read_adc10(7); - - switch (muxNum) { - case MUX_BATT: - s_anaFilt[TX_VOLTAGE] = temp_ana; - nextMuxNum = MUX_MAX; - break; - case MUX_THR: - case MUX_AIL: - // Digital switch depend from input voltage - // take half voltage to determine digital state - pf7_digital[muxNum-1] = (temp_ana >= (s_anaFilt[TX_VOLTAGE] / 2)) ? 1 : 0; - break; - } - - // set the mux number for the next ADC convert, - // stabilize voltage before ADC read. - muxNum = nextMuxNum; - PORTC &= ~((1 << PC7) | (1 << PC6) | (1 << PC0)); // Clear CTRL ABC - switch (muxNum) { - case 1: - PORTC |= (1 << PC6); // Mux CTRL A : SW_THR - break; - case 2: - PORTC |= (1 << PC7); // Mux CTRL B : SW_AIL - break; - } -} -#endif - -void getADC() -{ -#if defined(TELEMETRY_MOD_14051) - readMultiplexAna(); - #define ADC_READ_COUNT 7 -#else - #define ADC_READ_COUNT 8 -#endif - - for (uint8_t adc_input=0; adc_input= 255 - int16_t s_traceWr; - int16_t s_traceCnt; -#else - uint8_t s_traceWr; - int16_t s_traceCnt; -#endif -uint8_t s_cnt_10s; -uint16_t s_cnt_samples_thr_10s; -uint16_t s_sum_samples_thr_10s; -#endif - -FORCEINLINE void evalTrims() -{ - uint8_t phase = mixerCurrentFlightMode; - for (uint8_t i=0; i throttle trim if applicable - int16_t trim = getTrimValue(phase, i); -#if !defined(PCBTARANIS) - if (i==THR_STICK && g_model.thrTrim) { - int16_t trimMin = g_model.extendedTrims ? TRIM_EXTENDED_MIN : TRIM_MIN; - trim = (((g_model.throttleReversed)?(int32_t)(trim+trimMin):(int32_t)(trim-trimMin)) * (RESX-anas[i])) >> (RESX_SHIFT+1); - } -#endif - if (trimsCheckTimer > 0) { - trim = 0; - } - - trims[i] = trim*2; - } -} - -#if !defined(PCBSTD) -uint8_t mSwitchDuration[1+NUM_ROTARY_ENCODERS] = { 0 }; -#define CFN_PRESSLONG_DURATION 100 -#endif - - -uint8_t s_mixer_first_run_done = false; - -void doMixerCalculations() -{ - static tmr10ms_t lastTMR = 0; - - tmr10ms_t tmr10ms = get_tmr10ms(); - uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); - // handle tick10ms overrun - // correct overflow handling costs a lot of code; happens only each 11 min; - // therefore forget the exact calculation and use only 1 instead; good compromise - -#if !defined(CPUARM) - lastTMR = tmr10ms; -#endif - - getADC(); - -#if defined(PCBTARANIS) - processSbusInput(); -#endif - - getSwitchesPosition(!s_mixer_first_run_done); - -#if defined(CPUARM) - lastTMR = tmr10ms; -#endif - -#if defined(PCBSKY9X) && !defined(REVA) && !defined(SIMU) - Current_analogue = (Current_analogue*31 + s_anaFilt[8] ) >> 5 ; - if (Current_analogue > Current_max) - Current_max = Current_analogue ; -#elif defined(PCBGRUVIN9X) && !defined(SIMU) - // For PCB V4, use our own 1.2V, external reference (connected to ADC3) - ADCSRB &= ~(1< NUM_POTS) { - uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; - val = channelOutputs[ch]; - - LimitData *lim = limitAddress(ch); - int16_t gModelMax = LIMIT_MAX_RESX(lim); - int16_t gModelMin = LIMIT_MIN_RESX(lim); - - if (lim->revert) - val = -val + gModelMax; - else - val = val - gModelMin; - -#if defined(PPM_LIMITS_SYMETRICAL) - if (lim->symetrical) { - val -= calc1000toRESX(lim->offset); - } -#endif - - gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default - // usually max is 1024 min is -1024 --> max-min = 2048 full range - -#ifdef ACCURAT_THROTTLE_TIMER - if (gModelMax!=0 && gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs -#else - // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) - gModelMax+=255; // force rounding up --> gModelMax is bigger --> val is smaller - gModelMax >>= (10-2); - - if (gModelMax!=0 && gModelMax!=8) { - val = (val << 3) / gModelMax; // rescaling only needed if Min, Max differs - } -#endif - - if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits - } - else { -#ifdef PCBTARANIS - val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; -#else - val = RESX + (g_model.thrTraceSrc == 0 ? rawAnas[THR_STICK] : calibratedStick[g_model.thrTraceSrc+NUM_STICKS-1]); -#endif - } - -#if defined(ACCURAT_THROTTLE_TIMER) - val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4) -#else - val >>= (RESX_SHIFT-4); // calibrate it -#endif - - // Timers start - for (uint8_t i=0; istate == TMR_OFF) { - timerState->state = TMR_RUNNING; - timerState->cnt = 0; - timerState->sum = 0; - } - - if (tm == TMRMODE_THR_REL) { - timerState->cnt++; - timerState->sum+=val; - } - - if ((timerState->val_10ms += tick10ms) >= 100) { - timerState->val_10ms -= 100 ; - int16_t newTimerVal = timerState->val; - if (tv) newTimerVal = tv - newTimerVal; - - if (tm == TMRMODE_ABS) { - newTimerVal++; - } - else if (tm == TMRMODE_THR) { - if (val) newTimerVal++; - } - else if (tm == TMRMODE_THR_REL) { - // @@@ open.20.fsguruh: why so complicated? we have already a s_sum field; use it for the half seconds (not showable) as well - // check for s_cnt[i]==0 is not needed because we are shure it is at least 1 -#if defined(ACCURAT_THROTTLE_TIMER) - if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) - newTimerVal++; // add second used of throttle - timerState->sum -= 128*timerState->cnt; - } -#else - if ((timerState->sum/timerState->cnt) >= 32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) - newTimerVal++; // add second used of throttle - timerState->sum -= 32*timerState->cnt; - } -#endif - timerState->cnt=0; - } - else if (tm == TMRMODE_THR_TRG) { - if (val) { - timerState->state = TMR_TRIGGED; - } - if (timerState->state == TMR_TRIGGED) { - newTimerVal++; - } - } - else { - if (tm > 0) tm -= (TMRMODE_COUNT-1); - if (getSwitch(tm)) - newTimerVal++; - } - - switch (timerState->state) { - case TMR_RUNNING: - if (tv && newTimerVal>=(int16_t)tv) { - AUDIO_TIMER_00(g_model.timers[i].countdownBeep); - timerState->state = TMR_NEGATIVE; - } - break; - case TMR_NEGATIVE: - if (newTimerVal >= (int16_t)tv + MAX_ALERT_TIME) timerState->state = TMR_STOPPED; - break; - } - - if (tv) newTimerVal = tv - newTimerVal; // if counting backwards - display backwards - - if (newTimerVal != timerState->val) { - timerState->val = newTimerVal; - if (timerState->state == TMR_RUNNING) { - if (g_model.timers[i].countdownBeep && g_model.timers[i].start) { - if (newTimerVal==30) AUDIO_TIMER_30(); - if (newTimerVal==20) AUDIO_TIMER_20(); - if (newTimerVal<=10) AUDIO_TIMER_LT10(g_model.timers[i].countdownBeep, newTimerVal); - } - if (g_model.timers[i].minuteBeep && (newTimerVal % 60)==0) { - AUDIO_TIMER_MINUTE(newTimerVal); - } - } - } - } - } - } //endfor timer loop (only two) - - static uint8_t s_cnt_100ms; - static uint8_t s_cnt_1s; - static uint8_t s_cnt_samples_thr_1s; - static uint16_t s_sum_samples_thr_1s; - - s_cnt_samples_thr_1s++; - s_sum_samples_thr_1s+=val; - - if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec - s_cnt_100ms -= 10; - s_cnt_1s += 1; - - logicalSwitchesTimerTick(); - - if (s_cnt_1s >= 10) { // 1sec - s_cnt_1s -= 10; - sessionTimer += 1; - - struct t_inactivity *ptrInactivity = &inactivity; - FORCE_INDIRECT(ptrInactivity) ; - ptrInactivity->counter++; - if ((((uint8_t)ptrInactivity->counter)&0x07)==0x01 && g_eeGeneral.inactivityTimer && g_vbat100mV>50 && ptrInactivity->counter > ((uint16_t)g_eeGeneral.inactivityTimer*60)) - AUDIO_INACTIVITY(); - -#if defined(AUDIO) - if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1); - if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2); - if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3); -#endif - -#if defined(ACCURAT_THROTTLE_TIMER) - val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; - s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps - if (val) s_timeCumThr += 1; - s_sum_samples_thr_1s>>=2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this -#else - val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; - s_timeCum16ThrP += (val>>1); - if (val) s_timeCumThr += 1; -#endif - -#if defined(THRTRACE) - // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size. - // in case buffer runs out, it wraps around - // resolution for y axis is only 32, therefore no higher value makes sense - s_cnt_samples_thr_10s += s_cnt_samples_thr_1s; - s_sum_samples_thr_10s += s_sum_samples_thr_1s; - - if (++s_cnt_10s >= 10) { // 10s - s_cnt_10s -= 10; - val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s; - s_sum_samples_thr_10s = 0; - s_cnt_samples_thr_10s = 0; - - s_traceBuf[s_traceWr++] = val; - if (s_traceWr >= MAXTRACE) s_traceWr = 0; - if (s_traceCnt >= 0) s_traceCnt++; - } -#endif - - s_cnt_samples_thr_1s = 0; - s_sum_samples_thr_1s = 0; - } - } - -#if defined(PXX) || defined(DSM2) - static uint8_t countRangecheck = 0; - for (uint8_t i=0; i= 250) { - countRangecheck = 0; - AUDIO_PLAY(AU_FRSKY_CHEEP); - } - } - } -#endif - -#if defined(CPUARM) - checkTrims(); -#endif - } - - s_mixer_first_run_done = true; -} - - - -#if defined(NAVIGATION_STICKS) -uint8_t StickScrollAllowed; -uint8_t StickScrollTimer; -static const pm_uint8_t rate[] PROGMEM = { 0, 0, 100, 40, 16, 7, 3, 1 } ; - -uint8_t calcStickScroll( uint8_t index ) -{ - uint8_t direction; - int8_t value; - - if ( ( g_eeGeneral.stickMode & 1 ) == 0 ) - index ^= 3; - - value = calibratedStick[index] / 128; - direction = value > 0 ? 0x80 : 0; - if (value < 0) - value = -value; // (abs) - if (value > 7) - value = 7; - value = pgm_read_byte(rate+(uint8_t)value); - if (value) - StickScrollTimer = STICK_SCROLL_TIMEOUT; // Seconds - return value | direction; -} -#endif - -void opentxStart() -{ - doSplash(); - -#if defined(DEBUG_TRACE_BUFFER) - trace_event(trace_start, 0x12345678); -#endif - -#if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU) - for (int i=0; i<500 && !Card_initialized; i++) { - CoTickDelay(1); // 2ms - } -#endif - -#if defined(CPUARM) - eeLoadModel(g_eeGeneral.currModel); -#endif - -#if defined(GUI) - checkAlarm(); - checkAll(); -#endif - -#if defined(GUI) - if (g_eeGeneral.chkSum != evalChkSum()) { - chainMenu(menuFirstCalib); - } -#endif - -} - -#if defined(CPUARM) || defined(CPUM2560) -void opentxClose() -{ - AUDIO_BYE(); - -#if defined(FRSKY) - // TODO needed? telemetryEnd(); -#endif - -#if defined(LUA) - luaClose(); -#endif - -#if defined(SDCARD) - closeLogs(); -#endif - -#if defined(HAPTIC) - hapticOff(); -#endif - - saveTimers(); - -#if defined(CPUARM) - for (int i=0; i> 6) == 2) { - for (uint8_t i=0; i value ) - { - repeater = 0; - if ( evt == 0 ) - { - if ( direction ) - { - evt = EVT_KEY_FIRST(KEY_UP); - } - else - { - evt = EVT_KEY_FIRST(KEY_DOWN); - } - } - } - } - else - { - value = calcStickScroll( 3 ); - direction = value & 0x80; - value &= 0x7F; - if ( value ) - { - if ( repeater > value ) - { - repeater = 0; - if ( evt == 0 ) - { - if ( direction ) - { - evt = EVT_KEY_FIRST(KEY_RIGHT); - } - else - { - evt = EVT_KEY_FIRST(KEY_LEFT); - } - } - } - } - } - } - } - else { - StickScrollTimer = 0; // Seconds - } - StickScrollAllowed = 1 ; - return evt; -} -#endif - -void checkBattery() -{ - static uint8_t counter = 0; -#if defined(GUI) && !defined(COLORLCD) - // TODO not the right menu I think ... - if (g_menuStack[g_menuStackPtr] == menuGeneralDiagAna) { - g_vbat100mV = 0; - counter = 0; - } -#endif - if (counter-- == 0) { - counter = 10; - int32_t instant_vbat = anaIn(TX_VOLTAGE); -#if defined(PCBTARANIS) - instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.vBatCalib)/128) * BATT_SCALE; - instant_vbat >>= 11; - instant_vbat += 2; // because of the diode -#elif defined(PCBSKY9X) - instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.vBatCalib)/128) * 4191; - instant_vbat /= 55296; -#elif defined(CPUM2560) - instant_vbat = (instant_vbat*1112 + instant_vbat*g_eeGeneral.vBatCalib + (BandGap<<2)) / (BandGap<<3); -#else - instant_vbat = (instant_vbat*16 + instant_vbat*g_eeGeneral.vBatCalib/8) / BandGap; -#endif - - static uint8_t s_batCheck; - static uint16_t s_batSum; - -#if defined(VOICE) - s_batCheck += 8; -#else - s_batCheck += 32; -#endif - - s_batSum += instant_vbat; - - if (g_vbat100mV == 0) { - g_vbat100mV = instant_vbat; - s_batSum = 0; - s_batCheck = 0; - } -#if defined(VOICE) - else if (!(s_batCheck & 0x3f)) { -#else - else if (s_batCheck == 0) { -#endif - g_vbat100mV = s_batSum / 8; - s_batSum = 0; -#if defined(VOICE) - if (s_batCheck != 0) { - // no alarms - } - else -#endif - if (g_vbat100mV <= g_eeGeneral.vBatWarn && g_vbat100mV>50) { - AUDIO_TX_BATTERY_LOW(); - } -#if defined(PCBSKY9X) - else if (g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { - AUDIO_TX_TEMP_HIGH(); - } - else if (g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.currentCalib)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { - AUDIO_TX_MAH_HIGH(); - } -#endif - } - } - -} - -int16_t g_ppmIns[NUM_TRAINER]; -uint8_t ppmInState = 0; // 0=unsync 1..8= wait for value i-1 -uint8_t ppmInValid = 0; - -#if !defined(SIMU) && !defined(CPUARM) - -volatile uint8_t g_tmr16KHz; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow -ISR(TIMER_16KHZ_VECT, ISR_NOBLOCK) -{ - g_tmr16KHz++; // gruvin: Not 16KHz. Overflows occur at 61.035Hz (1/256th of 15.625KHz) - // to give *16.384ms* intervals. Kind of matters for accuracy elsewhere. ;) - // g_tmr16KHz is used to software-construct a 16-bit timer - // from TIMER-0 (8-bit). See getTmr16KHz, below. -} - -uint16_t getTmr16KHz() -{ - while(1){ - uint8_t hb = g_tmr16KHz; - uint8_t lb = COUNTER_16KHZ; - if(hb-g_tmr16KHz==0) return (hb<<8)|lb; - } -} - -#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) -// Clocks every 128 uS -ISR(TIMER_AUDIO_VECT, ISR_NOBLOCK) -{ - cli(); - PAUSE_AUDIO_INTERRUPT(); // stop reentrance - sei(); - -#if defined(AUDIO) - AUDIO_DRIVER(); -#endif - -#if defined(VOICE) - VOICE_DRIVER(); -#endif - - cli(); - RESUME_AUDIO_INTERRUPT(); - sei(); -} -#endif - -// Clocks every 10ms -ISR(TIMER_10MS_VECT, ISR_NOBLOCK) -{ - // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that - static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed - -#if defined(AUDIO) - AUDIO_HEARTBEAT(); -#endif - -#if defined(BUZZER) - BUZZER_HEARTBEAT(); -#endif - -#if defined(HAPTIC) - HAPTIC_HEARTBEAT(); -#endif - - per10ms(); - - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - TIMER_10MS_COMPVAL += bump; -} - -// Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz -// equating to one count every half millisecond. (2 counts = 1ms). Control channel -// count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), -// corresponding to a PPM signal in the range 0.8ms to 2.2ms (1.5ms at center). -// (The timer is free-running and is thus not reset to zero at each capture interval.) -ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with ISR_NOBLOCK -{ - static uint16_t lastCapt; - - uint16_t capture=ICR3; - - // Prevent rentrance for this IRQ only - PAUSE_PPMIN_INTERRUPT(); - sei(); // enable other interrupts - - uint16_t val = (capture - lastCapt) / 2; - - // G: We process g_ppmIns immediately here, to make servo movement as smooth as possible - // while under trainee control - if (val>4000 && val < 16000) { // G: Prioritize reset pulse. (Needed when less than 8 incoming pulses) - ppmInState = 1; // triggered - } - else { - if (ppmInState>0 && ppmInState<=8) { - if (val>800 && val<2200) { // if valid pulse-width range - ppmInValid = PPM_IN_VALID_TIMEOUT; - g_ppmIns[ppmInState++ - 1] = (int16_t)(val - 1500) * (uint8_t)(g_eeGeneral.PPM_Multiplier+10)/10; //+-500 != 512, but close enough. - } - else { - ppmInState = 0; // not triggered - } - } - } - - lastCapt = capture; - - cli(); // disable other interrupts for stack pops before this function's RETI - RESUME_PPMIN_INTERRUPT(); -} -#endif - -#if defined(DSM2_SERIAL) && !defined(CPUARM) -FORCEINLINE void DSM2_USART0_vect() -{ - UDR0 = *((uint16_t*)pulses2MHzRPtr); // transmit next byte - - pulses2MHzRPtr += sizeof(uint16_t); - - if (pulses2MHzRPtr == pulses2MHzWPtr) { // if reached end of DSM2 data buffer ... - UCSR0B &= ~(1 << UDRIE0); // disable UDRE0 interrupt - } -} -#endif - -#if !defined(SIMU) && !defined(CPUARM) - -#if defined (FRSKY) - -// USART0 Transmit Data Register Emtpy ISR -FORCEINLINE void FRSKY_USART0_vect() -{ - if (frskyTxBufferCount > 0) { - UDR0 = frskyTxBuffer[--frskyTxBufferCount]; - } - else { - UCSR0B &= ~(1 << UDRIE0); // disable UDRE0 interrupt - } -} - -ISR(USART0_UDRE_vect) -{ -#if defined(FRSKY) && defined(DSM2_SERIAL) - if (IS_DSM2_PROTOCOL(g_model.protocol)) { // TODO not s_current_protocol? - DSM2_USART0_vect(); - } - else { - FRSKY_USART0_vect(); - } -#elif defined(FRSKY) - FRSKY_USART0_vect(); -#else - DSM2_USART0_vect(); -#endif -} -#endif -#endif - -#if defined(PCBTARANIS) - #define INSTANT_TRIM_MARGIN 10 /* around 1% */ -#else - #define INSTANT_TRIM_MARGIN 15 /* around 1.5% */ -#endif - -void instantTrim() -{ -#if defined(PCBTARANIS) - int16_t anas_0[NUM_INPUTS]; - evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks); - memcpy(anas_0, anas, sizeof(anas_0)); -#endif - - evalInputs(e_perout_mode_notrainer); - - for (uint8_t stick=0; sticksrcRaw-MIXSRC_Rud == stick) { - delta = anas[ed->chn] - anas_0[ed->chn]; - break; - } - } -#else - int16_t delta = anas[stick]; -#endif - if (abs(delta) >= INSTANT_TRIM_MARGIN) { - int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); - setTrimValue(trim_phase, stick, trim); - } - } - } - - eeDirty(EE_MODEL); - AUDIO_WARNING2(); -} - -void copySticksToOffset(uint8_t ch) -{ - pauseMixerCalculations(); - int32_t zero = (int32_t)channelOutputs[ch]; - - evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0); - int32_t val = chans[ch]; - LimitData *ld = limitAddress(ch); - limit_min_max_t lim = LIMIT_MIN(ld); - if (val < 0) { - val = -val; - lim = LIMIT_MIN(ld); - } -#if defined(CPUARM) - zero = (zero*256000 - val*lim) / (1024*256-val); -#else - zero = (zero*25600 - val*lim) / (26214-val); -#endif - ld->offset = (ld->revert ? -zero : zero); - resumeMixerCalculations(); - eeDirty(EE_MODEL); -} - -void copyTrimsToOffset(uint8_t ch) -{ - int16_t zero; - - pauseMixerCalculations(); - - evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims - zero = applyLimits(ch, chans[ch]); - - evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims - - int16_t output = applyLimits(ch, chans[ch]) - zero; - int16_t v = g_model.limitData[ch].offset; - if (g_model.limitData[ch].revert) output = -output; -#if defined(CPUARM) - v += (output * 125) / 128; -#else - v += output; -#endif - g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire - - resumeMixerCalculations(); - eeDirty(EE_MODEL); -} - -void moveTrimsToOffsets() // copy state of 3 primary to subtrim -{ - int16_t zeros[NUM_CHNOUT]; - - pauseMixerCalculations(); - - evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims - for (uint8_t i=0; ival) { - g_model.timers[i].value = timerState->val; - eeDirty(EE_MODEL); - } - } - } - -#if defined(CPUARM) && !defined(REVA) - if (sessionTimer > 0) { - g_eeGeneral.globalTimer += sessionTimer; - eeDirty(EE_GENERAL); - sessionTimer = 0; - } -#endif -} -#endif - -#if defined(ROTARY_ENCODERS) - volatile rotenc_t g_rotenc[ROTARY_ENCODERS] = {0}; -#elif defined(ROTARY_ENCODER_NAVIGATION) - volatile rotenc_t g_rotenc[1] = {0}; -#endif - -#if !defined(CPUARM) && !defined(SIMU) -extern unsigned char __bss_end ; -#define STACKPTR _SFR_IO16(0x3D) -void stack_paint() -{ - // Init Stack while interrupts are disabled - unsigned char *p ; - unsigned char *q ; - - p = (unsigned char *) STACKPTR ; - q = &__bss_end ; - p -= 2 ; - while ( p > q ) { - *p-- = 0x55 ; - } -} - -uint16_t stack_free() -{ - unsigned char *p ; - - p = &__bss_end + 1 ; - while ( *p++ == 0x55 ); - return p - &__bss_end ; -} -#endif - -#if defined(CPUM2560) - #define OPENTX_INIT_ARGS const uint8_t mcusr -#elif defined(PCBSTD) - #define OPENTX_INIT_ARGS const uint8_t mcusr -#else - #define OPENTX_INIT_ARGS -#endif - -void opentxInit(OPENTX_INIT_ARGS) -{ - eeReadAll(); - -#if defined(CPUARM) - if (UNEXPECTED_SHUTDOWN()) { - unexpectedShutdown = 1; - } -#endif - -#if defined(PCBTARANIS) - BACKLIGHT_ON(); -#endif - -#if MENUS_LOCK == 1 - getMovedSwitch(); - if (TRIMS_PRESSED() && g_eeGeneral.switchUnlockStates==switches_states) { - readonly = false; - } -#endif - -#if defined(VOICE) - setVolume(g_eeGeneral.speakerVolume+VOLUME_LEVEL_DEF); -#endif - -#if defined(CPUARM) - audioQueue.start(); - setBacklight(g_eeGeneral.backlightBright); -#endif - -#if defined(PCBSKY9X) - // Set ADC gains here - setSticksGain(g_eeGeneral.sticksGain); -#endif - -#if defined(BLUETOOTH) - btInit(); -#endif - -#if defined(RTCLOCK) && !defined(COPROCESSOR) - rtcInit(); -#endif - - if (g_eeGeneral.backlightMode != e_backlight_mode_off) backlightOn(); // on Tx start turn the light on - - if (UNEXPECTED_SHUTDOWN()) { -#if !defined(CPUARM) - // is done above on ARM - unexpectedShutdown = 1; -#endif -#if defined(CPUARM) - eeLoadModel(g_eeGeneral.currModel); -#endif - } - else { - opentxStart(); - } - -#if defined(CPUARM) || defined(CPUM2560) - if (!g_eeGeneral.unexpectedShutdown) { - g_eeGeneral.unexpectedShutdown = 1; - eeDirty(EE_GENERAL); - } -#endif - -#if defined(GUI) - lcdSetContrast(); -#endif - backlightOn(); - -#if defined(PCBTARANIS) - uart3Init(g_eeGeneral.uart3Mode, MODEL_TELEMETRY_PROTOCOL()); -#endif - -#if defined(PCBSKY9X) && !defined(SIMU) - init_trainer_capture(); -#endif - -#if !defined(CPUARM) - doMixerCalculations(); -#endif - - startPulses(); - - wdt_enable(WDTO_500MS); -} - -#if !defined(SIMU) -int main(void) -{ - // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's - // important to disable it before commencing with system initialisation (or - // we could put a bunch more wdt_reset()s in. But I don't like that approach - // during boot up.) -#if defined(CPUM2560) || defined(CPUM2561) - uint8_t mcusr = MCUSR; // save the WDT (etc) flags - MCUSR = 0; // must be zeroed before disabling the WDT -#elif defined(PCBSTD) - uint8_t mcusr = MCUCSR; - MCUCSR = 0; -#endif -#if defined(PCBTARANIS) - g_eeGeneral.contrast=30; -#endif - wdt_disable(); - - boardInit(); - -#if defined(GUI) && !defined(PCBTARANIS) - lcdInit(); -#endif - - stack_paint(); - -#if defined(GUI) - g_menuStack[0] = menuMainView; - #if MENUS_LOCK != 2/*no menus*/ - g_menuStack[1] = menuModelSelect; - #endif -#endif - -#if defined(GUI) && !defined(PCBTARANIS) - lcdSetRefVolt(25); -#endif - - sei(); // interrupts needed for telemetryInit and eeReadAll. - -#if defined(FRSKY) && !defined(DSM2_SERIAL) - telemetryInit(); -#endif - -#if defined(DSM2_SERIAL) && !defined(FRSKY) - DSM2_Init(); -#endif - -#ifdef JETI - JETI_Init(); -#endif - -#ifdef ARDUPILOT - ARDUPILOT_Init(); -#endif - -#ifdef NMEA - NMEA_Init(); -#endif - -#ifdef MAVLINK - MAVLINK_Init(); -#endif - -#ifdef MENU_ROTARY_SW - init_rotary_sw(); -#endif - -#if !defined(CPUARM) - opentxInit(mcusr); -#endif - -#if defined(CPUARM) - tasksStart(); -#else -#if defined(CPUM2560) - uint8_t shutdown_state = 0; -#endif - - while (1) { -#if defined(CPUM2560) - if ((shutdown_state=pwrCheck()) > e_power_trainer) - break; -#endif - - perMain(); - - if (heartbeat == HEART_WDT_CHECK) { - wdt_reset(); - heartbeat = 0; - } - } -#endif - -#if defined(CPUM2560) - // Time to switch off - lcd_clear(); - displayPopup(STR_SHUTDOWN); - opentxClose(); - lcd_clear() ; - lcdRefresh() ; - pwrOff(); // Only turn power off if necessary - wdt_disable(); - while(1); // never return from main() - there is no code to return back, if any delays occurs in physical power it does dead loop. -#endif -} -#endif // !SIMU +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "opentx.h" +#include "timers.h" + +#if defined(COLORLCD) +#elif defined(PCBTARANIS) + const pm_uchar asterisk_lbm[] PROGMEM = { + #include "bitmaps/Taranis/asterisk.lbm" + }; +#else + const pm_uchar asterisk_lbm[] PROGMEM = { + #include "bitmaps/9X/asterisk.lbm" + }; +#endif + +EEGeneral g_eeGeneral; +ModelData g_model; + +#if defined(SDCARD) +Clipboard clipboard; +#endif + +#if defined(PCBTARANIS) && defined(SDCARD) +uint8_t modelBitmap[MODEL_BITMAP_SIZE]; +void loadModelBitmap(char *name, uint8_t *bitmap) +{ + uint8_t len = zlen(name, LEN_BITMAP_NAME); + if (len > 0) { + char lfn[] = BITMAPS_PATH "/xxxxxxxxxx.bmp"; + strncpy(lfn+sizeof(BITMAPS_PATH), name, len); + strcpy(lfn+sizeof(BITMAPS_PATH)+len, BITMAPS_EXT); + if (bmpLoad(bitmap, lfn, MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT) == 0) { + return; + } + } + +#if !defined(COLORLCD) + // In all error cases, we set the default logo + memcpy(bitmap, logo_taranis, MODEL_BITMAP_SIZE); +#endif +} +#endif + +#if !defined(CPUARM) +uint8_t g_tmr1Latency_max; +uint8_t g_tmr1Latency_min; +uint16_t lastMixerDuration; +#endif + +uint8_t unexpectedShutdown = 0; + +/* AVR: mixer duration in 1/16ms */ +/* ARM: mixer duration in 0.5us */ +uint16_t maxMixerDuration; + +#if defined(AUDIO) && !defined(CPUARM) +audioQueue audio; +#endif + +uint8_t heartbeat; + +uint8_t stickMode; + +#if defined(OVERRIDE_CHANNEL_FUNCTION) +safetych_t safetyCh[NUM_CHNOUT]; +#endif + +union ReusableBuffer reusableBuffer; + +const pm_uint8_t bchout_ar[] PROGMEM = { + 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39, + 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78, + 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4, + 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 }; + +uint8_t channel_order(uint8_t x) +{ + return ( ((pgm_read_byte(bchout_ar + g_eeGeneral.templateSetup) >> (6-(x-1) * 2)) & 3 ) + 1 ); +} + +/* +mode1 rud ele thr ail +mode2 rud thr ele ail +mode3 ail ele thr rud +mode4 ail thr ele rud +*/ +const pm_uint8_t modn12x3[] PROGMEM = { + 0, 1, 2, 3, + 0, 2, 1, 3, + 3, 1, 2, 0, + 3, 2, 1, 0 }; + +volatile tmr10ms_t g_tmr10ms; + +#if defined(CPUARM) +volatile uint8_t rtc_count = 0; +uint32_t watchdogTimeout = 0; + +void watchdogSetTimeout(uint32_t timeout) +{ + watchdogTimeout = timeout; +} +#endif + +void per10ms() +{ + g_tmr10ms++; + +#if defined(CPUARM) + if (watchdogTimeout) { + watchdogTimeout -= 1; + wdt_reset(); // Retrigger hardware watchdog + } +#endif + +#if defined(GUI) + if (lightOffCounter) lightOffCounter--; + if (flashCounter) flashCounter--; + if (s_noHi) s_noHi--; +#endif + + if (trimsCheckTimer) trimsCheckTimer--; + if (ppmInValid) ppmInValid--; + +#if defined(CPUARM) + if (trimsDisplayTimer) + trimsDisplayTimer--; + else + trimsDisplayMask = 0; +#endif + +#if defined(RTCLOCK) + /* Update global Date/Time every 100 per10ms cycles */ + if (++g_ms100 == 100) { + g_rtcTime++; // inc global unix timestamp one second +#if defined(COPROCESSOR) + if (g_rtcTime < 60 || rtc_count<5) { + rtcInit(); + rtc_count++; + } + else { + coprocReadData(true); + } +#endif + g_ms100 = 0; + } +#endif + + readKeysAndTrims(); + +#if defined(ROTARY_ENCODER_NAVIGATION) + if (IS_RE_NAVIGATION_ENABLE()) { + static rotenc_t rePreviousValue; + rotenc_t reNewValue = (g_rotenc[NAVIGATION_RE_IDX()] / ROTARY_ENCODER_GRANULARITY); + int8_t scrollRE = reNewValue - rePreviousValue; + if (scrollRE) { + rePreviousValue = reNewValue; + putEvent(scrollRE < 0 ? EVT_ROTARY_LEFT : EVT_ROTARY_RIGHT); + } + uint8_t evt = s_evt; + if (EVT_KEY_MASK(evt) == BTN_REa + NAVIGATION_RE_IDX()) { + if (IS_KEY_BREAK(evt)) { + putEvent(EVT_ROTARY_BREAK); + } + else if (IS_KEY_LONG(evt)) { + putEvent(EVT_ROTARY_LONG); + } + } + } +#endif + +#if defined(FRSKY) || defined(JETI) + if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol[0])) + telemetryInterrupt10ms(); +#endif + + // These moved here from evalFlightModeMixes() to improve beep trigger reliability. +#if defined(PWM_BACKLIGHT) + if ((g_tmr10ms&0x03) == 0x00) + backlightFade(); // increment or decrement brightness until target brightness is reached +#endif + +#if !defined(AUDIO) + if (mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) AUDIO_MIX_WARNING(1); + if (mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) AUDIO_MIX_WARNING(2); + if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3); +#endif + +#if defined(SDCARD) + sdPoll10ms(); +#endif + + heartbeat |= HEART_TIMER_10MS; +} + +FlightModeData *flightModeAddress(uint8_t idx) +{ + return &g_model.flightModeData[idx]; +} + +ExpoData *expoAddress(uint8_t idx ) +{ + return &g_model.expoData[idx]; +} + +MixData *mixAddress(uint8_t idx) +{ + return &g_model.mixData[idx]; +} + +LimitData *limitAddress(uint8_t idx) +{ + return &g_model.limitData[idx]; +} + +#if defined(CPUM64) +void memclear(void *ptr, uint8_t size) +{ + memset(ptr, 0, size); +} +#endif + +void generalDefault() +{ + memclear(&g_eeGeneral, sizeof(g_eeGeneral)); + g_eeGeneral.version = EEPROM_VER; + g_eeGeneral.variant = EEPROM_VARIANT; + g_eeGeneral.contrast = 25; + +#if defined(PCBTARANIS) + g_eeGeneral.vBatWarn = 65; + g_eeGeneral.vBatMin = -30; + g_eeGeneral.vBatMax = -40; +#else + g_eeGeneral.vBatWarn = 90; +#endif + +#if defined(DEFAULT_MODE) + g_eeGeneral.stickMode = DEFAULT_MODE-1; +#endif + +#if defined(PCBTARANIS) + g_eeGeneral.templateSetup = 17; /* TAER */ +#endif + +#if !defined(CPUM64) + g_eeGeneral.backlightMode = e_backlight_mode_all; + g_eeGeneral.lightAutoOff = 2; + g_eeGeneral.inactivityTimer = 10; +#endif + +#if defined(CPUARM) + g_eeGeneral.wavVolume = 2; + g_eeGeneral.backgroundVolume = 1; +#endif + + g_eeGeneral.chkSum = 0xFFFF; +} + +uint16_t evalChkSum() +{ + uint16_t sum = 0; + const int16_t *calibValues = (const int16_t *) &g_eeGeneral.calib[0]; + for (int i=0; i<12; i++) + sum += calibValues[i]; + return sum; +} + +#if defined(PCBTARANIS) +void clearInputs() +{ + memset(g_model.expoData, 0, sizeof(g_model.expoData)); // clear all expos +} + +void defaultInputs() +{ + clearInputs(); + + for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; + expo->curve.type = CURVE_REF_EXPO; + expo->chn = i; + expo->weight = 100; + expo->mode = 3; // TODO constant + for (int c=0; c<4; c++) { +#if defined(TRANSLATIONS_CZ) && defined(CPUARM) + g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+STR_INPUTNAMES[0]*(stick_index-1)+c]); +#else + g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[1+STR_VSRCRAW[0]*stick_index+c]); +#endif + } + } + eeDirty(EE_MODEL); +} +#endif + +#if defined(TEMPLATES) +inline void applyDefaultTemplate() +{ + applyTemplate(TMPL_SIMPLE_4CH); +} +#else +void applyDefaultTemplate() +{ + for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; + expo->curve.type = CURVE_REF_EXPO; + expo->chn = i; + expo->weight = 100; + expo->mode = 3; // TODO constant + for (int c=0; c<4; c++) { +#if defined(TRANSLATIONS_CZ) && defined(CPUARM) + g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+STR_INPUTNAMES[0]*(stick_index-1)+c]); +#else + g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[1+STR_VSRCRAW[0]*stick_index+c]); +#endif + } +#endif + + MixData *mix = mixAddress(i); + mix->destCh = i; + mix->weight = 100; + +#if defined(PCBTARANIS) + mix->srcRaw = i+1; +#else + mix->srcRaw = MIXSRC_Rud - 1 + channel_order(i+1); +#endif + } + eeDirty(EE_MODEL); +} +#endif + +#if defined(CPUARM) +void checkModelIdUnique(uint8_t id) +{ + for (uint8_t i=0; ichn > index) + break; + else if (line->chn < index) + continue; + else if (line->srcRaw >= MIXSRC_FIRST_LOGICAL_SWITCH) + return true; + } + return false; +} +#endif + +#if defined(AUTOSOURCE) +int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) +{ + int8_t result = 0; + static tmr10ms_t s_move_last_time = 0; + +#if defined(PCBTARANIS) + static int16_t inputsStates[MAX_INPUTS]; + if (min <= MIXSRC_FIRST_INPUT) { + for (uint8_t i=0; i 512) { + if (!isInputRecursive(i)) { + result = MIXSRC_FIRST_INPUT+i; + break; + } + } + } + } +#endif + + static int16_t sourcesStates[NUM_STICKS+NUM_POTS]; + if (result == 0) { + for (uint8_t i=0; i 512) { + result = MIXSRC_Rud+i; + break; + } + } + } + + bool recent = ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10); + if (recent) { + result = 0; + } + + if (result || recent) { +#if defined(PCBTARANIS) + memcpy(inputsStates, anas, sizeof(inputsStates)); +#endif + memcpy(sourcesStates, calibratedStick, sizeof(sourcesStates)); + } + + s_move_last_time = get_tmr10ms(); + return result; +} +#endif + +#if defined(FLIGHT_MODES) +uint8_t getFlightMode() +{ + for (uint8_t i=1; iswtch && getSwitch(phase->swtch)) { + return i; + } + } + return 0; +} +#endif + +trim_t getRawTrimValue(uint8_t phase, uint8_t idx) +{ + FlightModeData *p = flightModeAddress(phase); +#if defined(PCBSTD) + return (((trim_t)p->trim[idx]) << 2) + ((p->trim_ext >> (2*idx)) & 0x03); +#else + return p->trim[idx]; +#endif +} + +int getTrimValue(uint8_t phase, uint8_t idx) +{ +#if defined(PCBTARANIS) + int result = 0; + for (uint8_t i=0; i> 1; + if (p == phase || phase == 0) { + return result + v.value; + } + else { + phase = p; + if (v.mode % 2 != 0) { + result += v.value; + } + } + } + } + return 0; +#else + return getRawTrimValue(getTrimFlightPhase(phase, idx), idx); +#endif +} + +#if defined(PCBTARANIS) +bool setTrimValue(uint8_t phase, uint8_t idx, int trim) +{ + for (uint8_t i=0; itrim[idx]; + if (v.mode == TRIM_MODE_NONE) + return false; + unsigned int p = v.mode >> 1; + if (p == phase || phase == 0) { + v.value = trim; + break; + } + else if (v.mode % 2 == 0) { + phase = p; + } + else { + v.value = limit(TRIM_EXTENDED_MIN, trim - getTrimValue(p, idx), TRIM_EXTENDED_MAX); + break; + } + } + eeDirty(EE_MODEL); + return true; +} +#else +void setTrimValue(uint8_t phase, uint8_t idx, int trim) +{ +#if defined(PCBSTD) + FlightModeData *p = flightModeAddress(phase); + p->trim[idx] = (int8_t)(trim >> 2); + idx <<= 1; + p->trim_ext = (p->trim_ext & ~(0x03 << idx)) + (((trim & 0x03) << idx)); +#else + FlightModeData *p = flightModeAddress(phase); + p->trim[idx] = trim; +#endif + eeDirty(EE_MODEL); +} +#endif + +#if !defined(PCBTARANIS) +uint8_t getTrimFlightPhase(uint8_t phase, uint8_t idx) +{ + for (uint8_t i=0; i= phase) result++; + phase = result; + } + return 0; +} +#endif + +#if defined(ROTARY_ENCODERS) +uint8_t getRotaryEncoderFlightPhase(uint8_t idx) +{ + uint8_t phase = mixerCurrentFlightMode; + for (uint8_t i=0; irotaryEncoders[idx]; + if (value <= ROTARY_ENCODER_MAX) return phase; + uint8_t result = value-ROTARY_ENCODER_MAX-1; + if (result >= phase) result++; + phase = result; + } + return 0; +} + +int16_t getRotaryEncoder(uint8_t idx) +{ + return flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]; +} + +void incRotaryEncoder(uint8_t idx, int8_t inc) +{ + g_rotenc[idx] += inc; + int16_t *value = &(flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]); + *value = limit((int16_t)-1024, (int16_t)(*value + (inc * 8)), (int16_t)+1024); + eeDirty(EE_MODEL); +} +#endif + +#if defined(GVARS) + +#if defined(PCBSTD) + #define SET_GVAR_VALUE(idx, phase, value) \ + (GVAR_VALUE(idx, phase) = value, eeDirty(EE_MODEL)) +#else + #define SET_GVAR_VALUE(idx, phase, value) \ + GVAR_VALUE(idx, phase) = value; \ + eeDirty(EE_MODEL); \ + if (g_model.gvars[idx].popup) { \ + s_gvar_last = idx; \ + s_gvar_timer = GVAR_DISPLAY_TIME; \ + } +#endif + +#if defined(PCBSTD) +int16_t getGVarValue(int16_t x, int16_t min, int16_t max) +{ + if (GV_IS_GV_VALUE(x, min, max)) { + int8_t idx = GV_INDEX_CALCULATION(x, max); + int8_t mul = 1; + + if (idx < 0) { + idx = -1-idx; + mul = -1; + } + + x = GVAR_VALUE(idx, -1) * mul; + } + + return limit(min, x, max); +} + +void setGVarValue(uint8_t idx, int8_t value) +{ + if (GVAR_VALUE(idx, -1) != value) { + SET_GVAR_VALUE(idx, -1, value); + } +} +#else +uint8_t s_gvar_timer = 0; +uint8_t s_gvar_last = 0; + +uint8_t getGVarFlightPhase(uint8_t phase, uint8_t idx) +{ + for (uint8_t i=0; i= phase) result++; + phase = result; + } + return 0; +} + +int16_t getGVarValue(int16_t x, int16_t min, int16_t max, int8_t phase) +{ + if (GV_IS_GV_VALUE(x, min, max)) { + int8_t idx = GV_INDEX_CALCULATION(x, max); + int8_t mul = 1; + + if (idx < 0) { + idx = -1-idx; + mul = -1; + } + + x = GVAR_VALUE(idx, getGVarFlightPhase(phase, idx)) * mul; + } + return limit(min, x, max); +} + +void setGVarValue(uint8_t idx, int16_t value, int8_t phase) +{ + phase = getGVarFlightPhase(phase, idx); + if (GVAR_VALUE(idx, phase) != value) { + SET_GVAR_VALUE(idx, phase, value); + } +} +#endif + +#endif + +#if defined(CPUARM) +getvalue_t convert16bitsTelemValue(source_t channel, ls_telemetry_value_t value) +{ + return value; +} + +getvalue_t convert8bitsTelemValue(source_t channel, ls_telemetry_value_t value) +{ + return value; +} + +#if defined(FRSKY) +ls_telemetry_value_t minTelemValue(source_t channel) +{ + return 0; +} + +ls_telemetry_value_t maxTelemValue(source_t channel) +{ + return 30000; +} +#endif + +ls_telemetry_value_t max8bitsTelemValue(source_t channel) +{ + return 30000; +} + +#elif defined(FRSKY) + +/* +ls_telemetry_value_t minTelemValue(uint8_t channel) +{ + switch (channel) { + case TELEM_TIMER1: + case TELEM_TIMER2: + return -3600; + case TELEM_ALT: + case TELEM_MIN_ALT: + case TELEM_MAX_ALT: + case TELEM_GPSALT: + return -500; + case TELEM_T1: + case TELEM_MAX_T1: + case TELEM_T2: + case TELEM_MAX_T2: + return -30; + case TELEM_ACCx: + case TELEM_ACCy: + case TELEM_ACCz: + return -1000; + case TELEM_VSPEED: + return -3000; + default: + return 0; + } +} +*/ +ls_telemetry_value_t maxTelemValue(uint8_t channel) +{ + switch (channel) { + case TELEM_FUEL: + case TELEM_RSSI_TX: + case TELEM_RSSI_RX: + return 100; + case TELEM_HDG: + return 180; + default: + return 255; + } +} +#endif + +#if !defined(CPUARM) +getvalue_t convert8bitsTelemValue(uint8_t channel, ls_telemetry_value_t value) +{ + getvalue_t result; + switch (channel) { + case TELEM_TIMER1: + case TELEM_TIMER2: + result = value * 5; + break; +#if defined(FRSKY) + case TELEM_ALT: + case TELEM_GPSALT: + case TELEM_MAX_ALT: + case TELEM_MIN_ALT: + result = value * 8 - 500; + break; + case TELEM_RPM: + case TELEM_MAX_RPM: + result = value * 50; + break; + case TELEM_T1: + case TELEM_T2: + case TELEM_MAX_T1: + case TELEM_MAX_T2: + result = (getvalue_t)value - 30; + break; + case TELEM_CELL: + case TELEM_HDG: + case TELEM_SPEED: + case TELEM_MAX_SPEED: + result = value * 2; + break; + case TELEM_ASPEED: + case TELEM_MAX_ASPEED: + result = value * 20; + break; + case TELEM_DIST: + case TELEM_MAX_DIST: + result = value * 8; + break; + case TELEM_CURRENT: + case TELEM_POWER: + case TELEM_MAX_CURRENT: + case TELEM_MAX_POWER: + result = value * 5; + break; + case TELEM_CONSUMPTION: + result = value * 100; + break; + case TELEM_VSPEED: + result = ((getvalue_t)value - 125) * 10; + break; +#endif + default: + result = value; + break; + } + return result; +} +#endif + +#if defined(FRSKY)&& !defined(CPUARM) +FORCEINLINE void convertUnit(getvalue_t & val, uint8_t & unit) +{ + if (IS_IMPERIAL_ENABLE()) { + if (unit == UNIT_TEMPERATURE) { + val += 18; + val *= 115; + val >>= 6; + } + if (unit == UNIT_DIST) { + // m to ft *105/32 + val = val * 3 + (val >> 2) + (val >> 5); + } + if (unit == UNIT_FEET) { + unit = UNIT_DIST; + } + if (unit == UNIT_KTS) { + // kts to mph + unit = UNIT_SPEED; + val = (val * 23) / 20; + } + } + else { + if (unit == UNIT_KTS) { + // kts to km/h + unit = UNIT_SPEED; +#if defined(CPUARM) + val = (val * 1852) / 1000; +#else + val = (val * 50) / 27; +#endif + } + } + + if (unit == UNIT_HDG) { + unit = UNIT_TEMPERATURE; + } +} +#endif + +#define INAC_STICKS_SHIFT 6 +#define INAC_SWITCHES_SHIFT 8 +bool inputsMoved() +{ + uint8_t sum = 0; + for (uint8_t i=0; i> INAC_STICKS_SHIFT; + for (uint8_t i=0; i> INAC_SWITCHES_SHIFT; + + if (abs((int8_t)(sum-inactivity.sum)) > 1) { + inactivity.sum = sum; + return true; + } + else { + return false; + } +} + +void checkBacklight() +{ + static uint8_t tmr10ms ; + +#if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION) + rotencPoll(); +#endif + + uint8_t x = g_blinkTmr10ms; + if (tmr10ms != x) { + tmr10ms = x; + if (inputsMoved()) { + inactivity.counter = 0; + if (g_eeGeneral.backlightMode & e_backlight_mode_sticks) + backlightOn(); + } + + bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || lightOffCounter || isFunctionActive(FUNCTION_BACKLIGHT)); + if (flashCounter) backlightOn = !backlightOn; + if (backlightOn) + BACKLIGHT_ON(); + else + BACKLIGHT_OFF(); + +#if defined(PCBSTD) && defined(VOICE) && !defined(SIMU) + Voice.voice_process() ; +#endif + } +} + +void backlightOn() +{ + lightOffCounter = ((uint16_t)g_eeGeneral.lightAutoOff*250) << 1; +} + +#if MENUS_LOCK == 1 +bool readonly = true; +bool readonlyUnlocked() +{ + if (readonly) { + POPUP_WARNING(STR_MODS_FORBIDDEN); + return false; + } + else { + return true; + } +} +#endif + +#if defined(SPLASH) +void doSplash() +{ + if (SPLASH_NEEDED()) { + displaySplash(); + +#if !defined(CPUARM) + AUDIO_TADA(); +#endif + +#if defined(PCBSTD) + lcdSetContrast(); +#elif !defined(PCBTARANIS) + tmr10ms_t curTime = get_tmr10ms() + 10; + uint8_t contrast = 10; + lcdSetRefVolt(contrast); +#endif + + getADC(); // init ADC array + + inputsMoved(); + + tmr10ms_t tgtime = get_tmr10ms() + SPLASH_TIMEOUT; + while (tgtime != get_tmr10ms()) { +#if defined(SIMU) + SIMU_SLEEP(1); +#elif defined(CPUARM) + CoTickDelay(1); +#endif + + getADC(); + +#if defined(FSPLASH) + // Splash is forced, we can't skip it + if (!(g_eeGeneral.splashMode & 0x04)) { +#endif + + if (keyDown() || inputsMoved()) return; + +#if defined(FSPLASH) + } +#endif + + if (pwrCheck()==e_power_off) return; + +#if !defined(PCBTARANIS) && !defined(PCBSTD) + if (curTime < get_tmr10ms()) { + curTime += 10; + if (contrast < g_eeGeneral.contrast) { + contrast += 1; + lcdSetRefVolt(contrast); + } + } +#endif + + checkBacklight(); + } + } +} +#else +#define Splash() +#define doSplash() +#endif + +#if defined(GUI) +void checkAll() +{ +#if !defined(PCBSKY9X) + checkLowEEPROM(); +#endif + +#if defined(MODULE_ALWAYS_SEND_PULSES) + startupWarningState = STARTUP_WARNING_THROTTLE; +#else + checkTHR(); + checkSwitches(); +#endif + +#if defined(CPUARM) + if (g_model.displayChecklist && modelHasNotes()) { + pushModelNotes(); + } +#endif + +#if defined(CPUARM) + if (!clearKeyEvents()) { + displayPopup(STR_KEYSTUCK); + tmr10ms_t tgtime = get_tmr10ms() + 500; + while (tgtime != get_tmr10ms()) { +#if defined(SIMU) + SIMU_SLEEP(1); +#elif defined(CPUARM) + CoTickDelay(1); +#endif + wdt_reset(); + } + } +#else // #if defined(CPUARM) + clearKeyEvents(); +#endif // #if defined(CPUARM) + + START_SILENCE_PERIOD(); +} +#endif // GUI + +#if defined(MODULE_ALWAYS_SEND_PULSES) +void checkStartupWarnings() +{ + if (startupWarningState < STARTUP_WARNING_DONE) { + if (startupWarningState == STARTUP_WARNING_THROTTLE) + checkTHR(); + else + checkSwitches(); + } +} +#endif + +#if !defined(PCBSKY9X) +void checkLowEEPROM() +{ + if (g_eeGeneral.disableMemoryWarning) return; + if (EeFsGetFree() < 100) { + ALERT(STR_EEPROMWARN, STR_EEPROMLOWMEM, AU_ERROR); + } +} +#endif + +void checkTHR() +{ + uint8_t thrchn = ((g_model.thrTraceSrc==0) || (g_model.thrTraceSrc>NUM_POTS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1; + // throttle channel is either the stick according stick mode (already handled in evalInputs) + // or P1 to P3; + // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS) we assume the throttle stick is the input + // no other information available at the moment, and good enough to my option (otherwise too much exceptions...) + +#if defined(MODULE_ALWAYS_SEND_PULSES) + int16_t v = calibratedStick[thrchn]; + if (v<=THRCHK_DEADBAND-1024 || g_model.disableThrottleWarning || pwrCheck()==e_power_off || keyDown()) { + startupWarningState = STARTUP_WARNING_THROTTLE+1; + } + else { + calibratedStick[thrchn] = -1024; +#if !defined(PCBTARANIS) + if (thrchn < NUM_STICKS) { + rawAnas[thrchn] = anas[thrchn] = calibratedStick[thrchn]; + } +#endif + MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); + } +#else + if (g_model.disableThrottleWarning) return; + getADC(); + evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job + + int16_t v = calibratedStick[thrchn]; + if (v<=(THRCHK_DEADBAND-1024)) return; // prevent warning if throttle input OK + + // first - display warning; also deletes inputs if any have been before + MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); + + while (1) { + + SIMU_SLEEP(1); + + getADC(); + + evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job + v = calibratedStick[thrchn]; + + if (pwrCheck()==e_power_off || keyDown() || v<=(THRCHK_DEADBAND-1024)) + break; + + checkBacklight(); + + wdt_reset(); + } +#endif +} + +void checkAlarm() // added by Gohst +{ + if (g_eeGeneral.disableAlarmWarning) + return; + + if (IS_SOUND_OFF()) + ALERT(STR_ALARMSWARN, STR_ALARMSDISABLED, AU_ERROR); +} + +void alert(const pm_char * t, const pm_char *s MESSAGE_SOUND_ARG) +{ + MESSAGE(t, s, STR_PRESSANYKEY, sound); + + while(1) + { + SIMU_SLEEP(1); + + if (pwrCheck() == e_power_off) { + // the radio has been powered off during the ALERT + pwrOff(); // turn power off now + } + + if (keyDown()) return; // wait for key release + + checkBacklight(); + + wdt_reset(); + } +} + +#if defined(GVARS) +int8_t trimGvar[NUM_STICKS] = { -1, -1, -1, -1 }; +#endif + +#if defined(CPUARM) +void checkTrims() +{ + uint8_t event = getEvent(true); + if (event && !IS_KEY_BREAK(event)) { + int8_t k = EVT_KEY_MASK(event) - TRM_BASE; +#else +uint8_t checkTrim(uint8_t event) +{ + int8_t k = EVT_KEY_MASK(event) - TRM_BASE; + if (k>=0 && k<8 && !IS_KEY_BREAK(event)) { +#endif + // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP + uint8_t idx = CONVERT_MODE((uint8_t)k/2); + uint8_t phase; + int before; + bool thro; + +#if defined(CPUARM) + trimsDisplayTimer = 200; // 2 seconds + trimsDisplayMask |= (1<=mark && beforemark))) { + after = mark; + beepTrim = (mark == 0 ? 1 : 2); + } + } + + if ((beforeTRIM_MAX) || (before>after && after TRIM_EXTENDED_MAX) { + after = TRIM_EXTENDED_MAX; + } + +#if defined(GVARS) + if (TRIM_REUSED(idx)) { + SET_GVAR_VALUE(trimGvar[idx], phase, after); + } + else +#endif + { +#if defined(PCBTARANIS) + if (!setTrimValue(phase, idx, after)) { + // we don't play a beep, so we exit now the function + return; + } +#else + setTrimValue(phase, idx, after); +#endif + } + +#if defined(AUDIO) + // toneFreq higher/lower according to trim position + // limit the frequency, range -125 to 125 = toneFreq: 19 to 101 + if (after > TRIM_MAX) + after = TRIM_MAX; + if (after < TRIM_MIN) + after = TRIM_MIN; +#if defined(CPUARM) + after <<= 3; + after += 120*16; +#else + after >>= 2; + after += 60; +#endif +#endif + + if (beepTrim) { + if (beepTrim == 1) { + AUDIO_TRIM_MIDDLE(after); + pauseEvents(event); + } + else { + AUDIO_TRIM_END(after); + killEvents(event); + } + } + else { + AUDIO_TRIM(event, after); + } +#if !defined(CPUARM) + return 0; +#endif + } +#if !defined(CPUARM) + return event; +#endif +} + +#if !defined(SIMU) +static uint16_t s_anaFilt[NUMBER_ANALOG]; +#endif + +#if defined(SIMU) +uint16_t BandGap = 225; +#elif defined(CPUM2560) +// #define STARTADCONV (ADCSRA = (1<> 3; +#if defined(PCBTARANIS) + if (calibrationState) v = temp[x] >> 1; + StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x]; + if (!calibrationState && IS_POT_MULTIPOS(x) && calib->count>0 && calib->count> 4); + s_anaFilt[x] = 2*RESX; + for (int i=0; icount; i++) { + if (vShifted < calib->steps[i]) { + s_anaFilt[x] = i*2*RESX/calib->count; + break; + } + } + } + else +#endif + s_anaFilt[x] = v; + } +} +#else + +/** + * Read ADC using 10 bits + */ +inline uint16_t read_adc10(uint8_t adc_input) +{ + uint16_t temp_ana; + ADMUX = adc_input|ADC_VREF_TYPE; +#if defined(TELEMETRY_MOD_14051) + ADCSRA &= 0x87; +#endif + ADCSRA |= 1 << ADSC; // Start the AD conversion + while (ADCSRA & (1 << ADSC)); // Wait for the AD conversion to complete + temp_ana = ADC; + ADCSRA |= 1 << ADSC; // Start the second AD conversion + while (ADCSRA & (1 << ADSC)); // Wait for the AD conversion to complete + temp_ana += ADC; + return temp_ana; +} + +#if defined(TELEMETRY_MOD_14051) +enum MuxInput { + MUX_BATT, + MUX_THR, + MUX_AIL, + MUX_MAX = MUX_AIL +}; + +uint8_t pf7_digital[2]; +/** + * Update ADC PF7 using 14051 multiplexer + * X0 : Battery voltage + * X1 : THR SW + * X2 : AIL SW + */ +void readMultiplexAna() +{ + static uint8_t muxNum = MUX_BATT; + uint16_t temp_ana; + uint8_t nextMuxNum = muxNum-1; + + DDRC |= 0xC1; + temp_ana = read_adc10(7); + + switch (muxNum) { + case MUX_BATT: + s_anaFilt[TX_VOLTAGE] = temp_ana; + nextMuxNum = MUX_MAX; + break; + case MUX_THR: + case MUX_AIL: + // Digital switch depend from input voltage + // take half voltage to determine digital state + pf7_digital[muxNum-1] = (temp_ana >= (s_anaFilt[TX_VOLTAGE] / 2)) ? 1 : 0; + break; + } + + // set the mux number for the next ADC convert, + // stabilize voltage before ADC read. + muxNum = nextMuxNum; + PORTC &= ~((1 << PC7) | (1 << PC6) | (1 << PC0)); // Clear CTRL ABC + switch (muxNum) { + case 1: + PORTC |= (1 << PC6); // Mux CTRL A : SW_THR + break; + case 2: + PORTC |= (1 << PC7); // Mux CTRL B : SW_AIL + break; + } +} +#endif + +void getADC() +{ +#if defined(TELEMETRY_MOD_14051) + readMultiplexAna(); + #define ADC_READ_COUNT 7 +#else + #define ADC_READ_COUNT 8 +#endif + + for (uint8_t adc_input=0; adc_input= 255 + int16_t s_traceWr; + int16_t s_traceCnt; +#else + uint8_t s_traceWr; + int16_t s_traceCnt; +#endif +uint8_t s_cnt_10s; +uint16_t s_cnt_samples_thr_10s; +uint16_t s_sum_samples_thr_10s; +#endif + +FORCEINLINE void evalTrims() +{ + uint8_t phase = mixerCurrentFlightMode; + for (uint8_t i=0; i throttle trim if applicable + int16_t trim = getTrimValue(phase, i); +#if !defined(PCBTARANIS) + if (i==THR_STICK && g_model.thrTrim) { + int16_t trimMin = g_model.extendedTrims ? TRIM_EXTENDED_MIN : TRIM_MIN; + trim = (((g_model.throttleReversed)?(int32_t)(trim+trimMin):(int32_t)(trim-trimMin)) * (RESX-anas[i])) >> (RESX_SHIFT+1); + } +#endif + if (trimsCheckTimer > 0) { + trim = 0; + } + + trims[i] = trim*2; + } +} + +#if !defined(PCBSTD) +uint8_t mSwitchDuration[1+NUM_ROTARY_ENCODERS] = { 0 }; +#define CFN_PRESSLONG_DURATION 100 +#endif + + +uint8_t s_mixer_first_run_done = false; + +void doMixerCalculations() +{ + static tmr10ms_t lastTMR = 0; + + tmr10ms_t tmr10ms = get_tmr10ms(); + uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); + // handle tick10ms overrun + // correct overflow handling costs a lot of code; happens only each 11 min; + // therefore forget the exact calculation and use only 1 instead; good compromise + +#if !defined(CPUARM) + lastTMR = tmr10ms; +#endif + + getADC(); + +#if defined(PCBTARANIS) + processSbusInput(); +#endif + + getSwitchesPosition(!s_mixer_first_run_done); + +#if defined(CPUARM) + lastTMR = tmr10ms; +#endif + +#if defined(PCBSKY9X) && !defined(REVA) && !defined(SIMU) + Current_analogue = (Current_analogue*31 + s_anaFilt[8] ) >> 5 ; + if (Current_analogue > Current_max) + Current_max = Current_analogue ; +#elif defined(PCBGRUVIN9X) && !defined(SIMU) + // For PCB V4, use our own 1.2V, external reference (connected to ADC3) + ADCSRB &= ~(1< NUM_POTS) { + uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; + val = channelOutputs[ch]; + + LimitData *lim = limitAddress(ch); + int16_t gModelMax = LIMIT_MAX_RESX(lim); + int16_t gModelMin = LIMIT_MIN_RESX(lim); + + if (lim->revert) + val = -val + gModelMax; + else + val = val - gModelMin; + +#if defined(PPM_LIMITS_SYMETRICAL) + if (lim->symetrical) { + val -= calc1000toRESX(lim->offset); + } +#endif + + gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default + // usually max is 1024 min is -1024 --> max-min = 2048 full range + +#ifdef ACCURAT_THROTTLE_TIMER + if (gModelMax!=0 && gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs +#else + // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) + gModelMax+=255; // force rounding up --> gModelMax is bigger --> val is smaller + gModelMax >>= (10-2); + + if (gModelMax!=0 && gModelMax!=8) { + val = (val << 3) / gModelMax; // rescaling only needed if Min, Max differs + } +#endif + + if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits + } + else { +#ifdef PCBTARANIS + val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; +#else + val = RESX + (g_model.thrTraceSrc == 0 ? rawAnas[THR_STICK] : calibratedStick[g_model.thrTraceSrc+NUM_STICKS-1]); +#endif + } + +#if defined(ACCURAT_THROTTLE_TIMER) + val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4) +#else + val >>= (RESX_SHIFT-4); // calibrate it +#endif + + evalTimers(val, tick10ms); + + static uint8_t s_cnt_100ms; + static uint8_t s_cnt_1s; + static uint8_t s_cnt_samples_thr_1s; + static uint16_t s_sum_samples_thr_1s; + + s_cnt_samples_thr_1s++; + s_sum_samples_thr_1s+=val; + + if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec + s_cnt_100ms -= 10; + s_cnt_1s += 1; + + logicalSwitchesTimerTick(); + + if (s_cnt_1s >= 10) { // 1sec + s_cnt_1s -= 10; + sessionTimer += 1; + + struct t_inactivity *ptrInactivity = &inactivity; + FORCE_INDIRECT(ptrInactivity) ; + ptrInactivity->counter++; + if ((((uint8_t)ptrInactivity->counter)&0x07)==0x01 && g_eeGeneral.inactivityTimer && g_vbat100mV>50 && ptrInactivity->counter > ((uint16_t)g_eeGeneral.inactivityTimer*60)) + AUDIO_INACTIVITY(); + +#if defined(AUDIO) + if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1); + if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2); + if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3); +#endif + +#if defined(ACCURAT_THROTTLE_TIMER) + val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; + s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps + if (val) s_timeCumThr += 1; + s_sum_samples_thr_1s>>=2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this +#else + val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; + s_timeCum16ThrP += (val>>1); + if (val) s_timeCumThr += 1; +#endif + +#if defined(THRTRACE) + // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size. + // in case buffer runs out, it wraps around + // resolution for y axis is only 32, therefore no higher value makes sense + s_cnt_samples_thr_10s += s_cnt_samples_thr_1s; + s_sum_samples_thr_10s += s_sum_samples_thr_1s; + + if (++s_cnt_10s >= 10) { // 10s + s_cnt_10s -= 10; + val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s; + s_sum_samples_thr_10s = 0; + s_cnt_samples_thr_10s = 0; + + s_traceBuf[s_traceWr++] = val; + if (s_traceWr >= MAXTRACE) s_traceWr = 0; + if (s_traceCnt >= 0) s_traceCnt++; + } +#endif + + s_cnt_samples_thr_1s = 0; + s_sum_samples_thr_1s = 0; + } + } + +#if defined(PXX) || defined(DSM2) + static uint8_t countRangecheck = 0; + for (uint8_t i=0; i= 250) { + countRangecheck = 0; + AUDIO_PLAY(AU_FRSKY_CHEEP); + } + } + } +#endif + +#if defined(CPUARM) + checkTrims(); +#endif + } + + s_mixer_first_run_done = true; +} + + + +#if defined(NAVIGATION_STICKS) +uint8_t StickScrollAllowed; +uint8_t StickScrollTimer; +static const pm_uint8_t rate[] PROGMEM = { 0, 0, 100, 40, 16, 7, 3, 1 } ; + +uint8_t calcStickScroll( uint8_t index ) +{ + uint8_t direction; + int8_t value; + + if ( ( g_eeGeneral.stickMode & 1 ) == 0 ) + index ^= 3; + + value = calibratedStick[index] / 128; + direction = value > 0 ? 0x80 : 0; + if (value < 0) + value = -value; // (abs) + if (value > 7) + value = 7; + value = pgm_read_byte(rate+(uint8_t)value); + if (value) + StickScrollTimer = STICK_SCROLL_TIMEOUT; // Seconds + return value | direction; +} +#endif + +void opentxStart() +{ + doSplash(); + +#if defined(DEBUG_TRACE_BUFFER) + trace_event(trace_start, 0x12345678); +#endif + +#if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU) + for (int i=0; i<500 && !Card_initialized; i++) { + CoTickDelay(1); // 2ms + } +#endif + +#if defined(CPUARM) + eeLoadModel(g_eeGeneral.currModel); +#endif + +#if defined(GUI) + checkAlarm(); + checkAll(); +#endif + +#if defined(GUI) + if (g_eeGeneral.chkSum != evalChkSum()) { + chainMenu(menuFirstCalib); + } +#endif + +} + +#if defined(CPUARM) || defined(CPUM2560) +void opentxClose() +{ + AUDIO_BYE(); + +#if defined(FRSKY) + // TODO needed? telemetryEnd(); +#endif + +#if defined(LUA) + luaClose(); +#endif + +#if defined(SDCARD) + closeLogs(); +#endif + +#if defined(HAPTIC) + hapticOff(); +#endif + + saveTimers(); + +#if defined(CPUARM) + for (int i=0; i> 6) == 2) { + for (uint8_t i=0; i value ) + { + repeater = 0; + if ( evt == 0 ) + { + if ( direction ) + { + evt = EVT_KEY_FIRST(KEY_UP); + } + else + { + evt = EVT_KEY_FIRST(KEY_DOWN); + } + } + } + } + else + { + value = calcStickScroll( 3 ); + direction = value & 0x80; + value &= 0x7F; + if ( value ) + { + if ( repeater > value ) + { + repeater = 0; + if ( evt == 0 ) + { + if ( direction ) + { + evt = EVT_KEY_FIRST(KEY_RIGHT); + } + else + { + evt = EVT_KEY_FIRST(KEY_LEFT); + } + } + } + } + } + } + } + else { + StickScrollTimer = 0; // Seconds + } + StickScrollAllowed = 1 ; + return evt; +} +#endif + +void checkBattery() +{ + static uint8_t counter = 0; +#if defined(GUI) && !defined(COLORLCD) + // TODO not the right menu I think ... + if (g_menuStack[g_menuStackPtr] == menuGeneralDiagAna) { + g_vbat100mV = 0; + counter = 0; + } +#endif + if (counter-- == 0) { + counter = 10; + int32_t instant_vbat = anaIn(TX_VOLTAGE); +#if defined(PCBTARANIS) + instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.vBatCalib)/128) * BATT_SCALE; + instant_vbat >>= 11; + instant_vbat += 2; // because of the diode +#elif defined(PCBSKY9X) + instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.vBatCalib)/128) * 4191; + instant_vbat /= 55296; +#elif defined(CPUM2560) + instant_vbat = (instant_vbat*1112 + instant_vbat*g_eeGeneral.vBatCalib + (BandGap<<2)) / (BandGap<<3); +#else + instant_vbat = (instant_vbat*16 + instant_vbat*g_eeGeneral.vBatCalib/8) / BandGap; +#endif + + static uint8_t s_batCheck; + static uint16_t s_batSum; + +#if defined(VOICE) + s_batCheck += 8; +#else + s_batCheck += 32; +#endif + + s_batSum += instant_vbat; + + if (g_vbat100mV == 0) { + g_vbat100mV = instant_vbat; + s_batSum = 0; + s_batCheck = 0; + } +#if defined(VOICE) + else if (!(s_batCheck & 0x3f)) { +#else + else if (s_batCheck == 0) { +#endif + g_vbat100mV = s_batSum / 8; + s_batSum = 0; +#if defined(VOICE) + if (s_batCheck != 0) { + // no alarms + } + else +#endif + if (g_vbat100mV <= g_eeGeneral.vBatWarn && g_vbat100mV>50) { + AUDIO_TX_BATTERY_LOW(); + } +#if defined(PCBSKY9X) + else if (g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { + AUDIO_TX_TEMP_HIGH(); + } + else if (g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.currentCalib)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { + AUDIO_TX_MAH_HIGH(); + } +#endif + } + } + +} + +int16_t g_ppmIns[NUM_TRAINER]; +uint8_t ppmInState = 0; // 0=unsync 1..8= wait for value i-1 +uint8_t ppmInValid = 0; + +#if !defined(SIMU) && !defined(CPUARM) + +volatile uint8_t g_tmr16KHz; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow +ISR(TIMER_16KHZ_VECT, ISR_NOBLOCK) +{ + g_tmr16KHz++; // gruvin: Not 16KHz. Overflows occur at 61.035Hz (1/256th of 15.625KHz) + // to give *16.384ms* intervals. Kind of matters for accuracy elsewhere. ;) + // g_tmr16KHz is used to software-construct a 16-bit timer + // from TIMER-0 (8-bit). See getTmr16KHz, below. +} + +uint16_t getTmr16KHz() +{ + while(1){ + uint8_t hb = g_tmr16KHz; + uint8_t lb = COUNTER_16KHZ; + if(hb-g_tmr16KHz==0) return (hb<<8)|lb; + } +} + +#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) +// Clocks every 128 uS +ISR(TIMER_AUDIO_VECT, ISR_NOBLOCK) +{ + cli(); + PAUSE_AUDIO_INTERRUPT(); // stop reentrance + sei(); + +#if defined(AUDIO) + AUDIO_DRIVER(); +#endif + +#if defined(VOICE) + VOICE_DRIVER(); +#endif + + cli(); + RESUME_AUDIO_INTERRUPT(); + sei(); +} +#endif + +// Clocks every 10ms +ISR(TIMER_10MS_VECT, ISR_NOBLOCK) +{ + // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that + static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed + +#if defined(AUDIO) + AUDIO_HEARTBEAT(); +#endif + +#if defined(BUZZER) + BUZZER_HEARTBEAT(); +#endif + +#if defined(HAPTIC) + HAPTIC_HEARTBEAT(); +#endif + + per10ms(); + + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + TIMER_10MS_COMPVAL += bump; +} + +// Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz +// equating to one count every half millisecond. (2 counts = 1ms). Control channel +// count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), +// corresponding to a PPM signal in the range 0.8ms to 2.2ms (1.5ms at center). +// (The timer is free-running and is thus not reset to zero at each capture interval.) +ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with ISR_NOBLOCK +{ + static uint16_t lastCapt; + + uint16_t capture=ICR3; + + // Prevent rentrance for this IRQ only + PAUSE_PPMIN_INTERRUPT(); + sei(); // enable other interrupts + + uint16_t val = (capture - lastCapt) / 2; + + // G: We process g_ppmIns immediately here, to make servo movement as smooth as possible + // while under trainee control + if (val>4000 && val < 16000) { // G: Prioritize reset pulse. (Needed when less than 8 incoming pulses) + ppmInState = 1; // triggered + } + else { + if (ppmInState>0 && ppmInState<=8) { + if (val>800 && val<2200) { // if valid pulse-width range + ppmInValid = PPM_IN_VALID_TIMEOUT; + g_ppmIns[ppmInState++ - 1] = (int16_t)(val - 1500) * (uint8_t)(g_eeGeneral.PPM_Multiplier+10)/10; //+-500 != 512, but close enough. + } + else { + ppmInState = 0; // not triggered + } + } + } + + lastCapt = capture; + + cli(); // disable other interrupts for stack pops before this function's RETI + RESUME_PPMIN_INTERRUPT(); +} +#endif + +#if defined(DSM2_SERIAL) && !defined(CPUARM) +FORCEINLINE void DSM2_USART0_vect() +{ + UDR0 = *((uint16_t*)pulses2MHzRPtr); // transmit next byte + + pulses2MHzRPtr += sizeof(uint16_t); + + if (pulses2MHzRPtr == pulses2MHzWPtr) { // if reached end of DSM2 data buffer ... + UCSR0B &= ~(1 << UDRIE0); // disable UDRE0 interrupt + } +} +#endif + +#if !defined(SIMU) && !defined(CPUARM) + +#if defined (FRSKY) + +// USART0 Transmit Data Register Emtpy ISR +FORCEINLINE void FRSKY_USART0_vect() +{ + if (frskyTxBufferCount > 0) { + UDR0 = frskyTxBuffer[--frskyTxBufferCount]; + } + else { + UCSR0B &= ~(1 << UDRIE0); // disable UDRE0 interrupt + } +} + +ISR(USART0_UDRE_vect) +{ +#if defined(FRSKY) && defined(DSM2_SERIAL) + if (IS_DSM2_PROTOCOL(g_model.protocol)) { // TODO not s_current_protocol? + DSM2_USART0_vect(); + } + else { + FRSKY_USART0_vect(); + } +#elif defined(FRSKY) + FRSKY_USART0_vect(); +#else + DSM2_USART0_vect(); +#endif +} +#endif +#endif + +#if defined(PCBTARANIS) + #define INSTANT_TRIM_MARGIN 10 /* around 1% */ +#else + #define INSTANT_TRIM_MARGIN 15 /* around 1.5% */ +#endif + +void instantTrim() +{ +#if defined(PCBTARANIS) + int16_t anas_0[NUM_INPUTS]; + evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks); + memcpy(anas_0, anas, sizeof(anas_0)); +#endif + + evalInputs(e_perout_mode_notrainer); + + for (uint8_t stick=0; sticksrcRaw-MIXSRC_Rud == stick) { + delta = anas[ed->chn] - anas_0[ed->chn]; + break; + } + } +#else + int16_t delta = anas[stick]; +#endif + if (abs(delta) >= INSTANT_TRIM_MARGIN) { + int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); + setTrimValue(trim_phase, stick, trim); + } + } + } + + eeDirty(EE_MODEL); + AUDIO_WARNING2(); +} + +void copySticksToOffset(uint8_t ch) +{ + pauseMixerCalculations(); + int32_t zero = (int32_t)channelOutputs[ch]; + + evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0); + int32_t val = chans[ch]; + LimitData *ld = limitAddress(ch); + limit_min_max_t lim = LIMIT_MIN(ld); + if (val < 0) { + val = -val; + lim = LIMIT_MIN(ld); + } +#if defined(CPUARM) + zero = (zero*256000 - val*lim) / (1024*256-val); +#else + zero = (zero*25600 - val*lim) / (26214-val); +#endif + ld->offset = (ld->revert ? -zero : zero); + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void copyTrimsToOffset(uint8_t ch) +{ + int16_t zero; + + pauseMixerCalculations(); + + evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims + zero = applyLimits(ch, chans[ch]); + + evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims + + int16_t output = applyLimits(ch, chans[ch]) - zero; + int16_t v = g_model.limitData[ch].offset; + if (g_model.limitData[ch].revert) output = -output; +#if defined(CPUARM) + v += (output * 125) / 128; +#else + v += output; +#endif + g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire + + resumeMixerCalculations(); + eeDirty(EE_MODEL); +} + +void moveTrimsToOffsets() // copy state of 3 primary to subtrim +{ + int16_t zeros[NUM_CHNOUT]; + + pauseMixerCalculations(); + + evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims + for (uint8_t i=0; i q ) { + *p-- = 0x55 ; + } +} + +uint16_t stack_free() +{ + unsigned char *p ; + + p = &__bss_end + 1 ; + while ( *p++ == 0x55 ); + return p - &__bss_end ; +} +#endif + +#if defined(CPUM2560) + #define OPENTX_INIT_ARGS const uint8_t mcusr +#elif defined(PCBSTD) + #define OPENTX_INIT_ARGS const uint8_t mcusr +#else + #define OPENTX_INIT_ARGS +#endif + +void opentxInit(OPENTX_INIT_ARGS) +{ + eeReadAll(); + +#if defined(CPUARM) + if (UNEXPECTED_SHUTDOWN()) { + unexpectedShutdown = 1; + } +#endif + +#if defined(PCBTARANIS) + BACKLIGHT_ON(); +#endif + +#if MENUS_LOCK == 1 + getMovedSwitch(); + if (TRIMS_PRESSED() && g_eeGeneral.switchUnlockStates==switches_states) { + readonly = false; + } +#endif + +#if defined(VOICE) + setVolume(g_eeGeneral.speakerVolume+VOLUME_LEVEL_DEF); +#endif + +#if defined(CPUARM) + audioQueue.start(); + setBacklight(g_eeGeneral.backlightBright); +#endif + +#if defined(PCBSKY9X) + // Set ADC gains here + setSticksGain(g_eeGeneral.sticksGain); +#endif + +#if defined(BLUETOOTH) + btInit(); +#endif + +#if defined(RTCLOCK) && !defined(COPROCESSOR) + rtcInit(); +#endif + + if (g_eeGeneral.backlightMode != e_backlight_mode_off) backlightOn(); // on Tx start turn the light on + + if (UNEXPECTED_SHUTDOWN()) { +#if !defined(CPUARM) + // is done above on ARM + unexpectedShutdown = 1; +#endif +#if defined(CPUARM) + eeLoadModel(g_eeGeneral.currModel); +#endif + } + else { + opentxStart(); + } + +#if defined(CPUARM) || defined(CPUM2560) + if (!g_eeGeneral.unexpectedShutdown) { + g_eeGeneral.unexpectedShutdown = 1; + eeDirty(EE_GENERAL); + } +#endif + +#if defined(GUI) + lcdSetContrast(); +#endif + backlightOn(); + +#if defined(PCBTARANIS) + uart3Init(g_eeGeneral.uart3Mode, MODEL_TELEMETRY_PROTOCOL()); +#endif + +#if defined(PCBSKY9X) && !defined(SIMU) + init_trainer_capture(); +#endif + +#if !defined(CPUARM) + doMixerCalculations(); +#endif + + startPulses(); + + wdt_enable(WDTO_500MS); +} + +#if !defined(SIMU) +int main(void) +{ + // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's + // important to disable it before commencing with system initialisation (or + // we could put a bunch more wdt_reset()s in. But I don't like that approach + // during boot up.) +#if defined(CPUM2560) || defined(CPUM2561) + uint8_t mcusr = MCUSR; // save the WDT (etc) flags + MCUSR = 0; // must be zeroed before disabling the WDT +#elif defined(PCBSTD) + uint8_t mcusr = MCUCSR; + MCUCSR = 0; +#endif +#if defined(PCBTARANIS) + g_eeGeneral.contrast=30; +#endif + wdt_disable(); + + boardInit(); + +#if defined(GUI) && !defined(PCBTARANIS) + lcdInit(); +#endif + + stack_paint(); + +#if defined(GUI) + g_menuStack[0] = menuMainView; + #if MENUS_LOCK != 2/*no menus*/ + g_menuStack[1] = menuModelSelect; + #endif +#endif + +#if defined(GUI) && !defined(PCBTARANIS) + lcdSetRefVolt(25); +#endif + + sei(); // interrupts needed for telemetryInit and eeReadAll. + +#if defined(FRSKY) && !defined(DSM2_SERIAL) + telemetryInit(); +#endif + +#if defined(DSM2_SERIAL) && !defined(FRSKY) + DSM2_Init(); +#endif + +#ifdef JETI + JETI_Init(); +#endif + +#ifdef ARDUPILOT + ARDUPILOT_Init(); +#endif + +#ifdef NMEA + NMEA_Init(); +#endif + +#ifdef MAVLINK + MAVLINK_Init(); +#endif + +#ifdef MENU_ROTARY_SW + init_rotary_sw(); +#endif + +#if !defined(CPUARM) + opentxInit(mcusr); +#endif + +#if defined(CPUARM) + tasksStart(); +#else +#if defined(CPUM2560) + uint8_t shutdown_state = 0; +#endif + + while (1) { +#if defined(CPUM2560) + if ((shutdown_state=pwrCheck()) > e_power_trainer) + break; +#endif + + perMain(); + + if (heartbeat == HEART_WDT_CHECK) { + wdt_reset(); + heartbeat = 0; + } + } +#endif + +#if defined(CPUM2560) + // Time to switch off + lcd_clear(); + displayPopup(STR_SHUTDOWN); + opentxClose(); + lcd_clear() ; + lcdRefresh() ; + pwrOff(); // Only turn power off if necessary + wdt_disable(); + while(1); // never return from main() - there is no code to return back, if any delays occurs in physical power it does dead loop. +#endif +} +#endif // !SIMU diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 2615f5d14..e79e0665b 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -270,6 +270,15 @@ #define convertSimuPath(x) (x) #endif +#if !defined(CPUM64) && !defined(ACCURAT_THROTTLE_TIMER) + // code cost is about 16 bytes for higher throttle accuracy for timer + // would not be noticable anyway, because all version up to this change had only 16 steps; + // now it has already 32 steps; this define would increase to 128 steps + #if !defined(ACCURAT_THROTTLE_TIMER) + #define ACCURAT_THROTTLE_TIMER + #endif +#endif + // RESX range is used for internal calculation; The menu says -100.0 to 100.0; internally it is -1024 to 1024 to allow some optimizations #define RESX_SHIFT 10 #define RESX 1024 @@ -867,16 +876,6 @@ extern uint16_t sessionTimer; extern uint16_t s_timeCumThr; extern uint16_t s_timeCum16ThrP; -struct TimerState { - uint16_t cnt; - uint16_t sum; - uint8_t state; - int16_t val; - uint8_t val_10ms; -}; - -extern TimerState timersStates[MAX_TIMERS]; - #if defined(OVERRIDE_CHANNEL_FUNCTION) #if defined(CPUARM) typedef int16_t safetych_t; @@ -895,12 +894,6 @@ extern uint8_t trimsDisplayTimer; extern uint8_t trimsDisplayMask; #endif -#define TMR_OFF 0 -#define TMR_RUNNING 1 -#define TMR_NEGATIVE 2 -#define TMR_STOPPED 3 -#define TMR_TRIGGED 4 -void timerReset(uint8_t idx); void flightReset(); extern uint8_t unexpectedShutdown; @@ -1068,12 +1061,6 @@ inline void resumeMixerCalculations() #define resumeMixerCalculations() #endif -#if defined(CPUARM) || defined(CPUM2560) -void saveTimers(); -#else -#define saveTimers() -#endif - void generalDefault(); void modelDefault(uint8_t id); diff --git a/radio/src/tests/timers.cpp b/radio/src/tests/timers.cpp new file mode 100644 index 000000000..ae1987409 --- /dev/null +++ b/radio/src/tests/timers.cpp @@ -0,0 +1,284 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "gtests.h" +#include "opentx.h" +#include "timers.h" + +/* +struct TimerState { + uint16_t cnt; + uint16_t sum; + uint8_t state; + int16_t val; + uint8_t val_10ms; +}; + +PACK(typedef struct t_TimerData { + int8_t mode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw + uint16_t start; + uint8_t countdownBeep:2; + uint8_t minuteBeep:1; + uint8_t persistent:2; // 0 off, 1 flight, 2 manual reset + uint8_t spare:3; + uint16_t value; +}) TimerData; + +enum TimerModes { + TMRMODE_NONE, + TMRMODE_ABS, + TMRMODE_THR, + TMRMODE_THR_REL, + TMRMODE_THR_TRG, + TMRMODE_COUNT +}; + +enum CountDownModes { + COUNTDOWN_SILENT, + COUNTDOWN_BEEPS, + COUNTDOWN_VOICE +}; + +*/ + +#if !defined(CPUARM) +#undef timerSet +void timerSet(int idx, int16_t val) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = val; + timerState.val_10ms = 0 ; +} +#endif // #if !defined(CPUARM) + +#if defined(ACCURAT_THROTTLE_TIMER) + #define THR_100 128 // approximately 10% full throttle + #define THR_50 64 // approximately 10% full throttle + #define THR_10 13 // approximately 10% full throttle +#else + #define THR_100 32 // approximately 10% full throttle + #define THR_50 16 // approximately 10% full throttle + #define THR_10 3 // approximately 10% full throttle +#endif + + +#define TEST_AB_EQUAL(a, b) if (a != b) { return ::testing::AssertionFailure() << \ + #a "= " << (uint32_t)a << ", " << #b "= " << (uint32_t)b; }; + +void initModelTimer(uint32_t idx, uint8_t mode, int16_t start = 0) +{ + g_model.timers[0].mode = mode; + g_model.timers[0].start = start; + g_model.timers[0].countdownBeep = COUNTDOWN_SILENT; + g_model.timers[0].minuteBeep = 0; + g_model.timers[0].spare = 0; +#if defined(CPUARM) || defined(CPUM2560) + g_model.timers[0].persistent = 0; + g_model.timers[0].value = 0; +#endif +} + +/* + Run timers for n seconds and test the end state +*/ +::testing::AssertionResult evalTimersForNSecondsAndTest(unsigned int n, uint8_t throttle, uint32_t idx, uint8_t state, int16_t value) +{ + unsigned int noLoops = n * 100; + while(noLoops--) + { + evalTimers(throttle, 1); + } + TEST_AB_EQUAL(timersStates[idx].state, state); + TEST_AB_EQUAL(timersStates[idx].val, value); + return ::testing::AssertionSuccess(); +} + +TEST(Timers, timerReset) +{ + initModelTimer(0, TMRMODE_THR_REL, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 200)); + + initModelTimer(1, TMRMODE_THR_REL, 0); + timerReset(1); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 0)); +} + +#if defined(CPUARM) +TEST(Timers, timerSet) +{ + timerSet(0, 500); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + + timerSet(1, 300); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 300)); +} +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) +TEST(Timers, saveRestoreTimers) +{ + g_model.timers[0].persistent = 1; + g_model.timers[1].persistent = 1; + timerSet(0, 500); + timerSet(1, 1500); + saveTimers(); + EXPECT_EQ(g_model.timers[0].value, 500); + EXPECT_EQ(g_model.timers[1].value, 1500); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 1500)); + + timerReset(0); + timerReset(1); + restoreTimers(); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 1500)); +} +#endif + +TEST(Timers, timerOff) +{ + initModelTimer(0, TMRMODE_NONE, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(10, THR_100, 0, TMR_OFF, 0)); +} + +TEST(Timers, timerAbsolute) +{ + initModelTimer(0, TMRMODE_ABS, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 101)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 201)); + + // max timer value test + timerSet(0, TIMER_MAX-10); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, TIMER_MAX-9)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, TIMER_MAX)); + + // test down-running + g_model.timers[0].start = 200; + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED, -101)); + + // min timer value test + timerSet(0, TIMER_MIN+10); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, TIMER_MIN+9)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, TIMER_MIN)); +} + +TEST(Timers, timerThrottle) +{ + initModelTimer(0, TMRMODE_THR, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 200)); + + // test down-running + g_model.timers[0].start = 200; + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-101)); +} + + + +TEST(Timers, timerThrottleRelative) +{ + initModelTimer(0, TMRMODE_THR_REL, 0); + + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_RUNNING, 150)); // 50% throttle == 50s +#if defined(ACCURAT_THROTTLE_TIMER) + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 160)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 160)); +#else + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 159)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 159)); +#endif + + // test down-running + initModelTimer(0, TMRMODE_THR_REL, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(200, THR_50, 0, TMR_NEGATIVE, -1)); // 50% throttle == 100s +#if defined(ACCURAT_THROTTLE_TIMER) + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_NEGATIVE,-11)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-111)); +#else + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_NEGATIVE,-10)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-110)); +#endif +} + +TEST(Timers, timerThrottleTriggered) +{ + initModelTimer(0, TMRMODE_THR_TRG, 0); + + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_OFF, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10-1, 0, TMR_OFF, 0)); // below threshold + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 300)); + + // test down-running + initModelTimer(0, TMRMODE_THR_TRG, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_OFF, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10-1, 0, TMR_OFF, 200)); // below threshold + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(101, THR_50, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(10, 0, 0, TMR_NEGATIVE,-11)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_STOPPED,-111)); +} diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp new file mode 100644 index 000000000..c24b429a5 --- /dev/null +++ b/radio/src/timers.cpp @@ -0,0 +1,209 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "opentx.h" +#include "timers.h" + +TimerState timersStates[MAX_TIMERS] = { 0 }; + + +void timerReset(uint8_t idx) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = g_model.timers[idx].start; + timerState.val_10ms = 0 ; +} + +#if defined(CPUARM) +void timerSet(int idx, int16_t val) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = val; + timerState.val_10ms = 0 ; +} +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) +void restoreTimers() +{ + for (uint8_t i=0; ival) { + g_model.timers[i].value = timerState->val; + eeDirty(EE_MODEL); + } + } + } + +#if defined(CPUARM) && !defined(REVA) + if (sessionTimer > 0) { + g_eeGeneral.globalTimer += sessionTimer; + eeDirty(EE_GENERAL); + sessionTimer = 0; + } +#endif +} +#endif // #if defined(CPUARM) || defined(CPUM2560) + +#if defined(ACCURAT_THROTTLE_TIMER) + #define THR_TRG_TRESHOLD 13 // approximately 10% full throttle +#else + #define THR_TRG_TRESHOLD 3 // approximately 10% full throttle +#endif + +void evalTimers(int16_t throttle, uint8_t tick10ms) +{ + for (uint8_t i=0; istate == TMR_OFF) && (tm != TMRMODE_THR_TRG)) { + timerState->state = TMR_RUNNING; + timerState->cnt = 0; + timerState->sum = 0; + } + + if (tm == TMRMODE_THR_REL) { + timerState->cnt++; + timerState->sum += throttle; + } + + if ((timerState->val_10ms += tick10ms) >= 100) { + if (timerState->val == TIMER_MAX) break; + if (timerState->val == TIMER_MIN) break; + + timerState->val_10ms -= 100 ; + int16_t newTimerVal = timerState->val; + if (tv) newTimerVal = tv - newTimerVal; + + if (tm == TMRMODE_ABS) { + newTimerVal++; + } + else if (tm == TMRMODE_THR) { + if (throttle) newTimerVal++; + } + else if (tm == TMRMODE_THR_REL) { + // @@@ open.20.fsguruh: why so complicated? we have already a s_sum field; use it for the half seconds (not showable) as well + // check for s_cnt[i]==0 is not needed because we are shure it is at least 1 + #if defined(ACCURAT_THROTTLE_TIMER) + if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) + newTimerVal++; // add second used of throttle + timerState->sum -= 128*timerState->cnt; + } + #else + if ((timerState->sum/timerState->cnt) >= 32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) + newTimerVal++; // add second used of throttle + timerState->sum -= 32*timerState->cnt; + } + #endif + timerState->cnt=0; + } + else if (tm == TMRMODE_THR_TRG) { + // we can't rely on (throttle || newTimerVal > 0) as a detection if timer should be running + // because having persistent timer brakes this rule + if ((throttle > THR_TRG_TRESHOLD) && timerState->state == TMR_OFF) { + timerState->state = TMR_RUNNING; // start timer running + timerState->cnt = 0; + timerState->sum = 0; + TRACE("Timer[%d] THr triggered", i); + } + if (timerState->state != TMR_OFF) newTimerVal++; + } + else { + if (tm > 0) tm -= (TMRMODE_COUNT-1); + if (getSwitch(tm)) + newTimerVal++; + } + + switch (timerState->state) { + case TMR_RUNNING: + if (tv && newTimerVal>=(int16_t)tv) { + AUDIO_TIMER_00(g_model.timers[i].countdownBeep); + timerState->state = TMR_NEGATIVE; + TRACE("Timer[%d] negative", i); + } + break; + case TMR_NEGATIVE: + if (newTimerVal >= (int16_t)tv + MAX_ALERT_TIME) { + timerState->state = TMR_STOPPED; + TRACE("Timer[%d] stopped state at %d", i, newTimerVal); + } + break; + } + + if (tv) newTimerVal = tv - newTimerVal; // if counting backwards - display backwards + + if (newTimerVal != timerState->val) { + timerState->val = newTimerVal; + if (timerState->state == TMR_RUNNING) { + if (g_model.timers[i].countdownBeep && g_model.timers[i].start) { + if (newTimerVal==30) { + AUDIO_TIMER_30(); + TRACE("Timer[%d] 30s announcement", i); + } + if (newTimerVal==20) { + AUDIO_TIMER_20(); + TRACE("Timer[%d] 20s announcement", i); + } + if (newTimerVal<=10) { + AUDIO_TIMER_LT10(g_model.timers[i].countdownBeep, newTimerVal); + TRACE("Timer[%d] %ds announcement", i, newTimerVal); + } + } + if (g_model.timers[i].minuteBeep && (newTimerVal % 60)==0) { + AUDIO_TIMER_MINUTE(newTimerVal); + TRACE("Timer[%d] %d minute announcement", i, newTimerVal/60); + } + } + } + } + } + } +} diff --git a/radio/src/timers.h b/radio/src/timers.h new file mode 100644 index 000000000..a19ce1a57 --- /dev/null +++ b/radio/src/timers.h @@ -0,0 +1,74 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef timers_h +#define timers_h + +#define TMR_OFF 0 +#define TMR_RUNNING 1 +#define TMR_NEGATIVE 2 +#define TMR_STOPPED 3 + +#define TIMER_MAX (32767) +#define TIMER_MIN (-32767-1) + +struct TimerState { + uint16_t cnt; + uint16_t sum; + uint8_t state; + int16_t val; + uint8_t val_10ms; +}; + +extern TimerState timersStates[MAX_TIMERS]; + +void timerReset(uint8_t idx); + +#if defined(CPUARM) +void timerSet(int idx, int16_t val); +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) + void saveTimers(); + void restoreTimers(); +#else + #define saveTimers() + #define restoreTimers() +#endif + +void evalTimers(int16_t throttle, uint8_t tick10ms); + +#endif