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.
23 RadioData g_eeGeneral
;
31 uint8_t g_tmr1Latency_max
;
32 uint8_t g_tmr1Latency_min
;
33 uint16_t lastMixerDuration
;
36 uint8_t unexpectedShutdown
= 0;
38 /* AVR: mixer duration in 1/16ms */
39 /* ARM: mixer duration in 0.5us */
40 uint16_t maxMixerDuration
;
42 #if defined(AUDIO) && !defined(CPUARM)
48 #if defined(OVERRIDE_CHANNEL_FUNCTION)
49 safetych_t safetyCh
[MAX_OUTPUT_CHANNELS
];
52 union ReusableBuffer reusableBuffer
;
54 const pm_uint8_t bchout_ar
[] PROGMEM
= {
55 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39,
56 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78,
57 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4,
58 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 };
60 uint8_t channel_order(uint8_t x
)
62 return ( ((pgm_read_byte(bchout_ar
+ g_eeGeneral
.templateSetup
) >> (6-(x
-1) * 2)) & 3 ) + 1 );
71 const pm_uint8_t modn12x3
[] PROGMEM
= {
77 volatile tmr10ms_t g_tmr10ms
;
80 volatile uint8_t rtc_count
= 0;
81 uint32_t watchdogTimeout
= 0;
83 void watchdogSuspend(uint32_t timeout
)
85 watchdogTimeout
= timeout
;
94 if (watchdogTimeout
) {
96 wdt_reset(); // Retrigger hardware watchdog
101 if (lightOffCounter
) lightOffCounter
--;
102 if (flashCounter
) flashCounter
--;
103 if (noHighlightCounter
) noHighlightCounter
--;
106 if (trimsCheckTimer
) trimsCheckTimer
--;
107 if (ppmInputValidityTimer
) ppmInputValidityTimer
--;
110 if (trimsDisplayTimer
)
113 trimsDisplayMask
= 0;
117 /* Update global Date/Time every 100 per10ms cycles */
118 if (++g_ms100
== 100) {
119 g_rtcTime
++; // inc global unix timestamp one second
120 #if defined(COPROCESSOR)
121 if (g_rtcTime
< 60 || rtc_count
<5) {
126 coprocReadData(true);
135 #if defined(ROTARY_ENCODER_NAVIGATION)
136 if (IS_ROTARY_ENCODER_NAVIGATION_ENABLE()) {
137 static rotenc_t rePreviousValue
;
138 rotenc_t reNewValue
= (ROTARY_ENCODER_NAVIGATION_VALUE
/ ROTARY_ENCODER_GRANULARITY
);
139 int8_t scrollRE
= reNewValue
- rePreviousValue
;
141 rePreviousValue
= reNewValue
;
142 putEvent(scrollRE
< 0 ? EVT_ROTARY_LEFT
: EVT_ROTARY_RIGHT
);
145 // rotary encoder navigation speed (acceleration) detection/calculation
147 static uint32_t lastTick
= 0;
148 static uint32_t delay
= 0;
149 delay
= (((get_tmr10ms() - lastTick
) << 3) + delay
) >> 1; // Modified moving average filter used for smoother change of speed
150 lastTick
= get_tmr10ms();
151 if (delay
< ROTENC_DELAY_HIGHSPEED
)
152 rotencSpeed
= ROTENC_HIGHSPEED
;
153 else if (delay
< ROTENC_DELAY_MIDSPEED
)
154 rotencSpeed
= ROTENC_MIDSPEED
;
156 rotencSpeed
= ROTENC_LOWSPEED
;
162 #if defined(TELEMETRY_FRSKY) || defined(TELEMETRY_JETI)
163 if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol
[0])) {
164 telemetryInterrupt10ms();
168 // These moved here from evalFlightModeMixes() to improve beep trigger reliability.
169 #if defined(PWM_BACKLIGHT)
170 if ((g_tmr10ms
&0x03) == 0x00)
171 backlightFade(); // increment or decrement brightness until target brightness is reached
175 if (mixWarning
& 1) if(((g_tmr10ms
&0xFF)== 0)) AUDIO_MIX_WARNING(1);
176 if (mixWarning
& 2) if(((g_tmr10ms
&0xFF)== 64) || ((g_tmr10ms
&0xFF)== 72)) AUDIO_MIX_WARNING(2);
177 if (mixWarning
& 4) if(((g_tmr10ms
&0xFF)==128) || ((g_tmr10ms
&0xFF)==136) || ((g_tmr10ms
&0xFF)==144)) AUDIO_MIX_WARNING(3);
184 heartbeat
|= HEART_TIMER_10MS
;
187 FlightModeData
*flightModeAddress(uint8_t idx
)
189 return &g_model
.flightModeData
[idx
];
192 ExpoData
*expoAddress(uint8_t idx
)
194 return &g_model
.expoData
[idx
];
197 MixData
*mixAddress(uint8_t idx
)
199 return &g_model
.mixData
[idx
];
202 LimitData
*limitAddress(uint8_t idx
)
204 return &g_model
.limitData
[idx
];
208 void memclear(void *ptr
, uint8_t size
)
210 memset(ptr
, 0, size
);
214 void memswap(void * a
, void * b
, uint8_t size
)
216 uint8_t * x
= (uint8_t *)a
;
217 uint8_t * y
= (uint8_t *)b
;
227 void generalDefault()
229 memclear(&g_eeGeneral
, sizeof(g_eeGeneral
));
230 g_eeGeneral
.version
= EEPROM_VER
;
231 g_eeGeneral
.variant
= EEPROM_VARIANT
;
233 #if !defined(PCBHORUS)
234 g_eeGeneral
.contrast
= LCD_CONTRAST_DEFAULT
;
237 #if defined(PCBFLAMENCO)
238 g_eeGeneral
.vBatWarn
= 33;
239 g_eeGeneral
.vBatMin
= -60; // 0 is 9.0V
240 g_eeGeneral
.vBatMax
= -78; // 0 is 12.0V
241 #elif defined(PCBHORUS)
243 g_eeGeneral
.potsConfig
= 0x1B; // S1 = pot, 6P = multipos, S2 = pot with detent
245 g_eeGeneral
.potsConfig
= 0x19; // S1 = pot without detent, 6P = multipos, S2 = pot with detent
247 g_eeGeneral
.slidersConfig
= 0x0f; // 4 sliders
248 g_eeGeneral
.blOffBright
= 20;
250 g_eeGeneral
.potsConfig
= 0x07; // S1 = pot without detent, S2 = pot with detent
251 #elif defined(PCBTARANIS)
252 g_eeGeneral
.potsConfig
= 0x05; // S1 and S2 = pots with detent
253 g_eeGeneral
.slidersConfig
= 0x03; // LS and RS = sliders with detent
257 g_eeGeneral
.switchConfig
= 0x000006ff; // 4x3POS, 1x2POS, 1xTOGGLE
258 #elif defined(PCBTARANIS) || defined(PCBHORUS)
259 g_eeGeneral
.switchConfig
= 0x00007bff; // 6x3POS, 1x2POS, 1xTOGGLE
264 g_eeGeneral
.vBatWarn
= 87;
265 g_eeGeneral
.vBatMin
= -5;
266 g_eeGeneral
.vBatMax
= -5;
267 #elif defined(PCBTARANIS)
269 g_eeGeneral
.vBatWarn
= 65;
270 g_eeGeneral
.vBatMin
= -30;
271 g_eeGeneral
.vBatMax
= -40;
273 g_eeGeneral
.vBatWarn
= 90;
276 #if defined(DEFAULT_MODE)
277 g_eeGeneral
.stickMode
= DEFAULT_MODE
-1;
280 #if defined(PCBFLAMENCO)
281 g_eeGeneral
.templateSetup
= 21; /* AETR */
282 #elif defined(PCBTARANIS)
283 g_eeGeneral
.templateSetup
= 17; /* TAER */
286 #if defined(PCBFLAMENCO)
287 g_eeGeneral
.inactivityTimer
= 50;
288 #elif !defined(CPUM64)
289 g_eeGeneral
.backlightMode
= e_backlight_mode_all
;
290 g_eeGeneral
.lightAutoOff
= 2;
291 g_eeGeneral
.inactivityTimer
= 10;
295 g_eeGeneral
.ttsLanguage
[0] = 'e';
296 g_eeGeneral
.ttsLanguage
[1] = 'n';
297 g_eeGeneral
.wavVolume
= 2;
298 g_eeGeneral
.backgroundVolume
= 1;
302 for (int i
=0; i
<NUM_STICKS
; ++i
) {
303 g_eeGeneral
.trainer
.mix
[i
].mode
= 2;
304 g_eeGeneral
.trainer
.mix
[i
].srcChn
= channel_order(i
+1) - 1;
305 g_eeGeneral
.trainer
.mix
[i
].studWeight
= 100;
310 const int8_t defaultName
[] = { 20, -1, -18, -1, -14, -9, -19 };
311 memcpy(g_eeGeneral
.bluetoothName
, defaultName
, sizeof(defaultName
));
315 strcpy(g_eeGeneral
.currModelFilename
, DEFAULT_MODEL_FILENAME
);
318 #if defined(PCBHORUS)
319 strcpy(g_eeGeneral
.themeName
, theme
->getName());
323 g_eeGeneral
.chkSum
= 0xFFFF;
326 uint16_t evalChkSum()
329 const int16_t *calibValues
= (const int16_t *) &g_eeGeneral
.calib
[0];
330 for (int i
=0; i
<12; i
++)
331 sum
+= calibValues
[i
];
335 #if defined(VIRTUAL_INPUTS)
338 memset(g_model
.expoData
, 0, sizeof(g_model
.expoData
)); // clear all expos
345 for (int i
=0; i
<NUM_STICKS
; i
++) {
346 uint8_t stick_index
= channel_order(i
+1);
347 ExpoData
*expo
= expoAddress(i
);
348 expo
->srcRaw
= MIXSRC_Rud
- 1 + stick_index
;
349 expo
->curve
.type
= CURVE_REF_EXPO
;
352 expo
->mode
= 3; // TODO constant
353 #if defined(TRANSLATIONS_CZ)
354 for (int c
=0; c
<4; c
++) {
355 g_model
.inputNames
[i
][c
] = char2idx(STR_INPUTNAMES
[1+4*(stick_index
-1)+c
]);
358 for (int c
=0; c
<3; c
++) {
359 g_model
.inputNames
[i
][c
] = char2idx(STR_VSRCRAW
[2+4*stick_index
+c
]);
361 #if LEN_INPUT_NAME > 3
362 g_model
.inputNames
[i
][3] = '\0';
366 storageDirty(EE_MODEL
);
370 #if defined(TEMPLATES)
371 inline void applyDefaultTemplate()
373 applyTemplate(TMPL_SIMPLE_4CH
); // calls storageDirty internally
376 void applyDefaultTemplate()
378 #if defined(VIRTUAL_INPUTS)
379 defaultInputs(); // calls storageDirty internally
381 storageDirty(EE_MODEL
);
384 for (int i
=0; i
<NUM_STICKS
; i
++) {
385 MixData
* mix
= mixAddress(i
);
388 #if defined(VIRTUAL_INPUTS)
391 mix
->srcRaw
= MIXSRC_Rud
- 1 + channel_order(i
+1);
397 #if defined(CPUARM) && defined(EEPROM)
398 void checkModelIdUnique(uint8_t index
, uint8_t module
)
400 uint8_t modelId
= g_model
.header
.modelId
[module
];
401 uint8_t additionalOnes
= 0;
402 char * name
= reusableBuffer
.msgbuf
.msg
;
404 memset(reusableBuffer
.msgbuf
.msg
, 0, sizeof(reusableBuffer
.msgbuf
.msg
));
407 for (uint8_t i
= 0; i
< MAX_MODELS
; i
++) {
409 if (modelId
== modelHeaders
[i
].modelId
[module
]) {
410 if ((WARNING_LINE_LEN
- 4 - (name
- reusableBuffer
.msgbuf
.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 ",")
411 if (reusableBuffer
.msgbuf
.msg
[0] != 0) {
412 name
= strAppend(name
, ", ");
414 if (modelHeaders
[i
].name
[0] == 0) {
415 name
= strAppend(name
, STR_MODEL
);
416 name
= strAppendUnsigned(name
+strlen(name
),i
, 2);
419 name
+= zchar2str(name
, modelHeaders
[i
].name
, LEN_MODEL_NAME
);
430 if (additionalOnes
) {
431 name
= strAppend(name
, " (+");
432 name
= strAppendUnsigned(name
, additionalOnes
);
433 name
= strAppend(name
, ")");
436 if (reusableBuffer
.msgbuf
.msg
[0] != 0) {
437 POPUP_WARNING(STR_MODELIDUSED
);
438 SET_WARNING_INFO(reusableBuffer
.msgbuf
.msg
, sizeof(reusableBuffer
.msgbuf
.msg
), 0);
443 void modelDefault(uint8_t id
)
445 memset(&g_model
, 0, sizeof(g_model
));
447 applyDefaultTemplate();
449 #if defined(LUA) && defined(PCBTARANIS) //Horus uses menuModelWizard() for wizard
450 if (isFileAvailable(WIZARD_PATH
"/" WIZARD_NAME
)) {
451 f_chdir(WIZARD_PATH
);
452 luaExec(WIZARD_NAME
);
456 #if defined(PCBTARANIS) || defined(PCBHORUS)
457 g_model
.moduleData
[INTERNAL_MODULE
].type
= MODULE_TYPE_XJT
;
458 g_model
.moduleData
[INTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(INTERNAL_MODULE
);
459 #elif defined(PCBSKY9X)
460 g_model
.moduleData
[EXTERNAL_MODULE
].type
= MODULE_TYPE_PPM
;
463 #if defined(CPUARM) && defined(EEPROM)
464 for (int i
=0; i
<NUM_MODULES
; i
++) {
465 modelHeaders
[id
].modelId
[i
] = g_model
.header
.modelId
[i
] = id
+1;
467 checkModelIdUnique(id
, 0);
470 #if defined(CPUARM) && defined(FLIGHT_MODES) && defined(GVARS)
471 for (int p
=1; p
<MAX_FLIGHT_MODES
; p
++) {
472 for (int i
=0; i
<MAX_GVARS
; i
++) {
473 g_model
.flightModeData
[p
].gvars
[i
] = GVAR_MAX
+1;
478 #if defined(FLIGHT_MODES) && defined(ROTARY_ENCODERS)
479 for (int p
=1; p
<MAX_FLIGHT_MODES
; p
++) {
480 for (int i
=0; i
<ROTARY_ENCODERS
; i
++) {
481 g_model
.flightModeData
[p
].rotaryEncoders
[i
] = ROTARY_ENCODER_MAX
+1;
486 #if defined(TELEMETRY_MAVLINK)
487 g_model
.mavlink
.rc_rssi_scale
= 15;
488 g_model
.mavlink
.pc_rssi_en
= 1;
492 strcpy(g_model
.header
.name
, "\015\361\374\373\364");
493 g_model
.header
.name
[5] = '\033' + id
/10;
494 g_model
.header
.name
[6] = '\033' + id
%10;
497 #if defined(PCBHORUS)
498 extern const LayoutFactory
* defaultLayout
;
499 delete customScreens
[0];
500 customScreens
[0] = defaultLayout
->create(&g_model
.screenData
[0].layoutData
);
501 strcpy(g_model
.screenData
[0].layoutName
, "Layout2P1");
502 extern const WidgetFactory
* defaultWidget
;
503 customScreens
[0]->createWidget(0, defaultWidget
);
504 // enable switch warnings
505 for (int i
=0; i
<NUM_SWITCHES
; i
++) {
506 g_model
.switchWarningState
|= (1 << (3*i
));
511 #if defined(VIRTUAL_INPUTS)
512 bool isInputRecursive(int index
)
514 ExpoData
* line
= expoAddress(0);
515 for (int i
=0; i
<MAX_EXPOS
; i
++, line
++) {
516 if (line
->chn
> index
)
518 else if (line
->chn
< index
)
520 else if (line
->srcRaw
>= MIXSRC_FIRST_LOGICAL_SWITCH
)
527 #if defined(AUTOSOURCE)
528 int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS
)
531 static tmr10ms_t s_move_last_time
= 0;
533 #if defined(VIRTUAL_INPUTS)
534 static int16_t inputsStates
[MAX_INPUTS
];
535 if (min
<= MIXSRC_FIRST_INPUT
) {
536 for (uint8_t i
=0; i
<MAX_INPUTS
; i
++) {
537 if (abs(anas
[i
] - inputsStates
[i
]) > 512) {
538 if (!isInputRecursive(i
)) {
539 result
= MIXSRC_FIRST_INPUT
+i
;
547 static int16_t sourcesStates
[NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_MOUSE_ANALOGS
];
549 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
; i
++) {
550 if (abs(calibratedAnalogs
[i
] - sourcesStates
[i
]) > 512) {
551 result
= MIXSRC_Rud
+i
;
557 bool recent
= ((tmr10ms_t
)(get_tmr10ms() - s_move_last_time
) > 10);
562 if (result
|| recent
) {
563 #if defined(VIRTUAL_INPUTS)
564 memcpy(inputsStates
, anas
, sizeof(inputsStates
));
566 memcpy(sourcesStates
, calibratedAnalogs
, sizeof(sourcesStates
));
569 s_move_last_time
= get_tmr10ms();
574 #if defined(FLIGHT_MODES)
575 uint8_t getFlightMode()
577 for (uint8_t i
=1; i
<MAX_FLIGHT_MODES
; i
++) {
578 FlightModeData
*phase
= &g_model
.flightModeData
[i
];
579 if (phase
->swtch
&& getSwitch(phase
->swtch
)) {
587 trim_t
getRawTrimValue(uint8_t phase
, uint8_t idx
)
589 FlightModeData
* p
= flightModeAddress(phase
);
591 return (((trim_t
)p
->trim
[idx
]) << 2) + ((p
->trim_ext
>> (2*idx
)) & 0x03);
597 int getTrimValue(uint8_t phase
, uint8_t idx
)
601 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
602 trim_t v
= getRawTrimValue(phase
, idx
);
603 if (v
.mode
== TRIM_MODE_NONE
) {
607 unsigned int p
= v
.mode
>> 1;
608 if (p
== phase
|| phase
== 0) {
609 return result
+ v
.value
;
613 if (v
.mode
% 2 != 0) {
621 return getRawTrimValue(getTrimFlightMode(phase
, idx
), idx
);
626 bool setTrimValue(uint8_t phase
, uint8_t idx
, int trim
)
628 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
629 trim_t
& v
= flightModeAddress(phase
)->trim
[idx
];
630 if (v
.mode
== TRIM_MODE_NONE
)
632 unsigned int p
= v
.mode
>> 1;
633 if (p
== phase
|| phase
== 0) {
637 else if (v
.mode
% 2 == 0) {
641 v
.value
= limit
<int>(TRIM_EXTENDED_MIN
, trim
- getTrimValue(p
, idx
), TRIM_EXTENDED_MAX
);
645 storageDirty(EE_MODEL
);
649 void setTrimValue(uint8_t phase
, uint8_t idx
, int trim
)
652 FlightModeData
*p
= flightModeAddress(phase
);
653 p
->trim
[idx
] = (int8_t)(trim
>> 2);
655 p
->trim_ext
= (p
->trim_ext
& ~(0x03 << idx
)) + (((trim
& 0x03) << idx
));
657 FlightModeData
*p
= flightModeAddress(phase
);
660 storageDirty(EE_MODEL
);
665 uint8_t getTrimFlightMode(uint8_t phase
, uint8_t idx
)
667 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
668 if (phase
== 0) return 0;
669 trim_t trim
= getRawTrimValue(phase
, idx
);
670 if (trim
<= TRIM_EXTENDED_MAX
) return phase
;
671 uint8_t result
= trim
-TRIM_EXTENDED_MAX
-1;
672 if (result
>= phase
) result
++;
679 #if defined(ROTARY_ENCODERS)
680 uint8_t getRotaryEncoderFlightMode(uint8_t idx
)
682 uint8_t phase
= mixerCurrentFlightMode
;
683 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
684 if (phase
== 0) return 0;
685 int16_t value
= flightModeAddress(phase
)->rotaryEncoders
[idx
];
686 if (value
<= ROTARY_ENCODER_MAX
) return phase
;
687 uint8_t result
= value
-ROTARY_ENCODER_MAX
-1;
688 if (result
>= phase
) result
++;
694 int16_t getRotaryEncoder(uint8_t idx
)
696 return flightModeAddress(getRotaryEncoderFlightMode(idx
))->rotaryEncoders
[idx
];
699 void incRotaryEncoder(uint8_t idx
, int8_t inc
)
701 rotencValue
[idx
] += inc
;
702 int16_t *value
= &(flightModeAddress(getRotaryEncoderFlightMode(idx
))->rotaryEncoders
[idx
]);
703 *value
= limit((int16_t)-RESX
, (int16_t)(*value
+ (inc
* 8)), (int16_t)+RESX
);
704 storageDirty(EE_MODEL
);
709 getvalue_t
convert16bitsTelemValue(source_t channel
, ls_telemetry_value_t value
)
714 getvalue_t
convert8bitsTelemValue(source_t channel
, ls_telemetry_value_t value
)
719 #if defined(TELEMETRY_FRSKY)
720 ls_telemetry_value_t
minTelemValue(source_t channel
)
725 ls_telemetry_value_t
maxTelemValue(source_t channel
)
731 ls_telemetry_value_t
max8bitsTelemValue(source_t channel
)
736 #elif defined(TELEMETRY_FRSKY)
739 ls_telemetry_value_t minTelemValue(uint8_t channel)
766 ls_telemetry_value_t
maxTelemValue(uint8_t channel
)
782 getvalue_t
convert8bitsTelemValue(uint8_t channel
, ls_telemetry_value_t value
)
790 #if defined(TELEMETRY_FRSKY)
795 result
= value
* 8 - 500;
805 result
= (getvalue_t
)value
- 30;
810 case TELEM_MAX_SPEED
:
814 case TELEM_MAX_ASPEED
:
823 case TELEM_MAX_CURRENT
:
824 case TELEM_MAX_POWER
:
827 case TELEM_CONSUMPTION
:
828 result
= value
* 100;
831 result
= ((getvalue_t
)value
- 125) * 10;
842 #define INAC_STICKS_SHIFT 6
843 #define INAC_SWITCHES_SHIFT 8
847 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
; i
++)
848 sum
+= anaIn(i
) >> INAC_STICKS_SHIFT
;
849 for (uint8_t i
=0; i
<NUM_SWITCHES
; i
++)
850 sum
+= getValue(MIXSRC_FIRST_SWITCH
+i
) >> INAC_SWITCHES_SHIFT
;
852 if (abs((int8_t)(sum
-inactivity
.sum
)) > 1) {
853 inactivity
.sum
= sum
;
861 void checkBacklight()
863 static uint8_t tmr10ms
;
865 #if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION)
869 uint8_t x
= g_blinkTmr10ms
;
873 inactivity
.counter
= 0;
874 if (g_eeGeneral
.backlightMode
& e_backlight_mode_sticks
) {
879 bool backlightOn
= (g_eeGeneral
.backlightMode
== e_backlight_mode_on
|| lightOffCounter
|| isFunctionActive(FUNCTION_BACKLIGHT
));
880 if (flashCounter
) backlightOn
= !backlightOn
;
886 #if defined(PCBSTD) && defined(VOICE) && !defined(SIMU)
887 Voice
.voice_process() ;
892 #if defined(PCBFLAMENCO)
895 uint8_t reg
= i2cReadBQ24195(0x00);
897 i2cWriteBQ24195(0x00, reg
& 0x7F);
902 void doLoopCommonActions()
906 #if defined(PCBFLAMENCO)
913 lightOffCounter
= ((uint16_t)g_eeGeneral
.lightAutoOff
*250) << 1;
917 bool readonly
= true;
918 bool readonlyUnlocked()
921 POPUP_WARNING(STR_MODS_FORBIDDEN
);
933 #if defined(PWR_BUTTON_PRESS)
934 bool refresh
= false;
937 if (SPLASH_NEEDED()) {
946 #elif defined(PCBSKY9X)
947 tmr10ms_t curTime
= get_tmr10ms() + 10;
948 uint8_t contrast
= 10;
949 lcdSetRefVolt(contrast
);
952 getADC(); // init ADC array
956 tmr10ms_t tgtime
= get_tmr10ms() + SPLASH_TIMEOUT
;
958 while (tgtime
> get_tmr10ms()) {
961 #elif defined(CPUARM)
968 // Splash is forced, we can't skip it
969 if (!(g_eeGeneral
.splashMode
& 0x04)) {
972 if (keyDown() || inputsMoved()) return;
978 #if defined(PWR_BUTTON_PRESS)
979 uint32_t pwr_check
= pwrCheck();
980 if (pwr_check
== e_power_off
) {
983 else if (pwr_check
== e_power_press
) {
986 else if (pwr_check
== e_power_on
&& refresh
) {
991 if (pwrCheck() == e_power_off
) {
996 #if defined(SPLASH_FRSKY)
997 static uint8_t secondSplash
= false;
998 if (!secondSplash
&& get_tmr10ms() >= tgtime
-200) {
1004 #if defined(PCBSKY9X)
1005 if (curTime
< get_tmr10ms()) {
1007 if (contrast
< g_eeGeneral
.contrast
) {
1009 lcdSetRefVolt(contrast
);
1014 doLoopCommonActions();
1023 #if defined(SDCARD) && defined(CPUARM)
1024 void checkSDVersion()
1029 char version
[sizeof(REQUIRED_SDCARD_VERSION
)-1];
1030 char error
[sizeof(TR_WRONG_SDCARDVERSION
)+ sizeof(version
)];
1032 strAppend(strAppend(error
, STR_WRONG_SDCARDVERSION
, sizeof(TR_WRONG_SDCARDVERSION
)), REQUIRED_SDCARD_VERSION
, sizeof(REQUIRED_SDCARD_VERSION
));
1033 FRESULT result
= f_open(&versionFile
, "/opentx.sdcard.version", FA_OPEN_EXISTING
| FA_READ
);
1034 if (result
== FR_OK
) {
1035 if (f_read(&versionFile
, &version
, sizeof(version
), &read
) != FR_OK
||
1036 read
!= sizeof(version
) ||
1037 strncmp(version
, REQUIRED_SDCARD_VERSION
, sizeof(version
)) != 0) {
1038 TRACE("SD card version mismatch: %.*s, %s", sizeof(REQUIRED_SDCARD_VERSION
)-1, version
, REQUIRED_SDCARD_VERSION
);
1039 ALERT(STR_SD_CARD
, error
, AU_ERROR
);
1041 f_close(&versionFile
);
1044 ALERT(STR_SD_CARD
, error
, AU_ERROR
);
1050 #if defined(PCBTARANIS) || defined(PCBHORUS)
1051 void checkFailsafe()
1053 for (int i
=0; i
<NUM_MODULES
; i
++) {
1054 if (IS_MODULE_PXX(i
)) {
1055 ModuleData
& moduleData
= g_model
.moduleData
[i
];
1056 if (HAS_RF_PROTOCOL_FAILSAFE(moduleData
.rfProtocol
) && moduleData
.failsafeMode
== FAILSAFE_NOT_SET
) {
1057 ALERT(STR_FAILSAFEWARN
, STR_NO_FAILSAFE
, AU_ERROR
);
1064 #define checkFailsafe()
1067 void checkRSSIAlarmsDisabled()
1069 if (g_model
.rssiAlarms
.disabled
) {
1070 ALERT(STR_RSSIALARM_WARN
, STR_NO_RSSIALARM
, AU_ERROR
);
1078 #if defined(EEPROM_RLC)
1082 #if defined(MODULE_ALWAYS_SEND_PULSES)
1083 startupWarningState
= STARTUP_WARNING_THROTTLE
;
1085 if (g_eeGeneral
.chkSum
== evalChkSum()) {
1092 checkRSSIAlarmsDisabled();
1095 #if defined(SDCARD) && defined(CPUARM)
1100 if (g_model
.displayChecklist
&& modelHasNotes()) {
1106 if (!clearKeyEvents()) {
1107 showMessageBox(STR_KEYSTUCK
);
1108 tmr10ms_t tgtime
= get_tmr10ms() + 500;
1109 while (tgtime
!= get_tmr10ms()) {
1112 #elif defined(CPUARM)
1118 #else // #if defined(CPUARM)
1120 #endif // #if defined(CPUARM)
1122 START_SILENCE_PERIOD();
1126 #if defined(MODULE_ALWAYS_SEND_PULSES)
1127 void checkStartupWarnings()
1129 if (startupWarningState
< STARTUP_WARNING_DONE
) {
1130 if (startupWarningState
== STARTUP_WARNING_THROTTLE
)
1138 #if defined(EEPROM_RLC)
1139 void checkLowEEPROM()
1141 if (g_eeGeneral
.disableMemoryWarning
) return;
1142 if (EeFsGetFree() < 100) {
1143 ALERT(STR_STORAGE_WARNING
, STR_EEPROMLOWMEM
, AU_ERROR
);
1150 uint8_t thrchn
= ((g_model
.thrTraceSrc
==0) || (g_model
.thrTraceSrc
>NUM_POTS
+NUM_SLIDERS
)) ? THR_STICK
: g_model
.thrTraceSrc
+NUM_STICKS
-1;
1151 // throttle channel is either the stick according stick mode (already handled in evalInputs)
1153 // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS+NUM_SLIDERS) we assume the throttle stick is the input
1154 // no other information available at the moment, and good enough to my option (otherwise too much exceptions...)
1156 #if defined(MODULE_ALWAYS_SEND_PULSES)
1157 int16_t v
= calibratedAnalogs
[thrchn
];
1158 if (v
<=THRCHK_DEADBAND
-1024 || g_model
.disableThrottleWarning
|| pwrCheck()==e_power_off
|| keyDown()) {
1159 startupWarningState
= STARTUP_WARNING_THROTTLE
+1;
1162 calibratedAnalogs
[thrchn
] = -1024;
1163 #if !defined(VIRTUAL_INPUTS)
1164 if (thrchn
< NUM_STICKS
) {
1165 rawAnas
[thrchn
] = anas
[thrchn
] = calibratedAnalogs
[thrchn
];
1168 RAISE_ALERT(STR_THROTTLEWARN
, STR_THROTTLENOTIDLE
, STR_PRESSANYKEYTOSKIP
, AU_THROTTLE_ALERT
);
1171 if (g_model
.disableThrottleWarning
) {
1175 GET_ADC_IF_MIXER_NOT_RUNNING();
1177 evalInputs(e_perout_mode_notrainer
); // let do evalInputs do the job
1179 int16_t v
= calibratedAnalogs
[thrchn
];
1180 if (v
<= THRCHK_DEADBAND
-1024) {
1181 return; // prevent warning if throttle input OK
1184 // first - display warning; also deletes inputs if any have been before
1186 RAISE_ALERT(STR_THROTTLEWARN
, STR_THROTTLENOTIDLE
, STR_PRESSANYKEYTOSKIP
, AU_THROTTLE_ALERT
);
1188 #if defined(PWR_BUTTON_PRESS)
1189 bool refresh
= false;
1194 GET_ADC_IF_MIXER_NOT_RUNNING();
1196 evalInputs(e_perout_mode_notrainer
); // let do evalInputs do the job
1198 v
= calibratedAnalogs
[thrchn
];
1200 #if defined(PWR_BUTTON_PRESS)
1201 uint32_t pwr_check
= pwrCheck();
1202 if (pwr_check
== e_power_off
) {
1205 else if (pwr_check
== e_power_press
) {
1208 else if (pwr_check
== e_power_on
&& refresh
) {
1209 RAISE_ALERT(STR_THROTTLEWARN
, STR_THROTTLENOTIDLE
, STR_PRESSANYKEYTOSKIP
, AU_NONE
);
1213 if (pwrCheck() == e_power_off
) {
1218 if (keyDown() || v
<= THRCHK_DEADBAND
-1024) {
1222 doLoopCommonActions();
1237 void checkAlarm() // added by Gohst
1239 if (g_eeGeneral
.disableAlarmWarning
) {
1243 if (IS_SOUND_OFF()) {
1244 ALERT(STR_ALARMSWARN
, STR_ALARMSDISABLED
, AU_ERROR
);
1248 void alert(const pm_char
* title
, const pm_char
* msg ALERT_SOUND_ARG
)
1252 TRACE("ALERT %s: %s", title
, msg
);
1254 RAISE_ALERT(title
, msg
, STR_PRESSANYKEY
, sound
);
1256 #if defined(PWR_BUTTON_PRESS)
1257 bool refresh
= false;
1266 if (keyDown()) break; // wait for key release
1268 doLoopCommonActions();
1272 #if defined(PWR_BUTTON_PRESS)
1273 uint32_t pwr_check
= pwrCheck();
1274 if (pwr_check
== e_power_off
) {
1277 else if (pwr_check
== e_power_press
) {
1280 else if (pwr_check
== e_power_on
&& refresh
) {
1281 RAISE_ALERT(title
, msg
, STR_PRESSANYKEY
, AU_NONE
);
1285 if (pwrCheck() == e_power_off
) {
1286 boardOff(); // turn power off now
1295 int8_t trimGvar
[NUM_STICKS
+NUM_AUX_TRIMS
] = { -1, -1, -1, -1 };
1301 event_t event
= getEvent(true);
1302 if (event
&& !IS_KEY_BREAK(event
)) {
1303 int8_t k
= EVT_KEY_MASK(event
) - TRM_BASE
;
1305 uint8_t checkTrim(event_t event
)
1307 int8_t k
= EVT_KEY_MASK(event
) - TRM_BASE
;
1308 if (k
>=0 && k
<8 && !IS_KEY_BREAK(event
)) {
1310 // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP
1311 uint8_t idx
= CONVERT_MODE((uint8_t)k
/2);
1317 trimsDisplayTimer
= 200; // 2 seconds
1318 trimsDisplayMask
|= (1<<idx
);
1322 if (TRIM_REUSED(idx
)) {
1326 phase
= getGVarFlightMode(mixerCurrentFlightMode
, trimGvar
[idx
]);
1328 before
= GVAR_VALUE(trimGvar
[idx
], phase
);
1332 phase
= getTrimFlightMode(mixerCurrentFlightMode
, idx
);
1334 before
= getTrimValue(phase
, idx
);
1336 before
= getRawTrimValue(phase
, idx
);
1338 thro
= (idx
==THR_STICK
&& g_model
.thrTrim
);
1341 phase
= getTrimFlightMode(mixerCurrentFlightMode
, idx
);
1343 before
= getTrimValue(phase
, idx
);
1345 before
= getRawTrimValue(phase
, idx
);
1347 thro
= (idx
==THR_STICK
&& g_model
.thrTrim
);
1349 int8_t trimInc
= g_model
.trimInc
+ 1;
1350 int8_t v
= (trimInc
==-1) ? min(32, abs(before
)/4+1) : (1 << trimInc
); // TODO flash saving if (trimInc < 0)
1351 if (thro
) v
= 4; // if throttle trim and trim trottle then step=4
1352 int16_t after
= (k
&1) ? before
+ v
: before
- v
; // positive = k&1
1353 bool beepTrim
= false;
1355 if (!thro
&& before
!=0 && ((!(after
< 0) == (before
< 0)) || after
==0)) { //forcing a stop at centerered trim when changing sides
1358 AUDIO_TRIM_MIDDLE();
1361 else if (before
>TRIM_MIN
&& after
<=TRIM_MIN
) {
1366 else if (before
<TRIM_MAX
&& after
>=TRIM_MAX
) {
1372 if ((before
<after
&& after
>TRIM_MAX
) || (before
>after
&& after
<TRIM_MIN
)) {
1373 if (!g_model
.extendedTrims
|| TRIM_REUSED(idx
)) after
= before
;
1376 if (after
< TRIM_EXTENDED_MIN
) {
1377 after
= TRIM_EXTENDED_MIN
;
1379 if (after
> TRIM_EXTENDED_MAX
) {
1380 after
= TRIM_EXTENDED_MAX
;
1384 if (TRIM_REUSED(idx
)) {
1385 SET_GVAR_VALUE(trimGvar
[idx
], phase
, after
);
1391 if (!setTrimValue(phase
, idx
, after
)) {
1392 // we don't play a beep, so we exit now the function
1396 setTrimValue(phase
, idx
, after
);
1401 AUDIO_TRIM_PRESS(after
);
1404 #if !defined(CPUARM)
1408 #if !defined(CPUARM)
1414 uint16_t s_anaFilt
[NUM_ANALOGS
];
1418 uint16_t BandGap
= 225;
1419 #elif defined(CPUM2560)
1420 // #define STARTADCONV (ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2) | (1<<ADSC) | (1 << ADIE))
1421 // G: Note that the above would have set the ADC prescaler to 128, equating to
1422 // 125KHz sample rate. We now sample at 500KHz, with oversampling and other
1423 // filtering options to produce 11-bit results.
1424 uint16_t BandGap
= 2040;
1425 #elif defined(PCBSTD)
1429 #if defined(JITTER_MEASURE)
1430 JitterMeter
<uint16_t> rawJitter
[NUM_ANALOGS
];
1431 JitterMeter
<uint16_t> avgJitter
[NUM_ANALOGS
];
1432 tmr10ms_t jitterResetTime
= 0;
1435 #if defined(VIRTUAL_INPUTS)
1436 #define JITTER_FILTER_STRENGTH 4 // tune this value, bigger value - more filtering (range: 1-5) (see explanation below)
1437 #define ANALOG_SCALE 1 // tune this value, bigger value - more filtering (range: 0-1) (see explanation below)
1439 #define JITTER_ALPHA (1<<JITTER_FILTER_STRENGTH)
1440 #define ANALOG_MULTIPLIER (1<<ANALOG_SCALE)
1441 #define ANA_FILT(chan) (s_anaFilt[chan] / (JITTER_ALPHA * ANALOG_MULTIPLIER))
1442 #if (JITTER_ALPHA * ANALOG_MULTIPLIER > 32)
1443 #error "JITTER_FILTER_STRENGTH and ANALOG_SCALE are too big, their summ should be <= 5 !!!"
1446 #define ANALOG_SCALE 0
1447 #define JITTER_ALPHA 1
1448 #define ANALOG_MULTIPLIER 1
1449 #define ANA_FILT(chan) (s_anaFilt[chan])
1454 uint16_t anaIn(uint8_t chan
)
1456 #if defined(VIRTUAL_INPUTS)
1457 return ANA_FILT(chan
);
1459 #if defined(TELEMETRY_MOD_14051) || defined(TELEMETRY_MOD_14051_SWAPPED)
1460 static const pm_char crossAna
[] PROGMEM
= {3,1,2,0,4,5,6,0/* shouldn't be used */,TX_VOLTAGE
};
1462 static const pm_char crossAna
[] PROGMEM
= {3,1,2,0,4,5,6,7};
1464 #if defined(FRSKY_STICKS)
1465 volatile uint16_t temp
= s_anaFilt
[pgm_read_byte(crossAna
+chan
)]; // volatile saves here 40 bytes; maybe removed for newer AVR when available
1466 if (chan
< NUM_STICKS
&& (g_eeGeneral
.stickReverse
& (1 << chan
))) {
1471 volatile uint16_t *p
= &s_anaFilt
[pgm_read_byte(crossAna
+chan
)];
1480 #if defined(JITTER_MEASURE)
1481 if (JITTER_MEASURE_ACTIVE() && jitterResetTime
< get_tmr10ms()) {
1482 // reset jitter measurement every second
1483 for (uint32_t x
=0; x
<NUM_ANALOGS
; x
++) {
1484 rawJitter
[x
].reset();
1485 avgJitter
[x
].reset();
1487 jitterResetTime
= get_tmr10ms() + 100; //every second
1491 DEBUG_TIMER_START(debugTimerAdcRead
);
1493 DEBUG_TIMER_STOP(debugTimerAdcRead
);
1495 for (uint8_t x
=0; x
<NUM_ANALOGS
; x
++) {
1496 uint16_t v
= getAnalogValue(x
) >> (1 - ANALOG_SCALE
);
1498 #if defined(VIRTUAL_INPUTS)
1500 // * pass trough any big change directly
1501 // * for small change use Modified moving average (MMA) filter
1505 // Normal MMA filter has this formula:
1506 // <out> = ((ALPHA-1)*<out> + <in>)/ALPHA
1508 // If calculation is done this way with integer arithmetics, then any small change in
1509 // input signal is lost. One way to combat that, is to rearrange the formula somewhat,
1510 // to store a more precise (larger) number between iterations. The basic idea is to
1511 // store undivided value between iterations. Therefore an new variable <filtered> is
1512 // used. The new formula becomes:
1513 // <filtered> = <filtered> - <filtered>/ALPHA + <in>
1514 // <out> = <filtered>/ALPHA (use only when out is needed)
1516 // The above formula with a maximum allowed ALPHA value (we are limited by
1517 // the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still had
1518 // some jitter (a value of 1 was observed). The jitter might be bigger on other
1521 // So another idea is to use larger input values for filtering. So instead of using
1522 // input in a range from 0 to 2047, we use twice larger number (temp[x] is divided less)
1524 // This also means that ALPHA must be lowered (remember 16 bit limit), but test results
1525 // have proved that this kind of filtering gives better results. So the recommended values
1527 // JITTER_FILTER_STRENGTH 4
1530 // Variables mapping:
1532 // * <out> = s_anaFilt[x]
1533 uint16_t previous
= s_anaFilt
[x
] / JITTER_ALPHA
;
1534 uint16_t diff
= (v
> previous
) ? (v
- previous
) : (previous
- v
);
1535 if (!g_eeGeneral
.jitterFilter
&& diff
< (10*ANALOG_MULTIPLIER
)) { // g_eeGeneral.jitterFilter is inverted, 0 - active
1536 // apply jitter filter
1537 s_anaFilt
[x
] = (s_anaFilt
[x
] - previous
) + v
;
1540 #endif // #if defined(VIRTUAL_INPUTS)
1542 //use unfiltered value
1543 s_anaFilt
[x
] = v
* JITTER_ALPHA
;
1546 #if defined(JITTER_MEASURE)
1547 if (JITTER_MEASURE_ACTIVE()) {
1548 avgJitter
[x
].measure(ANA_FILT(x
));
1552 #define ANAFILT_MAX (2 * RESX * JITTER_ALPHA * ANALOG_MULTIPLIER - 1)
1553 StepsCalibData
* calib
= (StepsCalibData
*) &g_eeGeneral
.calib
[x
];
1554 if (IS_POT_MULTIPOS(x
) && IS_MULTIPOS_CALIBRATED(calib
)) {
1555 // TODO: consider adding another low pass filter to eliminate multipos switching glitches
1556 uint8_t vShifted
= ANA_FILT(x
) >> 4;
1557 s_anaFilt
[x
] = ANAFILT_MAX
;
1558 for (uint32_t i
=0; i
<calib
->count
; i
++) {
1559 if (vShifted
< calib
->steps
[i
]) {
1560 s_anaFilt
[x
] = (i
* ANAFILT_MAX
) / calib
->count
;
1567 #endif // #if defined(CPUARM)
1571 uint8_t g_vbat100mV
= 0;
1572 uint16_t lightOffCounter
;
1573 uint8_t flashCounter
= 0;
1575 uint16_t sessionTimer
;
1576 uint16_t s_timeCumThr
; // THR in 1/16 sec
1577 uint16_t s_timeCum16ThrP
; // THR% in 1/16 sec
1579 uint8_t trimsCheckTimer
= 0;
1582 uint8_t trimsDisplayTimer
= 0;
1583 uint8_t trimsDisplayMask
= 0;
1586 void flightReset(uint8_t check
)
1588 // 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)
1589 // TODO check if the vario / background music are stopped correctly if switching to a model which doesn't have these functions enabled
1591 if (!IS_MANUAL_RESET_TIMER(0)) {
1596 if (!IS_MANUAL_RESET_TIMER(1)) {
1602 if (!IS_MANUAL_RESET_TIMER(2)) {
1607 #if defined(TELEMETRY_FRSKY)
1611 s_mixer_first_run_done
= false;
1613 START_SILENCE_PERIOD();
1617 logicalSwitchesReset();
1624 #if defined(THRTRACE)
1625 uint8_t s_traceBuf
[MAXTRACE
];
1628 uint16_t s_cnt_samples_thr_10s
;
1629 uint16_t s_sum_samples_thr_10s
;
1634 uint8_t phase
= mixerCurrentFlightMode
;
1635 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_AUX_TRIMS
; i
++) {
1636 // do trim -> throttle trim if applicable
1637 int16_t trim
= getTrimValue(phase
, i
);
1638 #if !defined(CPUARM)
1639 if (i
==THR_STICK
&& g_model
.thrTrim
) {
1640 int16_t trimMin
= g_model
.extendedTrims
? TRIM_EXTENDED_MIN
: TRIM_MIN
;
1641 trim
= (((g_model
.throttleReversed
)?(int32_t)(trim
+trimMin
):(int32_t)(trim
-trimMin
)) * (RESX
-anas
[i
])) >> (RESX_SHIFT
+1);
1644 if (trimsCheckTimer
> 0) {
1652 #if !defined(PCBSTD)
1653 uint8_t mSwitchDuration
[1+NUM_ROTARY_ENCODERS
] = { 0 };
1654 #define CFN_PRESSLONG_DURATION 100
1658 uint8_t s_mixer_first_run_done
= false;
1660 void doMixerCalculations()
1662 static tmr10ms_t lastTMR
= 0;
1664 tmr10ms_t tmr10ms
= get_tmr10ms();
1665 uint8_t tick10ms
= (tmr10ms
>= lastTMR
? tmr10ms
- lastTMR
: 1);
1666 // handle tick10ms overrun
1667 // correct overflow handling costs a lot of code; happens only each 11 min;
1668 // therefore forget the exact calculation and use only 1 instead; good compromise
1671 DEBUG_TIMER_START(debugTimerGetAdc
);
1673 DEBUG_TIMER_STOP(debugTimerGetAdc
);
1675 DEBUG_TIMER_START(debugTimerGetSwitches
);
1676 getSwitchesPosition(!s_mixer_first_run_done
);
1677 DEBUG_TIMER_STOP(debugTimerGetSwitches
);
1679 #if defined(PCBSKY9X) && !defined(REVA) && !defined(SIMU)
1680 Current_analogue
= (Current_analogue
*31 + s_anaFilt
[8] ) >> 5 ;
1681 if (Current_analogue
> Current_max
)
1682 Current_max
= Current_analogue
;
1685 #if !defined(CPUARM)
1686 adcPrepareBandgap();
1689 DEBUG_TIMER_START(debugTimerEvalMixes
);
1690 evalMixes(tick10ms
);
1691 DEBUG_TIMER_STOP(debugTimerEvalMixes
);
1693 #if !defined(CPUARM)
1694 // Bandgap has had plenty of time to settle...
1698 DEBUG_TIMER_START(debugTimerMixes10ms
);
1701 #if !defined(CPUM64) && !defined(ACCURAT_THROTTLE_TIMER)
1702 // code cost is about 16 bytes for higher throttle accuracy for timer
1703 // would not be noticable anyway, because all version up to this change had only 16 steps;
1704 // now it has already 32 steps; this define would increase to 128 steps
1705 #define ACCURAT_THROTTLE_TIMER
1708 /* Throttle trace */
1711 if (g_model
.thrTraceSrc
> NUM_POTS
+NUM_SLIDERS
) {
1712 uint8_t ch
= g_model
.thrTraceSrc
-NUM_POTS
-NUM_SLIDERS
-1;
1713 val
= channelOutputs
[ch
];
1715 LimitData
*lim
= limitAddress(ch
);
1716 int16_t gModelMax
= LIMIT_MAX_RESX(lim
);
1717 int16_t gModelMin
= LIMIT_MIN_RESX(lim
);
1720 val
= -val
+ gModelMax
;
1722 val
= val
- gModelMin
;
1724 #if defined(PPM_LIMITS_SYMETRICAL)
1725 if (lim
->symetrical
) {
1726 val
-= calc1000toRESX(lim
->offset
);
1730 gModelMax
-= gModelMin
; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default
1731 // usually max is 1024 min is -1024 --> max-min = 2048 full range
1733 #ifdef ACCURAT_THROTTLE_TIMER
1734 if (gModelMax
!=0 && gModelMax
!=2048) val
= (int32_t) (val
<< 11) / (gModelMax
); // rescaling only needed if Min, Max differs
1736 // @@@ 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)
1737 gModelMax
+=255; // force rounding up --> gModelMax is bigger --> val is smaller
1738 gModelMax
>>= (10-2);
1740 if (gModelMax
!=0 && gModelMax
!=8) {
1741 val
= (val
<< 3) / gModelMax
; // rescaling only needed if Min, Max differs
1745 if (val
<0) val
=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits
1748 #if defined(VIRTUAL_INPUTS)
1749 val
= RESX
+ calibratedAnalogs
[g_model
.thrTraceSrc
== 0 ? THR_STICK
: g_model
.thrTraceSrc
+NUM_STICKS
-1];
1751 val
= RESX
+ (g_model
.thrTraceSrc
== 0 ? rawAnas
[THR_STICK
] : calibratedAnalogs
[g_model
.thrTraceSrc
+NUM_STICKS
-1]);
1755 #if defined(ACCURAT_THROTTLE_TIMER)
1756 val
>>= (RESX_SHIFT
-6); // calibrate it (resolution increased by factor 4)
1758 val
>>= (RESX_SHIFT
-4); // calibrate it
1761 evalTimers(val
, tick10ms
);
1763 static uint8_t s_cnt_100ms
;
1764 static uint8_t s_cnt_1s
;
1765 static uint8_t s_cnt_samples_thr_1s
;
1766 static uint16_t s_sum_samples_thr_1s
;
1768 s_cnt_samples_thr_1s
++;
1769 s_sum_samples_thr_1s
+=val
;
1771 if ((s_cnt_100ms
+= tick10ms
) >= 10) { // 0.1sec
1775 logicalSwitchesTimerTick();
1776 checkTrainerSignalWarning();
1778 if (s_cnt_1s
>= 10) { // 1sec
1782 struct t_inactivity
*ptrInactivity
= &inactivity
;
1783 FORCE_INDIRECT(ptrInactivity
) ;
1784 ptrInactivity
->counter
++;
1785 if ((((uint8_t)ptrInactivity
->counter
)&0x07)==0x01 && g_eeGeneral
.inactivityTimer
&& g_vbat100mV
>50 && ptrInactivity
->counter
> ((uint16_t)g_eeGeneral
.inactivityTimer
*60))
1789 if (mixWarning
& 1) if ((sessionTimer
&0x03)==0) AUDIO_MIX_WARNING(1);
1790 if (mixWarning
& 2) if ((sessionTimer
&0x03)==1) AUDIO_MIX_WARNING(2);
1791 if (mixWarning
& 4) if ((sessionTimer
&0x03)==2) AUDIO_MIX_WARNING(3);
1794 #if defined(ACCURAT_THROTTLE_TIMER)
1795 val
= s_sum_samples_thr_1s
/ s_cnt_samples_thr_1s
;
1796 s_timeCum16ThrP
+= (val
>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps
1797 if (val
) s_timeCumThr
+= 1;
1798 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
1800 val
= s_sum_samples_thr_1s
/ s_cnt_samples_thr_1s
;
1801 s_timeCum16ThrP
+= (val
>>1);
1802 if (val
) s_timeCumThr
+= 1;
1805 #if defined(THRTRACE)
1806 // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size.
1807 // in case buffer runs out, it wraps around
1808 // resolution for y axis is only 32, therefore no higher value makes sense
1809 s_cnt_samples_thr_10s
+= s_cnt_samples_thr_1s
;
1810 s_sum_samples_thr_10s
+= s_sum_samples_thr_1s
;
1812 if (++s_cnt_10s
>= 10) { // 10s
1814 val
= s_sum_samples_thr_10s
/ s_cnt_samples_thr_10s
;
1815 s_sum_samples_thr_10s
= 0;
1816 s_cnt_samples_thr_10s
= 0;
1817 s_traceBuf
[s_traceWr
% MAXTRACE
] = val
;
1822 s_cnt_samples_thr_1s
= 0;
1823 s_sum_samples_thr_1s
= 0;
1827 #if defined(PXX) || defined(DSM2)
1828 static uint8_t countRangecheck
= 0;
1829 for (uint8_t i
=0; i
<NUM_MODULES
; ++i
) {
1830 #if defined(MULTIMODULE)
1831 if (moduleFlag
[i
] != MODULE_NORMAL_MODE
|| (i
== EXTERNAL_MODULE
&& multiModuleStatus
.isBinding())) {
1833 if (moduleFlag
[i
] != MODULE_NORMAL_MODE
) {
1835 if (++countRangecheck
>= 250) {
1836 countRangecheck
= 0;
1837 AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP
);
1847 DEBUG_TIMER_STOP(debugTimerMixes10ms
);
1849 s_mixer_first_run_done
= true;
1852 #if defined(NAVIGATION_STICKS)
1853 uint8_t StickScrollAllowed
;
1854 uint8_t StickScrollTimer
;
1855 static const pm_uint8_t rate
[] PROGMEM
= { 0, 0, 100, 40, 16, 7, 3, 1 } ;
1857 uint8_t calcStickScroll( uint8_t index
)
1862 if ( ( g_eeGeneral
.stickMode
& 1 ) == 0 )
1865 value
= calibratedAnalogs
[index
] / 128;
1866 direction
= value
> 0 ? 0x80 : 0;
1868 value
= -value
; // (abs)
1871 value
= pgm_read_byte(rate
+(uint8_t)value
);
1873 StickScrollTimer
= STICK_SCROLL_TIMEOUT
; // Seconds
1874 return value
| direction
;
1879 #define OPENTX_START_ARGS uint8_t splash=true
1880 #define OPENTX_START_SPLASH_NEEDED() (splash)
1882 #define OPENTX_START_ARGS
1883 #define OPENTX_START_SPLASH_NEEDED() true
1886 void opentxStart(OPENTX_START_ARGS
)
1888 TRACE("opentxStart");
1891 if (main_thread_running
== 2) {
1896 uint8_t calibration_needed
= (g_eeGeneral
.chkSum
!= evalChkSum());
1899 if (!calibration_needed
&& OPENTX_START_SPLASH_NEEDED()) {
1904 #if defined(DEBUG_TRACE_BUFFER)
1905 trace_event(trace_start
, 0x12345678);
1908 #if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU)
1909 for (int i
=0; i
<500 && !Card_initialized
; i
++) {
1910 CoTickDelay(1); // 2ms
1914 #if defined(NIGHTLY_BUILD_WARNING)
1915 ALERT(STR_NIGHTLY_WARNING
, TR_NIGHTLY_NOTSAFE
, AU_ERROR
);
1919 if (calibration_needed
) {
1920 chainMenu(menuFirstCalib
);
1929 #if defined(CPUARM) || defined(CPUM2560)
1930 void opentxClose(uint8_t shutdown
)
1932 TRACE("opentxClose");
1936 watchdogSuspend(2000/*20s*/);
1938 pausePulses(); // stop mixer task to disable trims processing while in shutdown
1940 #if defined(TELEMETRY_FRSKY)
1941 // TODO needed? telemetryEnd();
1944 luaClose(&lsScripts
);
1945 #if defined(PCBHORUS)
1946 luaClose(&lsWidgets
);
1958 storageFlushCurrentModel();
1960 #if defined(CPUARM) && !defined(REVA)
1961 if (sessionTimer
> 0) {
1962 g_eeGeneral
.globalTimer
+= sessionTimer
;
1967 #if defined(PCBSKY9X)
1968 uint32_t mAhUsed
= g_eeGeneral
.mAhUsed
+ Current_used
* (488 + g_eeGeneral
.txCurrentCalibration
) / 8192 / 36;
1969 if (g_eeGeneral
.mAhUsed
!= mAhUsed
) {
1970 g_eeGeneral
.mAhUsed
= mAhUsed
;
1974 g_eeGeneral
.unexpectedShutdown
= 0;
1975 storageDirty(EE_GENERAL
);
1979 while (IS_PLAYING(ID_PLAY_PROMPT_BASE
+ AU_BYE
)) {
1991 #if defined(USB_MASS_STORAGE)
1994 TRACE("opentxResume");
1996 menuHandlers
[0] = menuMainView
;
2000 #if defined(PCBHORUS)
2008 referenceSystemAudioFiles();
2011 #if defined(CPUARM) || defined(CPUM2560)
2012 if (!g_eeGeneral
.unexpectedShutdown
) {
2013 g_eeGeneral
.unexpectedShutdown
= 1;
2014 storageDirty(EE_GENERAL
);
2020 #if defined(NAVIGATION_STICKS)
2021 uint8_t getSticksNavigationEvent()
2024 if (StickScrollAllowed
) {
2025 if ( StickScrollTimer
) {
2026 static uint8_t repeater
;
2030 if ( repeater
< 128 )
2034 value
= calcStickScroll( 2 );
2035 direction
= value
& 0x80;
2039 if ( repeater
> value
)
2046 evt
= EVT_KEY_FIRST(KEY_UP
);
2050 evt
= EVT_KEY_FIRST(KEY_DOWN
);
2057 value
= calcStickScroll( 3 );
2058 direction
= value
& 0x80;
2062 if ( repeater
> value
)
2069 evt
= EVT_KEY_FIRST(KEY_RIGHT
);
2073 evt
= EVT_KEY_FIRST(KEY_LEFT
);
2082 StickScrollTimer
= 0; // Seconds
2084 StickScrollAllowed
= 1 ;
2089 #if !defined(CPUARM)
2092 static uint8_t counter
= 0;
2093 #if defined(GUI) && !defined(COLORLCD)
2094 // TODO not the right menu I think ...
2095 if (menuHandlers
[menuLevel
] == menuRadioDiagAnalogs
) {
2100 if (counter
-- == 0) {
2102 int32_t instant_vbat
= anaIn(TX_VOLTAGE
);
2103 #if defined(CPUM2560)
2104 instant_vbat
= (instant_vbat
*1112 + instant_vbat
*g_eeGeneral
.txVoltageCalibration
+ (BandGap
<<2)) / (BandGap
<<3);
2106 instant_vbat
= (instant_vbat
*16 + instant_vbat
*g_eeGeneral
.txVoltageCalibration
/8) / BandGap
;
2109 static uint8_t s_batCheck
;
2110 static uint16_t s_batSum
;
2118 s_batSum
+= instant_vbat
;
2120 if (g_vbat100mV
== 0) {
2121 g_vbat100mV
= instant_vbat
;
2126 else if (!(s_batCheck
& 0x3f)) {
2128 else if (s_batCheck
== 0) {
2130 g_vbat100mV
= s_batSum
/ 8;
2133 if (s_batCheck
!= 0) {
2138 if (IS_TXBATT_WARNING() && g_vbat100mV
>50) {
2139 AUDIO_TX_BATTERY_LOW();
2144 #endif // #if !defined(CPUARM)
2147 #if !defined(SIMU) && !defined(CPUARM)
2149 volatile uint8_t g_tmr16KHz
; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow
2150 ISR(TIMER_16KHZ_VECT
, ISR_NOBLOCK
)
2152 g_tmr16KHz
++; // gruvin: Not 16KHz. Overflows occur at 61.035Hz (1/256th of 15.625KHz)
2153 // to give *16.384ms* intervals. Kind of matters for accuracy elsewhere. ;)
2154 // g_tmr16KHz is used to software-construct a 16-bit timer
2155 // from TIMER-0 (8-bit). See getTmr16KHz, below.
2158 uint16_t getTmr16KHz()
2161 uint8_t hb
= g_tmr16KHz
;
2162 uint8_t lb
= COUNTER_16KHZ
;
2163 if(hb
-g_tmr16KHz
==0) return (hb
<<8)|lb
;
2167 #if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE))
2168 // Clocks every 128 uS
2169 ISR(TIMER_AUDIO_VECT
, ISR_NOBLOCK
)
2172 PAUSE_AUDIO_INTERRUPT(); // stop reentrance
2184 RESUME_AUDIO_INTERRUPT();
2189 // Clocks every 10ms
2190 ISR(TIMER_10MS_VECT
, ISR_NOBLOCK
)
2192 // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that
2193 static uint8_t accuracyWarble
; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed
2209 uint8_t bump
= (!(++accuracyWarble
& 0x03)) ? 157 : 156;
2210 TIMER_10MS_COMPVAL
+= bump
;
2213 // Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz
2214 // equating to one count every half millisecond. (2 counts = 1ms). Control channel
2215 // count delta values thus can range from about 1600 to 4400 counts (800us to 2200us),
2216 // corresponding to a PPM signal in the range 0.8ms to 2.2ms (1.5ms at center).
2217 // (The timer is free-running and is thus not reset to zero at each capture interval.)
2218 ISR(TIMER3_CAPT_vect
) // G: High frequency noise can cause stack overflo with ISR_NOBLOCK
2220 uint16_t capture
=ICR3
;
2222 // Prevent rentrance for this IRQ only
2223 PAUSE_PPMIN_INTERRUPT();
2224 sei(); // enable other interrupts
2226 captureTrainerPulses(capture
);
2228 cli(); // disable other interrupts for stack pops before this function's RETI
2229 RESUME_PPMIN_INTERRUPT();
2233 #if defined(DSM2_SERIAL) && !defined(CPUARM)
2234 FORCEINLINE
void DSM2_USART_vect()
2236 UDR0
= *((uint16_t*)pulses2MHzRPtr
); // transmit next byte
2238 pulses2MHzRPtr
+= sizeof(uint16_t);
2240 if (pulses2MHzRPtr
== pulses2MHzWPtr
) { // if reached end of DSM2 data buffer ...
2241 UCSRB_N(TLM_USART
) &= ~(1 << UDRIE_N(TLM_USART
)); // disable UDRE interrupt
2246 #if !defined(SIMU) && !defined(CPUARM)
2248 #if defined(TELEMETRY_FRSKY)
2250 FORCEINLINE
void FRSKY_USART_vect()
2252 if (frskyTxBufferCount
> 0) {
2253 UDR_N(TLM_USART
) = frskyTxBuffer
[--frskyTxBufferCount
];
2256 UCSRB_N(TLM_USART
) &= ~(1 << UDRIE_N(TLM_USART
)); // disable UDRE interrupt
2260 // USART0/1 Transmit Data Register Emtpy ISR
2261 ISR(USART_UDRE_vect_N(TLM_USART
))
2263 #if defined(TELEMETRY_FRSKY) && defined(DSM2_SERIAL)
2264 if (IS_DSM2_PROTOCOL(g_model
.protocol
)) { // TODO not s_current_protocol?
2270 #elif defined(TELEMETRY_FRSKY)
2280 #define INSTANT_TRIM_MARGIN 10 /* around 1% */
2282 #define INSTANT_TRIM_MARGIN 15 /* around 1.5% */
2287 #if defined(VIRTUAL_INPUTS)
2288 int16_t anas_0
[NUM_INPUTS
];
2289 evalInputs(e_perout_mode_notrainer
| e_perout_mode_nosticks
);
2290 memcpy(anas_0
, anas
, sizeof(anas_0
));
2293 evalInputs(e_perout_mode_notrainer
);
2295 for (uint8_t stick
=0; stick
<NUM_STICKS
; stick
++) {
2296 if (stick
!=THR_STICK
) {
2297 // don't instant trim the throttle stick
2298 uint8_t trim_phase
= getTrimFlightMode(mixerCurrentFlightMode
, stick
);
2299 #if defined(VIRTUAL_INPUTS)
2301 for (int e
=0; e
<MAX_EXPOS
; e
++) {
2302 ExpoData
* ed
= expoAddress(e
);
2303 if (!EXPO_VALID(ed
)) break; // end of list
2304 if (ed
->srcRaw
-MIXSRC_Rud
== stick
) {
2305 delta
= anas
[ed
->chn
] - anas_0
[ed
->chn
];
2310 int16_t delta
= anas
[stick
];
2312 if (abs(delta
) >= INSTANT_TRIM_MARGIN
) {
2313 int16_t trim
= limit
<int16_t>(TRIM_EXTENDED_MIN
, (delta
+ trims
[stick
]) / 2, TRIM_EXTENDED_MAX
);
2314 setTrimValue(trim_phase
, stick
, trim
);
2319 storageDirty(EE_MODEL
);
2323 void copySticksToOffset(uint8_t ch
)
2325 pauseMixerCalculations();
2326 int32_t zero
= (int32_t)channelOutputs
[ch
];
2328 evalFlightModeMixes(e_perout_mode_nosticks
+e_perout_mode_notrainer
, 0);
2329 int32_t val
= chans
[ch
];
2330 LimitData
*ld
= limitAddress(ch
);
2331 limit_min_max_t lim
= LIMIT_MIN(ld
);
2334 lim
= LIMIT_MIN(ld
);
2337 zero
= (zero
*256000 - val
*lim
) / (1024*256-val
);
2339 zero
= (zero
*25600 - val
*lim
) / (26214-val
);
2341 ld
->offset
= (ld
->revert
? -zero
: zero
);
2342 resumeMixerCalculations();
2343 storageDirty(EE_MODEL
);
2346 void copyTrimsToOffset(uint8_t ch
)
2350 pauseMixerCalculations();
2352 evalFlightModeMixes(e_perout_mode_noinput
, 0); // do output loop - zero input sticks and trims
2353 zero
= applyLimits(ch
, chans
[ch
]);
2355 evalFlightModeMixes(e_perout_mode_noinput
-e_perout_mode_notrims
, 0); // do output loop - only trims
2357 int16_t output
= applyLimits(ch
, chans
[ch
]) - zero
;
2358 int16_t v
= g_model
.limitData
[ch
].offset
;
2359 if (g_model
.limitData
[ch
].revert
) output
= -output
;
2361 v
+= (output
* 125) / 128;
2365 g_model
.limitData
[ch
].offset
= limit((int16_t)-1000, (int16_t)v
, (int16_t)1000); // make sure the offset doesn't go haywire
2367 resumeMixerCalculations();
2368 storageDirty(EE_MODEL
);
2371 void moveTrimsToOffsets() // copy state of 3 primary to subtrim
2373 int16_t zeros
[MAX_OUTPUT_CHANNELS
];
2375 pauseMixerCalculations();
2377 evalFlightModeMixes(e_perout_mode_noinput
, 0); // do output loop - zero input sticks and trims
2378 for (uint8_t i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
2379 zeros
[i
] = applyLimits(i
, chans
[i
]);
2382 evalFlightModeMixes(e_perout_mode_noinput
-e_perout_mode_notrims
, 0); // do output loop - only trims
2384 for (uint8_t i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
2385 int16_t output
= applyLimits(i
, chans
[i
]) - zeros
[i
];
2386 int16_t v
= g_model
.limitData
[i
].offset
;
2387 if (g_model
.limitData
[i
].revert
) output
= -output
;
2389 v
+= (output
* 125) / 128;
2393 g_model
.limitData
[i
].offset
= limit((int16_t)-1000, (int16_t)v
, (int16_t)1000); // make sure the offset doesn't go haywire
2396 // reset all trims, except throttle (if throttle trim)
2397 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_AUX_TRIMS
; i
++) {
2398 if (i
!=THR_STICK
|| !g_model
.thrTrim
) {
2399 int16_t original_trim
= getTrimValue(mixerCurrentFlightMode
, i
);
2400 for (uint8_t phase
=0; phase
<MAX_FLIGHT_MODES
; phase
++) {
2402 trim_t trim
= getRawTrimValue(phase
, i
);
2403 if (trim
.mode
/ 2 == phase
)
2404 setTrimValue(phase
, i
, trim
.value
- original_trim
);
2406 trim_t trim
= getRawTrimValue(phase
, i
);
2407 if (trim
<= TRIM_EXTENDED_MAX
)
2408 setTrimValue(phase
, i
, trim
- original_trim
);
2414 resumeMixerCalculations();
2416 storageDirty(EE_MODEL
);
2420 #if defined(ROTARY_ENCODERS)
2421 volatile rotenc_t rotencValue
[ROTARY_ENCODERS
] = {0};
2422 #elif defined(ROTARY_ENCODER_NAVIGATION)
2423 volatile rotenc_t rotencValue
[1] = {0};
2426 #if defined(CPUARM) && defined(ROTARY_ENCODER_NAVIGATION)
2427 uint8_t rotencSpeed
;
2430 #if !defined(CPUARM) && !defined(SIMU)
2431 extern unsigned char __bss_end
;
2432 #define STACKPTR _SFR_IO16(0x3D)
2435 // Init Stack while interrupts are disabled
2439 p
= (unsigned char *) STACKPTR
;
2447 uint16_t stackAvailable()
2451 p
= &__bss_end
+ 1 ;
2452 while ( *p
++ == 0x55 );
2453 return p
- &__bss_end
;
2457 #if defined(CPUM2560)
2458 #define OPENTX_INIT_ARGS const uint8_t mcusr
2459 #elif defined(PCBSTD)
2460 #define OPENTX_INIT_ARGS const uint8_t mcusr
2462 #define OPENTX_INIT_ARGS
2465 void opentxInit(OPENTX_INIT_ARGS
)
2467 #if defined(DEBUG) && defined(USB_SERIAL)
2468 // CoTickDelay(5000); // 10s
2471 TRACE("opentxInit");
2474 menuHandlers
[0] = menuMainView
;
2475 #if MENUS_LOCK != 2/*no menus*/
2476 menuHandlers
[1] = menuModelSelect
;
2480 #if defined(RTCLOCK) && !defined(COPROCESSOR)
2481 rtcInit(); // RTC must be initialized before rambackupRestore() is called
2485 storageReadRadioSettings();
2488 // Radios handle UNEXPECTED_SHUTDOWN() differently:
2489 // * radios with WDT and EEPROM and CPU controlled power use Reset status register
2490 // and eeGeneral.unexpectedShutdown
2491 // * radios with SDCARD model storage use Reset status register and special
2492 // variables in RAM. They can not use eeGeneral.unexpectedShutdown
2493 // * radios without CPU controlled power can only use Reset status register (if available)
2494 if (UNEXPECTED_SHUTDOWN()) {
2495 TRACE("Unexpected Shutdown detected");
2496 unexpectedShutdown
= 1;
2499 #if defined(SDCARD) && !defined(PCBMEGA2560)
2500 // SDCARD related stuff, only done if not unexpectedShutdown
2501 if (!unexpectedShutdown
) {
2508 storageReadCurrentModel();
2511 #if defined(PCBHORUS)
2512 if (!unexpectedShutdown
) {
2513 // g_model.topbarData is still zero here (because it was not yet read from SDCARD),
2514 // but we only remember the pointer to in in constructor.
2515 // The storageReadAll() needs topbar object, so it must be created here
2517 // clang does not like this at all, turn into a warning so that -Werror does not stop here
2518 // taking address of packed member 'topbarData' of class or structure 'ModelData' may result in an unaligned pointer value [-Werror,-Waddress-of-packed-member]
2519 #pragma clang diagnostic push
2520 #pragma clang diagnostic warning "-Waddress-of-packed-member"
2522 topbar
= new Topbar(&g_model
.topbarData
);
2525 #pragma clang diagnostic pop
2528 // lua widget state must also be prepared before the call to storageReadAll()
2529 LUA_INIT_THEMES_AND_WIDGETS();
2533 // handling of storage for radios that have no EEPROM
2534 #if !defined(EEPROM)
2535 #if defined(RAMBACKUP)
2536 if (unexpectedShutdown
) {
2537 // SDCARD not available, try to restore last model from RAM
2538 TRACE("rambackupRestore");
2547 #endif // #if !defined(EEPROM)
2549 #if defined(SERIAL2)
2550 serial2Init(g_eeGeneral
.serial2Mode
, modelTelemetryProtocol());
2553 #if defined(PCBTARANIS)
2559 if (TRIMS_PRESSED() && g_eeGeneral
.switchUnlockStates
==switches_states
) {
2564 #if defined(VOICE) && defined(CPUARM)
2565 currentSpeakerVolume
= requiredSpeakerVolume
= g_eeGeneral
.speakerVolume
+ VOLUME_LEVEL_DEF
;
2566 #if !defined(SOFTWARE_VOLUME)
2567 setScaledVolume(currentSpeakerVolume
);
2572 referenceSystemAudioFiles();
2577 #if defined(PCBSKY9X)
2578 // Set ADC gains here
2579 setSticksGain(g_eeGeneral
.sticksGain
);
2582 #if defined(PCBSKY9X) && defined(BLUETOOTH)
2586 #if defined(PCBHORUS)
2591 if (g_eeGeneral
.backlightMode
!= e_backlight_mode_off
) {
2592 // on Tx start turn the light on
2596 if (!unexpectedShutdown
) {
2600 #if defined(CPUARM) || defined(CPUM2560)
2601 // TODO Horus does not need this
2602 if (!g_eeGeneral
.unexpectedShutdown
) {
2603 g_eeGeneral
.unexpectedShutdown
= 1;
2604 storageDirty(EE_GENERAL
);
2613 #if defined(PCBSKY9X) && !defined(SIMU)
2614 init_trainer_capture();
2617 #if !defined(CPUARM)
2618 doMixerCalculations();
2623 wdt_enable(WDTO_500MS
);
2627 void * simuMain(void *)
2632 // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's
2633 // important to disable it before commencing with system initialisation (or
2634 // we could put a bunch more wdt_reset()s in. But I don't like that approach
2636 #if defined(CPUM2560) || defined(CPUM2561)
2637 uint8_t mcusr
= MCUSR
; // save the WDT (etc) flags
2638 MCUSR
= 0; // must be zeroed before disabling the WDT
2639 MCUCR
= 0x80 ; // Disable JTAG port that can interfere with POT3
2640 MCUCR
= 0x80 ; // Must be done twice
2641 #elif defined(PCBSTD)
2642 uint8_t mcusr
= MCUCSR
;
2643 MCUCSR
= 0x80 ; // Disable JTAG port that can interfere with POT3
2644 MCUCSR
= 0x80 ; // Must be done twice
2646 #if defined(PCBTARANIS)
2647 g_eeGeneral
.contrast
= LCD_CONTRAST_DEFAULT
;
2653 #if defined(GUI) && !defined(PCBTARANIS) && !defined(PCBFLAMENCO) && !defined(PCBHORUS)
2662 #if defined(GUI) && !defined(PCBTARANIS)
2663 // lcdSetRefVolt(25);
2666 #if defined(SPLASH) && (defined(PCBTARANIS) || defined(PCBHORUS))
2670 sei(); // interrupts needed now
2672 #if !defined(CPUARM) && defined(TELEMETRY_FRSKY) && !defined(DSM2_SERIAL)
2676 #if defined(DSM2_SERIAL) && !defined(TELEMETRY_FRSKY)
2680 #if defined(TELEMETRY_JETI)
2684 #if defined(TELEMETRY_ARDUPILOT)
2688 #if defined(TELEMETRY_NMEA)
2692 #if defined(TELEMETRY_MAVLINK)
2696 #if defined(MENU_ROTARY_SW)
2700 #if defined(PCBHORUS)
2701 if (!IS_FIRMWARE_COMPATIBLE_WITH_BOARD()) {
2702 runFatalErrorScreen(STR_WRONG_PCBREV
);
2706 #if !defined(EEPROM)
2707 if (!SD_CARD_PRESENT() && !UNEXPECTED_SHUTDOWN()) {
2708 runFatalErrorScreen(STR_NO_SDCARD
);
2716 #if defined(CPUM2560)
2717 uint8_t shutdown_state
= 0;
2720 #if defined(PCBFLAMENCO)
2721 // TODO not here it's an ARM board ... menuEntryTime = get_tmr10ms() - 200;
2725 #if defined(CPUM2560)
2726 if ((shutdown_state
=pwrCheck()) > e_power_trainer
)
2731 if (main_thread_running
== 0)
2737 if (heartbeat
== HEART_WDT_CHECK
) {
2744 #if defined(CPUM2560)
2745 // Time to switch off
2748 boardOff(); // Only turn power off if necessary
2749 wdt_disable(); // this function is provided by AVR Libc
2750 while(1); // never return from main() - there is no code to return back, if any delays occurs in physical power it does dead loop.
2758 #if defined(PWR_BUTTON_PRESS)
2759 uint32_t pwr_press_time
= 0;
2761 uint32_t pwrPressedDuration()
2763 if (pwr_press_time
== 0) {
2767 return get_tmr10ms() - pwr_press_time
;
2773 const char * message
= NULL
;
2775 enum PwrCheckState
{
2781 static uint8_t pwr_check_state
= PWR_CHECK_ON
;
2783 if (pwr_check_state
== PWR_CHECK_OFF
) {
2786 else if (pwrPressed()) {
2787 if (TELEMETRY_STREAMING()) {
2788 message
= STR_MODEL_STILL_POWERED
;
2790 if (pwr_check_state
== PWR_CHECK_PAUSED
) {
2793 else if (pwr_press_time
== 0) {
2794 pwr_press_time
= get_tmr10ms();
2795 if (message
&& !g_eeGeneral
.disableRssiPoweroffAlarm
) {
2796 audioEvent(AU_MODEL_STILL_POWERED
);
2800 inactivity
.counter
= 0;
2801 if (g_eeGeneral
.backlightMode
!= e_backlight_mode_off
) {
2804 if (get_tmr10ms() - pwr_press_time
> PWR_PRESS_SHUTDOWN_DELAY
) {
2805 #if defined(SHUTDOWN_CONFIRMATION)
2809 POPUP_CONFIRMATION("Confirm Shutdown");
2810 event_t evt
= getEvent(false);
2811 DISPLAY_WARNING(evt
);
2813 if (warningResult
== true) {
2814 pwr_check_state
= PWR_CHECK_OFF
;
2817 else if (!warningText
) {
2818 // shutdown has been cancelled
2819 pwr_check_state
= PWR_CHECK_PAUSED
;
2824 while ((TELEMETRY_STREAMING() && !g_eeGeneral
.disableRssiPoweroffAlarm
)) {
2827 POPUP_CONFIRMATION("Confirm Shutdown");
2828 event_t evt
= getEvent(false);
2829 DISPLAY_WARNING(evt
);
2831 if (warningResult
) {
2832 pwr_check_state
= PWR_CHECK_OFF
;
2835 else if (!warningText
) {
2836 // shutdown has been cancelled
2837 pwr_check_state
= PWR_CHECK_PAUSED
;
2841 haptic
.play(15, 3, PLAY_NOW
);
2842 pwr_check_state
= PWR_CHECK_OFF
;
2847 drawShutdownAnimation(pwrPressedDuration(), message
);
2848 return e_power_press
;
2853 pwr_check_state
= PWR_CHECK_ON
;
2859 #elif defined(CPUARM)
2862 #if defined(SOFT_PWR_CTRL)
2872 #if defined(TRAINER_PWR)
2873 if (TRAINER_CONNECTED()) {
2874 return e_power_trainer
;
2878 if (!g_eeGeneral
.disableRssiPoweroffAlarm
) {
2879 if (TELEMETRY_STREAMING()) {
2880 RAISE_ALERT(STR_MODEL
, STR_MODEL_STILL_POWERED
, STR_PRESS_ENTER_TO_CONFIRM
, AU_MODEL_STILL_POWERED
);
2881 while (TELEMETRY_STREAMING()) {
2888 else if (readKeys() == (1 << KEY_ENTER
)) {