Various fixes around Companion trainer mode (#7116)
[opentx.git] / radio / src / opentx.cpp
blob65e682496e090c12e790d1e64bf66665eee0f850
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <io/frsky_firmware_update.h>
22 #include "opentx.h"
24 RadioData g_eeGeneral;
25 ModelData g_model;
27 #if defined(SDCARD)
28 Clipboard clipboard;
29 #endif
31 GlobalData globalData;
33 uint16_t maxMixerDuration; // step = 0.01ms
34 uint8_t heartbeat;
36 #if defined(OVERRIDE_CHANNEL_FUNCTION)
37 safetych_t safetyCh[MAX_OUTPUT_CHANNELS];
38 #endif
40 // __DMA for the MSC_BOT_Data member
41 union ReusableBuffer reusableBuffer __DMA;
43 #if defined(STM32)
44 uint8_t* MSC_BOT_Data = reusableBuffer.MSC_BOT_Data;
45 #endif
47 #if defined(DEBUG_LATENCY)
48 uint8_t latencyToggleSwitch = 0;
49 #endif
51 const uint8_t bchout_ar[] = {
52 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39,
53 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78,
54 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4,
55 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 };
57 uint8_t channelOrder(uint8_t setup, uint8_t x)
59 return ((*(bchout_ar + setup) >> (6 - (x - 1) * 2)) & 3) + 1;
62 uint8_t channelOrder(uint8_t x)
64 return channelOrder(g_eeGeneral.templateSetup, x);
68 mode1 rud ele thr ail
69 mode2 rud thr ele ail
70 mode3 ail ele thr rud
71 mode4 ail thr ele rud
73 const uint8_t modn12x3[] = {
74 0, 1, 2, 3,
75 0, 2, 1, 3,
76 3, 1, 2, 0,
77 3, 2, 1, 0 };
79 volatile uint8_t rtc_count = 0;
80 uint32_t watchdogTimeout = 0;
82 void watchdogSuspend(uint32_t timeout)
84 watchdogTimeout = timeout;
87 #if defined(DEBUG_LATENCY)
88 void toggleLatencySwitch()
90 latencyToggleSwitch ^= 1;
92 #if defined(PCBHORUS)
93 if (latencyToggleSwitch)
94 GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN);
95 else
96 GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN);
97 #else
98 if (latencyToggleSwitch)
99 sportUpdatePowerOn();
100 else
101 sportUpdatePowerOff();
102 #endif
104 #endif
106 void per10ms()
108 g_tmr10ms++;
110 if (watchdogTimeout) {
111 watchdogTimeout -= 1;
112 WDG_RESET(); // Retrigger hardware watchdog
115 #if defined(GUI)
116 if (lightOffCounter) lightOffCounter--;
117 if (flashCounter) flashCounter--;
118 if (noHighlightCounter) noHighlightCounter--;
119 #endif
121 if (trimsCheckTimer) trimsCheckTimer--;
122 if (ppmInputValidityTimer) ppmInputValidityTimer--;
124 if (trimsDisplayTimer)
125 trimsDisplayTimer--;
126 else
127 trimsDisplayMask = 0;
129 #if defined(DEBUG_LATENCY_END_TO_END)
130 static tmr10ms_t lastLatencyToggle = 0;
131 if (g_tmr10ms - lastLatencyToggle == 10) {
132 lastLatencyToggle = g_tmr10ms;
133 toggleLatencySwitch();
135 #endif
137 #if defined(RTCLOCK)
138 /* Update global Date/Time every 100 per10ms cycles */
139 if (++g_ms100 == 100) {
140 g_rtcTime++; // inc global unix timestamp one second
141 #if defined(COPROCESSOR)
142 if (g_rtcTime < 60 || rtc_count<5) {
143 rtcInit();
144 rtc_count++;
146 else {
147 coprocReadData(true);
149 #endif
150 g_ms100 = 0;
152 #endif
154 readKeysAndTrims();
156 #if defined(ROTARY_ENCODER_NAVIGATION)
157 if (IS_ROTARY_ENCODER_NAVIGATION_ENABLE()) {
158 static rotenc_t rePreviousValue;
159 static bool cw = false;
160 rotenc_t reNewValue = (ROTARY_ENCODER_NAVIGATION_VALUE / ROTARY_ENCODER_GRANULARITY);
161 rotenc_t scrollRE = reNewValue - rePreviousValue;
162 if (scrollRE) {
163 static uint32_t lastEvent;
164 rePreviousValue = reNewValue;
166 bool new_cw = (scrollRE < 0) ? false : true;
167 if ((g_tmr10ms - lastEvent >= 10) || (cw == new_cw)) { // 100ms
169 putEvent(new_cw ? EVT_ROTARY_RIGHT : EVT_ROTARY_LEFT);
171 // rotary encoder navigation speed (acceleration) detection/calculation
172 static uint32_t delay = 2*ROTENC_DELAY_MIDSPEED;
174 if (new_cw == cw) {
175 // Modified moving average filter used for smoother change of speed
176 delay = (((g_tmr10ms - lastEvent) << 3) + delay) >> 1;
178 else {
179 delay = 2*ROTENC_DELAY_MIDSPEED;
182 if (delay < ROTENC_DELAY_HIGHSPEED)
183 rotencSpeed = ROTENC_HIGHSPEED;
184 else if (delay < ROTENC_DELAY_MIDSPEED)
185 rotencSpeed = ROTENC_MIDSPEED;
186 else
187 rotencSpeed = ROTENC_LOWSPEED;
188 cw = new_cw;
189 lastEvent = g_tmr10ms;
193 #endif
195 telemetryInterrupt10ms();
197 // These moved here from evalFlightModeMixes() to improve beep trigger reliability.
198 #if defined(PWM_BACKLIGHT)
199 if ((g_tmr10ms&0x03) == 0x00)
200 backlightFade(); // increment or decrement brightness until target brightness is reached
201 #endif
203 #if !defined(AUDIO)
204 if (mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) AUDIO_MIX_WARNING(1);
205 if (mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) AUDIO_MIX_WARNING(2);
206 if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3);
207 #endif
209 #if defined(SDCARD)
210 sdPoll10ms();
211 #endif
213 outputTelemetryBuffer.per10ms();
215 heartbeat |= HEART_TIMER_10MS;
218 FlightModeData *flightModeAddress(uint8_t idx)
220 return &g_model.flightModeData[idx];
223 ExpoData *expoAddress(uint8_t idx )
225 return &g_model.expoData[idx];
228 MixData *mixAddress(uint8_t idx)
230 return &g_model.mixData[idx];
233 LimitData *limitAddress(uint8_t idx)
235 return &g_model.limitData[idx];
239 void memswap(void * a, void * b, uint8_t size)
241 uint8_t * x = (uint8_t *)a;
242 uint8_t * y = (uint8_t *)b;
243 uint8_t temp ;
245 while (size--) {
246 temp = *x;
247 *x++ = *y;
248 *y++ = temp;
252 #if defined(PXX2)
253 void setDefaultOwnerId()
255 for (uint8_t i = 0; i < PXX2_LEN_REGISTRATION_ID; i++) {
256 g_eeGeneral.ownerRegistrationID[i] = (cpu_uid[1 + i] & 0x3f) - 26;
259 #endif
261 void generalDefault()
263 memclear(&g_eeGeneral, sizeof(g_eeGeneral));
264 g_eeGeneral.version = EEPROM_VER;
265 g_eeGeneral.variant = EEPROM_VARIANT;
267 #if defined(PCBHORUS)
268 g_eeGeneral.blOffBright = 20;
269 #else
270 g_eeGeneral.contrast = LCD_CONTRAST_DEFAULT;
271 #endif
273 #if defined(DEFAULT_POTS_CONFIG)
274 g_eeGeneral.potsConfig = DEFAULT_POTS_CONFIG;
275 #endif
277 #if defined(DEFAULT_SWITCH_CONFIG)
278 g_eeGeneral.switchConfig = DEFAULT_SWITCH_CONFIG;
279 #endif
281 #if defined(DEFAULT_SLIDERS_CONFIG)
282 g_eeGeneral.slidersConfig = DEFAULT_SLIDERS_CONFIG;
283 #endif
285 // vBatWarn is voltage in 100mV, vBatMin is in 100mV but with -9V offset, vBatMax has a -12V offset
286 g_eeGeneral.vBatWarn = BATTERY_WARN;
287 if (BATTERY_MIN != 90)
288 g_eeGeneral.vBatMin = BATTERY_MIN - 90;
289 if (BATTERY_MAX != 120)
290 g_eeGeneral.vBatMax = BATTERY_MAX - 120;
292 #if defined(DEFAULT_MODE)
293 g_eeGeneral.stickMode = DEFAULT_MODE - 1;
294 #endif
296 #if defined(FRSKY_RELEASE)
297 g_eeGeneral.templateSetup = 17; /* TAER */
298 #endif
300 g_eeGeneral.backlightMode = e_backlight_mode_all;
301 g_eeGeneral.lightAutoOff = 2;
302 g_eeGeneral.inactivityTimer = 10;
304 g_eeGeneral.ttsLanguage[0] = 'e';
305 g_eeGeneral.ttsLanguage[1] = 'n';
306 g_eeGeneral.wavVolume = 2;
307 g_eeGeneral.backgroundVolume = 1;
309 for (int i=0; i<NUM_STICKS; ++i) {
310 g_eeGeneral.trainer.mix[i].mode = 2;
311 g_eeGeneral.trainer.mix[i].srcChn = channelOrder(i+1) - 1;
312 g_eeGeneral.trainer.mix[i].studWeight = 100;
315 #if defined(PCBX9E)
316 const int8_t defaultName[] = { 20, -1, -18, -1, -14, -9, -19 };
317 memcpy(g_eeGeneral.bluetoothName, defaultName, sizeof(defaultName));
318 #endif
320 #if !defined(EEPROM)
321 strcpy(g_eeGeneral.currModelFilename, DEFAULT_MODEL_FILENAME);
322 #endif
324 #if defined(COLORLCD)
325 strcpy(g_eeGeneral.themeName, theme->getName());
326 theme->init();
327 #endif
329 #if defined(PXX2)
330 setDefaultOwnerId();
331 #endif
333 g_eeGeneral.chkSum = 0xFFFF;
336 uint16_t evalChkSum()
338 uint16_t sum = 0;
339 const int16_t *calibValues = (const int16_t *) &g_eeGeneral.calib[0];
340 for (int i=0; i<12; i++)
341 sum += calibValues[i];
342 return sum;
345 void clearInputs()
347 memset(g_model.expoData, 0, sizeof(g_model.expoData)); // clear all expos
350 void defaultInputs()
352 clearInputs();
354 for (int i=0; i<NUM_STICKS; i++) {
355 uint8_t stick_index = channelOrder(i+1);
356 ExpoData *expo = expoAddress(i);
357 expo->srcRaw = MIXSRC_Rud - 1 + stick_index;
358 expo->curve.type = CURVE_REF_EXPO;
359 expo->chn = i;
360 expo->weight = 100;
361 expo->mode = 3; // TODO constant
362 #if defined(TRANSLATIONS_CZ)
363 for (int c=0; c<4; c++) {
364 g_model.inputNames[i][c] = char2zchar(STR_INPUTNAMES[1+4*(stick_index-1)+c]);
366 #else
367 for (int c=0; c<3; c++) {
368 g_model.inputNames[i][c] = char2zchar(STR_VSRCRAW[2 + 4 * stick_index + c]);
370 #if LEN_INPUT_NAME > 3
371 g_model.inputNames[i][3] = '\0';
372 #endif
373 #endif
375 storageDirty(EE_MODEL);
378 void applyDefaultTemplate()
380 defaultInputs(); // calls storageDirty internally
382 for (int i=0; i<NUM_STICKS; i++) {
383 MixData * mix = mixAddress(i);
384 mix->destCh = i;
385 mix->weight = 100;
386 mix->srcRaw = i+1;
390 #if defined(EEPROM)
391 void checkModelIdUnique(uint8_t index, uint8_t module)
393 if (isModuleXJTD8(module))
394 return;
396 uint8_t modelId = g_model.header.modelId[module];
397 uint8_t additionalOnes = 0;
398 char * name = reusableBuffer.moduleSetup.msg;
400 memset(reusableBuffer.moduleSetup.msg, 0, sizeof(reusableBuffer.moduleSetup.msg));
402 if (modelId != 0) {
403 for (uint8_t i = 0; i < MAX_MODELS; i++) {
404 if (i != index) {
405 if (modelId == modelHeaders[i].modelId[module]) {
406 if ((WARNING_LINE_LEN - 4 - (name - reusableBuffer.moduleSetup.msg)) > (signed)(modelHeaders[i].name[0] ? zlen(modelHeaders[i].name, LEN_MODEL_NAME) : sizeof(TR_MODEL) + 2)) { // you cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 (-2 for the ",")
407 if (reusableBuffer.moduleSetup.msg[0] != '\0') {
408 name = strAppend(name, ", ");
410 if (modelHeaders[i].name[0] == 0) {
411 name = strAppend(name, STR_MODEL);
412 name = strAppendUnsigned(name+strlen(name), i + 1, 2);
414 else {
415 name += zchar2str(name, modelHeaders[i].name, LEN_MODEL_NAME);
418 else {
419 additionalOnes++;
426 if (additionalOnes) {
427 name = strAppend(name, " (+");
428 name = strAppendUnsigned(name, additionalOnes);
429 strAppend(name, ")");
432 if (reusableBuffer.moduleSetup.msg[0] != '\0') {
433 POPUP_WARNING(STR_MODELIDUSED);
434 SET_WARNING_INFO(reusableBuffer.moduleSetup.msg, sizeof(reusableBuffer.moduleSetup.msg), 0);
438 uint8_t findNextUnusedModelId(uint8_t index, uint8_t module)
440 // assume 63 is the highest Model ID
441 // and use 64 bits
442 uint8_t usedModelIds[8];
443 memset(usedModelIds, 0, sizeof(usedModelIds));
445 for (uint8_t mod_i = 0; mod_i < MAX_MODELS; mod_i++) {
447 if (mod_i == index)
448 continue;
450 uint8_t id = modelHeaders[mod_i].modelId[module];
451 if (id == 0)
452 continue;
454 uint8_t mask = 1;
455 for (uint8_t i = 1; i < (id & 7); i++)
456 mask <<= 1;
458 usedModelIds[id >> 3] |= mask;
461 uint8_t new_id = 1;
462 uint8_t tst_mask = 1;
463 for (;new_id < getMaxRxNum(module); new_id++) {
464 if (!(usedModelIds[new_id >> 3] & tst_mask)) {
465 // found free ID
466 return new_id;
468 if ((tst_mask <<= 1) == 0)
469 tst_mask = 1;
472 // failed finding something...
473 return 0;
475 #endif
477 void modelDefault(uint8_t id)
479 memset(&g_model, 0, sizeof(g_model));
481 applyDefaultTemplate();
483 memcpy(g_model.modelRegistrationID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID);
485 #if defined(LUA) && defined(PCBTARANIS) //Horus uses menuModelWizard() for wizard
486 if (isFileAvailable(WIZARD_PATH "/" WIZARD_NAME)) {
487 f_chdir(WIZARD_PATH);
488 luaExec(WIZARD_NAME);
490 #endif
492 #if defined(FRSKY_RELEASE)
493 g_model.moduleData[INTERNAL_MODULE].type = IS_PXX2_INTERNAL_ENABLED() ? MODULE_TYPE_ISRM_PXX2 : MODULE_TYPE_XJT_PXX1;
494 g_model.moduleData[INTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE);
495 #endif
497 #if defined(PCBXLITE)
498 g_model.trainerData.mode = TRAINER_MODE_MASTER_BLUETOOTH;
499 #endif
501 #if defined(EEPROM)
502 for (int i=0; i<NUM_MODULES; i++) {
503 modelHeaders[id].modelId[i] = g_model.header.modelId[i] = id+1;
505 checkModelIdUnique(id, 0);
506 #endif
508 #if defined(FLIGHT_MODES) && defined(GVARS)
509 for (int p=1; p<MAX_FLIGHT_MODES; p++) {
510 for (int i=0; i<MAX_GVARS; i++) {
511 g_model.flightModeData[p].gvars[i] = GVAR_MAX+1;
514 #endif
516 #if !defined(EEPROM)
517 strcpy(g_model.header.name, "\015\361\374\373\364");
518 g_model.header.name[5] = '\033' + id/10;
519 g_model.header.name[6] = '\033' + id%10;
520 #endif
522 #if defined(PCBHORUS)
523 extern const LayoutFactory * defaultLayout;
524 delete customScreens[0];
525 customScreens[0] = defaultLayout->create(&g_model.screenData[0].layoutData);
526 strcpy(g_model.screenData[0].layoutName, "Layout2P1");
527 extern const WidgetFactory * defaultWidget;
528 customScreens[0]->createWidget(0, defaultWidget);
529 // enable switch warnings
530 for (int i=0; i<NUM_SWITCHES; i++) {
531 g_model.switchWarningState |= (1 << (3*i));
533 #endif
536 bool isInputRecursive(int index)
538 ExpoData * line = expoAddress(0);
539 for (int i=0; i<MAX_EXPOS; i++, line++) {
540 if (line->chn > index)
541 break;
542 else if (line->chn < index)
543 continue;
544 else if (line->srcRaw >= MIXSRC_FIRST_LOGICAL_SWITCH)
545 return true;
547 return false;
550 #if defined(AUTOSOURCE)
551 int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS)
553 int8_t result = 0;
554 static tmr10ms_t s_move_last_time = 0;
556 static int16_t inputsStates[MAX_INPUTS];
557 if (min <= MIXSRC_FIRST_INPUT) {
558 for (uint8_t i=0; i<MAX_INPUTS; i++) {
559 if (abs(anas[i] - inputsStates[i]) > 512) {
560 if (!isInputRecursive(i)) {
561 result = MIXSRC_FIRST_INPUT+i;
562 break;
568 static int16_t sourcesStates[NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_MOUSE_ANALOGS];
569 if (result == 0) {
570 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; i++) {
571 if (abs(calibratedAnalogs[i] - sourcesStates[i]) > 512) {
572 result = MIXSRC_Rud+i;
573 break;
578 bool recent = ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10);
579 if (recent) {
580 result = 0;
583 if (result || recent) {
584 memcpy(inputsStates, anas, sizeof(inputsStates));
585 memcpy(sourcesStates, calibratedAnalogs, sizeof(sourcesStates));
588 s_move_last_time = get_tmr10ms();
589 return result;
591 #endif
593 #if defined(FLIGHT_MODES)
594 uint8_t getFlightMode()
596 for (uint8_t i=1; i<MAX_FLIGHT_MODES; i++) {
597 FlightModeData *phase = &g_model.flightModeData[i];
598 if (phase->swtch && getSwitch(phase->swtch)) {
599 return i;
602 return 0;
604 #endif
606 trim_t getRawTrimValue(uint8_t phase, uint8_t idx)
608 FlightModeData * p = flightModeAddress(phase);
609 return p->trim[idx];
612 int getTrimValue(uint8_t phase, uint8_t idx)
614 int result = 0;
615 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
616 trim_t v = getRawTrimValue(phase, idx);
617 if (v.mode == TRIM_MODE_NONE) {
618 return result;
620 else {
621 unsigned int p = v.mode >> 1;
622 if (p == phase || phase == 0) {
623 return result + v.value;
625 else {
626 phase = p;
627 if (v.mode % 2 != 0) {
628 result += v.value;
633 return 0;
636 bool setTrimValue(uint8_t phase, uint8_t idx, int trim)
638 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
639 trim_t & v = flightModeAddress(phase)->trim[idx];
640 if (v.mode == TRIM_MODE_NONE)
641 return false;
642 unsigned int p = v.mode >> 1;
643 if (p == phase || phase == 0) {
644 v.value = trim;
645 break;
647 else if (v.mode % 2 == 0) {
648 phase = p;
650 else {
651 v.value = limit<int>(TRIM_EXTENDED_MIN, trim - getTrimValue(p, idx), TRIM_EXTENDED_MAX);
652 break;
655 storageDirty(EE_MODEL);
656 return true;
659 getvalue_t convert16bitsTelemValue(source_t channel, ls_telemetry_value_t value)
661 return value;
664 ls_telemetry_value_t maxTelemValue(source_t channel)
666 return 30000;
669 #define INAC_STICKS_SHIFT 6
670 #define INAC_SWITCHES_SHIFT 8
671 bool inputsMoved()
673 uint8_t sum = 0;
674 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; i++)
675 sum += anaIn(i) >> INAC_STICKS_SHIFT;
676 for (uint8_t i=0; i<NUM_SWITCHES; i++)
677 sum += getValue(MIXSRC_FIRST_SWITCH+i) >> INAC_SWITCHES_SHIFT;
678 #if defined(GYRO)
679 for (uint8_t i=0; i<2; i++)
680 sum += getValue(MIXSRC_GYRO1+i) >> INAC_STICKS_SHIFT;
681 #endif
683 if (abs((int8_t)(sum-inactivity.sum)) > 1) {
684 inactivity.sum = sum;
685 return true;
687 else {
688 return false;
692 void checkBacklight()
694 static uint8_t tmr10ms ;
696 uint8_t x = g_blinkTmr10ms;
697 if (tmr10ms != x) {
698 tmr10ms = x;
699 if (inputsMoved()) {
700 inactivity.counter = 0;
701 if (g_eeGeneral.backlightMode & e_backlight_mode_sticks) {
702 backlightOn();
706 bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || (g_eeGeneral.backlightMode != e_backlight_mode_off && lightOffCounter) || isFunctionActive(FUNCTION_BACKLIGHT));
707 if (flashCounter) backlightOn = !backlightOn;
708 if (backlightOn)
709 BACKLIGHT_ENABLE();
710 else
711 BACKLIGHT_DISABLE();
715 void doLoopCommonActions()
717 checkBacklight();
720 void backlightOn()
722 lightOffCounter = ((uint16_t)g_eeGeneral.lightAutoOff*250) << 1;
725 #if MENUS_LOCK == 1
726 bool readonly = true;
727 bool readonlyUnlocked()
729 if (readonly) {
730 POPUP_WARNING(STR_MODS_FORBIDDEN);
731 return false;
733 else {
734 return true;
737 #endif
739 #if defined(SPLASH)
740 void doSplash()
742 #if defined(PWR_BUTTON_PRESS)
743 bool refresh = false;
744 #endif
746 if (SPLASH_NEEDED()) {
747 backlightOn();
748 drawSplash();
750 #if defined(PCBSKY9X)
751 tmr10ms_t curTime = get_tmr10ms() + 10;
752 uint8_t contrast = 10;
753 lcdSetRefVolt(contrast);
754 #endif
756 getADC(); // init ADC array
758 inputsMoved();
760 tmr10ms_t tgtime = get_tmr10ms() + SPLASH_TIMEOUT;
762 while (tgtime > get_tmr10ms()) {
763 RTOS_WAIT_TICKS(1);
765 getADC();
767 if (keyDown() || inputsMoved()) return;
769 #if defined(PWR_BUTTON_PRESS)
770 uint32_t pwr_check = pwrCheck();
771 if (pwr_check == e_power_off) {
772 break;
774 else if (pwr_check == e_power_press) {
775 refresh = true;
777 else if (pwr_check == e_power_on && refresh) {
778 drawSplash();
779 refresh = false;
781 #else
782 if (pwrCheck() == e_power_off) {
783 return;
785 #endif
787 #if defined(FRSKY_RELEASE)
788 static uint8_t secondSplash = false;
789 if (!secondSplash && get_tmr10ms() >= tgtime-200) {
790 secondSplash = true;
791 drawSecondSplash();
793 #endif
795 #if defined(PCBSKY9X)
796 if (curTime < get_tmr10ms()) {
797 curTime += 10;
798 if (contrast < g_eeGeneral.contrast) {
799 contrast += 1;
800 lcdSetRefVolt(contrast);
803 #endif
805 doLoopCommonActions();
809 #else
810 #define Splash()
811 #define doSplash()
812 #endif
814 #if defined(SDCARD)
815 void checkSDVersion()
817 if (sdMounted()) {
818 FIL versionFile;
819 UINT read = 0;
820 char version[sizeof(REQUIRED_SDCARD_VERSION)-1];
821 char error[sizeof(TR_WRONG_SDCARDVERSION)+ sizeof(version)];
823 strAppend(strAppend(error, STR_WRONG_SDCARDVERSION, sizeof(TR_WRONG_SDCARDVERSION)), REQUIRED_SDCARD_VERSION, sizeof(REQUIRED_SDCARD_VERSION));
824 FRESULT result = f_open(&versionFile, "/opentx.sdcard.version", FA_OPEN_EXISTING | FA_READ);
825 if (result == FR_OK) {
826 if (f_read(&versionFile, &version, sizeof(version), &read) != FR_OK ||
827 read != sizeof(version) ||
828 strncmp(version, REQUIRED_SDCARD_VERSION, sizeof(version)) != 0) {
829 TRACE("SD card version mismatch: %.*s, %s", sizeof(REQUIRED_SDCARD_VERSION)-1, version, REQUIRED_SDCARD_VERSION);
830 ALERT(STR_SD_CARD, error, AU_ERROR);
832 f_close(&versionFile);
834 else {
835 ALERT(STR_SD_CARD, error, AU_ERROR);
839 #endif
841 #if defined(MULTIMODULE)
842 void checkMultiLowPower()
844 if (isModuleMultimodule(EXTERNAL_MODULE) && g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode) {
845 ALERT("MULTI", STR_WARN_MULTI_LOWPOWER, AU_ERROR);
846 return;
848 #if defined(INTERNAL_MODULE_MULTI)
849 if (isModuleMultimodule(INTERNAL_MODULE) && g_model.moduleData[INTERNAL_MODULE].multi.lowPowerMode) {
850 ALERT("MULTI", STR_WARN_MULTI_LOWPOWER, AU_ERROR);
852 #endif
854 #endif
856 #if defined(STM32)
857 static void checkRTCBattery()
859 if (isVBatBridgeEnabled()) {
860 if (getRTCBatteryVoltage() < 200) {
861 ALERT(STR_BATTERY, STR_WARN_RTC_BATTERY_LOW, AU_ERROR);
863 disableVBatBridge();
866 #endif
868 #if defined(PCBTARANIS) || defined(PCBHORUS)
869 void checkFailsafe()
871 for (int i=0; i<NUM_MODULES; i++) {
872 #if defined(MULTIMODULE)
873 if (isModuleMultimodule(i)) {
874 getMultiModuleStatus(i).requiresFailsafeCheck = true;
876 else
877 #endif
878 if (isModuleFailsafeAvailable(i)) {
879 ModuleData & moduleData = g_model.moduleData[i];
880 if (moduleData.failsafeMode == FAILSAFE_NOT_SET) {
881 ALERT(STR_FAILSAFEWARN, STR_NO_FAILSAFE, AU_ERROR);
882 break;
887 #else
888 #define checkFailsafe()
889 #endif
890 void checkRSSIAlarmsDisabled()
892 if (g_model.rssiAlarms.disabled) {
893 ALERT(STR_RSSIALARM_WARN, STR_NO_RSSIALARM, AU_ERROR);
897 #if defined(GUI)
898 void checkAll()
900 #if defined(EEPROM_RLC)
901 checkLowEEPROM();
902 #endif
904 // we don't check the throttle stick if the radio is not calibrated
905 if (g_eeGeneral.chkSum == evalChkSum()) {
906 checkThrottleStick();
909 checkSwitches();
910 checkFailsafe();
911 checkRSSIAlarmsDisabled();
913 #if defined(SDCARD)
914 checkSDVersion();
915 #endif
917 #if defined(STM32)
918 if (!g_eeGeneral.disableRtcWarning)
919 checkRTCBattery();
920 #endif
922 if (g_model.displayChecklist && modelHasNotes()) {
923 readModelNotes();
926 #if defined(MULTIMODULE)
927 checkMultiLowPower();
928 #endif
930 if (!waitKeysReleased()) {
931 showMessageBox(STR_KEYSTUCK);
932 tmr10ms_t tgtime = get_tmr10ms() + 500;
933 while (tgtime != get_tmr10ms()) {
934 RTOS_WAIT_MS(1);
935 WDG_RESET();
939 #if defined(EXTERNAL_ANTENNA) && defined(INTERNAL_MODULE_PXX1)
940 checkExternalAntenna();
941 #endif
943 START_SILENCE_PERIOD();
945 #endif // GUI
948 #if defined(EEPROM_RLC)
949 void checkLowEEPROM()
951 if (g_eeGeneral.disableMemoryWarning) return;
952 if (EeFsGetFree() < 100) {
953 ALERT(STR_STORAGE_WARNING, STR_EEPROMLOWMEM, AU_ERROR);
956 #endif
958 bool isThrottleWarningAlertNeeded()
960 if (g_model.disableThrottleWarning) {
961 return false;
964 // throttle channel is either the stick according stick mode (already handled in evalInputs)
965 // or P1 to P3;
966 // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS+NUM_SLIDERS) we assume the throttle stick is the input
967 // no other information available at the moment, and good enough to my option (otherwise too much exceptions...)
968 uint8_t thrchn = ((g_model.thrTraceSrc==0) || (g_model.thrTraceSrc>NUM_POTS+NUM_SLIDERS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1;
970 GET_ADC_IF_MIXER_NOT_RUNNING();
971 evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job
973 int16_t v = calibratedAnalogs[thrchn];
974 if (g_model.thrTraceSrc && g_model.throttleReversed) { // TODO : proper review of THR source definition and handling
975 v = -v;
977 return v > THRCHK_DEADBAND - 1024;
980 void checkThrottleStick()
982 if (!isThrottleWarningAlertNeeded()) {
983 return;
986 // first - display warning; also deletes inputs if any have been before
987 LED_ERROR_BEGIN();
988 RAISE_ALERT(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT);
990 #if defined(PWR_BUTTON_PRESS)
991 bool refresh = false;
992 #endif
994 while (!getEvent()) {
995 if (!isThrottleWarningAlertNeeded()) {
996 return;
999 #if defined(PWR_BUTTON_PRESS)
1000 uint32_t power = pwrCheck();
1001 if (power == e_power_off) {
1002 drawSleepBitmap();
1003 boardOff();
1004 break;
1006 else if (power == e_power_press) {
1007 refresh = true;
1009 else if (power == e_power_on && refresh) {
1010 RAISE_ALERT(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_NONE);
1011 refresh = false;
1013 #else
1014 if (pwrCheck() == e_power_off) {
1015 break;
1017 #endif
1019 doLoopCommonActions();
1021 WDG_RESET();
1023 RTOS_WAIT_MS(10);
1026 LED_ERROR_END();
1029 void checkAlarm() // added by Gohst
1031 if (g_eeGeneral.disableAlarmWarning) {
1032 return;
1035 if (IS_SOUND_OFF()) {
1036 ALERT(STR_ALARMSWARN, STR_ALARMSDISABLED, AU_ERROR);
1040 void alert(const char * title, const char * msg , uint8_t sound)
1042 LED_ERROR_BEGIN();
1044 TRACE("ALERT %s: %s", title, msg);
1046 RAISE_ALERT(title, msg, STR_PRESSANYKEY, sound);
1048 #if defined(PWR_BUTTON_PRESS)
1049 bool refresh = false;
1050 #endif
1052 while (true) {
1053 RTOS_WAIT_MS(10);
1055 if (keyDown()) // wait for key release
1056 break;
1058 doLoopCommonActions();
1060 WDG_RESET();
1062 const uint32_t pwr_check = pwrCheck();
1063 if (pwr_check == e_power_off) {
1064 drawSleepBitmap();
1065 boardOff();
1066 return; // only happens in SIMU, required for proper shutdown
1068 #if defined(PWR_BUTTON_PRESS)
1069 else if (pwr_check == e_power_press) {
1070 refresh = true;
1072 else if (pwr_check == e_power_on && refresh) {
1073 RAISE_ALERT(title, msg, STR_PRESSANYKEY, AU_NONE);
1074 refresh = false;
1076 #endif
1079 LED_ERROR_END();
1082 #if defined(GVARS)
1083 #if NUM_TRIMS == 6
1084 int8_t trimGvar[NUM_TRIMS] = { -1, -1, -1, -1, -1, -1 };
1085 #elif NUM_TRIMS == 4
1086 int8_t trimGvar[NUM_TRIMS] = { -1, -1, -1, -1 };
1087 #elif NUM_TRIMS == 2
1088 int8_t trimGvar[NUM_TRIMS] = { -1, -1 };
1089 #endif
1090 #endif
1092 void checkTrims()
1094 event_t event = getEvent(true);
1095 if (event && !IS_KEY_BREAK(event)) {
1096 int8_t k = EVT_KEY_MASK(event) - TRM_BASE;
1097 // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP
1098 uint8_t idx = CONVERT_MODE_TRIMS((uint8_t)k/2);
1099 uint8_t phase;
1100 int before;
1101 bool thro;
1103 trimsDisplayTimer = 200; // 2 seconds
1104 trimsDisplayMask |= (1<<idx);
1106 #if defined(GVARS)
1107 if (TRIM_REUSED(idx)) {
1108 phase = getGVarFlightMode(mixerCurrentFlightMode, trimGvar[idx]);
1109 before = GVAR_VALUE(trimGvar[idx], phase);
1110 thro = false;
1112 else {
1113 phase = getTrimFlightMode(mixerCurrentFlightMode, idx);
1114 before = getTrimValue(phase, idx);
1115 thro = (idx==THR_STICK && g_model.thrTrim);
1117 #else
1118 phase = getTrimFlightMode(mixerCurrentFlightMode, idx);
1119 before = getTrimValue(phase, idx);
1120 thro = (idx==THR_STICK && g_model.thrTrim);
1121 #endif
1122 int8_t trimInc = g_model.trimInc + 1;
1123 int8_t v = (trimInc==-1) ? min(32, abs(before)/4+1) : (1 << trimInc); // TODO flash saving if (trimInc < 0)
1124 if (thro) v = 4; // if throttle trim and trim trottle then step=4
1125 #if defined(GVARS)
1126 if (TRIM_REUSED(idx)) v = 1;
1127 #endif
1128 int16_t after = (k&1) ? before + v : before - v; // positive = k&1
1129 bool beepTrim = false;
1131 if (!thro && before!=0 && ((!(after < 0) == (before < 0)) || after==0)) { //forcing a stop at centerered trim when changing sides
1132 after = 0;
1133 beepTrim = true;
1134 AUDIO_TRIM_MIDDLE();
1135 pauseEvents(event);
1138 #if defined(GVARS)
1139 if (TRIM_REUSED(idx)) {
1140 int8_t gvar = trimGvar[idx];
1141 int16_t vmin = GVAR_MIN + g_model.gvars[gvar].min;
1142 int16_t vmax = GVAR_MAX - g_model.gvars[gvar].max;
1143 if (after < vmin) {
1144 after = vmin;
1145 beepTrim = true;
1146 AUDIO_TRIM_MIN();
1147 killEvents(event);
1149 else if (after > vmax) {
1150 after = vmax;
1151 beepTrim = true;
1152 AUDIO_TRIM_MAX();
1153 killEvents(event);
1156 SET_GVAR_VALUE(gvar, phase, after);
1158 else
1159 #endif
1161 if (before>TRIM_MIN && after<=TRIM_MIN) {
1162 beepTrim = true;
1163 AUDIO_TRIM_MIN();
1164 killEvents(event);
1166 else if (before<TRIM_MAX && after>=TRIM_MAX) {
1167 beepTrim = true;
1168 AUDIO_TRIM_MAX();
1169 killEvents(event);
1172 if ((before<after && after>TRIM_MAX) || (before>after && after<TRIM_MIN)) {
1173 if (!g_model.extendedTrims) after = before;
1176 if (after < TRIM_EXTENDED_MIN) {
1177 after = TRIM_EXTENDED_MIN;
1179 else if (after > TRIM_EXTENDED_MAX) {
1180 after = TRIM_EXTENDED_MAX;
1183 if (!setTrimValue(phase, idx, after)) {
1184 // we don't play a beep, so we exit now the function
1185 return;
1189 if (!beepTrim) {
1190 AUDIO_TRIM_PRESS(after);
1196 #if !defined(SIMU)
1197 uint16_t s_anaFilt[NUM_ANALOGS];
1198 #endif
1200 #if defined(JITTER_MEASURE)
1201 JitterMeter<uint16_t> rawJitter[NUM_ANALOGS];
1202 JitterMeter<uint16_t> avgJitter[NUM_ANALOGS];
1203 tmr10ms_t jitterResetTime = 0;
1204 #endif
1206 #define JITTER_FILTER_STRENGTH 4 // tune this value, bigger value - more filtering (range: 1-5) (see explanation below)
1207 #define ANALOG_SCALE 1 // tune this value, bigger value - more filtering (range: 0-1) (see explanation below)
1209 #define JITTER_ALPHA (1<<JITTER_FILTER_STRENGTH)
1210 #define ANALOG_MULTIPLIER (1<<ANALOG_SCALE)
1211 #define ANA_FILT(chan) (s_anaFilt[chan] / (JITTER_ALPHA * ANALOG_MULTIPLIER))
1212 #if (JITTER_ALPHA * ANALOG_MULTIPLIER > 32)
1213 #error "JITTER_FILTER_STRENGTH and ANALOG_SCALE are too big, their summ should be <= 5 !!!"
1214 #endif
1216 #if !defined(SIMU)
1217 uint16_t anaIn(uint8_t chan)
1219 return ANA_FILT(chan);
1222 void getADC()
1224 #if defined(JITTER_MEASURE)
1225 if (JITTER_MEASURE_ACTIVE() && jitterResetTime < get_tmr10ms()) {
1226 // reset jitter measurement every second
1227 for (uint32_t x=0; x<NUM_ANALOGS; x++) {
1228 rawJitter[x].reset();
1229 avgJitter[x].reset();
1231 jitterResetTime = get_tmr10ms() + 100; //every second
1233 #endif
1235 DEBUG_TIMER_START(debugTimerAdcRead);
1236 adcRead();
1237 DEBUG_TIMER_STOP(debugTimerAdcRead);
1239 for (uint8_t x=0; x<NUM_ANALOGS; x++) {
1240 uint16_t v = getAnalogValue(x) >> (1 - ANALOG_SCALE);
1242 // Jitter filter:
1243 // * pass trough any big change directly
1244 // * for small change use Modified moving average (MMA) filter
1246 // Explanation:
1248 // Normal MMA filter has this formula:
1249 // <out> = ((ALPHA-1)*<out> + <in>)/ALPHA
1251 // If calculation is done this way with integer arithmetics, then any small change in
1252 // input signal is lost. One way to combat that, is to rearrange the formula somewhat,
1253 // to store a more precise (larger) number between iterations. The basic idea is to
1254 // store undivided value between iterations. Therefore an new variable <filtered> is
1255 // used. The new formula becomes:
1256 // <filtered> = <filtered> - <filtered>/ALPHA + <in>
1257 // <out> = <filtered>/ALPHA (use only when out is needed)
1259 // The above formula with a maximum allowed ALPHA value (we are limited by
1260 // the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still had
1261 // some jitter (a value of 1 was observed). The jitter might be bigger on other
1262 // radios.
1264 // So another idea is to use larger input values for filtering. So instead of using
1265 // input in a range from 0 to 2047, we use twice larger number (temp[x] is divided less)
1267 // This also means that ALPHA must be lowered (remember 16 bit limit), but test results
1268 // have proved that this kind of filtering gives better results. So the recommended values
1269 // for filter are:
1270 // JITTER_FILTER_STRENGTH 4
1271 // ANALOG_SCALE 1
1273 // Variables mapping:
1274 // * <in> = v
1275 // * <out> = s_anaFilt[x]
1276 uint16_t previous = s_anaFilt[x] / JITTER_ALPHA;
1277 uint16_t diff = (v > previous) ? (v - previous) : (previous - v);
1278 if (!g_eeGeneral.jitterFilter && diff < (10*ANALOG_MULTIPLIER)) { // g_eeGeneral.jitterFilter is inverted, 0 - active
1279 // apply jitter filter
1280 s_anaFilt[x] = (s_anaFilt[x] - previous) + v;
1282 else {
1283 // use unfiltered value
1284 s_anaFilt[x] = v * JITTER_ALPHA;
1287 #if defined(JITTER_MEASURE)
1288 if (JITTER_MEASURE_ACTIVE()) {
1289 avgJitter[x].measure(ANA_FILT(x));
1291 #endif
1293 #define ANAFILT_MAX (2 * RESX * JITTER_ALPHA * ANALOG_MULTIPLIER - 1)
1294 StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x];
1295 if (IS_POT_MULTIPOS(x) && IS_MULTIPOS_CALIBRATED(calib)) {
1296 // TODO: consider adding another low pass filter to eliminate multipos switching glitches
1297 uint8_t vShifted = ANA_FILT(x) >> 4;
1298 s_anaFilt[x] = ANAFILT_MAX;
1299 for (uint32_t i=0; i<calib->count; i++) {
1300 if (vShifted < calib->steps[i]) {
1301 s_anaFilt[x] = (i * ANAFILT_MAX) / calib->count;
1302 break;
1309 #endif // SIMU
1311 uint8_t g_vbat100mV = 0;
1312 uint16_t lightOffCounter;
1313 uint8_t flashCounter = 0;
1315 uint16_t sessionTimer;
1316 uint16_t s_timeCumThr; // THR in 1/16 sec
1317 uint16_t s_timeCum16ThrP; // THR% in 1/16 sec
1319 uint8_t trimsCheckTimer = 0;
1321 uint8_t trimsDisplayTimer = 0;
1322 uint8_t trimsDisplayMask = 0;
1324 void flightReset(uint8_t check)
1326 // we don't reset the whole audio here (the hello.wav would be cut, if a prompt is queued before FlightReset, it should be played)
1327 // TODO check if the vario / background music are stopped correctly if switching to a model which doesn't have these functions enabled
1329 if (!IS_MANUAL_RESET_TIMER(0)) {
1330 timerReset(0);
1333 #if TIMERS > 1
1334 if (!IS_MANUAL_RESET_TIMER(1)) {
1335 timerReset(1);
1337 #endif
1339 #if TIMERS > 2
1340 if (!IS_MANUAL_RESET_TIMER(2)) {
1341 timerReset(2);
1343 #endif
1345 telemetryReset();
1347 s_mixer_first_run_done = false;
1349 START_SILENCE_PERIOD();
1351 RESET_THR_TRACE();
1353 logicalSwitchesReset();
1355 if (check) {
1356 checkAll();
1360 #if defined(THRTRACE)
1361 uint8_t s_traceBuf[MAXTRACE];
1362 uint16_t s_traceWr;
1363 uint8_t s_cnt_10s;
1364 uint16_t s_cnt_samples_thr_10s;
1365 uint16_t s_sum_samples_thr_10s;
1366 #endif
1368 void evalTrims()
1370 uint8_t phase = mixerCurrentFlightMode;
1371 for (uint8_t i=0; i<NUM_TRIMS; i++) {
1372 // do trim -> throttle trim if applicable
1373 int16_t trim = getTrimValue(phase, i);
1374 if (trimsCheckTimer > 0) {
1375 trim = 0;
1378 trims[i] = trim*2;
1382 uint8_t s_mixer_first_run_done = false;
1384 void doMixerCalculations()
1386 static tmr10ms_t lastTMR = 0;
1388 tmr10ms_t tmr10ms = get_tmr10ms();
1390 #if defined(DEBUG_LATENCY_MIXER_RF) || defined(DEBUG_LATENCY_RF_ONLY)
1391 static tmr10ms_t lastLatencyToggle = 0;
1392 if (tmr10ms - lastLatencyToggle >= 10) {
1393 lastLatencyToggle = tmr10ms;
1394 toggleLatencySwitch();
1396 #endif
1398 uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1);
1399 // handle tick10ms overrun
1400 // correct overflow handling costs a lot of code; happens only each 11 min;
1401 // therefore forget the exact calculation and use only 1 instead; good compromise
1402 lastTMR = tmr10ms;
1404 DEBUG_TIMER_START(debugTimerGetAdc);
1405 getADC();
1406 DEBUG_TIMER_STOP(debugTimerGetAdc);
1408 DEBUG_TIMER_START(debugTimerGetSwitches);
1409 getSwitchesPosition(!s_mixer_first_run_done);
1410 DEBUG_TIMER_STOP(debugTimerGetSwitches);
1412 #if defined(PCBSKY9X) && !defined(SIMU)
1413 Current_analogue = (Current_analogue*31 + s_anaFilt[8] ) >> 5 ;
1414 if (Current_analogue > Current_max)
1415 Current_max = Current_analogue ;
1416 #endif
1419 DEBUG_TIMER_START(debugTimerEvalMixes);
1420 evalMixes(tick10ms);
1421 DEBUG_TIMER_STOP(debugTimerEvalMixes);
1423 DEBUG_TIMER_START(debugTimerMixes10ms);
1424 if (tick10ms) {
1425 /* Throttle trace */
1426 int16_t val;
1428 if (g_model.thrTraceSrc > NUM_POTS+NUM_SLIDERS) {
1429 uint8_t ch = g_model.thrTraceSrc-NUM_POTS-NUM_SLIDERS-1;
1430 val = channelOutputs[ch];
1432 LimitData * lim = limitAddress(ch);
1433 int16_t gModelMax = LIMIT_MAX_RESX(lim);
1434 int16_t gModelMin = LIMIT_MIN_RESX(lim);
1436 if (lim->revert)
1437 val = -val + gModelMax;
1438 else
1439 val = val - gModelMin;
1441 #if defined(PPM_LIMITS_SYMETRICAL)
1442 if (lim->symetrical) {
1443 val -= calc1000toRESX(lim->offset);
1445 #endif
1447 gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default
1448 // usually max is 1024 min is -1024 --> max-min = 2048 full range
1450 if (gModelMax != 0 && gModelMax != 2048)
1451 val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs
1453 if (val < 0)
1454 val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits
1456 else {
1457 val = RESX + calibratedAnalogs[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1];
1460 val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4)
1462 evalTimers(val, tick10ms);
1464 static uint8_t s_cnt_100ms;
1465 static uint8_t s_cnt_1s;
1466 static uint8_t s_cnt_samples_thr_1s;
1467 static uint16_t s_sum_samples_thr_1s;
1469 s_cnt_samples_thr_1s++;
1470 s_sum_samples_thr_1s+=val;
1472 if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec
1473 s_cnt_100ms -= 10;
1474 s_cnt_1s += 1;
1476 logicalSwitchesTimerTick();
1477 checkTrainerSignalWarning();
1479 if (s_cnt_1s >= 10) { // 1sec
1480 s_cnt_1s -= 10;
1481 sessionTimer += 1;
1482 inactivity.counter++;
1483 if ((((uint8_t)inactivity.counter) & 0x07) == 0x01 && g_eeGeneral.inactivityTimer && inactivity.counter > ((uint16_t)g_eeGeneral.inactivityTimer * 60))
1484 AUDIO_INACTIVITY();
1486 #if defined(AUDIO)
1487 if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1);
1488 if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2);
1489 if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3);
1490 #endif
1492 val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s;
1493 s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps
1494 if (val)
1495 s_timeCumThr += 1;
1496 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
1498 #if defined(THRTRACE)
1499 // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size.
1500 // in case buffer runs out, it wraps around
1501 // resolution for y axis is only 32, therefore no higher value makes sense
1502 s_cnt_samples_thr_10s += s_cnt_samples_thr_1s;
1503 s_sum_samples_thr_10s += s_sum_samples_thr_1s;
1505 if (++s_cnt_10s >= 10) { // 10s
1506 s_cnt_10s -= 10;
1507 val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s;
1508 s_sum_samples_thr_10s = 0;
1509 s_cnt_samples_thr_10s = 0;
1510 s_traceBuf[s_traceWr % MAXTRACE] = val;
1511 s_traceWr++;
1513 #endif
1515 s_cnt_samples_thr_1s = 0;
1516 s_sum_samples_thr_1s = 0;
1520 #if defined(PXX) || defined(DSM2)
1521 static uint8_t countRangecheck = 0;
1522 for (uint8_t i=0; i<NUM_MODULES; ++i) {
1523 #if defined(MULTIMODULE)
1524 if (moduleState[i].mode >= MODULE_MODE_BEEP_FIRST || getMultiModuleStatus(i).isBinding()) {
1525 #else
1526 if (moduleState[i].mode >= MODULE_MODE_BEEP_FIRST) {
1527 #endif
1528 if (++countRangecheck >= 250) {
1529 countRangecheck = 0;
1530 AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP);
1534 #endif
1536 checkTrims();
1538 DEBUG_TIMER_STOP(debugTimerMixes10ms);
1540 s_mixer_first_run_done = true;
1543 #if !defined(OPENTX_START_DEFAULT_ARGS)
1544 #define OPENTX_START_DEFAULT_ARGS 0
1545 #endif
1547 void opentxStart(const uint8_t startOptions = OPENTX_START_DEFAULT_ARGS)
1549 TRACE("opentxStart(%u)", startOptions);
1551 uint8_t calibration_needed = !(startOptions & OPENTX_START_NO_CALIBRATION) && (g_eeGeneral.chkSum != evalChkSum());
1553 #if defined(GUI)
1554 if (!calibration_needed && !(startOptions & OPENTX_START_NO_SPLASH)) {
1555 doSplash();
1557 #endif
1559 #if defined(DEBUG_TRACE_BUFFER)
1560 trace_event(trace_start, 0x12345678);
1561 #endif
1563 #if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU)
1564 for (int i=0; i<500 && !Card_initialized; i++) {
1565 RTOS_WAIT_MS(2); // 2ms
1567 #endif
1569 #if defined(NIGHTLY_BUILD_WARNING)
1570 ALERT(STR_NIGHTLY_WARNING, TR_NIGHTLY_NOTSAFE, AU_ERROR);
1571 #endif
1573 #if defined(GUI)
1574 if (calibration_needed) {
1575 chainMenu(menuFirstCalib);
1577 else if (!(startOptions & OPENTX_START_NO_CHECKS)) {
1578 checkAlarm();
1579 checkAll();
1580 PLAY_MODEL_NAME();
1582 #endif
1585 void opentxClose(uint8_t shutdown)
1587 TRACE("opentxClose");
1589 if (shutdown) {
1590 watchdogSuspend(2000/*20s*/);
1591 pausePulses(); // stop mixer task to disable trims processing while in shutdown
1592 AUDIO_BYE();
1593 // TODO needed? telemetryEnd();
1594 #if defined(LUA)
1595 luaClose(&lsScripts);
1596 #if defined(COLORLCD)
1597 luaClose(&lsWidgets);
1598 #endif
1599 #endif
1600 #if defined(HAPTIC)
1601 hapticOff();
1602 #endif
1605 #if defined(SDCARD)
1606 logsClose();
1607 #endif
1609 storageFlushCurrentModel();
1611 if (sessionTimer > 0) {
1612 g_eeGeneral.globalTimer += sessionTimer;
1613 sessionTimer = 0;
1616 #if defined(PCBSKY9X)
1617 uint32_t mAhUsed = g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.txCurrentCalibration) / 8192 / 36;
1618 if (g_eeGeneral.mAhUsed != mAhUsed) {
1619 g_eeGeneral.mAhUsed = mAhUsed;
1621 #endif
1623 g_eeGeneral.unexpectedShutdown = 0;
1624 storageDirty(EE_GENERAL);
1625 storageCheck(true);
1627 while (IS_PLAYING(ID_PLAY_PROMPT_BASE + AU_BYE)) {
1628 RTOS_WAIT_MS(10);
1631 RTOS_WAIT_MS(100);
1633 #if defined(SDCARD)
1634 sdDone();
1635 #endif
1638 #if defined(STM32)
1639 void opentxResume()
1641 TRACE("opentxResume");
1643 menuHandlers[0] = menuMainView;
1645 sdMount();
1646 storageReadAll();
1648 #if defined(COLORLCD)
1649 loadTheme();
1650 loadFontCache();
1651 #endif
1653 // removed to avoid the double warnings (throttle, switch, etc.)
1654 // opentxStart(OPENTX_START_NO_SPLASH | OPENTX_START_NO_CALIBRATION | OPENTX_START_NO_CHECKS);
1656 referenceSystemAudioFiles();
1658 if (!g_eeGeneral.unexpectedShutdown) {
1659 g_eeGeneral.unexpectedShutdown = 1;
1660 storageDirty(EE_GENERAL);
1663 #endif
1665 #define INSTANT_TRIM_MARGIN 10 /* around 1% */
1667 void instantTrim()
1669 int16_t anas_0[MAX_INPUTS];
1670 evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks);
1671 memcpy(anas_0, anas, sizeof(anas_0));
1673 evalInputs(e_perout_mode_notrainer);
1675 for (uint8_t stick=0; stick<NUM_STICKS; stick++) {
1676 if (stick!=THR_STICK) {
1677 // don't instant trim the throttle stick
1678 uint8_t trim_phase = getTrimFlightMode(mixerCurrentFlightMode, stick);
1679 int16_t delta = 0;
1680 for (int e=0; e<MAX_EXPOS; e++) {
1681 ExpoData * ed = expoAddress(e);
1682 if (!EXPO_VALID(ed)) break; // end of list
1683 if (ed->srcRaw-MIXSRC_Rud == stick) {
1684 delta = anas[ed->chn] - anas_0[ed->chn];
1685 break;
1688 if (abs(delta) >= INSTANT_TRIM_MARGIN) {
1689 int16_t trim = limit<int16_t>(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX);
1690 setTrimValue(trim_phase, stick, trim);
1695 storageDirty(EE_MODEL);
1696 AUDIO_WARNING2();
1699 void copySticksToOffset(uint8_t ch)
1701 pauseMixerCalculations();
1702 int32_t zero = (int32_t)channelOutputs[ch];
1704 evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0);
1705 int32_t val = chans[ch];
1706 LimitData *ld = limitAddress(ch);
1707 limit_min_max_t lim = LIMIT_MIN(ld);
1708 if (val < 0) {
1709 val = -val;
1710 lim = LIMIT_MIN(ld);
1712 zero = (zero*256000 - val*lim) / (1024*256-val);
1713 ld->offset = (ld->revert ? -zero : zero);
1714 resumeMixerCalculations();
1715 storageDirty(EE_MODEL);
1718 void copyTrimsToOffset(uint8_t ch)
1720 int16_t zero;
1722 pauseMixerCalculations();
1724 evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims
1725 zero = applyLimits(ch, chans[ch]);
1727 evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims
1729 int16_t output = applyLimits(ch, chans[ch]) - zero;
1730 int16_t v = g_model.limitData[ch].offset;
1731 if (g_model.limitData[ch].revert) output = -output;
1732 v += (output * 125) / 128;
1733 g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire
1735 resumeMixerCalculations();
1736 storageDirty(EE_MODEL);
1740 #if defined(STARTUP_ANIMATION)
1742 inline uint32_t PWR_PRESS_DURATION_MIN()
1744 if (g_eeGeneral.version != EEPROM_VER)
1745 return 200;
1747 return (2 - g_eeGeneral.pwrOnSpeed) * 100;
1750 constexpr uint32_t PWR_PRESS_DURATION_MAX = 500; // 5s
1752 void runStartupAnimation()
1754 tmr10ms_t start = get_tmr10ms();
1755 tmr10ms_t duration = 0;
1756 bool isPowerOn = false;
1758 while (pwrPressed()) {
1759 duration = get_tmr10ms() - start;
1760 if (duration < PWR_PRESS_DURATION_MIN()) {
1761 drawStartupAnimation(duration, PWR_PRESS_DURATION_MIN());
1763 else if (duration >= PWR_PRESS_DURATION_MAX) {
1764 drawSleepBitmap();
1765 backlightDisable();
1767 else if (!isPowerOn) {
1768 isPowerOn = true;
1769 pwrOn();
1770 haptic.play(15, 3, PLAY_NOW);
1774 if (duration < PWR_PRESS_DURATION_MIN() || duration >= PWR_PRESS_DURATION_MAX) {
1775 boardOff();
1778 #endif
1780 void moveTrimsToOffsets() // copy state of 3 primary to subtrim
1782 int16_t zeros[MAX_OUTPUT_CHANNELS];
1784 pauseMixerCalculations();
1786 evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims
1787 for (uint8_t i=0; i<MAX_OUTPUT_CHANNELS; i++) {
1788 zeros[i] = applyLimits(i, chans[i]);
1791 evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims
1793 for (uint8_t i=0; i<MAX_OUTPUT_CHANNELS; i++) {
1794 int16_t output = applyLimits(i, chans[i]) - zeros[i];
1795 int16_t v = g_model.limitData[i].offset;
1796 if (g_model.limitData[i].revert) output = -output;
1797 v += (output * 125) / 128;
1798 g_model.limitData[i].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire
1801 // reset all trims, except throttle (if throttle trim)
1802 for (uint8_t i=0; i<NUM_TRIMS; i++) {
1803 if (i != THR_STICK || !g_model.thrTrim) {
1804 int16_t original_trim = getTrimValue(mixerCurrentFlightMode, i);
1805 for (uint8_t fm=0; fm<MAX_FLIGHT_MODES; fm++) {
1806 trim_t trim = getRawTrimValue(fm, i);
1807 if (trim.mode / 2 == fm)
1808 setTrimValue(fm, i, trim.value - original_trim);
1813 resumeMixerCalculations();
1815 storageDirty(EE_MODEL);
1816 AUDIO_WARNING2();
1819 #if defined(ROTARY_ENCODER_NAVIGATION)
1820 volatile rotenc_t rotencValue = 0;
1821 uint8_t rotencSpeed;
1822 #endif
1824 void opentxInit()
1826 TRACE("opentxInit");
1828 #if defined(GUI)
1829 menuHandlers[0] = menuMainView;
1830 #if MENUS_LOCK != 2/*no menus*/
1831 menuHandlers[1] = menuModelSelect;
1832 #endif
1833 #endif
1835 #if defined(EEPROM)
1836 bool radioSettingsValid = storageReadRadioSettings(false);
1837 #endif
1839 BACKLIGHT_ENABLE(); // we start the backlight during the startup animation
1841 #if defined(STARTUP_ANIMATION)
1842 if (WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) {
1843 pwrOn();
1845 else {
1846 runStartupAnimation();
1848 #else // defined(PWR_BUTTON_PRESS)
1849 pwrOn();
1850 #endif
1852 // Radios handle UNEXPECTED_SHUTDOWN() differently:
1853 // * radios with WDT and EEPROM and CPU controlled power use Reset status register
1854 // and eeGeneral.unexpectedShutdown
1855 // * radios with SDCARD model storage use Reset status register and special
1856 // variables in RAM. They can not use eeGeneral.unexpectedShutdown
1857 // * radios without CPU controlled power can only use Reset status register (if available)
1858 if (UNEXPECTED_SHUTDOWN()) {
1859 TRACE("Unexpected Shutdown detected");
1860 globalData.unexpectedShutdown = 1;
1863 #if defined(RTC_BACKUP_RAM)
1864 SET_POWER_REASON(0);
1865 #endif
1867 #if defined(SDCARD)
1868 // SDCARD related stuff, only done if not unexpectedShutdown
1869 if (!globalData.unexpectedShutdown) {
1870 sdInit();
1872 #if defined(AUTOUPDATE)
1873 sportStopSendByteLoop();
1874 if (f_stat(AUTOUPDATE_FILENAME, nullptr) == FR_OK) {
1875 FrSkyFirmwareInformation information;
1876 if (readFrSkyFirmwareInformation(AUTOUPDATE_FILENAME, information) == nullptr) {
1877 #if defined(BLUETOOTH)
1878 if (information.productFamily == FIRMWARE_FAMILY_BLUETOOTH_CHIP) {
1879 if (bluetooth.flashFirmware(AUTOUPDATE_FILENAME) == nullptr)
1880 f_unlink(AUTOUPDATE_FILENAME);
1882 #endif
1883 #if defined(HARDWARE_POWER_MANAGEMENT_UNIT)
1884 if (information.productFamily == FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT) {
1885 FrskyChipFirmwareUpdate device;
1886 if (device.flashFirmware(AUTOUPDATE_FILENAME, false) == nullptr)
1887 f_unlink(AUTOUPDATE_FILENAME);
1889 #endif
1892 #endif
1894 logsInit();
1896 #endif
1898 #if defined(EEPROM)
1899 if (!radioSettingsValid)
1900 storageReadRadioSettings();
1901 storageReadCurrentModel();
1902 #endif
1904 #if defined(COLORLCD)
1905 if (!globalData.unexpectedShutdown) {
1906 // g_model.topbarData is still zero here (because it was not yet read from SDCARD),
1907 // but we only remember the pointer to in in constructor.
1908 // The storageReadAll() needs topbar object, so it must be created here
1909 topbar = new Topbar(&g_model.topbarData);
1911 // lua widget state must also be prepared before the call to storageReadAll()
1912 LUA_INIT_THEMES_AND_WIDGETS();
1914 #endif
1916 // handling of storage for radios that have no EEPROM
1917 #if !defined(EEPROM)
1918 #if defined(RTC_BACKUP_RAM) && !defined(SIMU)
1919 if (globalData.unexpectedShutdown) {
1920 // SDCARD not available, try to restore last model from RAM
1921 TRACE("rambackupRestore");
1922 rambackupRestore();
1924 else {
1925 storageReadAll();
1927 #else
1928 storageReadAll();
1929 #endif
1930 #endif // #if !defined(EEPROM)
1932 #if defined(AUX_SERIAL)
1933 auxSerialInit(g_eeGeneral.auxSerialMode, modelTelemetryProtocol());
1934 #endif
1936 #if MENUS_LOCK == 1
1937 getMovedSwitch();
1938 if (TRIMS_PRESSED() && g_eeGeneral.switchUnlockStates==switches_states) {
1939 readonly = false;
1941 #endif
1943 currentSpeakerVolume = requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF;
1944 #if !defined(SOFTWARE_VOLUME)
1945 setScaledVolume(currentSpeakerVolume);
1946 #endif
1948 referenceSystemAudioFiles();
1949 audioQueue.start();
1950 BACKLIGHT_ENABLE();
1952 #if defined(PCBSKY9X)
1953 // Set ADC gains here
1954 setSticksGain(g_eeGeneral.sticksGain);
1955 #endif
1957 #if defined(PCBSKY9X) && defined(BLUETOOTH)
1958 btInit();
1959 #endif
1961 #if defined(COLORLCD)
1962 loadTheme();
1963 loadFontCache();
1964 #endif
1966 if (g_eeGeneral.backlightMode != e_backlight_mode_off) {
1967 // on Tx start turn the light on
1968 backlightOn();
1971 if (!globalData.unexpectedShutdown) {
1972 opentxStart();
1975 #if !defined(RTC_BACKUP_RAM)
1976 if (!g_eeGeneral.unexpectedShutdown) {
1977 g_eeGeneral.unexpectedShutdown = 1;
1978 storageDirty(EE_GENERAL);
1980 #endif
1982 #if defined(GUI)
1983 lcdSetContrast();
1984 #endif
1986 backlightOn();
1988 startPulses();
1990 WDG_ENABLE(WDG_DURATION);
1993 #if defined(SIMU)
1994 void simuMain()
1995 #else
1996 int main()
1997 #endif
1999 #if defined(STM32)
2000 TRACE("reusableBuffer: modelSel=%d, moduleSetup=%d, calib=%d, sdManager=%d, hardwareAndSettings=%d, spectrumAnalyser=%d, usb=%d",
2001 sizeof(reusableBuffer.modelsel),
2002 sizeof(reusableBuffer.moduleSetup),
2003 sizeof(reusableBuffer.calib),
2004 sizeof(reusableBuffer.sdManager),
2005 sizeof(reusableBuffer.hardwareAndSettings),
2006 sizeof(reusableBuffer.spectrumAnalyser),
2007 sizeof(reusableBuffer.MSC_BOT_Data));
2008 #endif
2010 // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's
2011 // important to disable it before commencing with system initialisation (or
2012 // we could put a bunch more WDG_RESET()s in. But I don't like that approach
2013 // during boot up.)
2014 #if defined(PCBTARANIS)
2015 g_eeGeneral.contrast = LCD_CONTRAST_DEFAULT;
2016 #endif
2018 boardInit();
2020 #if defined(COLORLCD)
2021 loadFonts();
2022 #endif
2024 #if !defined(SIMU)
2025 stackPaint();
2026 #endif
2028 #if defined(SPLASH) && !defined(STARTUP_ANIMATION)
2029 if (!UNEXPECTED_SHUTDOWN()) {
2030 drawSplash();
2032 #endif
2034 #if defined(PCBHORUS)
2035 if (!IS_FIRMWARE_COMPATIBLE_WITH_BOARD()) {
2036 runFatalErrorScreen(STR_WRONG_PCBREV);
2038 #endif
2040 #if !defined(EEPROM)
2041 if (!SD_CARD_PRESENT() && !UNEXPECTED_SHUTDOWN()) {
2042 runFatalErrorScreen(STR_NO_SDCARD);
2044 #endif
2046 tasksStart();
2049 #if !defined(SIMU)
2050 #if defined(PWR_BUTTON_PRESS)
2052 inline uint32_t PWR_PRESS_SHUTDOWN_DELAY()
2054 return (2 - g_eeGeneral.pwrOffSpeed) * 100;
2057 uint32_t pwr_press_time = 0;
2059 uint32_t pwrPressedDuration()
2061 if (pwr_press_time == 0) {
2062 return 0;
2064 else {
2065 return get_tmr10ms() - pwr_press_time;
2069 uint32_t pwrCheck()
2071 const char * message = nullptr;
2073 enum PwrCheckState {
2074 PWR_CHECK_ON,
2075 PWR_CHECK_OFF,
2076 PWR_CHECK_PAUSED,
2079 static uint8_t pwr_check_state = PWR_CHECK_ON;
2081 if (pwr_check_state == PWR_CHECK_OFF) {
2082 return e_power_off;
2084 else if (pwrPressed()) {
2085 if (TELEMETRY_STREAMING()) {
2086 message = STR_MODEL_STILL_POWERED;
2088 if (pwr_check_state == PWR_CHECK_PAUSED) {
2089 // nothing
2091 else if (pwr_press_time == 0) {
2092 pwr_press_time = get_tmr10ms();
2093 if (message && !g_eeGeneral.disableRssiPoweroffAlarm) {
2094 audioEvent(AU_MODEL_STILL_POWERED);
2097 else {
2098 inactivity.counter = 0;
2099 if (g_eeGeneral.backlightMode != e_backlight_mode_off) {
2100 BACKLIGHT_ENABLE();
2102 if (get_tmr10ms() - pwr_press_time > PWR_PRESS_SHUTDOWN_DELAY()) {
2103 #if defined(SHUTDOWN_CONFIRMATION)
2104 while (1) {
2105 #else
2106 while ((TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm)) {
2107 #endif
2108 lcdRefreshWait();
2109 lcdClear();
2111 POPUP_CONFIRMATION(STR_MODEL_SHUTDOWN, nullptr);
2112 SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0);
2113 event_t evt = getEvent(false);
2114 DISPLAY_WARNING(evt);
2115 lcdRefresh();
2117 if (warningResult) {
2118 pwr_check_state = PWR_CHECK_OFF;
2119 return e_power_off;
2121 else if (!warningText) {
2122 // shutdown has been cancelled
2123 pwr_check_state = PWR_CHECK_PAUSED;
2124 return e_power_on;
2127 haptic.play(15, 3, PLAY_NOW);
2128 pwr_check_state = PWR_CHECK_OFF;
2129 return e_power_off;
2131 else {
2132 drawShutdownAnimation(pwrPressedDuration(), PWR_PRESS_SHUTDOWN_DELAY(), message);
2133 return e_power_press;
2137 else {
2138 pwr_check_state = PWR_CHECK_ON;
2139 pwr_press_time = 0;
2142 return e_power_on;
2144 #else
2145 uint32_t pwrCheck()
2147 #if defined(SOFT_PWR_CTRL)
2148 if (pwrPressed()) {
2149 return e_power_on;
2151 #endif
2153 if (usbPlugged()) {
2154 return e_power_usb;
2157 #if defined(TRAINER_PWR)
2158 if (TRAINER_CONNECTED()) {
2159 return e_power_trainer;
2161 #endif
2163 if (!g_eeGeneral.disableRssiPoweroffAlarm) {
2164 if (TELEMETRY_STREAMING()) {
2165 RAISE_ALERT(STR_MODEL, STR_MODEL_STILL_POWERED, STR_PRESS_ENTER_TO_CONFIRM, AU_MODEL_STILL_POWERED);
2166 while (TELEMETRY_STREAMING()) {
2167 resetForcePowerOffRequest();
2168 RTOS_WAIT_MS(20);
2169 if (pwrPressed()) {
2170 return e_power_on;
2172 else if (readKeys() == (1 << KEY_ENTER)) {
2173 return e_power_off;
2179 return e_power_off;
2181 #endif // defined(PWR_BUTTON_PRESS)
2182 #endif // !defined(SIMU)