1
0
Fork 0
mirror of https://github.com/EdgeTX/edgetx.git synced 2025-07-23 16:25:12 +03:00
edgetx/radio/src/main.cpp
Bertrand Songis 2d4e98d15d
Bsongis/ram saving (#6685)
RAM saving. Stacks reduced to have some more RAM available than 2.2 on all boards
2019-08-30 15:46:57 +02:00

549 lines
14 KiB
C++

/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
uint8_t currentSpeakerVolume = 255;
uint8_t requiredSpeakerVolume = 255;
uint8_t mainRequestFlags = 0;
#if defined(STM32)
void onUSBConnectMenu(const char *result)
{
if (result == STR_USB_MASS_STORAGE) {
setSelectedUsbMode(USB_MASS_STORAGE_MODE);
}
else if (result == STR_USB_JOYSTICK) {
setSelectedUsbMode(USB_JOYSTICK_MODE);
}
else if (result == STR_USB_SERIAL) {
setSelectedUsbMode(USB_SERIAL_MODE);
}
}
#endif
void handleUsbConnection()
{
#if defined(STM32) && !defined(SIMU)
if (!usbStarted() && usbPlugged() && !(getSelectedUsbMode() == USB_UNSELECTED_MODE)) {
usbStart();
if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) {
opentxClose(false);
usbPluggedIn();
}
}
if (!usbStarted() && usbPlugged() && getSelectedUsbMode() == USB_UNSELECTED_MODE) {
if (g_eeGeneral.USBMode == USB_UNSELECTED_MODE && popupMenuItemsCount == 0) {
POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK);
POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE);
#if defined(DEBUG)
POPUP_MENU_ADD_ITEM(STR_USB_SERIAL);
#endif
POPUP_MENU_START(onUSBConnectMenu);
}
if (g_eeGeneral.USBMode != USB_UNSELECTED_MODE) {
setSelectedUsbMode(g_eeGeneral.USBMode);
}
}
if (usbStarted() && !usbPlugged()) {
usbStop();
if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) {
opentxResume();
}
#if !defined(BOOT)
setSelectedUsbMode(USB_UNSELECTED_MODE);
#endif
}
#endif // defined(STM32) && !defined(SIMU)
}
#if defined(JACK_DETECT_GPIO) && !defined(SIMU)
bool isJackPlugged()
{
// debounce
static bool debounced_state = 0;
static bool last_state = 0;
if (GPIO_ReadInputDataBit(JACK_DETECT_GPIO, JACK_DETECT_GPIO_PIN)) {
if (!last_state) {
debounced_state = false;
}
last_state = false;
}
else {
if (last_state) {
debounced_state = true;
}
last_state = true;
}
return debounced_state;
}
#endif
#if defined(PCBXLITES)
uint8_t jackState = SPEAKER_ACTIVE;
const char STR_JACK_HEADPHONE[] = "Headphone";
const char STR_JACK_TRAINER[] = "Trainer";
void onJackConnectMenu(const char * result)
{
if (result == STR_JACK_HEADPHONE) {
jackState = HEADPHONE_ACTIVE;
disableSpeaker();
enableHeadphone();
}
else if (result == STR_JACK_TRAINER) {
jackState = TRAINER_ACTIVE;
enableTrainer();
}
}
void handleJackConnection()
{
if (jackState == SPEAKER_ACTIVE && isJackPlugged()) {
if (g_eeGeneral.jackMode == JACK_HEADPHONE_MODE) {
jackState = HEADPHONE_ACTIVE;
disableSpeaker();
enableHeadphone();
}
else if (g_eeGeneral.jackMode == JACK_TRAINER_MODE) {
jackState = TRAINER_ACTIVE;
enableTrainer();
}
else if (popupMenuItemsCount == 0) {
POPUP_MENU_ADD_ITEM(STR_JACK_HEADPHONE);
POPUP_MENU_ADD_ITEM(STR_JACK_TRAINER);
POPUP_MENU_START(onJackConnectMenu);
}
}
else if (jackState == SPEAKER_ACTIVE && !isJackPlugged() && popupMenuItemsCount > 0 && popupMenuHandler == onJackConnectMenu) {
popupMenuItemsCount = 0;
}
else if (jackState != SPEAKER_ACTIVE && !isJackPlugged()) {
jackState = SPEAKER_ACTIVE;
enableSpeaker();
}
}
#endif
void checkSpeakerVolume()
{
if (currentSpeakerVolume != requiredSpeakerVolume) {
currentSpeakerVolume = requiredSpeakerVolume;
#if !defined(SOFTWARE_VOLUME)
setScaledVolume(currentSpeakerVolume);
#endif
}
}
#if defined(EEPROM)
void checkEeprom()
{
if (!usbPlugged()) {
if (eepromIsWriting())
eepromWriteProcess();
else if (TIME_TO_WRITE())
storageCheck(false);
}
}
#else
void checkEeprom()
{
#if defined(RAMBACKUP)
if (TIME_TO_RAMBACKUP()) {
rambackupWrite();
rambackupDirtyMsk = 0;
}
#endif
if (TIME_TO_WRITE()) {
storageCheck(false);
}
}
#endif
#define BAT_AVG_SAMPLES 8
void checkBatteryAlarms()
{
// TRACE("checkBatteryAlarms()");
if (IS_TXBATT_WARNING()) {
AUDIO_TX_BATTERY_LOW();
// TRACE("checkBatteryAlarms(): battery low");
}
#if defined(PCBSKY9X)
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
AUDIO_TX_MAH_HIGH();
}
#endif
}
void checkBattery()
{
static uint32_t batSum;
static uint8_t sampleCount;
// filter battery voltage by averaging it
if (g_vbat100mV == 0) {
g_vbat100mV = (getBatteryVoltage() + 5) / 10;
batSum = 0;
sampleCount = 0;
}
else {
batSum += getBatteryVoltage();
// TRACE("checkBattery(): sampled = %d", getBatteryVoltage());
if (++sampleCount >= BAT_AVG_SAMPLES) {
g_vbat100mV = (batSum + BAT_AVG_SAMPLES * 5 ) / (BAT_AVG_SAMPLES * 10);
batSum = 0;
sampleCount = 0;
// TRACE("checkBattery(): g_vbat100mV = %d", g_vbat100mV);
}
}
}
void periodicTick_1s()
{
checkBattery();
}
void periodicTick_10s()
{
checkBatteryAlarms();
#if defined(LUA)
checkLuaMemoryUsage();
#endif
}
void periodicTick()
{
static uint8_t count10s;
static uint32_t lastTime;
if ( (get_tmr10ms() - lastTime) >= 100 ) {
lastTime += 100;
periodicTick_1s();
if (++count10s >= 10) {
count10s = 0;
periodicTick_10s();
}
}
}
#if defined(GUI) && defined(COLORLCD)
void guiMain(event_t evt)
{
bool refreshNeeded = false;
#if defined(LUA)
uint32_t t0 = get_tmr10ms();
static uint32_t lastLuaTime = 0;
uint16_t interval = (lastLuaTime == 0 ? 0 : (t0 - lastLuaTime));
lastLuaTime = t0;
if (interval > maxLuaInterval) {
maxLuaInterval = interval;
}
// run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running)
DEBUG_TIMER_START(debugTimerLuaBg);
luaTask(0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT | RUN_TELEM_BG_SCRIPT, false);
DEBUG_TIMER_STOP(debugTimerLuaBg);
// wait for LCD DMA to finish before continuing, because code from this point
// is allowed to change the contents of LCD buffer
//
// WARNING: make sure no code above this line does any change to the LCD display buffer!
//
DEBUG_TIMER_START(debugTimerLcdRefreshWait);
lcdRefreshWait();
DEBUG_TIMER_STOP(debugTimerLcdRefreshWait);
// draw LCD from menus or from Lua script
// run Lua scripts that use LCD
DEBUG_TIMER_START(debugTimerLuaFg);
refreshNeeded = luaTask(evt, RUN_STNDAL_SCRIPT, true);
if (!refreshNeeded) {
refreshNeeded = luaTask(evt, RUN_TELEM_FG_SCRIPT, true);
}
DEBUG_TIMER_STOP(debugTimerLuaFg);
t0 = get_tmr10ms() - t0;
if (t0 > maxLuaDuration) {
maxLuaDuration = t0;
}
#else
lcdRefreshWait(); // WARNING: make sure no code above this line does any change to the LCD display buffer!
#endif
if (!refreshNeeded) {
DEBUG_TIMER_START(debugTimerMenus);
while (1) {
// normal GUI from menus
const char * warn = warningText;
uint8_t menu = popupMenuItemsCount;
static bool popupDisplayed = false;
if (warn || menu) {
if (popupDisplayed == false) {
menuHandlers[menuLevel](EVT_REFRESH);
lcdDrawBlackOverlay();
TIME_MEASURE_START(storebackup);
lcdStoreBackupBuffer();
TIME_MEASURE_STOP(storebackup);
}
if (popupDisplayed == false || evt) {
popupDisplayed = lcdRestoreBackupBuffer();
if (warn) {
DISPLAY_WARNING(evt);
}
if (menu) {
const char * result = runPopupMenu(evt);
if (result) {
TRACE("popupMenuHandler(%s)", result);
popupMenuHandler(result);
if (menuEvent == 0) {
evt = EVT_REFRESH;
continue;
}
}
}
refreshNeeded = true;
}
}
else {
if (popupDisplayed) {
if (evt == 0) {
evt = EVT_REFRESH;
}
popupDisplayed = false;
}
DEBUG_TIMER_START(debugTimerMenuHandlers);
refreshNeeded = menuHandlers[menuLevel](evt);
DEBUG_TIMER_STOP(debugTimerMenuHandlers);
}
if (menuEvent == EVT_ENTRY) {
menuVerticalPosition = 0;
menuHorizontalPosition = 0;
evt = menuEvent;
menuEvent = 0;
}
else if (menuEvent == EVT_ENTRY_UP) {
menuVerticalPosition = menuVerticalPositions[menuLevel];
menuHorizontalPosition = 0;
evt = menuEvent;
menuEvent = 0;
}
else {
break;
}
}
DEBUG_TIMER_STOP(debugTimerMenus);
}
if (refreshNeeded) {
DEBUG_TIMER_START(debugTimerLcdRefresh);
lcdRefresh();
DEBUG_TIMER_STOP(debugTimerLcdRefresh);
}
}
#elif defined(GUI)
void handleGui(event_t event) {
// if Lua standalone, run it and don't clear the screen (Lua will do it)
// else if Lua telemetry view, run it and don't clear the screen
// else clear scren and show normal menus
#if defined(LUA)
if (luaTask(event, RUN_STNDAL_SCRIPT, true)) {
// standalone script is active
}
else if (luaTask(event, RUN_TELEM_FG_SCRIPT, true)) {
// the telemetry screen is active
// prevent events from keys MENU, UP, DOWN, ENT(short) and EXIT(short) from reaching the normal menus,
// so Lua telemetry script can fully use them
if (event) {
uint8_t key = EVT_KEY_MASK(event);
#if defined(PCBXLITE)
// SHIFT + LEFT/RIGHT LONG used to change telemetry screen on XLITE
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)) {
#else
// no need to filter out MENU and ENT(short), because they are not used by menuViewTelemetryFrsky()
if (key == KEY_PLUS || key == KEY_MINUS || (!IS_KEY_LONG(event) && key == KEY_EXIT)) {
#endif
// TRACE("Telemetry script event 0x%02x killed", event);
event = 0;
}
}
menuHandlers[menuLevel](event);
// todo drawStatusLine(); here???
}
else
#endif
{
lcdClear();
menuHandlers[menuLevel](event);
drawStatusLine();
}
}
void guiMain(event_t evt)
{
#if defined(LUA)
// TODO better lua stopwatch
uint32_t t0 = get_tmr10ms();
static uint32_t lastLuaTime = 0;
uint16_t interval = (lastLuaTime == 0 ? 0 : (t0 - lastLuaTime));
lastLuaTime = t0;
if (interval > maxLuaInterval) {
maxLuaInterval = interval;
}
// run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running)
luaTask(0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT | RUN_TELEM_BG_SCRIPT, false);
t0 = get_tmr10ms() - t0;
if (t0 > maxLuaDuration) {
maxLuaDuration = t0;
}
#endif //#if defined(LUA)
// wait for LCD DMA to finish before continuing, because code from this point
// is allowed to change the contents of LCD buffer
//
// WARNING: make sure no code above this line does any change to the LCD display buffer!
//
lcdRefreshWait();
if (menuEvent) {
// we have a popupMenuActive entry or exit event
menuVerticalPosition = (menuEvent == EVT_ENTRY_UP) ? menuVerticalPositions[menuLevel] : 0;
menuHorizontalPosition = 0;
evt = menuEvent;
menuEvent = 0;
}
if (isEventCaughtByPopup()) {
handleGui(0);
}
else {
handleGui(evt);
evt = 0;
}
if (warningText) {
// show warning on top of the normal menus
DISPLAY_WARNING(evt);
}
else if (popupMenuItemsCount > 0) {
// show popup menu on top of the normal menus
const char * result = runPopupMenu(evt);
if (result) {
TRACE("popupMenuHandler(%s)", result);
popupMenuHandler(result);
}
}
lcdRefresh();
}
#endif
void perMain()
{
DEBUG_TIMER_START(debugTimerPerMain1);
#if defined(PCBSKY9X)
calcConsumption();
#endif
checkSpeakerVolume();
checkEeprom();
logsWrite();
handleUsbConnection();
#if defined(PCBXLITES)
handleJackConnection();
#endif
checkTrainerSettings();
periodicTick();
DEBUG_TIMER_STOP(debugTimerPerMain1);
if (mainRequestFlags & (1 << REQUEST_FLIGHT_RESET)) {
TRACE("Executing requested Flight Reset");
flightReset();
mainRequestFlags &= ~(1 << REQUEST_FLIGHT_RESET);
}
doLoopCommonActions();
event_t evt = getEvent(false);
#if defined(RAMBACKUP)
if (globalData.unexpectedShutdown) {
drawFatalErrorScreen(STR_EMERGENCY_MODE);
return;
}
#endif
#if defined(STM32)
bool sdcardPresent = SD_CARD_PRESENT();
if (sdcardPresent && !globalData.sdcardPresent) {
sdMount();
}
globalData.sdcardPresent = sdcardPresent;
#endif
#if !defined(EEPROM)
// In case the SD card is removed during the session
if (!SD_CARD_PRESENT() && !globalData.unexpectedShutdown) {
drawFatalErrorScreen(STR_NO_SDCARD);
return;
}
#endif
#if defined(STM32)
if (usbPlugged() && getSelectedUsbMode() == USB_MASS_STORAGE_MODE) {
// disable access to menus
lcdClear();
menuMainView(0);
lcdRefresh();
return;
}
#endif
#if defined(GUI)
DEBUG_TIMER_START(debugTimerGuiMain);
guiMain(evt);
DEBUG_TIMER_STOP(debugTimerGuiMain);
#endif
if (mainRequestFlags & (1 << REQUEST_SCREENSHOT)) {
writeScreenshot();
mainRequestFlags &= ~(1 << REQUEST_SCREENSHOT);
}
#if defined(PCBX9E) && !defined(SIMU)
toplcdRefreshStart();
setTopFirstTimer(getValue(MIXSRC_FIRST_TIMER+g_model.toplcdTimer));
setTopSecondTimer(g_eeGeneral.globalTimer + sessionTimer);
setTopRssi(TELEMETRY_RSSI());
setTopBatteryValue(g_vbat100mV);
setTopBatteryState(GET_TXBATT_BARS(10), IS_TXBATT_WARNING());
toplcdRefreshEnd();
#endif
#if defined(INTERNAL_GPS)
gpsWakeup();
#endif
}