/* * Copyright (C) EdgeTX * * Based on code named * opentx - https://github.com/opentx/opentx * 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 "edgetx.h" #include "os/sleep.h" #include "timers_driver.h" #include "tasks/mixer_task.h" #include "mixes.h" #include "switches.h" #if defined(FUNCTION_SWITCHES_RGB_LEDS) #include "hal/rgbleds.h" #endif #if defined(COLORLCD) #include "view_main.h" #endif #if defined(USBJ_EX) #include "usb_joystick.h" #endif #if defined(MULTIMODULE) #include "pulses/multi.h" #if defined(MULTI_PROTOLIST) #include "io/multi_protolist.h" #endif #endif uint8_t storageDirtyMsk; tmr10ms_t storageDirtyTime10ms; #if defined(RTC_BACKUP_RAM) uint8_t rambackupDirtyMsk = EE_GENERAL | EE_MODEL; tmr10ms_t rambackupDirtyTime10ms; #endif void storageDirty(uint8_t msk) { storageDirtyMsk |= msk; storageDirtyTime10ms = get_tmr10ms(); #if defined(RTC_BACKUP_RAM) rambackupDirtyMsk = storageDirtyMsk; rambackupDirtyTime10ms = storageDirtyTime10ms; #endif } void preModelLoad() { watchdogSuspend(500/*5s*/); logsClose(); bool needDelay = false; if (mixerTaskStarted()) { pulsesStop(); needDelay = true; } stopTrainer(); #if defined(FUNCTION_SWITCHES_RGB_LEDS) turnOffRGBLeds(); #endif if (needDelay) { sleep_ms(200); } } void postRadioSettingsLoad() { #if LCD_W == 128 // Prevent GVARS to be off when imported or manually modified yaml // Since there is no way to have those back g_eeGeneral.modelGVDisabled = false; #endif #if defined(PXX2) if (is_memclear(g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID)) { setDefaultOwnerId(); } #endif #if defined(PCBX12S) && defined(INTERNAL_GPS) // AUX2 is hardwired to AUX2 on X12S serialSetMode(SP_AUX2, UART_MODE_GPS); #endif #if defined(USB_SERIAL) // default VCP to CLI if not configured // to something else as NONE. if (isInternalModuleCrossfire() && serialGetMode(SP_VCP) == UART_MODE_NONE) { serialSetMode(SP_VCP, UART_MODE_CLI); } #endif #if defined(DEFAULT_INTERNAL_MODULE) if (!g_eeGeneral.internalModule) { g_eeGeneral.internalModule = DEFAULT_INTERNAL_MODULE; } #endif #if !defined(DEBUG) // clean up leftovers from a previous DEBUG config for (uint8_t port_nr = 0; port_nr < MAX_AUX_SERIAL; port_nr++) { if (serialGetMode(port_nr) == UART_MODE_DEBUG) serialSetMode(port_nr, UART_MODE_NONE); } #endif #if defined(COLORLCD) // Ensure ON brightness >= OFF brightness if ((BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright) < g_eeGeneral.blOffBright) g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; #endif } static bool sortMixerLines() { // simple bubble sort unsigned passes = 0; unsigned swaps; MixData tmp; do { swaps = 0; for (int i = 0; i < MAX_MIXERS - 1; i++) { auto a = mixAddress(i); auto b = mixAddress(i + 1); if (b->destCh < a->destCh) { if (is_memclear(b, sizeof(MixData))) break; memcpy(&tmp, a, sizeof(MixData)); memcpy(a, b, sizeof(MixData)); memcpy(b, &tmp, sizeof(MixData)); ++swaps; } } ++passes; } while(swaps > 0); // anything above 1 means that // we changed something return passes > 1; } static void sanitizeMixerLines() { bool dirty = sortMixerLines(); updateMixCount(); if (dirty) storageDirty(EE_MODEL); } void postModelLoad(bool alarms) { #if defined(COLORLCD) if (!g_model.hasScreenData(0)) LayoutFactory::loadDefaultLayout(); if (g_model.topbarWidgetWidth[0] == 0) { // Set default width for top bar widgets for (int i = 0; i < MAX_TOPBAR_ZONES; i += 1) g_model.topbarWidgetWidth[i] = 1; // Load 'date time' widget if slot is empty if (!g_model.getTopbarData()->hasWidget(MAX_TOPBAR_ZONES-1)) { g_model.getTopbarData()->setWidgetName(MAX_TOPBAR_ZONES-1, "Date Time"); storageDirty(EE_MODEL); } // Load 'radio info' widget if slot is empty if (!g_model.getTopbarData()->hasWidget(MAX_TOPBAR_ZONES-2)) { g_model.getTopbarData()->setWidgetName(MAX_TOPBAR_ZONES-2, "Radio Info"); storageDirty(EE_MODEL); } #if defined(INTERNAL_GPS) // Load 'internal gps' widget if slot is empty if (!g_model.getTopbarData()->hasWidget(MAX_TOPBAR_ZONES-3)) { g_model.getTopbarData()->setWidgetName(MAX_TOPBAR_ZONES-3, "Internal GPS"); storageDirty(EE_MODEL); } #endif } #elif LCD_W == 128 // Prevent GVARS to be off when imported or manually modified yaml // Since there is no way to have those back g_model.modelGVDisabled = false; #endif #if defined(FUNCTION_SWITCHES) setFSStartupPosition(); #endif // Convert 'noGlobalFunctions' to 'radioGFDisabled' // TODO: Remove sometime in the future (and remove 'noGlobalFunctions' property) if (g_model.noGlobalFunctions) { g_model.radioGFDisabled = OVERRIDE_OFF; g_model.noGlobalFunctions = 0; storageDirty(EE_MODEL); } // fix #2552: reset rssiSource to default none (= 0) if(g_model.rssiSource) { g_model.rssiSource = 0; storageDirty(EE_MODEL); } #if defined(STM32F4) && defined(CROSSFIRE) // Limit ext. CRSF speed to 3.75Mbps due to CRC errors at higher speeds if(isModuleCrossfire(EXTERNAL_MODULE) && g_model.moduleData[EXTERNAL_MODULE].crsf.telemetryBaudrate == 4) { TRACE("Downgrading external ELRS module baudrate"); g_model.moduleData[EXTERNAL_MODULE].crsf.telemetryBaudrate = 3; storageDirty(EE_MODEL); } #endif #if defined(PXX2) bool changed = false; if (is_memclear(g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID)) { if (!is_memclear(g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID)) { memcpy(g_model.modelRegistrationID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); changed = true; } } // fix colorLCD radios not writing yaml tag receivers if(isModulePXX2(INTERNAL_MODULE)) { ModuleData *intModule = &g_model.moduleData[INTERNAL_MODULE]; unsigned int oldVal = intModule->pxx2.receivers; for(uint8_t receiverIdx = 0; receiverIdx < 3; receiverIdx++) { if(intModule->pxx2.receiverName[receiverIdx][0]) intModule->pxx2.receivers |= (1 << receiverIdx); } if (oldVal != intModule->pxx2.receivers) changed = true; } if(isModulePXX2(EXTERNAL_MODULE)) { ModuleData *extModule = &g_model.moduleData[EXTERNAL_MODULE]; unsigned int oldVal = extModule->pxx2.receivers; for(uint8_t receiverIdx = 0; receiverIdx < 3; receiverIdx++) { if(extModule->pxx2.receiverName[receiverIdx][0]) extModule->pxx2.receivers |= (1 << receiverIdx); } if (oldVal != extModule->pxx2.receivers) changed = true; } if (changed) storageDirty(EE_MODEL); #endif #if defined(MULTIMODULE) && defined(MULTI_PROTOLIST) MultiRfProtocols::removeInstance(EXTERNAL_MODULE); #endif AUDIO_FLUSH(); flightReset(false); customFunctionsReset(); logicalSwitchesInit(false); restoreTimers(); for (int i=0; i> 3u] |= mask; } for (uint8_t id = 1; id <= getMaxRxNum(module); id++) { uint8_t mask = 1u << (id & 7u); if (!(usedModelIds[id >> 3u] & mask)) { // found free ID return id; } } // failed finding something... return 0; } #endif