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 uint8_t currentSpeakerVolume
= 255;
24 uint8_t requiredSpeakerVolume
= 255;
25 uint8_t mainRequestFlags
= 0;
28 void onUSBConnectMenu(const char *result
)
30 if (result
== STR_USB_MASS_STORAGE
) {
31 setSelectedUsbMode(USB_MASS_STORAGE_MODE
);
33 else if (result
== STR_USB_JOYSTICK
) {
34 setSelectedUsbMode(USB_JOYSTICK_MODE
);
36 else if (result
== STR_USB_SERIAL
) {
37 setSelectedUsbMode(USB_SERIAL_MODE
);
42 void handleUsbConnection()
44 #if defined(STM32) && !defined(SIMU)
45 if (!usbStarted() && usbPlugged() && !(getSelectedUsbMode() == USB_UNSELECTED_MODE
)) {
47 if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE
) {
52 if (!usbStarted() && usbPlugged() && getSelectedUsbMode() == USB_UNSELECTED_MODE
) {
53 if (g_eeGeneral
.USBMode
== USB_UNSELECTED_MODE
&& popupMenuItemsCount
== 0) {
54 POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK
);
55 POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE
);
57 POPUP_MENU_ADD_ITEM(STR_USB_SERIAL
);
59 POPUP_MENU_START(onUSBConnectMenu
);
61 if (g_eeGeneral
.USBMode
!= USB_UNSELECTED_MODE
) {
62 setSelectedUsbMode(g_eeGeneral
.USBMode
);
65 if (usbStarted() && !usbPlugged()) {
67 if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE
) {
71 setSelectedUsbMode(USB_UNSELECTED_MODE
);
74 #endif // defined(STM32) && !defined(SIMU)
77 #if defined(JACK_DETECT_GPIO) && !defined(SIMU)
81 static bool debounced_state
= 0;
82 static bool last_state
= 0;
84 if (GPIO_ReadInputDataBit(JACK_DETECT_GPIO
, JACK_DETECT_GPIO_PIN
)) {
86 debounced_state
= false;
92 debounced_state
= true;
96 return debounced_state
;
100 #if defined(PCBXLITES)
101 uint8_t jackState
= SPEAKER_ACTIVE
;
103 const char STR_JACK_HEADPHONE
[] = "Headphone";
104 const char STR_JACK_TRAINER
[] = "Trainer";
106 void onJackConnectMenu(const char * result
)
108 if (result
== STR_JACK_HEADPHONE
) {
109 jackState
= HEADPHONE_ACTIVE
;
113 else if (result
== STR_JACK_TRAINER
) {
114 jackState
= TRAINER_ACTIVE
;
119 void handleJackConnection()
121 if (jackState
== SPEAKER_ACTIVE
&& isJackPlugged()) {
122 if (g_eeGeneral
.jackMode
== JACK_HEADPHONE_MODE
) {
123 jackState
= HEADPHONE_ACTIVE
;
127 else if (g_eeGeneral
.jackMode
== JACK_TRAINER_MODE
) {
128 jackState
= TRAINER_ACTIVE
;
131 else if (popupMenuItemsCount
== 0) {
132 POPUP_MENU_ADD_ITEM(STR_JACK_HEADPHONE
);
133 POPUP_MENU_ADD_ITEM(STR_JACK_TRAINER
);
134 POPUP_MENU_START(onJackConnectMenu
);
137 else if (jackState
== SPEAKER_ACTIVE
&& !isJackPlugged() && popupMenuItemsCount
> 0 && popupMenuHandler
== onJackConnectMenu
) {
138 popupMenuItemsCount
= 0;
140 else if (jackState
!= SPEAKER_ACTIVE
&& !isJackPlugged()) {
141 jackState
= SPEAKER_ACTIVE
;
147 void checkSpeakerVolume()
149 if (currentSpeakerVolume
!= requiredSpeakerVolume
) {
150 currentSpeakerVolume
= requiredSpeakerVolume
;
151 #if !defined(SOFTWARE_VOLUME)
152 setScaledVolume(currentSpeakerVolume
);
161 if (eepromIsWriting())
162 eepromWriteProcess();
163 else if (TIME_TO_WRITE())
170 #if defined(RTC_BACKUP_RAM) && !defined(SIMU)
171 if (TIME_TO_BACKUP_RAM()) {
173 rambackupDirtyMsk
= 0;
176 if (TIME_TO_WRITE()) {
182 #define BAT_AVG_SAMPLES 8
184 void checkBatteryAlarms()
186 // TRACE("checkBatteryAlarms()");
187 if (IS_TXBATT_WARNING()) {
188 AUDIO_TX_BATTERY_LOW();
189 // TRACE("checkBatteryAlarms(): battery low");
191 #if defined(PCBSKY9X)
192 else if (g_eeGeneral
.mAhWarn
&& (g_eeGeneral
.mAhUsed
+ Current_used
* (488 + g_eeGeneral
.txCurrentCalibration
)/8192/36) / 500 >= g_eeGeneral
.mAhWarn
) { // TODO move calculation into board file
200 static uint32_t batSum
;
201 static uint8_t sampleCount
;
202 // filter battery voltage by averaging it
203 if (g_vbat100mV
== 0) {
204 g_vbat100mV
= (getBatteryVoltage() + 5) / 10;
209 batSum
+= getBatteryVoltage();
210 // TRACE("checkBattery(): sampled = %d", getBatteryVoltage());
211 if (++sampleCount
>= BAT_AVG_SAMPLES
) {
212 g_vbat100mV
= (batSum
+ BAT_AVG_SAMPLES
* 5 ) / (BAT_AVG_SAMPLES
* 10);
215 // TRACE("checkBattery(): g_vbat100mV = %d", g_vbat100mV);
220 void periodicTick_1s()
225 void periodicTick_10s()
227 checkBatteryAlarms();
229 checkLuaMemoryUsage();
235 static uint8_t count10s
;
236 static uint32_t lastTime
;
237 if ( (get_tmr10ms() - lastTime
) >= 100 ) {
240 if (++count10s
>= 10) {
247 #if defined(GUI) && defined(COLORLCD)
248 void guiMain(event_t evt
)
250 bool refreshNeeded
= false;
253 uint32_t t0
= get_tmr10ms();
254 static uint32_t lastLuaTime
= 0;
255 uint16_t interval
= (lastLuaTime
== 0 ? 0 : (t0
- lastLuaTime
));
257 if (interval
> maxLuaInterval
) {
258 maxLuaInterval
= interval
;
261 // run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running)
262 DEBUG_TIMER_START(debugTimerLuaBg
);
263 luaTask(0, RUN_MIX_SCRIPT
| RUN_FUNC_SCRIPT
| RUN_TELEM_BG_SCRIPT
, false);
264 DEBUG_TIMER_STOP(debugTimerLuaBg
);
265 // wait for LCD DMA to finish before continuing, because code from this point
266 // is allowed to change the contents of LCD buffer
268 // WARNING: make sure no code above this line does any change to the LCD display buffer!
270 DEBUG_TIMER_START(debugTimerLcdRefreshWait
);
272 DEBUG_TIMER_STOP(debugTimerLcdRefreshWait
);
274 // draw LCD from menus or from Lua script
275 // run Lua scripts that use LCD
277 DEBUG_TIMER_START(debugTimerLuaFg
);
278 refreshNeeded
= luaTask(evt
, RUN_STNDAL_SCRIPT
, true);
279 if (!refreshNeeded
) {
280 refreshNeeded
= luaTask(evt
, RUN_TELEM_FG_SCRIPT
, true);
282 DEBUG_TIMER_STOP(debugTimerLuaFg
);
284 t0
= get_tmr10ms() - t0
;
285 if (t0
> maxLuaDuration
) {
289 lcdRefreshWait(); // WARNING: make sure no code above this line does any change to the LCD display buffer!
292 bool screenshotRequested
= (mainRequestFlags
& (1u << REQUEST_SCREENSHOT
));
294 if (!refreshNeeded
) {
295 DEBUG_TIMER_START(debugTimerMenus
);
297 // normal GUI from menus
298 const char * warn
= warningText
;
299 uint8_t menu
= popupMenuItemsCount
;
300 static bool popupDisplayed
= false;
302 if (popupDisplayed
== false) {
303 menuHandlers
[menuLevel
](EVT_REFRESH
);
304 lcdDrawBlackOverlay();
305 TIME_MEASURE_START(storebackup
);
306 lcdStoreBackupBuffer();
307 TIME_MEASURE_STOP(storebackup
);
309 if (popupDisplayed
== false || evt
|| screenshotRequested
) {
310 popupDisplayed
= lcdRestoreBackupBuffer();
312 DISPLAY_WARNING(evt
);
315 const char * result
= runPopupMenu(evt
);
317 TRACE("popupMenuHandler(%s)", result
);
318 auto handler
= popupMenuHandler
;
319 if (result
!= STR_UPDATE_LIST
)
322 if (menuEvent
== 0) {
328 refreshNeeded
= true;
332 if (popupDisplayed
) {
336 popupDisplayed
= false;
338 DEBUG_TIMER_START(debugTimerMenuHandlers
);
339 refreshNeeded
= menuHandlers
[menuLevel
](evt
);
340 DEBUG_TIMER_STOP(debugTimerMenuHandlers
);
343 if (menuEvent
== EVT_ENTRY
) {
344 menuVerticalPosition
= 0;
345 menuHorizontalPosition
= 0;
349 else if (menuEvent
== EVT_ENTRY_UP
) {
350 menuVerticalPosition
= menuVerticalPositions
[menuLevel
];
351 menuHorizontalPosition
= 0;
359 DEBUG_TIMER_STOP(debugTimerMenus
);
362 if (screenshotRequested
) {
364 mainRequestFlags
&= ~(1u << REQUEST_SCREENSHOT
);
368 DEBUG_TIMER_START(debugTimerLcdRefresh
);
370 DEBUG_TIMER_STOP(debugTimerLcdRefresh
);
374 void handleGui(event_t event
) {
375 // if Lua standalone, run it and don't clear the screen (Lua will do it)
376 // else if Lua telemetry view, run it and don't clear the screen
377 // else clear scren and show normal menus
379 if (luaTask(event
, RUN_STNDAL_SCRIPT
, true)) {
380 // standalone script is active
382 else if (luaTask(event
, RUN_TELEM_FG_SCRIPT
, true)) {
383 // the telemetry screen is active
384 // prevent events from keys MENU, UP, DOWN, ENT(short) and EXIT(short) from reaching the normal menus,
385 // so Lua telemetry script can fully use them
387 uint8_t key
= EVT_KEY_MASK(event
);
388 #if defined(PCBXLITE)
389 // SHIFT + LEFT/RIGHT LONG used to change telemetry screen on XLITE
390 if ((!IS_KEY_LONG(event
) && key
== KEY_RIGHT
&& IS_SHIFT_PRESSED()) || (!IS_KEY_LONG(event
) && key
== KEY_LEFT
&& IS_SHIFT_PRESSED()) || (!IS_KEY_LONG(event
) && key
== KEY_EXIT
)) {
392 // no need to filter out MENU and ENT(short), because they are not used by menuViewTelemetryFrsky()
393 if (key
== KEY_PLUS
|| key
== KEY_MINUS
|| (!IS_KEY_LONG(event
) && key
== KEY_EXIT
)) {
395 // TRACE("Telemetry script event 0x%02x killed", event);
399 menuHandlers
[menuLevel
](event
);
405 menuHandlers
[menuLevel
](event
);
410 void guiMain(event_t evt
)
413 // TODO better lua stopwatch
414 uint32_t t0
= get_tmr10ms();
415 static uint32_t lastLuaTime
= 0;
416 uint16_t interval
= (lastLuaTime
== 0 ? 0 : (t0
- lastLuaTime
));
418 if (interval
> maxLuaInterval
) {
419 maxLuaInterval
= interval
;
422 // run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running)
423 luaTask(0, RUN_MIX_SCRIPT
| RUN_FUNC_SCRIPT
| RUN_TELEM_BG_SCRIPT
, false);
425 t0
= get_tmr10ms() - t0
;
426 if (t0
> maxLuaDuration
) {
429 #endif //#if defined(LUA)
431 // wait for LCD DMA to finish before continuing, because code from this point
432 // is allowed to change the contents of LCD buffer
434 // WARNING: make sure no code above this line does any change to the LCD display buffer!
439 // we have a popupMenuActive entry or exit event
440 menuVerticalPosition
= (menuEvent
== EVT_ENTRY_UP
) ? menuVerticalPositions
[menuLevel
] : 0;
441 menuHorizontalPosition
= 0;
446 if (isEventCaughtByPopup()) {
455 // show warning on top of the normal menus
456 DISPLAY_WARNING(evt
);
458 else if (popupMenuItemsCount
> 0) {
459 // show popup menu on top of the normal menus
460 const char * result
= runPopupMenu(evt
);
462 TRACE("popupMenuHandler(%s)", result
);
463 auto handler
= popupMenuHandler
;
464 if (result
!= STR_UPDATE_LIST
)
472 if (mainRequestFlags
& (1 << REQUEST_SCREENSHOT
)) {
474 mainRequestFlags
&= ~(1 << REQUEST_SCREENSHOT
);
481 DEBUG_TIMER_START(debugTimerPerMain1
);
483 #if defined(PCBSKY9X)
486 checkSpeakerVolume();
489 handleUsbConnection();
490 #if defined(PCBXLITES)
491 handleJackConnection();
493 checkTrainerSettings();
495 DEBUG_TIMER_STOP(debugTimerPerMain1
);
497 if (mainRequestFlags
& (1 << REQUEST_FLIGHT_RESET
)) {
498 TRACE("Executing requested Flight Reset");
500 mainRequestFlags
&= ~(1 << REQUEST_FLIGHT_RESET
);
503 doLoopCommonActions();
505 event_t evt
= getEvent(false);
507 #if defined(RTC_BACKUP_RAM)
508 if (globalData
.unexpectedShutdown
) {
509 drawFatalErrorScreen(STR_EMERGENCY_MODE
);
515 if (SD_CARD_PRESENT() && !sdMounted()) {
521 // In case the SD card is removed during the session
522 if (!SD_CARD_PRESENT() && !globalData
.unexpectedShutdown
) {
523 drawFatalErrorScreen(STR_NO_SDCARD
);
529 if (usbPlugged() && getSelectedUsbMode() == USB_MASS_STORAGE_MODE
) {
530 // disable access to menus
539 DEBUG_TIMER_START(debugTimerGuiMain
);
541 DEBUG_TIMER_STOP(debugTimerGuiMain
);
544 #if defined(PCBX9E) && !defined(SIMU)
545 toplcdRefreshStart();
546 setTopFirstTimer(getValue(MIXSRC_FIRST_TIMER
+g_model
.toplcdTimer
));
547 setTopSecondTimer(g_eeGeneral
.globalTimer
+ sessionTimer
);
548 setTopRssi(TELEMETRY_RSSI());
549 setTopBatteryValue(g_vbat100mV
);
550 setTopBatteryState(GET_TXBATT_BARS(10), IS_TXBATT_WARNING());
554 #if defined(INTERNAL_GPS)