fixes trim re-use for T5 & T6 (#5873)
[opentx.git] / radio / src / functions.cpp
blobe397cb76cebcadabc821cb245eeff227dfec9496
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 CustomFunctionsContext modelFunctionsContext = { 0 };
25 #if defined(CPUARM)
26 CustomFunctionsContext globalFunctionsContext = { 0 };
27 #endif
29 #if defined(DEBUG)
31 * This is a test function for debugging purpose, you may insert there your code and compile with the option DEBUG=YES
33 void testFunc()
35 #ifdef SIMU
36 printf("testFunc\n"); fflush(stdout);
37 #endif
39 #endif
41 #if defined(VOICE)
42 PLAY_FUNCTION(playValue, source_t idx)
44 if (IS_FAI_FORBIDDEN(idx))
45 return;
47 if (idx == MIXSRC_NONE)
48 return;
50 getvalue_t val = getValue(idx);
52 #if defined(CPUARM)
53 if (idx >= MIXSRC_FIRST_TELEM) {
54 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[(idx-MIXSRC_FIRST_TELEM) / 3];
55 uint8_t attr = 0;
56 if (telemetrySensor.prec > 0) {
57 if (telemetrySensor.prec == 2) {
58 if (val >= 5000) {
59 val = div_and_round(val, 100);
61 else {
62 val = div_and_round(val, 10);
63 attr = PREC1;
66 else {
67 if (val >= 500) {
68 val = div_and_round(val, 10);
70 else {
71 attr = PREC1;
75 PLAY_NUMBER(val, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, attr);
77 else if (idx >= MIXSRC_FIRST_TIMER && idx <= MIXSRC_LAST_TIMER) {
78 PLAY_DURATION(val, 0);
80 else if (idx == MIXSRC_TX_TIME) {
81 PLAY_DURATION(val*60, PLAY_TIME);
83 else if (idx == MIXSRC_TX_VOLTAGE) {
84 PLAY_NUMBER(val, UNIT_VOLTS, PREC1);
86 else {
87 if (idx <= MIXSRC_LAST_CH) {
88 val = calcRESXto100(val);
90 PLAY_NUMBER(val, 0, 0);
92 #else
93 switch (idx) {
94 case MIXSRC_FIRST_TELEM+TELEM_TX_VOLTAGE-1:
95 PLAY_NUMBER(val, 1+UNIT_VOLTS, PREC1);
96 break;
97 case MIXSRC_FIRST_TELEM+TELEM_TIMER1-1:
98 case MIXSRC_FIRST_TELEM+TELEM_TIMER2-1:
99 PLAY_DURATION(val, 0);
100 break;
101 #if defined(TELEMETRY_FRSKY)
102 case MIXSRC_FIRST_TELEM+TELEM_RSSI_TX-1:
103 case MIXSRC_FIRST_TELEM+TELEM_RSSI_RX-1:
104 PLAY_NUMBER(val, 1+UNIT_DB, 0);
105 break;
106 case MIXSRC_FIRST_TELEM+TELEM_MIN_A1-1:
107 case MIXSRC_FIRST_TELEM+TELEM_MIN_A2-1:
108 idx -= TELEM_MIN_A1-TELEM_A1;
109 // no break
110 case MIXSRC_FIRST_TELEM+TELEM_A1-1:
111 case MIXSRC_FIRST_TELEM+TELEM_A2-1:
112 if (TELEMETRY_STREAMING()) {
113 idx -= (MIXSRC_FIRST_TELEM+TELEM_A1-1);
114 uint8_t att = 0;
115 int16_t converted_value = div_and_round(applyChannelRatio(idx, val), 10);
116 if (ANA_CHANNEL_UNIT(idx) < UNIT_RAW) {
117 att = PREC1;
119 PLAY_NUMBER(converted_value, 1+ANA_CHANNEL_UNIT(idx), att);
121 break;
122 case MIXSRC_FIRST_TELEM+TELEM_CELL-1:
123 case MIXSRC_FIRST_TELEM+TELEM_MIN_CELL-1:
124 PLAY_NUMBER(div_and_round(val, 10), 1+UNIT_VOLTS, PREC1);
125 break;
127 case MIXSRC_FIRST_TELEM+TELEM_VFAS-1:
128 case MIXSRC_FIRST_TELEM+TELEM_CELLS_SUM-1:
129 case MIXSRC_FIRST_TELEM+TELEM_MIN_CELLS_SUM-1:
130 case MIXSRC_FIRST_TELEM+TELEM_MIN_VFAS-1:
131 PLAY_NUMBER(val, 1+UNIT_VOLTS, PREC1);
132 break;
134 case MIXSRC_FIRST_TELEM+TELEM_CURRENT-1:
135 case MIXSRC_FIRST_TELEM+TELEM_MAX_CURRENT-1:
136 PLAY_NUMBER(val, 1+UNIT_AMPS, PREC1);
137 break;
139 case MIXSRC_FIRST_TELEM+TELEM_ACCx-1:
140 case MIXSRC_FIRST_TELEM+TELEM_ACCy-1:
141 case MIXSRC_FIRST_TELEM+TELEM_ACCz-1:
142 PLAY_NUMBER(div_and_round(val, 10), 1+UNIT_G, PREC1);
143 break;
145 case MIXSRC_FIRST_TELEM+TELEM_VSPEED-1:
146 PLAY_NUMBER(div_and_round(val, 10), 1+UNIT_METERS_PER_SECOND, PREC1);
147 break;
149 case MIXSRC_FIRST_TELEM+TELEM_ASPEED-1:
150 case MIXSRC_FIRST_TELEM+TELEM_MAX_ASPEED-1:
151 PLAY_NUMBER(val/10, 1+UNIT_KTS, 0);
152 break;
154 case MIXSRC_FIRST_TELEM+TELEM_CONSUMPTION-1:
155 PLAY_NUMBER(val, 1+UNIT_MAH, 0);
156 break;
158 case MIXSRC_FIRST_TELEM+TELEM_POWER-1:
159 PLAY_NUMBER(val, 1+UNIT_WATTS, 0);
160 break;
162 case MIXSRC_FIRST_TELEM+TELEM_ALT-1:
163 case MIXSRC_FIRST_TELEM+TELEM_MIN_ALT-1:
164 case MIXSRC_FIRST_TELEM+TELEM_MAX_ALT-1:
165 #if defined(WS_HOW_HIGH)
166 if (IS_IMPERIAL_ENABLE() && IS_USR_PROTO_WS_HOW_HIGH())
167 PLAY_NUMBER(val, 1+UNIT_FEET, 0);
168 else
169 #endif
170 PLAY_NUMBER(val, 1+UNIT_DIST, 0);
171 break;
173 case MIXSRC_FIRST_TELEM+TELEM_RPM-1:
174 case MIXSRC_FIRST_TELEM+TELEM_MAX_RPM-1:
176 getvalue_t rpm = val;
177 if (rpm > 100)
178 rpm = 10 * div_and_round(rpm, 10);
179 if (rpm > 1000)
180 rpm = 10 * div_and_round(rpm, 10);
181 PLAY_NUMBER(rpm, 1+UNIT_RPMS, 0);
182 break;
185 case MIXSRC_FIRST_TELEM+TELEM_HDG-1:
186 PLAY_NUMBER(val, 1+UNIT_HDG, 0);
187 break;
189 default:
191 uint8_t unit = 1;
192 if (idx < MIXSRC_GVAR1)
193 val = calcRESXto100(val);
194 if (idx >= MIXSRC_FIRST_TELEM+TELEM_ALT-1 && idx <= MIXSRC_FIRST_TELEM+TELEM_GPSALT-1)
195 unit = idx - (MIXSRC_FIRST_TELEM+TELEM_ALT-1);
196 else if (idx >= MIXSRC_FIRST_TELEM+TELEM_MAX_T1-1 && idx <= MIXSRC_FIRST_TELEM+TELEM_MAX_DIST-1)
197 unit = 3 + idx - (MIXSRC_FIRST_TELEM+TELEM_MAX_T1-1);
199 unit = pgm_read_byte(bchunit_ar+unit);
200 PLAY_NUMBER(val, unit == UNIT_RAW ? 0 : unit+1, 0);
201 break;
203 #else
204 default:
205 PLAY_NUMBER(val, 0, 0);
206 break;
207 #endif
209 #endif
211 #endif
213 #if defined(CPUARM)
214 void playCustomFunctionFile(const CustomFunctionData * sd, uint8_t id)
216 if (sd->play.name[0] != '\0') {
217 char filename[sizeof(SOUNDS_PATH)+sizeof(sd->play.name)+sizeof(SOUNDS_EXT)] = SOUNDS_PATH "/";
218 strncpy(filename+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2);
219 strncpy(filename+sizeof(SOUNDS_PATH), sd->play.name, sizeof(sd->play.name));
220 filename[sizeof(SOUNDS_PATH)+sizeof(sd->play.name)] = '\0';
221 strcat(filename+sizeof(SOUNDS_PATH), SOUNDS_EXT);
222 PLAY_FILE(filename, sd->func==FUNC_BACKGND_MUSIC ? PLAY_BACKGROUND : 0, id);
225 #endif
227 #if defined(CPUARM)
228 bool isRepeatDelayElapsed(const CustomFunctionData * functions, CustomFunctionsContext & functionsContext, uint8_t index)
230 const CustomFunctionData * cfn = &functions[index];
231 tmr10ms_t tmr10ms = get_tmr10ms();
232 uint8_t repeatParam = CFN_PLAY_REPEAT(cfn);
233 if (!IS_SILENCE_PERIOD_ELAPSED() && repeatParam == CFN_PLAY_REPEAT_NOSTART) {
234 functionsContext.lastFunctionTime[index] = tmr10ms;
236 if (!functionsContext.lastFunctionTime[index] || (repeatParam && repeatParam!=CFN_PLAY_REPEAT_NOSTART && (signed)(tmr10ms-functionsContext.lastFunctionTime[index])>=100*repeatParam)) {
237 functionsContext.lastFunctionTime[index] = tmr10ms;
238 return true;
240 else {
241 return false;
244 #else
245 #define isRepeatDelayElapsed(...) true
246 #endif
248 #if defined(CPUARM)
249 #define VOLUME_HYSTERESIS 10 // how much must a input value change to actually be considered for new volume setting
250 getvalue_t requiredSpeakerVolumeRawLast = 1024 + 1; //initial value must be outside normal range
251 #endif
253 #if defined(CPUARM)
254 void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext & functionsContext)
255 #else
256 #define functions g_model.customFn
257 #define functionsContext modelFunctionsContext
258 void evalFunctions()
259 #endif
261 MASK_FUNC_TYPE newActiveFunctions = 0;
262 MASK_CFN_TYPE newActiveSwitches = 0;
264 #if defined(CPUARM)
265 uint8_t playFirstIndex = (functions == g_model.customFn ? 1 : 1+MAX_SPECIAL_FUNCTIONS);
266 #define PLAY_INDEX (i+playFirstIndex)
267 #else
268 #define PLAY_INDEX (i+1)
269 #endif
271 #if defined(ROTARY_ENCODERS) && defined(GVARS)
272 static rotenc_t rePreviousValues[ROTARY_ENCODERS];
273 #endif
275 #if defined(OVERRIDE_CHANNEL_FUNCTION)
276 for (uint8_t i=0; i<MAX_OUTPUT_CHANNELS; i++) {
277 safetyCh[i] = OVERRIDE_CHANNEL_UNDEFINED;
279 #endif
281 #if defined(GVARS)
282 for (uint8_t i=0; i<NUM_TRIMS; i++) {
283 trimGvar[i] = -1;
285 #endif
287 for (uint8_t i=0; i<MAX_SPECIAL_FUNCTIONS; i++) {
288 const CustomFunctionData * cfn = &functions[i];
289 swsrc_t swtch = CFN_SWITCH(cfn);
290 if (swtch) {
291 MASK_CFN_TYPE switch_mask = ((MASK_CFN_TYPE)1 << i);
293 #if defined(CPUARM)
294 bool active = getSwitch(swtch, IS_PLAY_FUNC(CFN_FUNC(cfn)) ? GETSWITCH_MIDPOS_DELAY : 0);
295 #else
296 bool active = getSwitch(swtch);
297 #endif
299 if (HAS_ENABLE_PARAM(CFN_FUNC(cfn))) {
300 active &= (bool)CFN_ACTIVE(cfn);
303 if (active || IS_PLAY_BOTH_FUNC(CFN_FUNC(cfn))) {
305 switch (CFN_FUNC(cfn)) {
307 #if defined(OVERRIDE_CHANNEL_FUNCTION)
308 case FUNC_OVERRIDE_CHANNEL:
309 safetyCh[CFN_CH_INDEX(cfn)] = CFN_PARAM(cfn);
310 break;
311 #endif
313 case FUNC_TRAINER:
315 uint8_t mask = 0x0f;
316 if (CFN_CH_INDEX(cfn) > 0) {
317 mask = (1<<(CFN_CH_INDEX(cfn)-1));
319 newActiveFunctions |= mask;
320 break;
323 case FUNC_INSTANT_TRIM:
324 newActiveFunctions |= (1 << FUNCTION_INSTANT_TRIM);
325 if (!isFunctionActive(FUNCTION_INSTANT_TRIM)) {
326 if (IS_INSTANT_TRIM_ALLOWED()) {
327 instantTrim();
330 break;
332 case FUNC_RESET:
333 switch (CFN_PARAM(cfn)) {
334 case FUNC_RESET_TIMER1:
335 case FUNC_RESET_TIMER2:
336 #if defined(CPUARM)
337 case FUNC_RESET_TIMER3:
338 #endif
339 timerReset(CFN_PARAM(cfn));
340 break;
341 case FUNC_RESET_FLIGHT:
342 if (!(functionsContext.activeSwitches & switch_mask)) {
343 #if defined(CPUARM)
344 mainRequestFlags |= (1 << REQUEST_FLIGHT_RESET); // on systems with threads flightReset() must not be called from the mixers thread!
345 #else
346 flightReset();
347 #endif // defined(CPUARM)
349 break;
350 #if defined(TELEMETRY_FRSKY)
351 case FUNC_RESET_TELEMETRY:
352 telemetryReset();
353 break;
354 #endif
356 #if ROTARY_ENCODERS > 0
357 case FUNC_RESET_ROTENC1:
358 #if ROTARY_ENCODERS > 1
359 case FUNC_RESET_ROTENC2:
360 #endif
361 rotencValue[CFN_PARAM(cfn)-FUNC_RESET_ROTENC1] = 0;
362 break;
363 #endif
365 #if defined(CPUARM)
366 if (CFN_PARAM(cfn)>=FUNC_RESET_PARAM_FIRST_TELEM) {
367 uint8_t item = CFN_PARAM(cfn)-FUNC_RESET_PARAM_FIRST_TELEM;
368 if (item < MAX_TELEMETRY_SENSORS) {
369 telemetryItems[item].clear();
372 #endif
373 break;
375 #if defined(CPUARM)
376 case FUNC_SET_TIMER:
377 timerSet(CFN_TIMER_INDEX(cfn), CFN_PARAM(cfn));
378 break;
380 case FUNC_SET_FAILSAFE:
382 setCustomFailsafe(CFN_PARAM(cfn));
385 #if defined(DANGEROUS_MODULE_FUNCTIONS)
386 case FUNC_RANGECHECK:
387 case FUNC_BIND:
389 unsigned int moduleIndex = CFN_PARAM(cfn);
390 if (moduleIndex < NUM_MODULES) {
391 moduleFlag[moduleIndex] = 1 + CFN_FUNC(cfn) - FUNC_RANGECHECK;
393 break;
395 #endif
396 #endif // defined(CPUARM)
398 #if defined(GVARS)
399 case FUNC_ADJUST_GVAR:
400 if (CFN_GVAR_MODE(cfn) == FUNC_ADJUST_GVAR_CONSTANT) {
401 SET_GVAR(CFN_GVAR_INDEX(cfn), CFN_PARAM(cfn), mixerCurrentFlightMode);
403 else if (CFN_GVAR_MODE(cfn) == FUNC_ADJUST_GVAR_GVAR) {
404 SET_GVAR(CFN_GVAR_INDEX(cfn), GVAR_VALUE(CFN_PARAM(cfn), getGVarFlightMode(mixerCurrentFlightMode, CFN_PARAM(cfn))), mixerCurrentFlightMode);
406 else if (CFN_GVAR_MODE(cfn) == FUNC_ADJUST_GVAR_INCDEC) {
407 if (!(functionsContext.activeSwitches & switch_mask)) {
408 #if defined(CPUARM)
409 SET_GVAR(CFN_GVAR_INDEX(cfn), limit<int16_t>(MODEL_GVAR_MIN(CFN_GVAR_INDEX(cfn)), GVAR_VALUE(CFN_GVAR_INDEX(cfn), getGVarFlightMode(mixerCurrentFlightMode, CFN_GVAR_INDEX(cfn))) + CFN_PARAM(cfn), MODEL_GVAR_MAX(CFN_GVAR_INDEX(cfn))), mixerCurrentFlightMode);
410 #else
411 SET_GVAR(CFN_GVAR_INDEX(cfn), GVAR_VALUE(CFN_GVAR_INDEX(cfn), getGVarFlightMode(mixerCurrentFlightMode, CFN_GVAR_INDEX(cfn))) + (CFN_PARAM(cfn) ? +1 : -1), mixerCurrentFlightMode);
412 #endif
415 else if (CFN_PARAM(cfn) >= MIXSRC_FIRST_TRIM && CFN_PARAM(cfn) <= MIXSRC_LAST_TRIM) {
416 trimGvar[CFN_PARAM(cfn)-MIXSRC_FIRST_TRIM] = CFN_GVAR_INDEX(cfn);
418 #if defined(ROTARY_ENCODERS)
419 else if (CFN_PARAM(cfn) >= MIXSRC_REa && CFN_PARAM(cfn) < MIXSRC_TrimRud) {
420 int8_t scroll = rePreviousValues[CFN_PARAM(cfn)-MIXSRC_REa] - (rotencValue[CFN_PARAM(cfn)-MIXSRC_REa] / ROTARY_ENCODER_GRANULARITY);
421 if (scroll) {
422 #if defined(CPUARM)
423 SET_GVAR(CFN_GVAR_INDEX(cfn), limit<int16_t>(MODEL_GVAR_MIN(CFN_GVAR_INDEX(cfn)), GVAR_VALUE(CFN_GVAR_INDEX(cfn), getGVarFlightMode(mixerCurrentFlightMode, CFN_GVAR_INDEX(cfn))) + scroll, MODEL_GVAR_MAX(CFN_GVAR_INDEX(cfn))), mixerCurrentFlightMode);
424 #else
425 SET_GVAR(CFN_GVAR_INDEX(cfn), GVAR_VALUE(CFN_GVAR_INDEX(cfn), getGVarFlightMode(mixerCurrentFlightMode, CFN_GVAR_INDEX(cfn))) + scroll, mixerCurrentFlightMode);
426 #endif
429 #endif
430 else {
431 #if defined(CPUARM)
432 SET_GVAR(CFN_GVAR_INDEX(cfn), limit<int16_t>(MODEL_GVAR_MIN(CFN_GVAR_INDEX(cfn)), calcRESXto100(getValue(CFN_PARAM(cfn))), MODEL_GVAR_MAX(CFN_GVAR_INDEX(cfn))), mixerCurrentFlightMode);
433 #else
434 SET_GVAR(CFN_GVAR_INDEX(cfn), calcRESXto100(getValue(CFN_PARAM(cfn))), mixerCurrentFlightMode);
435 #endif
437 break;
438 #endif
440 #if defined(MASTER_VOLUME)
441 case FUNC_VOLUME:
443 getvalue_t raw = getValue(CFN_PARAM(cfn));
444 // only set volume if input changed more than hysteresis
445 if (abs(requiredSpeakerVolumeRawLast - raw) > VOLUME_HYSTERESIS) {
446 requiredSpeakerVolumeRawLast = raw;
448 requiredSpeakerVolume = ((1024 + requiredSpeakerVolumeRawLast) * VOLUME_LEVEL_MAX) / 2048;
449 break;
451 #endif
453 #if defined(CPUARM) && defined(SDCARD)
454 case FUNC_PLAY_SOUND:
455 case FUNC_PLAY_TRACK:
456 case FUNC_PLAY_VALUE:
457 #if defined(HAPTIC)
458 case FUNC_HAPTIC:
459 #endif
461 if (isRepeatDelayElapsed(functions, functionsContext, i)) {
462 if (!IS_PLAYING(PLAY_INDEX)) {
463 if (CFN_FUNC(cfn) == FUNC_PLAY_SOUND) {
464 if (audioQueue.isEmpty()) {
465 AUDIO_PLAY(AU_SPECIAL_SOUND_FIRST + CFN_PARAM(cfn));
468 else if (CFN_FUNC(cfn) == FUNC_PLAY_VALUE) {
469 PLAY_VALUE(CFN_PARAM(cfn), PLAY_INDEX);
471 #if defined(HAPTIC)
472 else if (CFN_FUNC(cfn) == FUNC_HAPTIC) {
473 haptic.event(AU_SPECIAL_SOUND_LAST+CFN_PARAM(cfn));
475 #endif
476 else {
477 playCustomFunctionFile(cfn, PLAY_INDEX);
481 break;
484 case FUNC_BACKGND_MUSIC:
485 if (!(newActiveFunctions & (1 << FUNCTION_BACKGND_MUSIC))) {
486 newActiveFunctions |= (1 << FUNCTION_BACKGND_MUSIC);
487 if (!IS_PLAYING(PLAY_INDEX)) {
488 playCustomFunctionFile(cfn, PLAY_INDEX);
491 break;
493 case FUNC_BACKGND_MUSIC_PAUSE:
494 newActiveFunctions |= (1 << FUNCTION_BACKGND_MUSIC_PAUSE);
495 break;
497 #elif defined(VOICE)
498 case FUNC_PLAY_SOUND:
499 case FUNC_PLAY_TRACK:
500 case FUNC_PLAY_BOTH:
501 case FUNC_PLAY_VALUE:
503 tmr10ms_t tmr10ms = get_tmr10ms();
504 uint8_t repeatParam = CFN_PLAY_REPEAT(cfn);
505 if (!functionsContext.lastFunctionTime[i] || (CFN_FUNC(cfn)==FUNC_PLAY_BOTH && active!=(bool)(functionsContext.activeSwitches&switch_mask)) || (repeatParam && (signed)(tmr10ms-functionsContext.lastFunctionTime[i])>=1000*repeatParam)) {
506 functionsContext.lastFunctionTime[i] = tmr10ms;
507 uint8_t param = CFN_PARAM(cfn);
508 if (CFN_FUNC(cfn) == FUNC_PLAY_SOUND) {
509 AUDIO_PLAY(AU_SPECIAL_SOUND_FIRST+param);
511 else if (CFN_FUNC(cfn) == FUNC_PLAY_VALUE) {
512 PLAY_VALUE(param, PLAY_INDEX);
514 else {
515 #if defined(GVARS)
516 if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK && param > 250)
517 param = GVAR_VALUE(param-251, getGVarFlightMode(mixerCurrentFlightMode, param-251));
518 #endif
519 PUSH_CUSTOM_PROMPT(active ? param : param+1, PLAY_INDEX);
522 if (!active) {
523 // PLAY_BOTH would change activeFnSwitches otherwise
524 switch_mask = 0;
526 break;
528 #else
529 case FUNC_PLAY_SOUND:
531 tmr10ms_t tmr10ms = get_tmr10ms();
532 uint8_t repeatParam = CFN_PLAY_REPEAT(cfn);
533 if (!functionsContext.lastFunctionTime[i] || (repeatParam && (signed)(tmr10ms-functionsContext.lastFunctionTime[i])>=1000*repeatParam)) {
534 functionsContext.lastFunctionTime[i] = tmr10ms;
535 AUDIO_PLAY(AU_SPECIAL_SOUND_FIRST+CFN_PARAM(cfn));
537 break;
539 #endif
541 #if defined(TELEMETRY_FRSKY) && defined(VARIO)
542 case FUNC_VARIO:
543 newActiveFunctions |= (1 << FUNCTION_VARIO);
544 break;
545 #endif
547 #if defined(HAPTIC) && !defined(CPUARM)
548 case FUNC_HAPTIC:
550 tmr10ms_t tmr10ms = get_tmr10ms();
551 uint8_t repeatParam = CFN_PLAY_REPEAT(cfn);
552 if (!functionsContext.lastFunctionTime[i] || (repeatParam && (signed)(tmr10ms-functionsContext.lastFunctionTime[i])>=1000*repeatParam)) {
553 functionsContext.lastFunctionTime[i] = tmr10ms;
554 haptic.event(AU_SPECIAL_SOUND_LAST+CFN_PARAM(cfn));
556 break;
558 #endif
560 #if defined(SDCARD)
561 case FUNC_LOGS:
562 if (CFN_PARAM(cfn)) {
563 newActiveFunctions |= (1 << FUNCTION_LOGS);
564 logDelay = CFN_PARAM(cfn);
566 break;
567 #endif
569 case FUNC_BACKLIGHT:
570 newActiveFunctions |= (1 << FUNCTION_BACKLIGHT);
571 break;
573 #if defined(PCBTARANIS)
574 case FUNC_SCREENSHOT:
575 if (!(functionsContext.activeSwitches & switch_mask)) {
576 mainRequestFlags |= (1 << REQUEST_SCREENSHOT);
578 break;
579 #endif
581 #if defined(DEBUG)
582 case FUNC_TEST:
583 testFunc();
584 break;
585 #endif
588 newActiveSwitches |= switch_mask;
590 else {
591 functionsContext.lastFunctionTime[i] = 0;
592 #if defined(CPUARM) && defined(DANGEROUS_MODULE_FUNCTIONS)
593 if (functionsContext.activeSwitches & switch_mask) {
594 switch (CFN_FUNC(cfn)) {
595 case FUNC_RANGECHECK:
596 case FUNC_BIND:
598 unsigned int moduleIndex = CFN_PARAM(cfn);
599 if (moduleIndex < NUM_MODULES) {
600 moduleFlag[moduleIndex] = 0;
602 break;
606 #endif
611 functionsContext.activeSwitches = newActiveSwitches;
612 functionsContext.activeFunctions = newActiveFunctions;
614 #if defined(ROTARY_ENCODERS) && defined(GVARS)
615 for (uint8_t i=0; i<ROTARY_ENCODERS; i++) {
616 rePreviousValues[i] = (rotencValue[i] / ROTARY_ENCODER_GRANULARITY);
618 #endif
621 #if !defined(CPUARM)
622 #undef functions
623 #undef functionsContext
624 #endif