Companion: Russian UI (#7180)
[opentx.git] / radio / src / switches.cpp
blobb02c058612a1e9b7ff58a3d4c383117e7cc48953
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 "opentx.h"
23 #define CS_LAST_VALUE_INIT -32768
25 #if defined(PCBHORUS)
26 #define SWITCH_WARNING_LIST_X WARNING_LINE_X
27 #define SWITCH_WARNING_LIST_Y WARNING_LINE_Y+3*FH
28 #elif LCD_W >= 212
29 #define SWITCH_WARNING_LIST_X 60
30 #define SWITCH_WARNING_LIST_Y 4*FH+4
31 #else
32 #define SWITCH_WARNING_LIST_X 4
33 #define SWITCH_WARNING_LIST_Y 4*FH+4
34 #endif
36 enum LogicalSwitchContextState {
37 SWITCH_START,
38 SWITCH_DELAY,
39 SWITCH_ENABLE
42 PACK(struct LogicalSwitchContext {
43 uint8_t state:1;
44 uint8_t timerState:2;
45 uint8_t spare:5;
46 uint8_t timer;
47 int16_t lastValue;
48 });
50 PACK(struct LogicalSwitchesFlightModeContext {
51 LogicalSwitchContext lsw[MAX_LOGICAL_SWITCHES];
52 });
53 LogicalSwitchesFlightModeContext lswFm[MAX_FLIGHT_MODES];
55 #define LS_LAST_VALUE(fm, idx) lswFm[fm].lsw[idx].lastValue
57 #if defined(PCBTARANIS) || defined(PCBHORUS)
58 #if defined(PCBX9E)
59 tmr10ms_t switchesMidposStart[16];
60 #else
61 tmr10ms_t switchesMidposStart[6]; // TODO constant
62 #endif
63 uint64_t switchesPos = 0;
64 tmr10ms_t potsLastposStart[NUM_XPOTS];
65 uint8_t potsPos[NUM_XPOTS];
67 #define SWITCH_POSITION(sw) (switchesPos & ((MASK_CFN_TYPE)1<<(sw)))
68 #define POT_POSITION(sw) ((potsPos[(sw)/XPOTS_MULTIPOS_COUNT] & 0x0f) == ((sw) % XPOTS_MULTIPOS_COUNT))
70 div_t switchInfo(int switchPosition)
72 return div(switchPosition-SWSRC_FIRST_SWITCH, 3);
75 uint64_t check2PosSwitchPosition(uint8_t sw)
77 uint32_t index = (switchState(sw) ? sw : sw + 2);
78 uint64_t result = ((uint64_t)1 << index);
80 if (!(switchesPos & result)) {
81 PLAY_SWITCH_MOVED(index);
84 return result;
87 uint64_t check3PosSwitchPosition(uint8_t idx, uint8_t sw, bool startup)
89 uint64_t result;
90 uint32_t index;
92 if (switchState(sw)) {
93 index = sw;
94 result = ((MASK_CFN_TYPE)1 << index);
95 switchesMidposStart[idx] = 0;
97 else if (switchState(sw+2)) {
98 index = sw + 2;
99 result = ((MASK_CFN_TYPE)1 << index);
100 switchesMidposStart[idx] = 0;
102 else {
103 index = sw + 1;
104 if (startup || SWITCH_POSITION(index) || g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (switchesMidposStart[idx] && (tmr10ms_t)(get_tmr10ms() - switchesMidposStart[idx]) > SWITCHES_DELAY())) {
105 result = ((MASK_CFN_TYPE)1 << index);
106 switchesMidposStart[idx] = 0;
108 else {
109 result = (switchesPos & ((MASK_CFN_TYPE)0x7 << sw));
110 if (!switchesMidposStart[idx]) {
111 switchesMidposStart[idx] = get_tmr10ms();
116 if (!(switchesPos & result)) {
117 PLAY_SWITCH_MOVED(index);
120 return result;
123 #define CHECK_2POS(sw) newPos |= check2PosSwitchPosition(sw ## 0)
124 #define CHECK_3POS(idx, sw) newPos |= check3PosSwitchPosition(idx, sw ## 0, startup)
126 void getSwitchesPosition(bool startup)
128 uint64_t newPos = 0;
129 CHECK_3POS(0, SW_SA);
130 CHECK_3POS(1, SW_SB);
131 CHECK_3POS(2, SW_SC);
133 #if defined(PCBX9LITES)
134 CHECK_2POS(SW_SD);
135 CHECK_2POS(SW_SE);
136 CHECK_2POS(SW_SF);
137 CHECK_2POS(SW_SG);
138 #elif defined(PCBX9LITE)
139 CHECK_2POS(SW_SD);
140 CHECK_2POS(SW_SE);
141 #elif defined(PCBXLITES)
142 CHECK_3POS(3, SW_SD);
143 CHECK_2POS(SW_SE);
144 CHECK_2POS(SW_SF);
145 // no SWG and SWH on XLITES
146 #elif defined(PCBXLITE)
147 CHECK_3POS(3, SW_SD);
148 // no SWE, SWF, SWG and SWH on XLITE
149 #elif defined(PCBX7)
150 CHECK_3POS(3, SW_SD);
151 CHECK_2POS(SW_SF);
152 CHECK_2POS(SW_SH);
153 #else
154 CHECK_3POS(3, SW_SD);
155 CHECK_3POS(4, SW_SE);
156 CHECK_2POS(SW_SF);
157 CHECK_3POS(5, SW_SG);
158 CHECK_2POS(SW_SH);
159 #endif
161 #if defined(PCBX9DP) && PCBREV >= 2019
162 CHECK_2POS(SW_SI);
163 #endif
165 #if defined(PCBX7ACCESS)
166 CHECK_2POS(SW_SI);
167 #elif defined(PCBHORUS) || defined(PCBX7)
168 CHECK_2POS(SW_SI);
169 CHECK_2POS(SW_SJ);
170 #endif
172 #if defined(PCBX9E)
173 CHECK_3POS(6, SW_SI);
174 CHECK_3POS(7, SW_SJ);
175 CHECK_3POS(8, SW_SK);
176 CHECK_3POS(9, SW_SL);
177 CHECK_3POS(10, SW_SM);
178 CHECK_3POS(11, SW_SN);
179 CHECK_3POS(12, SW_SO);
180 CHECK_3POS(13, SW_SP);
181 CHECK_3POS(14, SW_SQ);
182 CHECK_3POS(15, SW_SR);
183 #endif
185 switchesPos = newPos;
187 for (int i=0; i<NUM_XPOTS; i++) {
188 if (IS_POT_MULTIPOS(POT1+i)) {
189 StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+i];
190 if (IS_MULTIPOS_CALIBRATED(calib)) {
191 uint8_t pos = anaIn(POT1+i) / (2*RESX/calib->count);
192 uint8_t previousPos = potsPos[i] >> 4;
193 uint8_t previousStoredPos = potsPos[i] & 0x0F;
194 if (startup) {
195 potsPos[i] = (pos << 4) | pos;
197 else if (pos != previousPos) {
198 potsLastposStart[i] = get_tmr10ms();
199 potsPos[i] = (pos << 4) | previousStoredPos;
201 else if (g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (tmr10ms_t)(get_tmr10ms() - potsLastposStart[i]) > SWITCHES_DELAY()) {
202 potsLastposStart[i] = 0;
203 potsPos[i] = (pos << 4) | pos;
204 if (previousStoredPos != pos) {
205 PLAY_SWITCH_MOVED(SWSRC_LAST_SWITCH+i*XPOTS_MULTIPOS_COUNT+pos);
214 getvalue_t getValueForLogicalSwitch(mixsrc_t i)
216 getvalue_t result = getValue(i);
217 if (i>=MIXSRC_FIRST_INPUT && i<=MIXSRC_LAST_INPUT) {
218 int8_t trimIdx = virtualInputsTrims[i-MIXSRC_FIRST_INPUT];
219 if (trimIdx >= 0) {
220 int16_t trim = trims[trimIdx];
221 if (trimIdx == THR_STICK && g_model.throttleReversed)
222 result -= trim;
223 else
224 result += trim;
227 return result;
229 #else
230 #define getValueForLogicalSwitch(i) getValue(i)
231 #endif
233 PACK(typedef struct {
234 uint8_t state;
235 uint8_t last;
236 }) ls_sticky_struct;
238 PACK(typedef struct {
239 uint16_t state:1;
240 uint16_t duration:15;
241 }) ls_stay_struct;
243 bool getLogicalSwitch(uint8_t idx)
245 LogicalSwitchData * ls = lswAddress(idx);
246 bool result;
248 swsrc_t s = ls->andsw;
250 if (ls->func == LS_FUNC_NONE || (s && !getSwitch(s))) {
251 if (ls->func != LS_FUNC_STICKY && ls->func != LS_FUNC_EDGE ) {
252 // AND switch must not affect STICKY and EDGE processing
253 LS_LAST_VALUE(mixerCurrentFlightMode, idx) = CS_LAST_VALUE_INIT;
255 result = false;
257 else if ((s=lswFamily(ls->func)) == LS_FAMILY_BOOL) {
258 bool res1 = getSwitch(ls->v1);
259 bool res2 = getSwitch(ls->v2);
260 switch (ls->func) {
261 case LS_FUNC_AND:
262 result = (res1 && res2);
263 break;
264 case LS_FUNC_OR:
265 result = (res1 || res2);
266 break;
267 // case LS_FUNC_XOR:
268 default:
269 result = (res1 ^ res2);
270 break;
273 else if (s == LS_FAMILY_TIMER) {
274 result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) <= 0);
276 else if (s == LS_FAMILY_STICKY) {
277 result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0));
279 else if (s == LS_FAMILY_EDGE) {
280 result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0));
282 else {
283 getvalue_t x = getValueForLogicalSwitch(ls->v1);
284 getvalue_t y;
285 if (s == LS_FAMILY_COMP) {
286 y = getValueForLogicalSwitch(ls->v2);
288 switch (ls->func) {
289 case LS_FUNC_EQUAL:
290 result = (x==y);
291 break;
292 case LS_FUNC_GREATER:
293 result = (x>y);
294 break;
295 default:
296 result = (x<y);
297 break;
300 else {
301 mixsrc_t v1 = ls->v1;
302 // Telemetry
303 if (v1 >= MIXSRC_FIRST_TELEM) {
304 if (!TELEMETRY_STREAMING() || IS_FAI_FORBIDDEN(v1-1)) {
305 result = false;
306 goto DurationAndDelayProcessing;
309 y = convertLswTelemValue(ls);
313 else if (v1 >= MIXSRC_GVAR1) {
314 y = ls->v2;
316 else {
317 y = calc100toRESX(ls->v2);
320 switch (ls->func) {
321 case LS_FUNC_VEQUAL:
322 result = (x==y);
323 break;
324 case LS_FUNC_VALMOSTEQUAL:
325 #if defined(GVARS)
326 if (v1 >= MIXSRC_GVAR1 && v1 <= MIXSRC_LAST_GVAR)
327 result = (x==y);
328 else
329 #endif
330 result = (abs(x-y) < (1024 / STICK_TOLERANCE));
331 break;
332 case LS_FUNC_VPOS:
333 result = (x>y);
334 break;
335 case LS_FUNC_VNEG:
336 result = (x<y);
337 break;
338 case LS_FUNC_APOS:
339 result = (abs(x)>y);
340 break;
341 case LS_FUNC_ANEG:
342 result = (abs(x)<y);
343 break;
344 default:
346 if (LS_LAST_VALUE(mixerCurrentFlightMode, idx) == CS_LAST_VALUE_INIT) {
347 LS_LAST_VALUE(mixerCurrentFlightMode, idx) = x;
349 int16_t diff = x - LS_LAST_VALUE(mixerCurrentFlightMode, idx);
350 bool update = false;
351 if (ls->func == LS_FUNC_DIFFEGREATER) {
352 if (y >= 0) {
353 result = (diff >= y);
354 if (diff < 0)
355 update = true;
357 else {
358 result = (diff <= y);
359 if (diff > 0)
360 update = true;
363 else {
364 result = (abs(diff) >= y);
366 if (result || update) {
367 LS_LAST_VALUE(mixerCurrentFlightMode, idx) = x;
369 break;
375 DurationAndDelayProcessing:
377 if (ls->delay || ls->duration) {
378 LogicalSwitchContext &context = lswFm[mixerCurrentFlightMode].lsw[idx];
379 if (result) {
380 if (context.timerState == SWITCH_START) {
381 // set delay timer
382 context.timerState = SWITCH_DELAY;
383 context.timer = (ls->func == LS_FUNC_EDGE ? 0 : ls->delay);
386 if (context.timerState == SWITCH_DELAY) {
387 if (context.timer) {
388 result = false; // return false while delay timer running
390 else {
391 // set duration timer
392 context.timerState = SWITCH_ENABLE;
393 context.timer = ls->duration;
397 if (context.timerState == SWITCH_ENABLE) {
398 result = (ls->duration==0 || context.timer>0); // return false after duration timer runs out
399 if (!result && ls->func == LS_FUNC_STICKY) {
400 ls_sticky_struct & lastValue = (ls_sticky_struct &)context.lastValue;
401 lastValue.state = 0;
405 else if (context.timerState == SWITCH_ENABLE && ls->duration > 0 && context.timer > 0) {
406 result = true;
408 else {
409 context.timerState = SWITCH_START;
410 context.timer = 0;
414 return result;
417 bool getSwitch(swsrc_t swtch, uint8_t flags)
419 bool result;
421 if (swtch == SWSRC_NONE)
422 return true;
424 uint8_t cs_idx = abs(swtch);
426 if (cs_idx == SWSRC_ONE) {
427 result = !s_mixer_first_run_done;
429 else if (cs_idx == SWSRC_ON) {
430 result = true;
432 #if defined(DEBUG_LATENCY)
433 else if (cs_idx == SWSRC_LATENCY_TOGGLE) {
434 result = latencyToggleSwitch;
436 #endif
437 else if (cs_idx <= SWSRC_LAST_SWITCH) {
438 #if defined(PCBTARANIS) || defined(PCBHORUS)
439 if (flags & GETSWITCH_MIDPOS_DELAY)
440 result = SWITCH_POSITION(cs_idx-SWSRC_FIRST_SWITCH);
441 else
442 result = switchState(cs_idx-SWSRC_FIRST_SWITCH);
443 #else
444 result = switchState(cs_idx-SWSRC_FIRST_SWITCH);
445 #endif
448 #if NUM_XPOTS > 0
449 else if (cs_idx <= SWSRC_LAST_MULTIPOS_SWITCH) {
450 result = POT_POSITION(cs_idx-SWSRC_FIRST_MULTIPOS_SWITCH);
452 #endif
453 else if (cs_idx <= SWSRC_LAST_TRIM) {
454 uint8_t idx = cs_idx - SWSRC_FIRST_TRIM;
455 idx = (CONVERT_MODE_TRIMS(idx/2) << 1) + (idx & 1);
456 result = trimDown(idx);
458 else if (cs_idx == SWSRC_RADIO_ACTIVITY) {
459 result = (inactivity.counter < 2);
461 else if (cs_idx >= SWSRC_FIRST_SENSOR) {
462 result = !telemetryItems[cs_idx-SWSRC_FIRST_SENSOR].isOld();
464 else if (cs_idx == SWSRC_TELEMETRY_STREAMING) {
465 result = TELEMETRY_STREAMING();
467 else if (cs_idx >= SWSRC_FIRST_FLIGHT_MODE) {
468 #if defined(FLIGHT_MODES)
469 uint8_t idx = cs_idx - SWSRC_FIRST_FLIGHT_MODE;
470 if (flags & GETSWITCH_MIDPOS_DELAY)
471 result = (idx == flightModeTransitionLast);
472 else
473 result = (idx == mixerCurrentFlightMode);
474 #else
475 result = false;
476 #endif
478 else {
479 cs_idx -= SWSRC_FIRST_LOGICAL_SWITCH;
480 result = lswFm[mixerCurrentFlightMode].lsw[cs_idx].state;
483 return swtch > 0 ? result : !result;
487 @brief Calculates new state of logical switches for mixerCurrentFlightMode
489 void evalLogicalSwitches(bool isCurrentFlightmode)
491 for (unsigned int idx=0; idx<MAX_LOGICAL_SWITCHES; idx++) {
492 LogicalSwitchContext & context = lswFm[mixerCurrentFlightMode].lsw[idx];
493 bool result = getLogicalSwitch(idx);
494 if (isCurrentFlightmode) {
495 if (result) {
496 if (!context.state) PLAY_LOGICAL_SWITCH_ON(idx);
498 else {
499 if (context.state) PLAY_LOGICAL_SWITCH_OFF(idx);
502 context.state = result;
506 swarnstate_t switches_states = 0;
507 swsrc_t getMovedSwitch()
509 static tmr10ms_t s_move_last_time = 0;
510 swsrc_t result = 0;
512 #if defined(PCBTARANIS) || defined(PCBHORUS)
513 for (int i=0; i<NUM_SWITCHES; i++) {
514 if (SWITCH_EXISTS(i)) {
515 swarnstate_t mask = ((swarnstate_t)0x03 << (i*2));
516 uint8_t prev = (switches_states & mask) >> (i*2);
517 uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024;
518 if (prev != next) {
519 switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*2));
520 result = 1+(3*i)+next;
524 #else
525 // return delivers 1 to 3 for ID1 to ID3
526 // 4..8 for all other switches if changed to true
527 // -4..-8 for all other switches if changed to false
528 // 9 for Trainer switch if changed to true; Change to false is ignored
529 swarnstate_t mask = 0x80;
530 for (uint8_t i=NUM_SWITCHES_POSITIONS; i>1; i--) {
531 bool prev;
532 prev = (switches_states & mask);
533 // don't use getSwitch here to always get the proper value, even getSwitch manipulates
534 bool next = switchState(i-1);
535 if (prev != next) {
536 if (((i<NUM_SWITCHES_POSITIONS) && (i>3)) || next==true)
537 result = next ? i : -i;
538 if (i<=3 && result==0) result = 1;
539 switches_states ^= mask;
541 mask >>= 1;
543 #endif
545 if ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10)
546 result = 0;
548 s_move_last_time = get_tmr10ms();
549 return result;
552 #if defined(GUI)
553 void checkSwitches()
555 swarnstate_t last_bad_switches = 0xff;
556 swarnstate_t states = g_model.switchWarningState;
558 #if defined(PCBTARANIS) || defined(PCBHORUS)
559 uint8_t bad_pots = 0, last_bad_pots = 0xff;
560 #endif
562 #if defined(PWR_BUTTON_PRESS)
563 bool refresh = false;
564 #endif
566 while (true) {
567 #if defined(PCBTARANIS) || defined(PCBHORUS)
568 #define GETADC_COUNT 1
569 #endif
571 #ifdef GETADC_COUNT
572 for (uint8_t i=0; i<GETADC_COUNT; i++) {
573 GET_ADC_IF_MIXER_NOT_RUNNING();
575 #undef GETADC_COUNT
576 #endif
578 getMovedSwitch();
580 bool warn = false;
581 #if defined(COLORLCD)
582 for (int i=0; i<NUM_SWITCHES; i++) {
583 if (SWITCH_WARNING_ALLOWED(i)) {
584 unsigned int state = ((states >> (3*i)) & 0x07);
585 if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) {
586 warn = true;
590 if (g_model.potsWarnMode) {
591 evalFlightModeMixes(e_perout_mode_normal, 0);
592 bad_pots = 0;
593 for (int i=0; i<NUM_POTS+NUM_SLIDERS; i++) {
594 if (!IS_POT_SLIDER_AVAILABLE(POT1+i)) {
595 continue;
597 if (!(g_model.potsWarnEnabled & (1 << i)) && (abs(g_model.potsWarnPosition[i] - GET_LOWRES_POT_POSITION(i)) > 1)) {
598 warn = true;
599 bad_pots |= (1<<i);
603 #elif defined(PCBTARANIS)
604 for (int i=0; i<NUM_SWITCHES; i++) {
605 if (SWITCH_WARNING_ALLOWED(i) && !(g_model.switchWarningEnable & (1<<i))) {
606 swarnstate_t mask = ((swarnstate_t)0x03 << (i*2));
607 if (!((states & mask) == (switches_states & mask))) {
608 warn = true;
612 if (g_model.potsWarnMode) {
613 evalFlightModeMixes(e_perout_mode_normal, 0);
614 bad_pots = 0;
615 for (int i=0; i<NUM_POTS+NUM_SLIDERS; i++) {
616 if (!IS_POT_SLIDER_AVAILABLE(POT1+i)) {
617 continue;
619 if (!(g_model.potsWarnEnabled & (1 << i)) && (abs(g_model.potsWarnPosition[i] - GET_LOWRES_POT_POSITION(i)) > 1)) {
620 warn = true;
621 bad_pots |= (1<<i);
625 #else
626 for (int i=0; i<NUM_SWITCHES-1; i++) {
627 if (!(g_model.switchWarningEnable & (1<<i))) {
628 if (i == 0) {
629 if ((states & 0x03) != (switches_states & 0x03)) {
630 warn = true;
633 else if ((states & (1<<(i+1))) != (switches_states & (1<<(i+1)))) {
634 warn = true;
638 #endif
640 if (!warn) {
641 break;
644 LED_ERROR_BEGIN();
645 backlightOn();
647 // first - display warning
648 #if defined(PCBTARANIS) || defined(PCBHORUS)
649 if (last_bad_switches != switches_states || last_bad_pots != bad_pots) {
650 drawAlertBox(STR_SWITCHWARN, nullptr, STR_PRESSANYKEYTOSKIP);
651 if (last_bad_switches == 0xff || last_bad_pots == 0xff) {
652 AUDIO_ERROR_MESSAGE(AU_SWITCH_ALERT);
654 int x = SWITCH_WARNING_LIST_X;
655 int y = SWITCH_WARNING_LIST_Y;
656 int numWarnings = 0;
657 for (int i=0; i<NUM_SWITCHES; ++i) {
658 #if defined(COLORLCD)
659 if (SWITCH_WARNING_ALLOWED(i)) {
660 unsigned int state = ((g_model.switchWarningState >> (3*i)) & 0x07);
661 if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) {
662 if (++numWarnings < 6) {
663 // LcdFlags attr = ((states & mask) == (switches_states & mask)) ? TEXT_COLOR : ALARM_COLOR;
664 LcdFlags attr = ALARM_COLOR;
665 drawSwitch(x, y, SWSRC_FIRST_SWITCH+i*3+state-1, attr);
666 x += 35;
670 #else
671 if (SWITCH_WARNING_ALLOWED(i) && !(g_model.switchWarningEnable & (1<<i))) {
672 swarnstate_t mask = ((swarnstate_t)0x03 << (i*2));
673 LcdFlags attr = ((states & mask) == (switches_states & mask)) ? 0 : INVERS;
674 if (attr) {
675 if (++numWarnings < 6) {
676 char c = "\300-\301"[(states & mask) >> (i*2)];
677 drawSource(x, y, MIXSRC_FIRST_SWITCH+i, attr);
678 lcdDrawChar(lcdNextPos, y, c, attr);
679 x = lcdNextPos + 3;
683 #endif
686 if (g_model.potsWarnMode) {
687 for (int i=0; i<NUM_POTS+NUM_SLIDERS; i++) {
688 if (!IS_POT_SLIDER_AVAILABLE(POT1+i)) {
689 continue;
691 if (!(g_model.potsWarnEnabled & (1 << i))) {
692 if (abs(g_model.potsWarnPosition[i] - GET_LOWRES_POT_POSITION(i)) > 1) {
693 if (++numWarnings < 6) {
694 #if defined(COLORLCD)
695 char s[8];
696 // TODO add an helper
697 strncpy(s, &STR_VSRCRAW[1+(NUM_STICKS+1+i)*STR_VSRCRAW[0]], STR_VSRCRAW[0]);
698 s[int(STR_VSRCRAW[0])] = '\0';
699 lcdDrawText(x, y, s, ALARM_COLOR);
700 x += 40;
701 #else
702 lcdDrawTextAtIndex(x, y, STR_VSRCRAW, NUM_STICKS + 1 + i, INVERS);
703 if (IS_POT(POT1 + i))
704 lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? 126 : 127, INVERS);
705 else
706 lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? '\300' : '\301', INVERS);
707 x = lcdNextPos + 3;
708 #endif
715 if (numWarnings >= 6) {
716 #if defined(COLORLCD)
717 lcdDrawText(x, y, "...", ALARM_COLOR);
718 #else
719 lcdDrawText(x, y, "...", 0);
720 #endif
723 last_bad_pots = bad_pots;
724 #else
725 if (last_bad_switches != switches_states) {
726 RAISE_ALERT(STR_SWITCHWARN, NULL, STR_PRESSANYKEYTOSKIP, last_bad_switches == 0xff ? AU_SWITCH_ALERT : AU_NONE);
727 uint8_t x = 2;
728 for (uint8_t i=0; i<NUM_SWITCHES-1; i++) {
729 uint8_t attr;
730 if (i == 0)
731 attr = ((states & 0x03) != (switches_states & 0x03)) ? INVERS : 0;
732 else
733 attr = (states & (1 << (i+1))) == (switches_states & (1 << (i+1))) ? 0 : INVERS;
734 if (!(g_model.switchWarningEnable & (1<<i)))
735 drawSwitch(x, 5*FH, (i>0?(i+3):(states&0x3)+1), attr);
736 x += 3*FW+FW/2;
738 #endif
740 lcdRefresh();
741 lcdSetContrast();
742 waitKeysReleased();
744 last_bad_switches = switches_states;
747 if (keyDown())
748 break;
750 #if defined(PWR_BUTTON_PRESS)
751 uint32_t power = pwrCheck();
752 if (power == e_power_off) {
753 drawSleepBitmap();
754 boardOff();
755 break;
757 else if (power == e_power_press) {
758 refresh = true;
760 else if (power == e_power_on && refresh) {
761 last_bad_switches = 0xff;
762 last_bad_pots = 0xff;
763 refresh = false;
765 #else
766 if (pwrCheck() == e_power_off) {
767 break;
769 #endif
771 checkBacklight();
773 WDG_RESET();
775 RTOS_WAIT_MS(10);
778 LED_ERROR_END();
780 #endif // GUI
782 void logicalSwitchesTimerTick()
784 for (uint8_t fm=0; fm<MAX_FLIGHT_MODES; fm++) {
785 for (uint8_t i=0; i<MAX_LOGICAL_SWITCHES; i++) {
786 LogicalSwitchData * ls = lswAddress(i);
787 if (ls->func == LS_FUNC_TIMER) {
788 int16_t * lastValue = &LS_LAST_VALUE(fm, i);
789 if (*lastValue == 0 || *lastValue == CS_LAST_VALUE_INIT) {
790 *lastValue = -lswTimerValue(ls->v1);
793 else if (*lastValue < 0) {
794 if (++(*lastValue) == 0)
795 *lastValue = lswTimerValue(ls->v2);
797 else { // if (*lastValue > 0)
798 *lastValue -= 1;
801 else if (ls->func == LS_FUNC_STICKY) {
802 ls_sticky_struct & lastValue = (ls_sticky_struct &)LS_LAST_VALUE(fm, i);
803 bool before = lastValue.last & 0x01;
804 if (lastValue.state) {
805 bool now = getSwitch(ls->v2);
806 if (now != before) {
807 lastValue.last ^= 1;
808 if (!before) {
809 lastValue.state = 0;
813 else {
814 bool now = getSwitch(ls->v1);
815 if (before != now) {
816 lastValue.last ^= 1;
817 if (!before) {
818 lastValue.state = 1;
823 else if (ls->func == LS_FUNC_EDGE) {
824 ls_stay_struct & lastValue = (ls_stay_struct &)LS_LAST_VALUE(fm, i);
825 // if this ls was reset by the logicalSwitchesReset() the lastValue will be set to CS_LAST_VALUE_INIT(0x8000)
826 // when it is unpacked into ls_stay_struct the lastValue.duration will have a value of 0x4000
827 // this will produce an instant true for edge logical switch if the second parameter is big enough.
828 // So we reset it here.
829 if (LS_LAST_VALUE(fm, i) == CS_LAST_VALUE_INIT) {
830 lastValue.duration = 0;
832 lastValue.state = false;
833 bool state = getSwitch(ls->v1);
834 if (state) {
835 if (ls->v3 == -1 && lastValue.duration == lswTimerValue(ls->v2))
836 lastValue.state = true;
837 if (lastValue.duration < 1000)
838 lastValue.duration++;
840 else {
841 if (lastValue.duration > lswTimerValue(ls->v2) && (ls->v3 == 0 || lastValue.duration <= lswTimerValue(ls->v2+ls->v3)))
842 lastValue.state = true;
843 lastValue.duration = 0;
847 // decrement delay/duration timer
848 LogicalSwitchContext &context = lswFm[fm].lsw[i];
849 if (context.timer) {
850 context.timer--;
856 LogicalSwitchData * lswAddress(uint8_t idx)
858 return &g_model.logicalSw[idx];
861 uint8_t lswFamily(uint8_t func)
863 if (func <= LS_FUNC_ANEG)
864 return LS_FAMILY_OFS;
865 else if (func <= LS_FUNC_XOR)
866 return LS_FAMILY_BOOL;
867 else if (func == LS_FUNC_EDGE)
868 return LS_FAMILY_EDGE;
869 else if (func <= LS_FUNC_LESS)
870 return LS_FAMILY_COMP;
871 else if (func <= LS_FUNC_ADIFFEGREATER)
872 return LS_FAMILY_DIFF;
873 else
874 return LS_FAMILY_TIMER+func-LS_FUNC_TIMER;
877 int16_t lswTimerValue(delayval_t val)
879 return (val < -109 ? 129+val : (val < 7 ? (113+val)*5 : (53+val)*10));
882 void logicalSwitchesReset()
884 memset(lswFm, 0, sizeof(lswFm));
886 for (uint8_t fm=0; fm<MAX_FLIGHT_MODES; fm++) {
887 for (uint8_t i=0; i<MAX_LOGICAL_SWITCHES; i++) {
888 LS_LAST_VALUE(fm, i) = CS_LAST_VALUE_INIT;
893 getvalue_t convertLswTelemValue(LogicalSwitchData * ls)
895 getvalue_t val;
896 val = convert16bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, ls->v2);
897 return val;
900 void logicalSwitchesCopyState(uint8_t src, uint8_t dst)
902 lswFm[dst] = lswFm[src];