From e9bcb05a9323dad3f775b0da4031052cf3fbc649 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Mar 2016 22:30:46 +0100 Subject: [PATCH] CRLF removed --- radio/src/buzzer.cpp | 68 +- radio/src/cli.cpp | 1334 ++++----- radio/src/curves.cpp | 874 +++--- radio/src/debug.cpp | 166 +- radio/src/logs.cpp | 746 ++--- radio/src/maths.cpp | 372 +-- radio/src/opentx.cpp | 5702 ++++++++++++++++++------------------ radio/src/rtc.cpp | 936 +++--- radio/src/serial.cpp | 74 +- radio/src/switches.cpp | 2228 +++++++------- radio/src/tasks_arm.cpp | 418 +-- radio/src/templates.cpp | 476 +-- radio/src/translations.cpp | 1424 ++++----- radio/src/vario.cpp | 272 +- 14 files changed, 7545 insertions(+), 7545 deletions(-) diff --git a/radio/src/buzzer.cpp b/radio/src/buzzer.cpp index 18f3f45b1..7ac4957d8 100644 --- a/radio/src/buzzer.cpp +++ b/radio/src/buzzer.cpp @@ -18,37 +18,37 @@ * GNU General Public License for more details. */ -#include "opentx.h" - -uint8_t g_beepCnt; -uint8_t beepAgain = 0; -uint8_t beepAgainOrig = 0; -uint8_t beepOn = false; -bool warble = false; -bool warbleC; - -// The various "beep" tone lengths -static const pm_uint8_t beepTab[] PROGMEM = { - // key, trim, warn2, warn1, error - 1, 1, 2, 10, 60, //xShort - 1, 1, 4, 20, 80, //short - 1, 1, 8, 30, 100, //normal - 2, 2, 15, 40, 120, //long - 5, 5, 30, 50, 150, //xLong -}; - -void beep(uint8_t val) -{ -#if defined(HAPTIC) && !defined(AUDIO) - haptic.event(val==0 ? AU_KEYPAD_UP : (val==4 ? AU_ERROR : AU_TIMER_LT10+beepAgain)); -#endif - -#if !defined(AUDIO) - if (g_eeGeneral.alarmsFlash && val>1) flashCounter = FLASH_DURATION; -#endif - - if (g_eeGeneral.beepMode>0 || (g_eeGeneral.beepMode==0 && val!=0) || (g_eeGeneral.beepMode==-1 && val>=3)) { - _beep(pgm_read_byte(beepTab+5*(2+g_eeGeneral.beepLength)+val)); - } -} - +#include "opentx.h" + +uint8_t g_beepCnt; +uint8_t beepAgain = 0; +uint8_t beepAgainOrig = 0; +uint8_t beepOn = false; +bool warble = false; +bool warbleC; + +// The various "beep" tone lengths +static const pm_uint8_t beepTab[] PROGMEM = { + // key, trim, warn2, warn1, error + 1, 1, 2, 10, 60, //xShort + 1, 1, 4, 20, 80, //short + 1, 1, 8, 30, 100, //normal + 2, 2, 15, 40, 120, //long + 5, 5, 30, 50, 150, //xLong +}; + +void beep(uint8_t val) +{ +#if defined(HAPTIC) && !defined(AUDIO) + haptic.event(val==0 ? AU_KEYPAD_UP : (val==4 ? AU_ERROR : AU_TIMER_LT10+beepAgain)); +#endif + +#if !defined(AUDIO) + if (g_eeGeneral.alarmsFlash && val>1) flashCounter = FLASH_DURATION; +#endif + + if (g_eeGeneral.beepMode>0 || (g_eeGeneral.beepMode==0 && val!=0) || (g_eeGeneral.beepMode==-1 && val>=3)) { + _beep(pgm_read_byte(beepTab+5*(2+g_eeGeneral.beepLength)+val)); + } +} + diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 5630c3d24..0e1d41370 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1,667 +1,667 @@ -/* - * 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" -#include "diskio.h" -#include -#include - -#define CLI_COMMAND_MAX_ARGS 8 -#define CLI_COMMAND_MAX_LEN 256 - -OS_TID cliTaskId; -TaskStack cliStack; -Fifo cliRxFifo; -uint8_t cliTracesEnabled = true; -// char cliLastLine[CLI_COMMAND_MAX_LEN+1]; - -typedef int (* CliFunction) (const char ** args); -int cliExecLine(char * line); -int cliExecCommand(const char ** argv); -int cliHelp(const char ** argv); - -struct CliCommand -{ - const char * name; - CliFunction func; - const char * args; -}; - -struct MemArea -{ - const char * name; - void * start; - int size; -}; - -void cliPrompt() -{ - serialPutc('>'); -} - -int toLongLongInt(const char ** argv, int index, long long int * val) -{ - if (*argv[index] == '\0') { - return 0; - } - else { - int base = 10; - const char * s = argv[index]; - if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') { - base = 16; - s = &argv[index][2]; - } - char * endptr = NULL; - *val = strtoll(s, &endptr, base); - if (*endptr == '\0') - return 1; - else { - serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]); - return -1; - } - } -} - -int toInt(const char ** argv, int index, int * val) -{ - long long int lval = 0; - int result = toLongLongInt(argv, index, &lval); - *val = (int)lval; - return result; -} - -int cliBeep(const char ** argv) -{ - int freq = BEEP_DEFAULT_FREQ; - int duration = 100; - if (toInt(argv, 1, &freq) >= 0 && toInt(argv, 2, &duration) >= 0) { - audioQueue.playTone(freq, duration, 20, PLAY_NOW); - } - return 0; -} - -int cliPlay(const char ** argv) -{ - audioQueue.playFile(argv[1], PLAY_NOW); - return 0; -} - -int cliLs(const char ** argv) -{ - FILINFO fno; - DIR dir; - char *fn; /* This function is assuming non-Unicode cfg. */ -#if _USE_LFN - TCHAR lfn[_MAX_LFN + 1]; - fno.lfname = lfn; - fno.lfsize = sizeof(lfn); -#endif - - FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */ - if (res == FR_OK) { - for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - -#if _USE_LFN - fn = *fno.lfname ? fno.lfname : fno.fname; -#else - fn = fno.fname; -#endif - serialPrint(fn); - } - } - else { - serialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]); - } - return 0; -} - -int cliRead(const char ** argv) -{ - FIL file; - uint32_t bytesRead = 0; - int bufferSize; - if (toInt(argv, 2, &bufferSize) == 0 || bufferSize < 0 ) { - serialPrint("%s: Invalid buffer size \"%s\"", argv[0], argv[2]); - return 0; - } - - uint8_t * buffer = (uint8_t*) malloc(bufferSize); - FRESULT result = f_open(&file, argv[1], FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { - free(buffer); - serialPrint("%s: File not found \"%s\"", argv[0], argv[1]); - return 0; - } - - tmr10ms_t start = get_tmr10ms(); - - while (true) { - UINT read; - result = f_read(&file, buffer, sizeof(buffer), &read); - if (result == FR_OK) { - if (read == 0) { - // end of file - f_close(&file); - break; - } - bytesRead += read; - } - } - uint32_t elapsedTime = (get_tmr10ms() - start) * 10; - if (elapsedTime == 0) elapsedTime = 1; - uint32_t speed = bytesRead / elapsedTime; - serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); - free(buffer); - return 0; -} - -int cliReadSD(const char ** argv) -{ - int startSector; - int numberOfSectors; - int bufferSectors; - if (toInt(argv, 1, &startSector) == 0 || startSector < 0 ) { - serialPrint("%s: Invalid start sector \"%s\"", argv[0], argv[1]); - return 0; - } - if (toInt(argv, 2, &numberOfSectors) == 0 || numberOfSectors < 0 ) { - serialPrint("%s: Invalid number of sectors \"%s\"", argv[0], argv[2]); - return 0; - } - - if (toInt(argv, 3, &bufferSectors) == 0 || bufferSectors < 0 ) { - serialPrint("%s: Invalid number of buffrer sectors \"%s\"", argv[0], argv[3]); - return 0; - } - - uint8_t * buffer = (uint8_t*) malloc(512*bufferSectors); - - uint32_t bytesRead = numberOfSectors * 512; - tmr10ms_t start = get_tmr10ms(); - - while (numberOfSectors > 0) { - DRESULT res = disk_read(0, buffer, startSector, bufferSectors); - if (res != RES_OK) { - serialPrint("disk_read error: %d", res); - free(buffer); - return 0; - } - if (numberOfSectors >= bufferSectors) { - numberOfSectors -= bufferSectors; - } - else { - numberOfSectors = 0; - } - } - - uint32_t elapsedTime = (get_tmr10ms() - start) * 10; - if (elapsedTime == 0) elapsedTime = 1; - uint32_t speed = bytesRead / elapsedTime; - serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); - free(buffer); - return 0; -} - - -int cliTrace(const char ** argv) -{ - if (!strcmp(argv[1], "on")) { - cliTracesEnabled = true; - } - else if (!strcmp(argv[1], "off")) { - cliTracesEnabled = false; - } - else { - serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]); - } - return 0; -} - -int cliStackInfo(const char ** argv) -{ - serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize() * 4); // stackSize() returns size in 32bit chunks - serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size()); - serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size()); - serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size()); - serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size()); - return 0; -} - -int cliMemoryInfo(const char ** argv) -{ - // struct mallinfo { - // int arena; /* total space allocated from system */ - // int ordblks; /* number of non-inuse chunks */ - // int smblks; /* unused -- always zero */ - // int hblks; /* number of mmapped regions */ - // int hblkhd; /* total space in mmapped regions */ - // int usmblks; /* unused -- always zero */ - // int fsmblks; /* unused -- always zero */ - // int uordblks; /* total allocated space */ - // int fordblks; /* total non-inuse space */ - // int keepcost; /* top-most, releasable (via malloc_trim) space */ - // }; - struct mallinfo info = mallinfo(); - serialPrint("arena %d", info.arena); - serialPrint("ordblks %d", info.ordblks); - serialPrint("uordblks %d", info.uordblks); - serialPrint("fordblks %d", info.fordblks); - serialPrint("keepcost %d", info.keepcost); - return 0; -} - -int cliReboot(const char ** argv) -{ -#if !defined(SIMU) - if (!strcmp(argv[1], "wdt")) { - // do a user requested watchdog test by pausing mixer thread - pausePulses(); - } - else { - NVIC_SystemReset(); - } -#endif - return 0; -} - -#if defined(PCBFLAMENCO) -int cliReadBQ24195(const char ** argv) -{ - int index = 0; - if (toInt(argv, 1, &index) > 0) { - serialPrint("BQ24195[%d] = 0x%02x", index, i2cReadBQ24195(index)); - } - else { - serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1]); - } - return 0; -} - -int cliWriteBQ24195(const char ** argv) -{ - int index = 0; - int data = 0; - if (toInt(argv, 1, &index) > 0 && toInt(argv, 2, &data) > 0) { - i2cWriteBQ24195(index, data); - } - else { - serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); - } - return 0; -} -#endif - -const MemArea memAreas[] = { - { "RCC", RCC, sizeof(RCC_TypeDef) }, - { "GPIOA", GPIOA, sizeof(GPIO_TypeDef) }, - { "GPIOB", GPIOB, sizeof(GPIO_TypeDef) }, - { "GPIOC", GPIOC, sizeof(GPIO_TypeDef) }, - { "GPIOD", GPIOD, sizeof(GPIO_TypeDef) }, - { "GPIOE", GPIOE, sizeof(GPIO_TypeDef) }, - { "GPIOF", GPIOF, sizeof(GPIO_TypeDef) }, - { "GPIOG", GPIOG, sizeof(GPIO_TypeDef) }, - { "USART1", USART1, sizeof(USART_TypeDef) }, - { "USART2", USART2, sizeof(USART_TypeDef) }, - { "USART3", USART3, sizeof(USART_TypeDef) }, - { NULL, NULL, 0 }, -}; - -int cliSet(const char ** argv) -{ - if (!strcmp(argv[1], "rtc")) { - struct gtm t; - int year, month, day, hour, minute, second; - if (toInt(argv, 2, &year) > 0 && toInt(argv, 3, &month) > 0 && toInt(argv, 4, &day) > 0 && toInt(argv, 5, &hour) > 0 && toInt(argv, 6, &minute) > 0 && toInt(argv, 7, &second) > 0) { - t.tm_year = year-1900; - t.tm_mon = month-1; - t.tm_mday = day; - t.tm_hour = hour; - t.tm_min = minute; - t.tm_sec = second; - g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated - rtcSetTime(&t); - } - else { - serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); - } - } - else if (!strcmp(argv[1], "volume")) { - int level = 0; - if (toInt(argv, 2, &level) > 0) { - setVolume(level); - } - else { - serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv[0], argv[1], argv[2]); - } - return 0; - } - return 0; -} - -int cliDisplay(const char ** argv) -{ - long long int address = 0; - - for (const MemArea * area = memAreas; area->name != NULL; area++) { - if (!strcmp(area->name, argv[1])) { - dump((uint8_t *)area->start, area->size); - return 0; - } - } - - if (!strcmp(argv[1], "keys")) { - for (int i=0; i VSysMin"); - serialPrint(reg & 0x02 ? "Thermal sensor bad" : "Thermal sensor ok"); - serialPrint(reg & 0x04 ? "Power ok" : "Power bad"); - serialPrint(reg & 0x08 ? "Connected to charger" : "Not connected to charger"); - const char * CHARGE_STATUS[] = { "Not Charging", "Precharge", "Fast Charging", "Charge done" }; - serialPrint(CHARGE_STATUS[(reg & 0x30) >> 4]); - const char * INPUT_STATUS[] = { "Unknown input", "USB host input", "USB adapter port input", "OTG input" }; - serialPrint(INPUT_STATUS[(reg & 0xC0) >> 6]); - } - { - uint8_t reg = i2cReadBQ24195(0x09); - if (reg & 0x80) serialPrint("Watchdog timer expiration"); - uint8_t chargerFault = (reg & 0x30) >> 4; - if (chargerFault == 0x01) - serialPrint("Input fault"); - else if (chargerFault == 0x02) - serialPrint("Thermal shutdown"); - else if (chargerFault == 0x03) - serialPrint("Charge safety timer expiration"); - if (reg & 0x08) serialPrint("Battery over voltage fault"); - uint8_t ntcFault = (reg & 0x07); - if (ntcFault == 0x05) - serialPrint("NTC cold"); - else if (ntcFault == 0x06) - serialPrint("NTC hot"); - } - } -#endif - else if (!strcmp(argv[1], "tim")) { - int timerNumber; - if (toInt(argv, 2, &timerNumber) > 0) { - TIM_TypeDef * tim = TIM1; - switch (timerNumber) { - case 1: - tim = TIM1; - break; - case 2: - tim = TIM2; - break; - default: - return 0; - } - serialPrint("TIM%d", timerNumber); - serialPrint(" CR1 0x%x", tim->CR1); - serialPrint(" CR2 0x%x", tim->CR2); - serialPrint(" DIER 0x%x", tim->DIER); - serialPrint(" SR 0x%x", tim->SR); - serialPrint(" EGR 0x%x", tim->EGR); - serialPrint(" CCMR1 0x%x", tim->CCMR1); - serialPrint(" CCMR2 0x%x", tim->CCMR2); - - serialPrint(" CNT 0x%x", tim->CNT); - serialPrint(" ARR 0x%x", tim->ARR); - serialPrint(" PSC 0x%x", tim->PSC); - - serialPrint(" CCER 0x%x", tim->CCER); - serialPrint(" CCR1 0x%x", tim->CCR1); - serialPrint(" CCR2 0x%x", tim->CCR2); - serialPrint(" CCR3 0x%x", tim->CCR3); - serialPrint(" CCR4 0x%x", tim->CCR4); - } - } - else if (toLongLongInt(argv, 1, &address) > 0) { - int size = 256; - if (toInt(argv, 2, &size) >= 0) { - dump((uint8_t *)address, size); - } - } - return 0; -} - -int cliDebugVars(const char ** argv) -{ -#if defined(PCBHORUS) - extern uint32_t ioMutexReq, ioMutexRel; - extern uint32_t sdReadRetries; - - serialPrint("ioMutexReq=%d", ioMutexReq); - serialPrint("ioMutexRel=%d", ioMutexRel); - serialPrint("sdReadRetries=%d", sdReadRetries); -#endif - return 0; -} - -int cliRepeat(const char ** argv) -{ - int interval = 0; - int counter = 0; - if (toInt(argv, 1, &interval) > 0 && argv[2]) { - interval *= 50; - counter = interval; - uint8_t c; - while (!cliRxFifo.pop(c) || !(c == '\r' || c == '\n' || c == ' ')) { - CoTickDelay(10); // 20ms - if (++counter >= interval) { - cliExecCommand(&argv[2]); - counter = 0; - } - } - } - else { - serialPrint("%s: Invalid arguments", argv[0]); - } - return 0; -} - -#if defined(JITTER_MEASURE) -int cliShowJitter(const char ** argv) -{ - serialPrint( "# anaIn rawJ avgJ"); - for (int i=0; icount; j++) { - serialPrint(" s%d %04X", j, calib->steps[j]); - } - } - } - return 0; -} -#endif - - -const CliCommand cliCommands[] = { - { "beep", cliBeep, "[] []" }, - { "ls", cliLs, "" }, - { "read", cliRead, "" }, - { "readsd", cliReadSD, " " }, - { "play", cliPlay, "" }, - { "print", cliDisplay, "
[] | " }, - { "reboot", cliReboot, "[wdt]" }, - { "set", cliSet, " " }, - { "stackinfo", cliStackInfo, "" }, - { "meminfo", cliMemoryInfo, "" }, - { "trace", cliTrace, "on | off" }, -#if defined(PCBFLAMENCO) - { "read_bq24195", cliReadBQ24195, "" }, - { "write_bq24195", cliWriteBQ24195, " " }, -#endif - { "help", cliHelp, "[]" }, - { "debugvars", cliDebugVars, "" }, - { "repeat", cliRepeat, " " }, -#if defined(JITTER_MEASURE) - { "jitter", cliShowJitter, "" }, -#endif - { NULL, NULL, NULL } /* sentinel */ -}; - -int cliHelp(const char ** argv) -{ - for (const CliCommand * command = cliCommands; command->name != NULL; command++) { - if (argv[1][0] == '\0' || !strcmp(command->name, argv[0])) { - serialPrint("%s %s", command->name, command->args); - if (argv[1][0] != '\0') { - return 0; - } - } - } - if (argv[1][0] != '\0') { - serialPrint("Invalid command \"%s\"", argv[0]); - } - return -1; -} - -int cliExecCommand(const char ** argv) -{ - if (argv[0][0] == '\0') - return 0; - - for (const CliCommand * command = cliCommands; command->name != NULL; command++) { - if (!strcmp(command->name, argv[0])) { - return command->func(argv); - } - } - serialPrint("Invalid command \"%s\"", argv[0]); - return -1; -} - -int cliExecLine(char * line) -{ - int len = strlen(line); - const char * argv[CLI_COMMAND_MAX_ARGS]; - memset(argv, 0, sizeof(argv)); - int argc = 1; - argv[0] = line; - for (int i=0; i +#include + +#define CLI_COMMAND_MAX_ARGS 8 +#define CLI_COMMAND_MAX_LEN 256 + +OS_TID cliTaskId; +TaskStack cliStack; +Fifo cliRxFifo; +uint8_t cliTracesEnabled = true; +// char cliLastLine[CLI_COMMAND_MAX_LEN+1]; + +typedef int (* CliFunction) (const char ** args); +int cliExecLine(char * line); +int cliExecCommand(const char ** argv); +int cliHelp(const char ** argv); + +struct CliCommand +{ + const char * name; + CliFunction func; + const char * args; +}; + +struct MemArea +{ + const char * name; + void * start; + int size; +}; + +void cliPrompt() +{ + serialPutc('>'); +} + +int toLongLongInt(const char ** argv, int index, long long int * val) +{ + if (*argv[index] == '\0') { + return 0; + } + else { + int base = 10; + const char * s = argv[index]; + if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') { + base = 16; + s = &argv[index][2]; + } + char * endptr = NULL; + *val = strtoll(s, &endptr, base); + if (*endptr == '\0') + return 1; + else { + serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]); + return -1; + } + } +} + +int toInt(const char ** argv, int index, int * val) +{ + long long int lval = 0; + int result = toLongLongInt(argv, index, &lval); + *val = (int)lval; + return result; +} + +int cliBeep(const char ** argv) +{ + int freq = BEEP_DEFAULT_FREQ; + int duration = 100; + if (toInt(argv, 1, &freq) >= 0 && toInt(argv, 2, &duration) >= 0) { + audioQueue.playTone(freq, duration, 20, PLAY_NOW); + } + return 0; +} + +int cliPlay(const char ** argv) +{ + audioQueue.playFile(argv[1], PLAY_NOW); + return 0; +} + +int cliLs(const char ** argv) +{ + FILINFO fno; + DIR dir; + char *fn; /* This function is assuming non-Unicode cfg. */ +#if _USE_LFN + TCHAR lfn[_MAX_LFN + 1]; + fno.lfname = lfn; + fno.lfsize = sizeof(lfn); +#endif + + FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */ + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); /* Read a directory item */ + if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + +#if _USE_LFN + fn = *fno.lfname ? fno.lfname : fno.fname; +#else + fn = fno.fname; +#endif + serialPrint(fn); + } + } + else { + serialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]); + } + return 0; +} + +int cliRead(const char ** argv) +{ + FIL file; + uint32_t bytesRead = 0; + int bufferSize; + if (toInt(argv, 2, &bufferSize) == 0 || bufferSize < 0 ) { + serialPrint("%s: Invalid buffer size \"%s\"", argv[0], argv[2]); + return 0; + } + + uint8_t * buffer = (uint8_t*) malloc(bufferSize); + FRESULT result = f_open(&file, argv[1], FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + free(buffer); + serialPrint("%s: File not found \"%s\"", argv[0], argv[1]); + return 0; + } + + tmr10ms_t start = get_tmr10ms(); + + while (true) { + UINT read; + result = f_read(&file, buffer, sizeof(buffer), &read); + if (result == FR_OK) { + if (read == 0) { + // end of file + f_close(&file); + break; + } + bytesRead += read; + } + } + uint32_t elapsedTime = (get_tmr10ms() - start) * 10; + if (elapsedTime == 0) elapsedTime = 1; + uint32_t speed = bytesRead / elapsedTime; + serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); + free(buffer); + return 0; +} + +int cliReadSD(const char ** argv) +{ + int startSector; + int numberOfSectors; + int bufferSectors; + if (toInt(argv, 1, &startSector) == 0 || startSector < 0 ) { + serialPrint("%s: Invalid start sector \"%s\"", argv[0], argv[1]); + return 0; + } + if (toInt(argv, 2, &numberOfSectors) == 0 || numberOfSectors < 0 ) { + serialPrint("%s: Invalid number of sectors \"%s\"", argv[0], argv[2]); + return 0; + } + + if (toInt(argv, 3, &bufferSectors) == 0 || bufferSectors < 0 ) { + serialPrint("%s: Invalid number of buffrer sectors \"%s\"", argv[0], argv[3]); + return 0; + } + + uint8_t * buffer = (uint8_t*) malloc(512*bufferSectors); + + uint32_t bytesRead = numberOfSectors * 512; + tmr10ms_t start = get_tmr10ms(); + + while (numberOfSectors > 0) { + DRESULT res = disk_read(0, buffer, startSector, bufferSectors); + if (res != RES_OK) { + serialPrint("disk_read error: %d", res); + free(buffer); + return 0; + } + if (numberOfSectors >= bufferSectors) { + numberOfSectors -= bufferSectors; + } + else { + numberOfSectors = 0; + } + } + + uint32_t elapsedTime = (get_tmr10ms() - start) * 10; + if (elapsedTime == 0) elapsedTime = 1; + uint32_t speed = bytesRead / elapsedTime; + serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); + free(buffer); + return 0; +} + + +int cliTrace(const char ** argv) +{ + if (!strcmp(argv[1], "on")) { + cliTracesEnabled = true; + } + else if (!strcmp(argv[1], "off")) { + cliTracesEnabled = false; + } + else { + serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]); + } + return 0; +} + +int cliStackInfo(const char ** argv) +{ + serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize() * 4); // stackSize() returns size in 32bit chunks + serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size()); + serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size()); + serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size()); + serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size()); + return 0; +} + +int cliMemoryInfo(const char ** argv) +{ + // struct mallinfo { + // int arena; /* total space allocated from system */ + // int ordblks; /* number of non-inuse chunks */ + // int smblks; /* unused -- always zero */ + // int hblks; /* number of mmapped regions */ + // int hblkhd; /* total space in mmapped regions */ + // int usmblks; /* unused -- always zero */ + // int fsmblks; /* unused -- always zero */ + // int uordblks; /* total allocated space */ + // int fordblks; /* total non-inuse space */ + // int keepcost; /* top-most, releasable (via malloc_trim) space */ + // }; + struct mallinfo info = mallinfo(); + serialPrint("arena %d", info.arena); + serialPrint("ordblks %d", info.ordblks); + serialPrint("uordblks %d", info.uordblks); + serialPrint("fordblks %d", info.fordblks); + serialPrint("keepcost %d", info.keepcost); + return 0; +} + +int cliReboot(const char ** argv) +{ +#if !defined(SIMU) + if (!strcmp(argv[1], "wdt")) { + // do a user requested watchdog test by pausing mixer thread + pausePulses(); + } + else { + NVIC_SystemReset(); + } +#endif + return 0; +} + +#if defined(PCBFLAMENCO) +int cliReadBQ24195(const char ** argv) +{ + int index = 0; + if (toInt(argv, 1, &index) > 0) { + serialPrint("BQ24195[%d] = 0x%02x", index, i2cReadBQ24195(index)); + } + else { + serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1]); + } + return 0; +} + +int cliWriteBQ24195(const char ** argv) +{ + int index = 0; + int data = 0; + if (toInt(argv, 1, &index) > 0 && toInt(argv, 2, &data) > 0) { + i2cWriteBQ24195(index, data); + } + else { + serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); + } + return 0; +} +#endif + +const MemArea memAreas[] = { + { "RCC", RCC, sizeof(RCC_TypeDef) }, + { "GPIOA", GPIOA, sizeof(GPIO_TypeDef) }, + { "GPIOB", GPIOB, sizeof(GPIO_TypeDef) }, + { "GPIOC", GPIOC, sizeof(GPIO_TypeDef) }, + { "GPIOD", GPIOD, sizeof(GPIO_TypeDef) }, + { "GPIOE", GPIOE, sizeof(GPIO_TypeDef) }, + { "GPIOF", GPIOF, sizeof(GPIO_TypeDef) }, + { "GPIOG", GPIOG, sizeof(GPIO_TypeDef) }, + { "USART1", USART1, sizeof(USART_TypeDef) }, + { "USART2", USART2, sizeof(USART_TypeDef) }, + { "USART3", USART3, sizeof(USART_TypeDef) }, + { NULL, NULL, 0 }, +}; + +int cliSet(const char ** argv) +{ + if (!strcmp(argv[1], "rtc")) { + struct gtm t; + int year, month, day, hour, minute, second; + if (toInt(argv, 2, &year) > 0 && toInt(argv, 3, &month) > 0 && toInt(argv, 4, &day) > 0 && toInt(argv, 5, &hour) > 0 && toInt(argv, 6, &minute) > 0 && toInt(argv, 7, &second) > 0) { + t.tm_year = year-1900; + t.tm_mon = month-1; + t.tm_mday = day; + t.tm_hour = hour; + t.tm_min = minute; + t.tm_sec = second; + g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated + rtcSetTime(&t); + } + else { + serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); + } + } + else if (!strcmp(argv[1], "volume")) { + int level = 0; + if (toInt(argv, 2, &level) > 0) { + setVolume(level); + } + else { + serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv[0], argv[1], argv[2]); + } + return 0; + } + return 0; +} + +int cliDisplay(const char ** argv) +{ + long long int address = 0; + + for (const MemArea * area = memAreas; area->name != NULL; area++) { + if (!strcmp(area->name, argv[1])) { + dump((uint8_t *)area->start, area->size); + return 0; + } + } + + if (!strcmp(argv[1], "keys")) { + for (int i=0; i VSysMin"); + serialPrint(reg & 0x02 ? "Thermal sensor bad" : "Thermal sensor ok"); + serialPrint(reg & 0x04 ? "Power ok" : "Power bad"); + serialPrint(reg & 0x08 ? "Connected to charger" : "Not connected to charger"); + const char * CHARGE_STATUS[] = { "Not Charging", "Precharge", "Fast Charging", "Charge done" }; + serialPrint(CHARGE_STATUS[(reg & 0x30) >> 4]); + const char * INPUT_STATUS[] = { "Unknown input", "USB host input", "USB adapter port input", "OTG input" }; + serialPrint(INPUT_STATUS[(reg & 0xC0) >> 6]); + } + { + uint8_t reg = i2cReadBQ24195(0x09); + if (reg & 0x80) serialPrint("Watchdog timer expiration"); + uint8_t chargerFault = (reg & 0x30) >> 4; + if (chargerFault == 0x01) + serialPrint("Input fault"); + else if (chargerFault == 0x02) + serialPrint("Thermal shutdown"); + else if (chargerFault == 0x03) + serialPrint("Charge safety timer expiration"); + if (reg & 0x08) serialPrint("Battery over voltage fault"); + uint8_t ntcFault = (reg & 0x07); + if (ntcFault == 0x05) + serialPrint("NTC cold"); + else if (ntcFault == 0x06) + serialPrint("NTC hot"); + } + } +#endif + else if (!strcmp(argv[1], "tim")) { + int timerNumber; + if (toInt(argv, 2, &timerNumber) > 0) { + TIM_TypeDef * tim = TIM1; + switch (timerNumber) { + case 1: + tim = TIM1; + break; + case 2: + tim = TIM2; + break; + default: + return 0; + } + serialPrint("TIM%d", timerNumber); + serialPrint(" CR1 0x%x", tim->CR1); + serialPrint(" CR2 0x%x", tim->CR2); + serialPrint(" DIER 0x%x", tim->DIER); + serialPrint(" SR 0x%x", tim->SR); + serialPrint(" EGR 0x%x", tim->EGR); + serialPrint(" CCMR1 0x%x", tim->CCMR1); + serialPrint(" CCMR2 0x%x", tim->CCMR2); + + serialPrint(" CNT 0x%x", tim->CNT); + serialPrint(" ARR 0x%x", tim->ARR); + serialPrint(" PSC 0x%x", tim->PSC); + + serialPrint(" CCER 0x%x", tim->CCER); + serialPrint(" CCR1 0x%x", tim->CCR1); + serialPrint(" CCR2 0x%x", tim->CCR2); + serialPrint(" CCR3 0x%x", tim->CCR3); + serialPrint(" CCR4 0x%x", tim->CCR4); + } + } + else if (toLongLongInt(argv, 1, &address) > 0) { + int size = 256; + if (toInt(argv, 2, &size) >= 0) { + dump((uint8_t *)address, size); + } + } + return 0; +} + +int cliDebugVars(const char ** argv) +{ +#if defined(PCBHORUS) + extern uint32_t ioMutexReq, ioMutexRel; + extern uint32_t sdReadRetries; + + serialPrint("ioMutexReq=%d", ioMutexReq); + serialPrint("ioMutexRel=%d", ioMutexRel); + serialPrint("sdReadRetries=%d", sdReadRetries); +#endif + return 0; +} + +int cliRepeat(const char ** argv) +{ + int interval = 0; + int counter = 0; + if (toInt(argv, 1, &interval) > 0 && argv[2]) { + interval *= 50; + counter = interval; + uint8_t c; + while (!cliRxFifo.pop(c) || !(c == '\r' || c == '\n' || c == ' ')) { + CoTickDelay(10); // 20ms + if (++counter >= interval) { + cliExecCommand(&argv[2]); + counter = 0; + } + } + } + else { + serialPrint("%s: Invalid arguments", argv[0]); + } + return 0; +} + +#if defined(JITTER_MEASURE) +int cliShowJitter(const char ** argv) +{ + serialPrint( "# anaIn rawJ avgJ"); + for (int i=0; icount; j++) { + serialPrint(" s%d %04X", j, calib->steps[j]); + } + } + } + return 0; +} +#endif + + +const CliCommand cliCommands[] = { + { "beep", cliBeep, "[] []" }, + { "ls", cliLs, "" }, + { "read", cliRead, "" }, + { "readsd", cliReadSD, " " }, + { "play", cliPlay, "" }, + { "print", cliDisplay, "
[] | " }, + { "reboot", cliReboot, "[wdt]" }, + { "set", cliSet, " " }, + { "stackinfo", cliStackInfo, "" }, + { "meminfo", cliMemoryInfo, "" }, + { "trace", cliTrace, "on | off" }, +#if defined(PCBFLAMENCO) + { "read_bq24195", cliReadBQ24195, "" }, + { "write_bq24195", cliWriteBQ24195, " " }, +#endif + { "help", cliHelp, "[]" }, + { "debugvars", cliDebugVars, "" }, + { "repeat", cliRepeat, " " }, +#if defined(JITTER_MEASURE) + { "jitter", cliShowJitter, "" }, +#endif + { NULL, NULL, NULL } /* sentinel */ +}; + +int cliHelp(const char ** argv) +{ + for (const CliCommand * command = cliCommands; command->name != NULL; command++) { + if (argv[1][0] == '\0' || !strcmp(command->name, argv[0])) { + serialPrint("%s %s", command->name, command->args); + if (argv[1][0] != '\0') { + return 0; + } + } + } + if (argv[1][0] != '\0') { + serialPrint("Invalid command \"%s\"", argv[0]); + } + return -1; +} + +int cliExecCommand(const char ** argv) +{ + if (argv[0][0] == '\0') + return 0; + + for (const CliCommand * command = cliCommands; command->name != NULL; command++) { + if (!strcmp(command->name, argv[0])) { + return command->func(argv); + } + } + serialPrint("Invalid command \"%s\"", argv[0]); + return -1; +} + +int cliExecLine(char * line) +{ + int len = strlen(line); + const char * argv[CLI_COMMAND_MAX_ARGS]; + memset(argv, 0, sizeof(argv)); + int argc = 1; + argv[0] = line; + for (int i=0; ipoints + 5; - #define MMULT 1024 - if (i == 0) { - //linear interpolation between 1st 2 points - //keep 3 decimal-places for m - if (crv->type == CURVE_TYPE_CUSTOM) { - int8_t x0 = CUSTOM_POINT_X(points, num_points, 0); - int8_t x1 = CUSTOM_POINT_X(points, num_points, 1); - if (x1 > x0) m = (MMULT * (points[1] - points[0])) / (x1 - x0); - } - else { - s32 delta = (2 * 100) / (num_points - 1); - m = (MMULT * (points[1] - points[0])) / delta; - } - } - else if (i == num_points - 1) { - //linear interpolation between last 2 points - //keep 3 decimal-places for m - if (crv->type == CURVE_TYPE_CUSTOM) { - int8_t x0 = CUSTOM_POINT_X(points, num_points, num_points-2); - int8_t x1 = CUSTOM_POINT_X(points, num_points, num_points-1); - if (x1 > x0) m = (MMULT * (points[num_points-1] - points[num_points-2])) / (x1 - x0); - } - else { - s32 delta = (2 * 100) / (num_points - 1); - m = (MMULT * (points[num_points-1] - points[num_points-2])) / delta; - } - } - else { - //apply monotone rules from - //http://en.wikipedia.org/wiki/Monotone_cubic_interpolation - //1) compute slopes of secant lines - s32 d0=0, d1=0; - if (crv->type == CURVE_TYPE_CUSTOM) { - int8_t x0 = CUSTOM_POINT_X(points, num_points, i-1); - int8_t x1 = CUSTOM_POINT_X(points, num_points, i); - int8_t x2 = CUSTOM_POINT_X(points, num_points, i+1); - if (x1 > x0) d0 = (MMULT * (points[i] - points[i-1])) / (x1 - x0); - if (x2 > x1) d1 = (MMULT * (points[i+1] - points[i])) / (x2 - x1); - } - else { - s32 delta = (2 * 100) / (num_points - 1); - d0 = (MMULT * (points[i] - points[i-1])) / (delta); - d1 = (MMULT * (points[i+1] - points[i])) / (delta); - } - //2) compute initial average tangent - m = (d0 + d1) / 2; - //3 check for horizontal lines - if (d0 == 0 || d1 == 0 || (d0 > 0 && d1 < 0) || (d0 < 0 && d1 > 0)) { - m = 0; - } - else if (MMULT * m / d0 > 3 * MMULT) { - m = 3 * d0; - } - else if (MMULT * m / d1 > 3 * MMULT) { - m = 3 * d1; - } - } - return m; -} - -/* The following is a hermite cubic spline. - The basis functions can be found here: - http://en.wikipedia.org/wiki/Cubic_Hermite_spline - The tangents are computed via the 'cubic monotone' rules (allowing for local-maxima) -*/ -int16_t hermite_spline(int16_t x, uint8_t idx) -{ - CurveInfo &crv = g_model.curves[idx]; - int8_t *points = curveAddress(idx); - uint8_t count = crv.points+5; - bool custom = (crv.type == CURVE_TYPE_CUSTOM); - - if (x < -RESX) - x = -RESX; - else if (x > RESX) - x = RESX; - - for (int i=0; i0 ? calc100toRESX(points[count+i-1]) : -RESX); - p3x = (i= p0x && x <= p3x) { - s32 p0y = calc100toRESX(points[i]); - s32 p3y = calc100toRESX(points[i+1]); - s32 m0 = compute_tangent(&crv, points, i); - s32 m3 = compute_tangent(&crv, points, i+1); - s32 y; - s32 h = p3x - p0x; - s32 t = (h > 0 ? (MMULT * (x - p0x)) / h : 0); - s32 t2 = t * t / MMULT; - s32 t3 = t2 * t / MMULT; - s32 h00 = 2*t3 - 3*t2 + MMULT; - s32 h10 = t3 - 2*t2 + t; - s32 h01 = -2*t3 + 3*t2; - s32 h11 = t3 - t2; - y = p0y * h00 + h * (m0 * h10 / MMULT) + p3y * h01 + h * (m3 * h11 / MMULT); - y /= MMULT; - return y; - } - } - return 0; -} -#endif - -int intpol(int x, uint8_t idx) // -100, -75, -50, -25, 0 ,25 ,50, 75, 100 -{ -#if defined(XCURVES) - CurveInfo &crv = g_model.curves[idx]; - int8_t *points = curveAddress(idx); - uint8_t count = crv.points+5; - bool custom = (crv.type == CURVE_TYPE_CUSTOM); -#else - CurveInfo crv = curveInfo(idx); - int8_t *points = crv.crv; - uint8_t count = crv.points; - bool custom = crv.custom; -#endif - int16_t erg = 0; - - x += RESXu; - - if (x <= 0) { - erg = (int16_t)points[0] * (RESX/4); - } - else if (x >= (RESX*2)) { - erg = (int16_t)points[count-1] * (RESX/4); - } - else { - uint16_t a=0, b=0; - uint8_t i; - if (custom) { - for (i=0; i 0 && x < 0) - x = (x * (1000 - curveParam)) / 1000; - else if (curveParam < 0 && x > 0) - x = (x * (1000 + curveParam)) / 1000; - return x; -#else - int curveParam = calc100to256(GET_GVAR(curve.value, -100, 100, mixerCurrentFlightMode)); - if (curveParam > 0 && x < 0) - x = (x * (256 - curveParam)) >> 8; - else if (curveParam < 0 && x > 0) - x = (x * (256 + curveParam)) >> 8; - return x; -#endif - } - - case CURVE_REF_EXPO: - { -#if defined(CPUARM) - int curveParam = GET_GVAR_PREC1(curve.value, -100, 100, mixerCurrentFlightMode) / 10; -#else - int curveParam = GET_GVAR(curve.value, -100, 100, mixerCurrentFlightMode); -#endif - return expo(x, curveParam); - } - - case CURVE_REF_FUNC: - switch (curve.value) { - case CURVE_X_GT0: - if (x < 0) x = 0; //x|x>0 - return x; - case CURVE_X_LT0: - if (x > 0) x = 0; //x|x<0 - return x; - case CURVE_ABS_X: // x|abs(x) - return abs(x); - case CURVE_F_GT0: //f|f>0 - return x > 0 ? RESX : 0; - case CURVE_F_LT0: //f|f<0 - return x < 0 ? -RESX : 0; - case CURVE_ABS_F: //f|abs(f) - return x > 0 ? RESX : -RESX; - } - break; - - case CURVE_REF_CUSTOM: - { - int curveParam = curve.value; - if (curveParam < 0) { - x = -x; - curveParam = -curveParam; - } - if (curveParam > 0 && curveParam <= MAX_CURVES) { - return applyCustomCurve(x, curveParam - 1); - } - break; - } - } - - return x; -} - -int applyCustomCurve(int x, uint8_t idx) -{ - if (idx >= MAX_CURVES) - return 0; - - CurveInfo &crv = g_model.curves[idx]; - if (crv.smooth) - return hermite_spline(x, idx); - else - return intpol(x, idx); -} -#elif defined(CURVES) -int applyCurve(int x, int8_t idx) -{ - /* already tried to have only one return at the end */ - switch(idx) { - case CURVE_NONE: - return x; - case CURVE_X_GT0: - if (x < 0) x = 0; //x|x>0 - return x; - case CURVE_X_LT0: - if (x > 0) x = 0; //x|x<0 - return x; - case CURVE_ABS_X: // x|abs(x) - return abs(x); - case CURVE_F_GT0: //f|f>0 - return x > 0 ? RESX : 0; - case CURVE_F_LT0: //f|f<0 - return x < 0 ? -RESX : 0; - case CURVE_ABS_F: //f|abs(f) - return x > 0 ? RESX : -RESX; - } - if (idx < 0) { - x = -x; - idx = -idx + CURVE_BASE - 1; - } - return applyCustomCurve(x, idx - CURVE_BASE); -} -#endif - -// #define EXTENDED_EXPO -// increases range of expo curve but costs about 82 bytes flash - -// expo-funktion: -// --------------- -// kmplot -// f(x,k)=exp(ln(x)*k/10) ;P[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] -// f(x,k)=x*x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] -// f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] -// f(x,k)=1+(x-1)*(x-1)*(x-1)*k/10 + (x-1)*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] -// don't know what this above should be, just confusing in my opinion, - -// here is the real explanation -// actually the real formula is -/* - f(x) = exp( ln(x) * 10^k) - if it is 10^k or e^k or 2^k etc. just defines the max distortion of the expo curve; I think 10 is useful - this gives values from 0 to 1 for x and output; k must be between -1 and +1 - we do not like to calculate with floating point. Therefore we rescale for x from 0 to 1024 and for k from -100 to +100 - f(x) = 1024 * ( e^( ln(x/1024) * 10^(k/100) ) ) - This would be really hard to be calculated by such a microcontroller - Therefore Thomas Husterer compared a few usual function something like x^3, x^4*something, which look similar - Actually the formula - f(x) = k*x^3+x*(1-k) - gives a similar form and should have even advantages compared to a original exp curve. - This function again expect x from 0 to 1 and k only from 0 to 1 - Therefore rescaling is needed like before: - f(x) = 1024* ((k/100)*(x/1024)^3 + (x/1024)*(100-k)/100) - some mathematical tricks - f(x) = (k*x*x*x/(1024*1024) + x*(100-k)) / 100 - for better rounding results we add the 50 - f(x) = (k*x*x*x/(1024*1024) + x*(100-k) + 50) / 100 - - because we now understand the formula, we can optimize it further - --> calc100to256(k) --> eliminates /100 by replacing with /256 which is just a simple shift right 8 - k is now between 0 and 256 - f(x) = (k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256 - */ - -// input parameters; -// x 0 to 1024; -// k 0 to 100; -// output between 0 and 1024 -unsigned int expou(unsigned int x, unsigned int k) -{ -#if defined(EXTENDED_EXPO) - bool extended; - if (k>80) { - extended=true; - } - else { - k += (k>>2); // use bigger values before extend, because the effect is anyway very very low - extended=false; - } -#endif - - k = calc100to256(k); - - uint32_t value = (uint32_t) x*x; - value *= (uint32_t)k; - value >>= 8; - value *= (uint32_t)x; - -#if defined(EXTENDED_EXPO) - if (extended) { // for higher values do more multiplications to get a stronger expo curve - value >>= 16; - value *= (uint32_t)x; - value >>= 4; - value *= (uint32_t)x; - } -#endif - - value >>= 12; - value += (uint32_t)(256-k)*x+128; - - return value>>8; -} - -int expo(int x, int k) -{ - if (k == 0) return x; - int y; - bool neg = (x < 0); - - if (neg) x = -x; - if (k<0) { - y = RESXu-expou(RESXu-x, -k); - } - else { - y = expou(x, k); - } - return neg? -y : y; -} +/* + * 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" + +#if defined(XCURVES) +int8_t *curveEnd[MAX_CURVES]; +void loadCurves() +{ + int8_t * tmp = g_model.points; + for (int i=0; ipoints + 5; + #define MMULT 1024 + if (i == 0) { + //linear interpolation between 1st 2 points + //keep 3 decimal-places for m + if (crv->type == CURVE_TYPE_CUSTOM) { + int8_t x0 = CUSTOM_POINT_X(points, num_points, 0); + int8_t x1 = CUSTOM_POINT_X(points, num_points, 1); + if (x1 > x0) m = (MMULT * (points[1] - points[0])) / (x1 - x0); + } + else { + s32 delta = (2 * 100) / (num_points - 1); + m = (MMULT * (points[1] - points[0])) / delta; + } + } + else if (i == num_points - 1) { + //linear interpolation between last 2 points + //keep 3 decimal-places for m + if (crv->type == CURVE_TYPE_CUSTOM) { + int8_t x0 = CUSTOM_POINT_X(points, num_points, num_points-2); + int8_t x1 = CUSTOM_POINT_X(points, num_points, num_points-1); + if (x1 > x0) m = (MMULT * (points[num_points-1] - points[num_points-2])) / (x1 - x0); + } + else { + s32 delta = (2 * 100) / (num_points - 1); + m = (MMULT * (points[num_points-1] - points[num_points-2])) / delta; + } + } + else { + //apply monotone rules from + //http://en.wikipedia.org/wiki/Monotone_cubic_interpolation + //1) compute slopes of secant lines + s32 d0=0, d1=0; + if (crv->type == CURVE_TYPE_CUSTOM) { + int8_t x0 = CUSTOM_POINT_X(points, num_points, i-1); + int8_t x1 = CUSTOM_POINT_X(points, num_points, i); + int8_t x2 = CUSTOM_POINT_X(points, num_points, i+1); + if (x1 > x0) d0 = (MMULT * (points[i] - points[i-1])) / (x1 - x0); + if (x2 > x1) d1 = (MMULT * (points[i+1] - points[i])) / (x2 - x1); + } + else { + s32 delta = (2 * 100) / (num_points - 1); + d0 = (MMULT * (points[i] - points[i-1])) / (delta); + d1 = (MMULT * (points[i+1] - points[i])) / (delta); + } + //2) compute initial average tangent + m = (d0 + d1) / 2; + //3 check for horizontal lines + if (d0 == 0 || d1 == 0 || (d0 > 0 && d1 < 0) || (d0 < 0 && d1 > 0)) { + m = 0; + } + else if (MMULT * m / d0 > 3 * MMULT) { + m = 3 * d0; + } + else if (MMULT * m / d1 > 3 * MMULT) { + m = 3 * d1; + } + } + return m; +} + +/* The following is a hermite cubic spline. + The basis functions can be found here: + http://en.wikipedia.org/wiki/Cubic_Hermite_spline + The tangents are computed via the 'cubic monotone' rules (allowing for local-maxima) +*/ +int16_t hermite_spline(int16_t x, uint8_t idx) +{ + CurveInfo &crv = g_model.curves[idx]; + int8_t *points = curveAddress(idx); + uint8_t count = crv.points+5; + bool custom = (crv.type == CURVE_TYPE_CUSTOM); + + if (x < -RESX) + x = -RESX; + else if (x > RESX) + x = RESX; + + for (int i=0; i0 ? calc100toRESX(points[count+i-1]) : -RESX); + p3x = (i= p0x && x <= p3x) { + s32 p0y = calc100toRESX(points[i]); + s32 p3y = calc100toRESX(points[i+1]); + s32 m0 = compute_tangent(&crv, points, i); + s32 m3 = compute_tangent(&crv, points, i+1); + s32 y; + s32 h = p3x - p0x; + s32 t = (h > 0 ? (MMULT * (x - p0x)) / h : 0); + s32 t2 = t * t / MMULT; + s32 t3 = t2 * t / MMULT; + s32 h00 = 2*t3 - 3*t2 + MMULT; + s32 h10 = t3 - 2*t2 + t; + s32 h01 = -2*t3 + 3*t2; + s32 h11 = t3 - t2; + y = p0y * h00 + h * (m0 * h10 / MMULT) + p3y * h01 + h * (m3 * h11 / MMULT); + y /= MMULT; + return y; + } + } + return 0; +} +#endif + +int intpol(int x, uint8_t idx) // -100, -75, -50, -25, 0 ,25 ,50, 75, 100 +{ +#if defined(XCURVES) + CurveInfo &crv = g_model.curves[idx]; + int8_t *points = curveAddress(idx); + uint8_t count = crv.points+5; + bool custom = (crv.type == CURVE_TYPE_CUSTOM); +#else + CurveInfo crv = curveInfo(idx); + int8_t *points = crv.crv; + uint8_t count = crv.points; + bool custom = crv.custom; +#endif + int16_t erg = 0; + + x += RESXu; + + if (x <= 0) { + erg = (int16_t)points[0] * (RESX/4); + } + else if (x >= (RESX*2)) { + erg = (int16_t)points[count-1] * (RESX/4); + } + else { + uint16_t a=0, b=0; + uint8_t i; + if (custom) { + for (i=0; i 0 && x < 0) + x = (x * (1000 - curveParam)) / 1000; + else if (curveParam < 0 && x > 0) + x = (x * (1000 + curveParam)) / 1000; + return x; +#else + int curveParam = calc100to256(GET_GVAR(curve.value, -100, 100, mixerCurrentFlightMode)); + if (curveParam > 0 && x < 0) + x = (x * (256 - curveParam)) >> 8; + else if (curveParam < 0 && x > 0) + x = (x * (256 + curveParam)) >> 8; + return x; +#endif + } + + case CURVE_REF_EXPO: + { +#if defined(CPUARM) + int curveParam = GET_GVAR_PREC1(curve.value, -100, 100, mixerCurrentFlightMode) / 10; +#else + int curveParam = GET_GVAR(curve.value, -100, 100, mixerCurrentFlightMode); +#endif + return expo(x, curveParam); + } + + case CURVE_REF_FUNC: + switch (curve.value) { + case CURVE_X_GT0: + if (x < 0) x = 0; //x|x>0 + return x; + case CURVE_X_LT0: + if (x > 0) x = 0; //x|x<0 + return x; + case CURVE_ABS_X: // x|abs(x) + return abs(x); + case CURVE_F_GT0: //f|f>0 + return x > 0 ? RESX : 0; + case CURVE_F_LT0: //f|f<0 + return x < 0 ? -RESX : 0; + case CURVE_ABS_F: //f|abs(f) + return x > 0 ? RESX : -RESX; + } + break; + + case CURVE_REF_CUSTOM: + { + int curveParam = curve.value; + if (curveParam < 0) { + x = -x; + curveParam = -curveParam; + } + if (curveParam > 0 && curveParam <= MAX_CURVES) { + return applyCustomCurve(x, curveParam - 1); + } + break; + } + } + + return x; +} + +int applyCustomCurve(int x, uint8_t idx) +{ + if (idx >= MAX_CURVES) + return 0; + + CurveInfo &crv = g_model.curves[idx]; + if (crv.smooth) + return hermite_spline(x, idx); + else + return intpol(x, idx); +} +#elif defined(CURVES) +int applyCurve(int x, int8_t idx) +{ + /* already tried to have only one return at the end */ + switch(idx) { + case CURVE_NONE: + return x; + case CURVE_X_GT0: + if (x < 0) x = 0; //x|x>0 + return x; + case CURVE_X_LT0: + if (x > 0) x = 0; //x|x<0 + return x; + case CURVE_ABS_X: // x|abs(x) + return abs(x); + case CURVE_F_GT0: //f|f>0 + return x > 0 ? RESX : 0; + case CURVE_F_LT0: //f|f<0 + return x < 0 ? -RESX : 0; + case CURVE_ABS_F: //f|abs(f) + return x > 0 ? RESX : -RESX; + } + if (idx < 0) { + x = -x; + idx = -idx + CURVE_BASE - 1; + } + return applyCustomCurve(x, idx - CURVE_BASE); +} +#endif + +// #define EXTENDED_EXPO +// increases range of expo curve but costs about 82 bytes flash + +// expo-funktion: +// --------------- +// kmplot +// f(x,k)=exp(ln(x)*k/10) ;P[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] +// f(x,k)=x*x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] +// f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] +// f(x,k)=1+(x-1)*(x-1)*(x-1)*k/10 + (x-1)*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] +// don't know what this above should be, just confusing in my opinion, + +// here is the real explanation +// actually the real formula is +/* + f(x) = exp( ln(x) * 10^k) + if it is 10^k or e^k or 2^k etc. just defines the max distortion of the expo curve; I think 10 is useful + this gives values from 0 to 1 for x and output; k must be between -1 and +1 + we do not like to calculate with floating point. Therefore we rescale for x from 0 to 1024 and for k from -100 to +100 + f(x) = 1024 * ( e^( ln(x/1024) * 10^(k/100) ) ) + This would be really hard to be calculated by such a microcontroller + Therefore Thomas Husterer compared a few usual function something like x^3, x^4*something, which look similar + Actually the formula + f(x) = k*x^3+x*(1-k) + gives a similar form and should have even advantages compared to a original exp curve. + This function again expect x from 0 to 1 and k only from 0 to 1 + Therefore rescaling is needed like before: + f(x) = 1024* ((k/100)*(x/1024)^3 + (x/1024)*(100-k)/100) + some mathematical tricks + f(x) = (k*x*x*x/(1024*1024) + x*(100-k)) / 100 + for better rounding results we add the 50 + f(x) = (k*x*x*x/(1024*1024) + x*(100-k) + 50) / 100 + + because we now understand the formula, we can optimize it further + --> calc100to256(k) --> eliminates /100 by replacing with /256 which is just a simple shift right 8 + k is now between 0 and 256 + f(x) = (k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256 + */ + +// input parameters; +// x 0 to 1024; +// k 0 to 100; +// output between 0 and 1024 +unsigned int expou(unsigned int x, unsigned int k) +{ +#if defined(EXTENDED_EXPO) + bool extended; + if (k>80) { + extended=true; + } + else { + k += (k>>2); // use bigger values before extend, because the effect is anyway very very low + extended=false; + } +#endif + + k = calc100to256(k); + + uint32_t value = (uint32_t) x*x; + value *= (uint32_t)k; + value >>= 8; + value *= (uint32_t)x; + +#if defined(EXTENDED_EXPO) + if (extended) { // for higher values do more multiplications to get a stronger expo curve + value >>= 16; + value *= (uint32_t)x; + value >>= 4; + value *= (uint32_t)x; + } +#endif + + value >>= 12; + value += (uint32_t)(256-k)*x+128; + + return value>>8; +} + +int expo(int x, int k) +{ + if (k == 0) return x; + int y; + bool neg = (x < 0); + + if (neg) x = -x; + if (k<0) { + y = RESXu-expou(RESXu-x, -k); + } + else { + y = expou(x, k); + } + return neg? -y : y; +} diff --git a/radio/src/debug.cpp b/radio/src/debug.cpp index 6ecd3abe4..4ddcefd3e 100644 --- a/radio/src/debug.cpp +++ b/radio/src/debug.cpp @@ -18,86 +18,86 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "stamp.h" -#include - -#if defined(SIMU) -traceCallbackFunc traceCallback = 0; -#endif - -#if defined(SIMU) -#define PRINTF_BUFFER_SIZE 1024 -void debugPrintf(const char * format, ...) -{ - va_list arglist; - char tmp[PRINTF_BUFFER_SIZE]; - - va_start(arglist, format); - vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist); - va_end(arglist); - fputs(tmp, stdout); - fflush(stdout); - if (traceCallback) { - traceCallback(tmp); - } -} -#endif - -#if defined(DEBUG_TRACE_BUFFER) -static struct TraceElement traceBuffer[TRACE_BUFFER_LEN]; -static uint8_t traceBufferPos; -gtime_t filltm(gtime_t *t, struct gtm *tp); - -void trace_event(enum TraceEvent event, uint32_t data) -{ - if (traceBufferPos >= TRACE_BUFFER_LEN) return; - __disable_irq(); - struct TraceElement * p = &traceBuffer[traceBufferPos++]; - __enable_irq(); - p->time = g_rtcTime; - p->time_ms = g_ms100; - p->event = event; - p->data = data; -} - -void trace_event_i(enum TraceEvent event, uint32_t data) -{ - if (traceBufferPos >= TRACE_BUFFER_LEN) return; - struct TraceElement * p = &traceBuffer[traceBufferPos++]; - p->time = g_rtcTime; - p->time_ms = g_ms100; - p->event = event; - p->data = data; -} - - -const struct TraceElement * getTraceElement(uint16_t idx) -{ - if (idx < TRACE_BUFFER_LEN) return &traceBuffer[idx]; - return 0; -} - - -void dumpTraceBuffer() -{ - TRACE("Dump of Trace Buffer (" VERSION " " DATE " " TIME "):"); - TRACE("# Time Event Data"); - for(int n = 0; n < TRACE_BUFFER_LEN; ++n) { - struct gtm tp; - filltm(&traceBuffer[n].time, &tp); - TRACE_INFO_WP("%02d ", n); - TRACE_INFO_WP("%4d-%02d-%02d,%02d:%02d:%02d.%02d0", tp.tm_year+1900, tp.tm_mon+1, tp.tm_mday, tp.tm_hour, tp.tm_min, tp.tm_sec, traceBuffer[n].time_ms); - TRACE(" %03d 0x%08x", traceBuffer[n].event, traceBuffer[n].data); - if (traceBuffer[n].time == 0 && traceBuffer[n].time_ms == 0) break; -#if !defined(SIMU) - if ((n % 5) == 0) { - while (!serial2TxFifo.empty()) { - CoTickDelay(1); - } - } -#endif - } - TRACE("End of Trace Buffer dump"); -} -#endif +#include "opentx.h" +#include "stamp.h" +#include + +#if defined(SIMU) +traceCallbackFunc traceCallback = 0; +#endif + +#if defined(SIMU) +#define PRINTF_BUFFER_SIZE 1024 +void debugPrintf(const char * format, ...) +{ + va_list arglist; + char tmp[PRINTF_BUFFER_SIZE]; + + va_start(arglist, format); + vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist); + va_end(arglist); + fputs(tmp, stdout); + fflush(stdout); + if (traceCallback) { + traceCallback(tmp); + } +} +#endif + +#if defined(DEBUG_TRACE_BUFFER) +static struct TraceElement traceBuffer[TRACE_BUFFER_LEN]; +static uint8_t traceBufferPos; +gtime_t filltm(gtime_t *t, struct gtm *tp); + +void trace_event(enum TraceEvent event, uint32_t data) +{ + if (traceBufferPos >= TRACE_BUFFER_LEN) return; + __disable_irq(); + struct TraceElement * p = &traceBuffer[traceBufferPos++]; + __enable_irq(); + p->time = g_rtcTime; + p->time_ms = g_ms100; + p->event = event; + p->data = data; +} + +void trace_event_i(enum TraceEvent event, uint32_t data) +{ + if (traceBufferPos >= TRACE_BUFFER_LEN) return; + struct TraceElement * p = &traceBuffer[traceBufferPos++]; + p->time = g_rtcTime; + p->time_ms = g_ms100; + p->event = event; + p->data = data; +} + + +const struct TraceElement * getTraceElement(uint16_t idx) +{ + if (idx < TRACE_BUFFER_LEN) return &traceBuffer[idx]; + return 0; +} + + +void dumpTraceBuffer() +{ + TRACE("Dump of Trace Buffer (" VERSION " " DATE " " TIME "):"); + TRACE("# Time Event Data"); + for(int n = 0; n < TRACE_BUFFER_LEN; ++n) { + struct gtm tp; + filltm(&traceBuffer[n].time, &tp); + TRACE_INFO_WP("%02d ", n); + TRACE_INFO_WP("%4d-%02d-%02d,%02d:%02d:%02d.%02d0", tp.tm_year+1900, tp.tm_mon+1, tp.tm_mday, tp.tm_hour, tp.tm_min, tp.tm_sec, traceBuffer[n].time_ms); + TRACE(" %03d 0x%08x", traceBuffer[n].event, traceBuffer[n].data); + if (traceBuffer[n].time == 0 && traceBuffer[n].time_ms == 0) break; +#if !defined(SIMU) + if ((n % 5) == 0) { + while (!serial2TxFifo.empty()) { + CoTickDelay(1); + } + } +#endif + } + TRACE("End of Trace Buffer dump"); +} +#endif diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index d1095ebe4..e9890c0f6 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -1,373 +1,373 @@ -/* - * 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" -#include "ff.h" - -FIL g_oLogFile = {0}; -const pm_char * g_logError = NULL; -uint8_t logDelay; - -#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) - #define get2PosState(sw) (switchState(SW_ ## sw ## 0) ? -1 : 1) -#else - #define get2PosState(sw) (switchState(SW_ ## sw) ? -1 : 1) -#endif - -#define get3PosState(sw) (switchState(SW_ ## sw ## 0) ? -1 : (switchState(SW_ ## sw ## 2) ? 1 : 0)) - -const pm_char * openLogs() -{ - // Determine and set log file filename - FRESULT result; - char filename[34]; // /LOGS/modelnamexxx-2013-01-01.log - - if (!sdMounted()) - return STR_NO_SDCARD; - - if (sdGetFreeSectors() == 0) - return STR_SDCARD_FULL; - - // check and create folder here - strcpy_P(filename, STR_LOGS_PATH); - const char * error = sdCheckAndCreateDirectory(filename); - if (error) { - return error; - } - - filename[sizeof(LOGS_PATH)-1] = '/'; - memcpy(&filename[sizeof(LOGS_PATH)], g_model.header.name, sizeof(g_model.header.name)); - filename[sizeof(LOGS_PATH)+sizeof(g_model.header.name)] = '\0'; - - uint8_t i = sizeof(LOGS_PATH)+sizeof(g_model.header.name)-1; - uint8_t len = 0; - while (i>sizeof(LOGS_PATH)-1) { - if (!len && filename[i]) - len = i+1; - if (len) { - if (filename[i]) - filename[i] = idx2char(filename[i]); - else - filename[i] = '_'; - } - i--; - } - - if (len == 0) { -#if defined(EEPROM) - uint8_t num = g_eeGeneral.currModel + 1; -#else - // TODO - uint8_t num = 1; -#endif - strcpy_P(&filename[sizeof(LOGS_PATH)], STR_MODEL); - filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL)] = (char)((num / 10) + '0'); - filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 1] = (char)((num % 10) + '0'); - len = sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 2; - } - - char * tmp = &filename[len]; - -#if defined(RTCLOCK) - tmp = strAppendDate(&filename[len]); -#endif - - strcpy_P(tmp, STR_LOGS_EXT); - - result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE); - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - - if (f_size(&g_oLogFile) == 0) { - writeHeader(); - } - else { - result = f_lseek(&g_oLogFile, f_size(&g_oLogFile)); // append - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - } - - return NULL; -} - -tmr10ms_t lastLogTime = 0; - -void closeLogs() -{ - if (f_close(&g_oLogFile) != FR_OK) { - // close failed, forget file - g_oLogFile.fs = 0; - } - lastLogTime = 0; -} - -#if !defined(CPUARM) -getvalue_t getConvertedTelemetryValue(getvalue_t val, uint8_t unit) -{ - convertUnit(val, unit); - return val; -} -#endif - -void writeHeader() -{ -#if defined(RTCLOCK) - f_puts("Date,Time,", &g_oLogFile); -#else - f_puts("Time,", &g_oLogFile); -#endif - -#if defined(FRSKY) -#if !defined(CPUARM) - f_puts("Buffer,RX,TX,A1,A2,", &g_oLogFile); -#if defined(FRSKY_HUB) - if (IS_USR_PROTO_FRSKY_HUB()) { - f_puts("GPS Date,GPS Time,Long,Lat,Course,GPS Speed(kts),GPS Alt,Baro Alt(", &g_oLogFile); - f_puts(TELEMETRY_BARO_ALT_UNIT, &g_oLogFile); - f_puts("),Vertical Speed,Air Speed(kts),Temp1,Temp2,RPM,Fuel," TELEMETRY_CELLS_LABEL "Current,Consumption,Vfas,AccelX,AccelY,AccelZ,", &g_oLogFile); - } -#endif -#if defined(WS_HOW_HIGH) - if (IS_USR_PROTO_WS_HOW_HIGH()) { - f_puts("WSHH Alt,", &g_oLogFile); - } -#endif -#endif - -#if defined(CPUARM) - char label[TELEM_LABEL_LEN+7]; - for (int i=0; i 0) { - tmr10ms_t tmr10ms = get_tmr10ms(); - if (lastLogTime == 0 || (tmr10ms_t)(tmr10ms - lastLogTime) >= (tmr10ms_t)logDelay*10) { - lastLogTime = tmr10ms; - - if (!g_oLogFile.fs) { - const pm_char * result = openLogs(); - if (result != NULL) { - if (result != error_displayed) { - error_displayed = result; - POPUP_WARNING(result); - } - return; - } - } - -#if defined(RTCLOCK) - { - static struct gtm utm; - static gtime_t lastRtcTime = 0; - if (g_rtcTime != lastRtcTime) { - lastRtcTime = g_rtcTime; - gettime(&utm); - } - f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+1900, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); - } -#else - f_printf(&g_oLogFile, "%d,", tmr10ms); -#endif - -#if defined(FRSKY) -#if !defined(CPUARM) - f_printf(&g_oLogFile, "%d,%d,%d,", frskyStreaming, RAW_FRSKY_MINMAX(frskyData.rssi[0]), RAW_FRSKY_MINMAX(frskyData.rssi[1])); - for (uint8_t i=0; isizeof(LOGS_PATH)-1) { + if (!len && filename[i]) + len = i+1; + if (len) { + if (filename[i]) + filename[i] = idx2char(filename[i]); + else + filename[i] = '_'; + } + i--; + } + + if (len == 0) { +#if defined(EEPROM) + uint8_t num = g_eeGeneral.currModel + 1; +#else + // TODO + uint8_t num = 1; +#endif + strcpy_P(&filename[sizeof(LOGS_PATH)], STR_MODEL); + filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL)] = (char)((num / 10) + '0'); + filename[sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 1] = (char)((num % 10) + '0'); + len = sizeof(LOGS_PATH) + PSIZE(TR_MODEL) + 2; + } + + char * tmp = &filename[len]; + +#if defined(RTCLOCK) + tmp = strAppendDate(&filename[len]); +#endif + + strcpy_P(tmp, STR_LOGS_EXT); + + result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE); + if (result != FR_OK) { + return SDCARD_ERROR(result); + } + + if (f_size(&g_oLogFile) == 0) { + writeHeader(); + } + else { + result = f_lseek(&g_oLogFile, f_size(&g_oLogFile)); // append + if (result != FR_OK) { + return SDCARD_ERROR(result); + } + } + + return NULL; +} + +tmr10ms_t lastLogTime = 0; + +void closeLogs() +{ + if (f_close(&g_oLogFile) != FR_OK) { + // close failed, forget file + g_oLogFile.fs = 0; + } + lastLogTime = 0; +} + +#if !defined(CPUARM) +getvalue_t getConvertedTelemetryValue(getvalue_t val, uint8_t unit) +{ + convertUnit(val, unit); + return val; +} +#endif + +void writeHeader() +{ +#if defined(RTCLOCK) + f_puts("Date,Time,", &g_oLogFile); +#else + f_puts("Time,", &g_oLogFile); +#endif + +#if defined(FRSKY) +#if !defined(CPUARM) + f_puts("Buffer,RX,TX,A1,A2,", &g_oLogFile); +#if defined(FRSKY_HUB) + if (IS_USR_PROTO_FRSKY_HUB()) { + f_puts("GPS Date,GPS Time,Long,Lat,Course,GPS Speed(kts),GPS Alt,Baro Alt(", &g_oLogFile); + f_puts(TELEMETRY_BARO_ALT_UNIT, &g_oLogFile); + f_puts("),Vertical Speed,Air Speed(kts),Temp1,Temp2,RPM,Fuel," TELEMETRY_CELLS_LABEL "Current,Consumption,Vfas,AccelX,AccelY,AccelZ,", &g_oLogFile); + } +#endif +#if defined(WS_HOW_HIGH) + if (IS_USR_PROTO_WS_HOW_HIGH()) { + f_puts("WSHH Alt,", &g_oLogFile); + } +#endif +#endif + +#if defined(CPUARM) + char label[TELEM_LABEL_LEN+7]; + for (int i=0; i 0) { + tmr10ms_t tmr10ms = get_tmr10ms(); + if (lastLogTime == 0 || (tmr10ms_t)(tmr10ms - lastLogTime) >= (tmr10ms_t)logDelay*10) { + lastLogTime = tmr10ms; + + if (!g_oLogFile.fs) { + const pm_char * result = openLogs(); + if (result != NULL) { + if (result != error_displayed) { + error_displayed = result; + POPUP_WARNING(result); + } + return; + } + } + +#if defined(RTCLOCK) + { + static struct gtm utm; + static gtime_t lastRtcTime = 0; + if (g_rtcTime != lastRtcTime) { + lastRtcTime = g_rtcTime; + gettime(&utm); + } + f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+1900, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); + } +#else + f_printf(&g_oLogFile, "%d,", tmr10ms); +#endif + +#if defined(FRSKY) +#if !defined(CPUARM) + f_printf(&g_oLogFile, "%d,%d,%d,", frskyStreaming, RAW_FRSKY_MINMAX(frskyData.rssi[0]), RAW_FRSKY_MINMAX(frskyData.rssi[1])); + for (uint8_t i=0; i forget it -#ifdef CORRECT_NEGATIVE_SHIFTS - int16_t res=(int16_t)x<<1; - //int8_t sign=(uint8_t) x>>7; - int8_t sign=(x<0?1:0); - - x-=sign; - res+=(x>>1); - res+=sign; - res+=(x>>4); - res+=sign; - return res; -#else - return ((int16_t)x<<1)+(x>>1)+(x>>4); -#endif -} - -int16_t calc100to256(int8_t x) // return x*2.56 -{ - return calc100to256_16Bits(x); -} - -int16_t calc100toRESX_16Bits(int16_t x) // return x*10.24 -{ -#ifdef CORRECT_NEGATIVE_SHIFTS - int16_t res= ((int16_t)x*41)>>2; - int8_t sign=(x<0?1:0); - //int8_t sign=(uint8_t) x>>7; - x-=sign; - res-=(x>>6); - res-=sign; - return res; -#else - // return (int16_t)x*10 + x/4 - x/64; - return ((x*41)>>2) - (x>>6); -#endif -} - -int16_t calc100toRESX(int8_t x) // return x*10.24 -{ - return calc100toRESX_16Bits(x); -} - -// return x*1.024 -int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie -{ - // return x + x/32 - x/128 + x/512; - int16_t y = x>>5; - x+=y; - y=y>>2; - x-=y; - return x+(y>>2); -} - -int16_t calcRESXto1000(int16_t x) // return x/1.024 -{ -// *1000/1024 = x - x/32 + x/128 - return (x - (x>>5) + (x>>7)); -} - -int8_t calcRESXto100(int16_t x) -{ - return (x*25) >> 8; -} -#endif - -#if defined(HELI) || defined(FRSKY_HUB) || defined(CPUARM) -uint16_t isqrt32(uint32_t n) -{ - uint16_t c = 0x8000; - uint16_t g = 0x8000; - - for (;;) { - if ((uint32_t)g*g > n) - g ^= c; - c >>= 1; - if(c == 0) - return g; - g |= c; - } -} -#endif - -/* - Division by 10 and rounding or fixed point arithmetic values - - Examples: - value -> result - 105 -> 11 - 104 -> 10 - -205 -> -21 - -204 -> -20 -*/ - -#if defined(PCBTARANIS) -double gpsToDouble(bool neg, int16_t bp, int16_t ap) -{ - double result = ap; - result /= 10000; - result += (bp % 100); - result /= 60; - result += (bp / 100); - return neg?-result:result; -} -#endif - -#if defined(FRSKY_HUB) && !defined(CPUARM) -void extractLatitudeLongitude(uint32_t * latitude, uint32_t * longitude) -{ - div_t qr = div(frskyData.hub.gpsLatitude_bp, 100); - *latitude = ((uint32_t)(qr.quot) * 1000000) + (((uint32_t)(qr.rem) * 10000 + frskyData.hub.gpsLatitude_ap) * 5) / 3; - - qr = div(frskyData.hub.gpsLongitude_bp, 100); - *longitude = ((uint32_t)(qr.quot) * 1000000) + (((uint32_t)(qr.rem) * 10000 + frskyData.hub.gpsLongitude_ap) * 5) / 3; -} - -void getGpsPilotPosition() -{ - extractLatitudeLongitude(&frskyData.hub.pilotLatitude, &frskyData.hub.pilotLongitude); - uint32_t lat = frskyData.hub.pilotLatitude / 10000; - uint32_t angle2 = (lat*lat) / 10000; - uint32_t angle4 = angle2 * angle2; - frskyData.hub.distFromEarthAxis = 139*(((uint32_t)10000000-((angle2*(uint32_t)123370)/81)+(angle4/25))/12500); - // TRACE("frskyData.hub.distFromEarthAxis=%d", frskyData.hub.distFromEarthAxis); -} - -void getGpsDistance() -{ - uint32_t lat, lng; - - extractLatitudeLongitude(&lat, &lng); - - // printf("lat=%d (%d), long=%d (%d)\n", lat, abs(lat - frskyData.hub.pilotLatitude), lng, abs(lng - frskyData.hub.pilotLongitude)); - - uint32_t angle = (lat > frskyData.hub.pilotLatitude) ? lat - frskyData.hub.pilotLatitude : frskyData.hub.pilotLatitude - lat; - uint32_t dist = EARTH_RADIUS * angle / 1000000; - uint32_t result = dist*dist; - - angle = (lng > frskyData.hub.pilotLongitude) ? lng - frskyData.hub.pilotLongitude : frskyData.hub.pilotLongitude - lng; - dist = frskyData.hub.distFromEarthAxis * angle / 1000000; - result += dist*dist; - - dist = abs(TELEMETRY_BARO_ALT_AVAILABLE() ? TELEMETRY_RELATIVE_BARO_ALT_BP : TELEMETRY_RELATIVE_GPS_ALT_BP); - result += dist*dist; - - frskyData.hub.gpsDistance = isqrt32(result); - if (frskyData.hub.gpsDistance > frskyData.hub.maxGpsDistance) - frskyData.hub.maxGpsDistance = frskyData.hub.gpsDistance; -} -#endif +/* + * 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" + +#if !defined(CPUARM) +// #define CORRECT_NEGATIVE_SHIFTS +// open.20.fsguruh; shift right operations do the rounding different for negative values compared to positive values +// so all negative divisions round always further down, which give absolute values bigger compared to a usual division +// this is noticable on the display, because instead of -100.0 -99.9 is shown; While in praxis I doublt somebody will notice a +// difference this is more a mental thing. Maybe people are distracted, because the easy calculations are obviously wrong +// this define would correct this, but costs 34 bytes code for stock version + +// currently we set this to active always, because it might cause a fault about 1% compared positive and negative values +// is done now in makefile + +int16_t calc100to256_16Bits(int16_t x) // return x*2.56 +{ + // y = 2*x + x/2 +x/16-x/512-x/2048 + // 512 and 2048 are out of scope from int8 input --> forget it +#ifdef CORRECT_NEGATIVE_SHIFTS + int16_t res=(int16_t)x<<1; + //int8_t sign=(uint8_t) x>>7; + int8_t sign=(x<0?1:0); + + x-=sign; + res+=(x>>1); + res+=sign; + res+=(x>>4); + res+=sign; + return res; +#else + return ((int16_t)x<<1)+(x>>1)+(x>>4); +#endif +} + +int16_t calc100to256(int8_t x) // return x*2.56 +{ + return calc100to256_16Bits(x); +} + +int16_t calc100toRESX_16Bits(int16_t x) // return x*10.24 +{ +#ifdef CORRECT_NEGATIVE_SHIFTS + int16_t res= ((int16_t)x*41)>>2; + int8_t sign=(x<0?1:0); + //int8_t sign=(uint8_t) x>>7; + x-=sign; + res-=(x>>6); + res-=sign; + return res; +#else + // return (int16_t)x*10 + x/4 - x/64; + return ((x*41)>>2) - (x>>6); +#endif +} + +int16_t calc100toRESX(int8_t x) // return x*10.24 +{ + return calc100toRESX_16Bits(x); +} + +// return x*1.024 +int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie +{ + // return x + x/32 - x/128 + x/512; + int16_t y = x>>5; + x+=y; + y=y>>2; + x-=y; + return x+(y>>2); +} + +int16_t calcRESXto1000(int16_t x) // return x/1.024 +{ +// *1000/1024 = x - x/32 + x/128 + return (x - (x>>5) + (x>>7)); +} + +int8_t calcRESXto100(int16_t x) +{ + return (x*25) >> 8; +} +#endif + +#if defined(HELI) || defined(FRSKY_HUB) || defined(CPUARM) +uint16_t isqrt32(uint32_t n) +{ + uint16_t c = 0x8000; + uint16_t g = 0x8000; + + for (;;) { + if ((uint32_t)g*g > n) + g ^= c; + c >>= 1; + if(c == 0) + return g; + g |= c; + } +} +#endif + +/* + Division by 10 and rounding or fixed point arithmetic values + + Examples: + value -> result + 105 -> 11 + 104 -> 10 + -205 -> -21 + -204 -> -20 +*/ + +#if defined(PCBTARANIS) +double gpsToDouble(bool neg, int16_t bp, int16_t ap) +{ + double result = ap; + result /= 10000; + result += (bp % 100); + result /= 60; + result += (bp / 100); + return neg?-result:result; +} +#endif + +#if defined(FRSKY_HUB) && !defined(CPUARM) +void extractLatitudeLongitude(uint32_t * latitude, uint32_t * longitude) +{ + div_t qr = div(frskyData.hub.gpsLatitude_bp, 100); + *latitude = ((uint32_t)(qr.quot) * 1000000) + (((uint32_t)(qr.rem) * 10000 + frskyData.hub.gpsLatitude_ap) * 5) / 3; + + qr = div(frskyData.hub.gpsLongitude_bp, 100); + *longitude = ((uint32_t)(qr.quot) * 1000000) + (((uint32_t)(qr.rem) * 10000 + frskyData.hub.gpsLongitude_ap) * 5) / 3; +} + +void getGpsPilotPosition() +{ + extractLatitudeLongitude(&frskyData.hub.pilotLatitude, &frskyData.hub.pilotLongitude); + uint32_t lat = frskyData.hub.pilotLatitude / 10000; + uint32_t angle2 = (lat*lat) / 10000; + uint32_t angle4 = angle2 * angle2; + frskyData.hub.distFromEarthAxis = 139*(((uint32_t)10000000-((angle2*(uint32_t)123370)/81)+(angle4/25))/12500); + // TRACE("frskyData.hub.distFromEarthAxis=%d", frskyData.hub.distFromEarthAxis); +} + +void getGpsDistance() +{ + uint32_t lat, lng; + + extractLatitudeLongitude(&lat, &lng); + + // printf("lat=%d (%d), long=%d (%d)\n", lat, abs(lat - frskyData.hub.pilotLatitude), lng, abs(lng - frskyData.hub.pilotLongitude)); + + uint32_t angle = (lat > frskyData.hub.pilotLatitude) ? lat - frskyData.hub.pilotLatitude : frskyData.hub.pilotLatitude - lat; + uint32_t dist = EARTH_RADIUS * angle / 1000000; + uint32_t result = dist*dist; + + angle = (lng > frskyData.hub.pilotLongitude) ? lng - frskyData.hub.pilotLongitude : frskyData.hub.pilotLongitude - lng; + dist = frskyData.hub.distFromEarthAxis * angle / 1000000; + result += dist*dist; + + dist = abs(TELEMETRY_BARO_ALT_AVAILABLE() ? TELEMETRY_RELATIVE_BARO_ALT_BP : TELEMETRY_RELATIVE_GPS_ALT_BP); + result += dist*dist; + + frskyData.hub.gpsDistance = isqrt32(result); + if (frskyData.hub.gpsDistance > frskyData.hub.maxGpsDistance) + frskyData.hub.maxGpsDistance = frskyData.hub.gpsDistance; +} +#endif diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 8abf439e9..862de9bec 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1,2851 +1,2851 @@ -/* - * 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" -#include "timers.h" - -RadioData g_eeGeneral; -ModelData g_model; - -#if defined(SDCARD) -Clipboard clipboard; -#endif - -#if defined(PCBTARANIS) -uint8_t modelBitmap[MODEL_BITMAP_SIZE] __DMA; - -bool loadModelBitmap(char * name, uint8_t * bitmap) -{ - uint8_t len = zlen(name, LEN_BITMAP_NAME); - if (len > 0) { - char lfn[] = BITMAPS_PATH "/xxxxxxxxxx.bmp"; - strncpy(lfn+sizeof(BITMAPS_PATH), name, len); - strcpy(lfn+sizeof(BITMAPS_PATH)+len, BITMAPS_EXT); - if (lcdLoadBitmap(bitmap, lfn, MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT)) { - return true; - } - } - - // In all error cases, we set the default logo - memcpy(bitmap, logo_taranis, MODEL_BITMAP_SIZE); - return false; -} -#endif - -#if !defined(CPUARM) -uint8_t g_tmr1Latency_max; -uint8_t g_tmr1Latency_min; -uint16_t lastMixerDuration; -#endif - -uint8_t unexpectedShutdown = 0; - -/* AVR: mixer duration in 1/16ms */ -/* ARM: mixer duration in 0.5us */ -uint16_t maxMixerDuration; - -#if defined(AUDIO) && !defined(CPUARM) -audioQueue audio; -#endif - -uint8_t heartbeat; - -uint8_t stickMode; - -#if defined(OVERRIDE_CHANNEL_FUNCTION) -safetych_t safetyCh[NUM_CHNOUT]; -#endif - -union ReusableBuffer reusableBuffer; - -const pm_uint8_t bchout_ar[] PROGMEM = { - 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39, - 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78, - 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4, - 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 }; - -uint8_t channel_order(uint8_t x) -{ - return ( ((pgm_read_byte(bchout_ar + g_eeGeneral.templateSetup) >> (6-(x-1) * 2)) & 3 ) + 1 ); -} - -/* -mode1 rud ele thr ail -mode2 rud thr ele ail -mode3 ail ele thr rud -mode4 ail thr ele rud -*/ -const pm_uint8_t modn12x3[] PROGMEM = { - 0, 1, 2, 3, - 0, 2, 1, 3, - 3, 1, 2, 0, - 3, 2, 1, 0 }; - -volatile tmr10ms_t g_tmr10ms; - -#if defined(CPUARM) -volatile uint8_t rtc_count = 0; -uint32_t watchdogTimeout = 0; - -void watchdogSetTimeout(uint32_t timeout) -{ - watchdogTimeout = timeout; -} -#endif - -void per10ms() -{ - g_tmr10ms++; - -#if defined(CPUARM) - if (watchdogTimeout) { - watchdogTimeout -= 1; - wdt_reset(); // Retrigger hardware watchdog - } -#endif - -#if defined(GUI) - if (lightOffCounter) lightOffCounter--; - if (flashCounter) flashCounter--; - if (noHighlightCounter) noHighlightCounter--; -#endif - - if (trimsCheckTimer) trimsCheckTimer--; - if (ppmInputValidityTimer) ppmInputValidityTimer--; - -#if defined(CPUARM) - if (trimsDisplayTimer) - trimsDisplayTimer--; - else - trimsDisplayMask = 0; -#endif - -#if defined(RTCLOCK) - /* Update global Date/Time every 100 per10ms cycles */ - if (++g_ms100 == 100) { - g_rtcTime++; // inc global unix timestamp one second -#if defined(COPROCESSOR) - if (g_rtcTime < 60 || rtc_count<5) { - rtcInit(); - rtc_count++; - } - else { - coprocReadData(true); - } -#endif - g_ms100 = 0; - } -#endif - - readKeysAndTrims(); - -#if defined(ROTARY_ENCODER_NAVIGATION) - if (IS_RE_NAVIGATION_ENABLE()) { - static rotenc_t rePreviousValue; - rotenc_t reNewValue = (g_rotenc[NAVIGATION_RE_IDX()] / ROTARY_ENCODER_GRANULARITY); - int8_t scrollRE = reNewValue - rePreviousValue; - if (scrollRE) { - rePreviousValue = reNewValue; - putEvent(scrollRE < 0 ? EVT_ROTARY_LEFT : EVT_ROTARY_RIGHT); - } - uint8_t evt = s_evt; - if (EVT_KEY_MASK(evt) == BTN_REa + NAVIGATION_RE_IDX()) { - if (IS_KEY_BREAK(evt)) { - putEvent(EVT_ROTARY_BREAK); - } - else if (IS_KEY_LONG(evt)) { - putEvent(EVT_ROTARY_LONG); - } - } - } -#endif - -#if defined(FRSKY) || defined(JETI) - if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol[0])) - telemetryInterrupt10ms(); -#endif - - // These moved here from evalFlightModeMixes() to improve beep trigger reliability. -#if defined(PWM_BACKLIGHT) - if ((g_tmr10ms&0x03) == 0x00) - backlightFade(); // increment or decrement brightness until target brightness is reached -#endif - -#if !defined(AUDIO) - if (mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) AUDIO_MIX_WARNING(1); - if (mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) AUDIO_MIX_WARNING(2); - if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3); -#endif - -#if defined(SDCARD) - sdPoll10ms(); -#endif - - heartbeat |= HEART_TIMER_10MS; -} - -FlightModeData *flightModeAddress(uint8_t idx) -{ - return &g_model.flightModeData[idx]; -} - -ExpoData *expoAddress(uint8_t idx ) -{ - return &g_model.expoData[idx]; -} - -MixData *mixAddress(uint8_t idx) -{ - return &g_model.mixData[idx]; -} - -LimitData *limitAddress(uint8_t idx) -{ - return &g_model.limitData[idx]; -} - -#if defined(CPUM64) -void memclear(void *ptr, uint8_t size) -{ - memset(ptr, 0, size); -} -#endif - -void memswap(void * a, void * b, uint8_t size) -{ - uint8_t * x = (uint8_t *)a; - uint8_t * y = (uint8_t *)b; - uint8_t temp ; - - while (size--) { - temp = *x; - *x++ = *y; - *y++ = temp; - } -} - -void generalDefault() -{ - memclear(&g_eeGeneral, sizeof(g_eeGeneral)); - g_eeGeneral.version = EEPROM_VER; - g_eeGeneral.variant = EEPROM_VARIANT; - -#if !defined(PCBHORUS) - g_eeGeneral.contrast = 25; -#endif - -#if defined(PCBFLAMENCO) - g_eeGeneral.vBatWarn = 33; - g_eeGeneral.vBatMin = -60; // 0 is 9.0V - g_eeGeneral.vBatMax = -78; // 0 is 12.0V -#elif defined(PCBHORUS) - g_eeGeneral.potsConfig = 0x05; // S1 and S2 = pots with detent - g_eeGeneral.slidersConfig = 0x03; // LS and RS = sliders with detent -#elif defined(PCBTARANIS) - g_eeGeneral.potsConfig = 0x05; // S1 and S2 = pots with detent - g_eeGeneral.slidersConfig = 0x03; // LS and RS = sliders with detent -#endif - -#if defined(PCBTARANIS) || defined(PCBHORUS) - g_eeGeneral.switchConfig = 0x00007bff; // 6x3POS, 1x2POS, 1xTOGGLE -#endif - -#if defined(PCBTARANIS) && defined(REV9E) - // NI-MH 9.6V - g_eeGeneral.vBatWarn = 87; - g_eeGeneral.vBatMin = -5; - g_eeGeneral.vBatMax = -5; -#elif defined(PCBTARANIS) - // NI-MH 7.2V - g_eeGeneral.vBatWarn = 65; - g_eeGeneral.vBatMin = -30; - g_eeGeneral.vBatMax = -40; -#else - g_eeGeneral.vBatWarn = 90; -#endif - -#if defined(DEFAULT_MODE) - g_eeGeneral.stickMode = DEFAULT_MODE-1; -#endif - -#if defined(PCBFLAMENCO) - g_eeGeneral.templateSetup = 21; /* AETR */ -#elif defined(PCBTARANIS) - g_eeGeneral.templateSetup = 17; /* TAER */ -#endif - -#if defined(PCBFLAMENCO) - g_eeGeneral.inactivityTimer = 50; -#elif !defined(CPUM64) - g_eeGeneral.backlightMode = e_backlight_mode_all; - g_eeGeneral.lightAutoOff = 2; - g_eeGeneral.inactivityTimer = 10; -#endif - -#if defined(CPUARM) - g_eeGeneral.wavVolume = 2; - g_eeGeneral.backgroundVolume = 1; -#endif - -#if defined(CPUARM) - for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; - expo->curve.type = CURVE_REF_EXPO; - expo->chn = i; - expo->weight = 100; - expo->mode = 3; // TODO constant -#if defined(TRANSLATIONS_CZ) - for (int c=0; c<4; c++) { - g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+4*(stick_index-1)+c]); - } - g_model.inputNames[i][4] = '\0'; -#else - for (int c=0; c<3; c++) { - g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[2+4*stick_index+c]); - } - g_model.inputNames[i][3] = '\0'; -#endif - } - storageDirty(EE_MODEL); -} -#endif - -#if defined(TEMPLATES) -inline void applyDefaultTemplate() -{ - applyTemplate(TMPL_SIMPLE_4CH); // calls storageDirty internally -} -#else -void applyDefaultTemplate() -{ -#if defined(VIRTUALINPUTS) - defaultInputs(); // calls storageDirty internally -#else - storageDirty(EE_MODEL); -#endif - - for (int i=0; idestCh = i; - mix->weight = 100; -#if defined(VIRTUALINPUTS) - mix->srcRaw = i+1; -#else - mix->srcRaw = MIXSRC_Rud - 1 + channel_order(i+1); -#endif - } -} -#endif - -#if defined(CPUARM) && defined(EEPROM) -void checkModelIdUnique(uint8_t index, uint8_t module) -{ - uint8_t modelId = g_model.header.modelId[module]; - if (modelId != 0) { - for (uint8_t i=0; icreate(&g_model.screenData[0].layoutData); - strcpy(g_model.screenData[0].layoutName, "Layout2P1"); - extern const WidgetFactory * defaultWidget; - customScreens[0]->createWidget(0, defaultWidget); -#endif -} - -#if defined(VIRTUALINPUTS) -bool isInputRecursive(int index) -{ - ExpoData * line = expoAddress(0); - for (int i=0; ichn > index) - break; - else if (line->chn < index) - continue; - else if (line->srcRaw >= MIXSRC_FIRST_LOGICAL_SWITCH) - return true; - } - return false; -} -#endif - -#if defined(AUTOSOURCE) -int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) -{ - int8_t result = 0; - static tmr10ms_t s_move_last_time = 0; - -#if defined(VIRTUALINPUTS) - static int16_t inputsStates[MAX_INPUTS]; - if (min <= MIXSRC_FIRST_INPUT) { - for (uint8_t i=0; i 512) { - if (!isInputRecursive(i)) { - result = MIXSRC_FIRST_INPUT+i; - break; - } - } - } - } -#endif - - static int16_t sourcesStates[NUM_STICKS+NUM_POTS]; - if (result == 0) { - for (uint8_t i=0; i 512) { - result = MIXSRC_Rud+i; - break; - } - } - } - - bool recent = ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10); - if (recent) { - result = 0; - } - - if (result || recent) { -#if defined(VIRTUALINPUTS) - memcpy(inputsStates, anas, sizeof(inputsStates)); -#endif - memcpy(sourcesStates, calibratedStick, sizeof(sourcesStates)); - } - - s_move_last_time = get_tmr10ms(); - return result; -} -#endif - -#if defined(FLIGHT_MODES) -uint8_t getFlightMode() -{ - for (uint8_t i=1; iswtch && getSwitch(phase->swtch)) { - return i; - } - } - return 0; -} -#endif - -trim_t getRawTrimValue(uint8_t phase, uint8_t idx) -{ - FlightModeData *p = flightModeAddress(phase); -#if defined(PCBSTD) - return (((trim_t)p->trim[idx]) << 2) + ((p->trim_ext >> (2*idx)) & 0x03); -#else - return p->trim[idx]; -#endif -} - -int getTrimValue(uint8_t phase, uint8_t idx) -{ -#if defined(VIRTUALINPUTS) - int result = 0; - for (uint8_t i=0; i> 1; - if (p == phase || phase == 0) { - return result + v.value; - } - else { - phase = p; - if (v.mode % 2 != 0) { - result += v.value; - } - } - } - } - return 0; -#else - return getRawTrimValue(getTrimFlightPhase(phase, idx), idx); -#endif -} - -#if defined(VIRTUALINPUTS) -bool setTrimValue(uint8_t phase, uint8_t idx, int trim) -{ - for (uint8_t i=0; itrim[idx]; - if (v.mode == TRIM_MODE_NONE) - return false; - unsigned int p = v.mode >> 1; - if (p == phase || phase == 0) { - v.value = trim; - break; - } - else if (v.mode % 2 == 0) { - phase = p; - } - else { - v.value = limit(TRIM_EXTENDED_MIN, trim - getTrimValue(p, idx), TRIM_EXTENDED_MAX); - break; - } - } - storageDirty(EE_MODEL); - return true; -} -#else -void setTrimValue(uint8_t phase, uint8_t idx, int trim) -{ -#if defined(PCBSTD) - FlightModeData *p = flightModeAddress(phase); - p->trim[idx] = (int8_t)(trim >> 2); - idx <<= 1; - p->trim_ext = (p->trim_ext & ~(0x03 << idx)) + (((trim & 0x03) << idx)); -#else - FlightModeData *p = flightModeAddress(phase); - p->trim[idx] = trim; -#endif - storageDirty(EE_MODEL); -} -#endif - -#if !defined(VIRTUALINPUTS) -uint8_t getTrimFlightPhase(uint8_t phase, uint8_t idx) -{ - for (uint8_t i=0; i= phase) result++; - phase = result; - } - return 0; -} -#endif - -#if defined(ROTARY_ENCODERS) -uint8_t getRotaryEncoderFlightPhase(uint8_t idx) -{ - uint8_t phase = mixerCurrentFlightMode; - for (uint8_t i=0; irotaryEncoders[idx]; - if (value <= ROTARY_ENCODER_MAX) return phase; - uint8_t result = value-ROTARY_ENCODER_MAX-1; - if (result >= phase) result++; - phase = result; - } - return 0; -} - -int16_t getRotaryEncoder(uint8_t idx) -{ - return flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]; -} - -void incRotaryEncoder(uint8_t idx, int8_t inc) -{ - g_rotenc[idx] += inc; - int16_t *value = &(flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]); - *value = limit((int16_t)-1024, (int16_t)(*value + (inc * 8)), (int16_t)+1024); - storageDirty(EE_MODEL); -} -#endif - -#if defined(GVARS) - -#if defined(PCBSTD) - #define SET_GVAR_VALUE(idx, phase, value) \ - (GVAR_VALUE(idx, phase) = value, storageDirty(EE_MODEL)) -#else - #define SET_GVAR_VALUE(idx, phase, value) \ - GVAR_VALUE(idx, phase) = value; \ - storageDirty(EE_MODEL); \ - if (g_model.gvars[idx].popup) { \ - gvarLastChanged = idx; \ - gvarDisplayTimer = GVAR_DISPLAY_TIME; \ - } -#endif - -#if defined(PCBSTD) -int16_t getGVarFieldValue(int16_t x, int16_t min, int16_t max) -{ - if (GV_IS_GV_VALUE(x, min, max)) { - int8_t idx = GV_INDEX_CALCULATION(x, max); - int8_t mul = 1; - - if (idx < 0) { - idx = -1-idx; - mul = -1; - } - - x = GVAR_VALUE(idx, -1) * mul; - } - - return limit(min, x, max); -} - -void setGVarValue(uint8_t idx, int8_t value) -{ - if (GVAR_VALUE(idx, -1) != value) { - SET_GVAR_VALUE(idx, -1, value); - } -} -#else -uint8_t gvarDisplayTimer = 0; -uint8_t gvarLastChanged = 0; - -uint8_t getGVarFlightMode(uint8_t fm, uint8_t gv) // TODO change params order to be consistent! -{ - for (uint8_t i=0; i= fm) result++; - fm = result; - } - return 0; -} - -int16_t getGVarValue(int8_t gv, int8_t fm) -{ - int8_t mul = 1; - if (gv < 0) { - gv = -1-gv; - mul = -1; - } - return GVAR_VALUE(gv, getGVarFlightMode(fm, gv)) * mul; -} - -int32_t getGVarValuePrec1(int8_t gv, int8_t fm) -{ - int8_t mul; - uint8_t prec = g_model.gvars[gv].prec; - if (prec == 0) - mul = 10; - else - mul = 1; - - if (gv < 0) { - gv = -1-gv; - mul = -mul; - } - return GVAR_VALUE(gv, getGVarFlightMode(fm, gv)) * mul; -} - -void setGVarValue(uint8_t gv, int16_t value, int8_t fm) -{ - fm = getGVarFlightMode(fm, gv); - if (GVAR_VALUE(gv, fm) != value) { - SET_GVAR_VALUE(gv, fm, value); - } -} - -int16_t getGVarFieldValue(int16_t val, int16_t min, int16_t max, int8_t fm) -{ - if (GV_IS_GV_VALUE(val, min, max)) { - int8_t gv = GV_INDEX_CALCULATION(val, max); - val = getGVarValue(gv, fm); - } - return limit(min, val, max); -} - -int32_t getGVarFieldValuePrec1(int16_t val, int16_t min, int16_t max, int8_t fm) -{ - if (GV_IS_GV_VALUE(val, min, max)) { - int8_t gv = GV_INDEX_CALCULATION(val, max); - val = getGVarValuePrec1(gv, fm); - } - else { - val *= 10; - } - return limit(min*10, val, max*10); -} -#endif - -#endif - -#if defined(CPUARM) -getvalue_t convert16bitsTelemValue(source_t channel, ls_telemetry_value_t value) -{ - return value; -} - -getvalue_t convert8bitsTelemValue(source_t channel, ls_telemetry_value_t value) -{ - return value; -} - -#if defined(FRSKY) -ls_telemetry_value_t minTelemValue(source_t channel) -{ - return 0; -} - -ls_telemetry_value_t maxTelemValue(source_t channel) -{ - return 30000; -} -#endif - -ls_telemetry_value_t max8bitsTelemValue(source_t channel) -{ - return 30000; -} - -#elif defined(FRSKY) - -/* -ls_telemetry_value_t minTelemValue(uint8_t channel) -{ - switch (channel) { - case TELEM_TIMER1: - case TELEM_TIMER2: - return -3600; - case TELEM_ALT: - case TELEM_MIN_ALT: - case TELEM_MAX_ALT: - case TELEM_GPSALT: - return -500; - case TELEM_T1: - case TELEM_MAX_T1: - case TELEM_T2: - case TELEM_MAX_T2: - return -30; - case TELEM_ACCx: - case TELEM_ACCy: - case TELEM_ACCz: - return -1000; - case TELEM_VSPEED: - return -3000; - default: - return 0; - } -} -*/ -ls_telemetry_value_t maxTelemValue(uint8_t channel) -{ - switch (channel) { - case TELEM_FUEL: - case TELEM_RSSI_TX: - case TELEM_RSSI_RX: - return 100; - case TELEM_HDG: - return 180; - default: - return 255; - } -} -#endif - -#if !defined(CPUARM) -getvalue_t convert8bitsTelemValue(uint8_t channel, ls_telemetry_value_t value) -{ - getvalue_t result; - switch (channel) { - case TELEM_TIMER1: - case TELEM_TIMER2: - result = value * 5; - break; -#if defined(FRSKY) - case TELEM_ALT: - case TELEM_GPSALT: - case TELEM_MAX_ALT: - case TELEM_MIN_ALT: - result = value * 8 - 500; - break; - case TELEM_RPM: - case TELEM_MAX_RPM: - result = value * 50; - break; - case TELEM_T1: - case TELEM_T2: - case TELEM_MAX_T1: - case TELEM_MAX_T2: - result = (getvalue_t)value - 30; - break; - case TELEM_CELL: - case TELEM_HDG: - case TELEM_SPEED: - case TELEM_MAX_SPEED: - result = value * 2; - break; - case TELEM_ASPEED: - case TELEM_MAX_ASPEED: - result = value * 20; - break; - case TELEM_DIST: - case TELEM_MAX_DIST: - result = value * 8; - break; - case TELEM_CURRENT: - case TELEM_POWER: - case TELEM_MAX_CURRENT: - case TELEM_MAX_POWER: - result = value * 5; - break; - case TELEM_CONSUMPTION: - result = value * 100; - break; - case TELEM_VSPEED: - result = ((getvalue_t)value - 125) * 10; - break; -#endif - default: - result = value; - break; - } - return result; -} -#endif - -#if defined(FRSKY)&& !defined(CPUARM) -FORCEINLINE void convertUnit(getvalue_t & val, uint8_t & unit) -{ - if (IS_IMPERIAL_ENABLE()) { - if (unit == UNIT_TEMPERATURE) { - val += 18; - val *= 115; - val >>= 6; - } - if (unit == UNIT_DIST) { - // m to ft *105/32 - val = val * 3 + (val >> 2) + (val >> 5); - } - if (unit == UNIT_FEET) { - unit = UNIT_DIST; - } - if (unit == UNIT_KTS) { - // kts to mph - unit = UNIT_SPEED; - val = (val * 23) / 20; - } - } - else { - if (unit == UNIT_KTS) { - // kts to km/h - unit = UNIT_SPEED; -#if defined(CPUARM) - val = (val * 1852) / 1000; -#else - val = (val * 50) / 27; -#endif - } - } - - if (unit == UNIT_HDG) { - unit = UNIT_TEMPERATURE; - } -} -#endif - -#define INAC_STICKS_SHIFT 6 -#define INAC_SWITCHES_SHIFT 8 -bool inputsMoved() -{ - uint8_t sum = 0; - for (uint8_t i=0; i> INAC_STICKS_SHIFT; - for (uint8_t i=0; i> INAC_SWITCHES_SHIFT; - - if (abs((int8_t)(sum-inactivity.sum)) > 1) { - inactivity.sum = sum; - return true; - } - else { - return false; - } -} - -void checkBacklight() -{ - static uint8_t tmr10ms ; - -#if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION) - rotencPoll(); -#endif - - uint8_t x = g_blinkTmr10ms; - if (tmr10ms != x) { - tmr10ms = x; - if (inputsMoved()) { - inactivity.counter = 0; - if (g_eeGeneral.backlightMode & e_backlight_mode_sticks) - backlightOn(); - } - - bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || lightOffCounter || isFunctionActive(FUNCTION_BACKLIGHT)); - if (flashCounter) backlightOn = !backlightOn; - if (backlightOn) - BACKLIGHT_ON(); - else - BACKLIGHT_OFF(); - -#if defined(PCBSTD) && defined(VOICE) && !defined(SIMU) - Voice.voice_process() ; -#endif - } -} - -#if defined(PCBFLAMENCO) -void checkUsbChip() -{ - uint8_t reg = i2cReadBQ24195(0x00); - if (reg & 0x80) { - i2cWriteBQ24195(0x00, reg & 0x7F); - } -} -#endif - -void doLoopCommonActions() -{ - checkBacklight(); - -#if defined(PCBFLAMENCO) - checkUsbChip(); -#endif -} - -void backlightOn() -{ - lightOffCounter = ((uint16_t)g_eeGeneral.lightAutoOff*250) << 1; -} - -#if MENUS_LOCK == 1 -bool readonly = true; -bool readonlyUnlocked() -{ - if (readonly) { - POPUP_WARNING(STR_MODS_FORBIDDEN); - return false; - } - else { - return true; - } -} -#endif - -#if defined(SPLASH) -void doSplash() -{ -#if defined(PWR_BUTTON_DELAY) - bool refresh = false; -#endif - - if (SPLASH_NEEDED()) { -#if !defined(PCBTARANIS) && !defined(PCBHORUS) - drawSplash(); -#endif - -#if !defined(CPUARM) - AUDIO_TADA(); -#endif - -#if defined(PCBSTD) - lcdSetContrast(); -#elif !defined(PCBHORUS) && !defined(PCBTARANIS) - tmr10ms_t curTime = get_tmr10ms() + 10; - uint8_t contrast = 10; - lcdSetRefVolt(contrast); -#endif - - getADC(); // init ADC array - - inputsMoved(); - - tmr10ms_t tgtime = get_tmr10ms() + SPLASH_TIMEOUT; - - while (tgtime > get_tmr10ms()) { -#if defined(SIMU) - SIMU_SLEEP(1); -#elif defined(CPUARM) - CoTickDelay(1); -#endif - - getADC(); - -#if defined(FSPLASH) - // Splash is forced, we can't skip it - if (!(g_eeGeneral.splashMode & 0x04)) { -#endif - - if (keyDown() || inputsMoved()) return; - -#if defined(FSPLASH) - } -#endif - -#if defined(PWR_BUTTON_DELAY) - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - break; - } - else if (pwr_check == e_power_press) { - refresh = true; - } - else if (pwr_check == e_power_on && refresh) { - drawSplash(); - refresh = false; - } -#else - if (pwrCheck() == e_power_off) { - return; - } -#endif - -#if !defined(PCBHORUS) && !defined(PCBTARANIS) && !defined(PCBSTD) - if (curTime < get_tmr10ms()) { - curTime += 10; - if (contrast < g_eeGeneral.contrast) { - contrast += 1; - lcdSetRefVolt(contrast); - } - } -#endif - - doLoopCommonActions(); - } - } -} -#else -#define Splash() -#define doSplash() -#endif - -#if defined(PCBTARANIS) -void checkFailsafe() -{ - for (int i=0; iNUM_POTS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1; - // throttle channel is either the stick according stick mode (already handled in evalInputs) - // or P1 to P3; - // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS) we assume the throttle stick is the input - // no other information available at the moment, and good enough to my option (otherwise too much exceptions...) - -#if defined(MODULE_ALWAYS_SEND_PULSES) - int16_t v = calibratedStick[thrchn]; - if (v<=THRCHK_DEADBAND-1024 || g_model.disableThrottleWarning || pwrCheck()==e_power_off || keyDown()) { - startupWarningState = STARTUP_WARNING_THROTTLE+1; - } - else { - calibratedStick[thrchn] = -1024; -#if !defined(VIRTUALINPUTS) - if (thrchn < NUM_STICKS) { - rawAnas[thrchn] = anas[thrchn] = calibratedStick[thrchn]; - } -#endif - MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); - } -#else - if (g_model.disableThrottleWarning) { - return; - } - - getADC(); - - evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job - - int16_t v = calibratedStick[thrchn]; - if (v <= THRCHK_DEADBAND-1024) { - return; // prevent warning if throttle input OK - } - - // first - display warning; also deletes inputs if any have been before - LED_ERROR_BEGIN(); - MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); - -#if defined(PWR_BUTTON_DELAY) - bool refresh = false; -#endif - - while (1) { - - SIMU_SLEEP(1); - - getADC(); - - evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job - - v = calibratedStick[thrchn]; - -#if defined(PWR_BUTTON_DELAY) - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - break; - } - else if (pwr_check == e_power_press) { - refresh = true; - } - else if (pwr_check == e_power_on && refresh) { - MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_NONE); - refresh = false; - } -#else - if (pwrCheck() == e_power_off) { - break; - } -#endif - - if (keyDown() || v <= THRCHK_DEADBAND-1024) { - break; - } - - doLoopCommonActions(); - - wdt_reset(); - } -#endif - - LED_ERROR_END(); -} - -void checkAlarm() // added by Gohst -{ - if (g_eeGeneral.disableAlarmWarning) { - return; - } - - if (IS_SOUND_OFF()) { - ALERT(STR_ALARMSWARN, STR_ALARMSDISABLED, AU_ERROR); - } -} - -void alert(const pm_char * t, const pm_char * s MESSAGE_SOUND_ARG) -{ - LED_ERROR_BEGIN(); - - MESSAGE(t, s, STR_PRESSANYKEY, sound); - -#if defined(PWR_BUTTON_DELAY) - bool refresh = false; -#endif - - while(1) { - SIMU_SLEEP(1); - - if (keyDown()) break; // wait for key release - - doLoopCommonActions(); - - wdt_reset(); - -#if defined(PWR_BUTTON_DELAY) - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - boardOff(); - } - else if (pwr_check == e_power_press) { - refresh = true; - } - else if (pwr_check == e_power_on && refresh) { - MESSAGE(t, s, STR_PRESSANYKEY, AU_NONE); - refresh = false; - } -#else - if (pwrCheck() == e_power_off) { - boardOff(); // turn power off now - } -#endif - } - - LED_ERROR_END(); -} - -#if defined(GVARS) -int8_t trimGvar[NUM_STICKS] = { -1, -1, -1, -1 }; -#endif - -#if defined(CPUARM) -void checkTrims() -{ - evt_t event = getEvent(true); - if (event && !IS_KEY_BREAK(event)) { - int8_t k = EVT_KEY_MASK(event) - TRM_BASE; -#else -uint8_t checkTrim(uint8_t event) -{ - int8_t k = EVT_KEY_MASK(event) - TRM_BASE; - if (k>=0 && k<8 && !IS_KEY_BREAK(event)) { -#endif - // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP - uint8_t idx = CONVERT_MODE((uint8_t)k/2); - uint8_t phase; - int before; - bool thro; - -#if defined(CPUARM) - trimsDisplayTimer = 200; // 2 seconds - trimsDisplayMask |= (1<=mark && beforemark))) { - after = mark; - beepTrim = (mark == 0 ? 1 : 2); - } - } - - if ((beforeTRIM_MAX) || (before>after && after TRIM_EXTENDED_MAX) { - after = TRIM_EXTENDED_MAX; - } - -#if defined(GVARS) - if (TRIM_REUSED(idx)) { - SET_GVAR_VALUE(trimGvar[idx], phase, after); - } - else -#endif - { -#if defined(VIRTUALINPUTS) - if (!setTrimValue(phase, idx, after)) { - // we don't play a beep, so we exit now the function - return; - } -#else - setTrimValue(phase, idx, after); -#endif - } - -#if defined(AUDIO) - // toneFreq higher/lower according to trim position - // limit the frequency, range -125 to 125 = toneFreq: 19 to 101 - if (after > TRIM_MAX) - after = TRIM_MAX; - if (after < TRIM_MIN) - after = TRIM_MIN; -#if defined(CPUARM) - after <<= 3; - after += 120*16; -#else - after >>= 2; - after += 60; -#endif -#endif - - if (beepTrim) { - if (beepTrim == 1) { - AUDIO_TRIM_MIDDLE(after); - pauseEvents(event); - } - else { - AUDIO_TRIM_END(after); - killEvents(event); - } - } - else { - AUDIO_TRIM(event, after); - } -#if !defined(CPUARM) - return 0; -#endif - } -#if !defined(CPUARM) - return event; -#endif -} - -#if !defined(SIMU) -uint16_t s_anaFilt[NUMBER_ANALOG]; -#endif - -#if defined(SIMU) -uint16_t BandGap = 225; -#elif defined(CPUM2560) -// #define STARTADCONV (ADCSRA = (1< rawJitter[NUMBER_ANALOG]; - JitterMeter avgJitter[NUMBER_ANALOG]; - tmr10ms_t jitterResetTime = 0; - #if defined(PCBTARANIS) - #define JITTER_MEASURE_ACTIVE() (menuHandlers[menuLevel] == menuGeneralDiagAna) - #elif defined(CLI) - #define JITTER_MEASURE_ACTIVE() (1) - #endif -#endif - - -#if defined(VIRTUALINPUTS) && defined(JITTER_FILTER) - #define JITTER_FILTER_STRENGTH 4 // tune this value, bigger value - more filtering (range: 1-5) (see explanation below) - #define ANALOG_SCALE 1 // tune this value, bigger value - more filtering (range: 0-3) (see explanation below) - - #define JITTER_ALPHA (1< 32) - #error "JITTER_FILTER_STRENGTH and ANALOG_SCALE are too big, their summ should be <= 5 !!!" - #endif -#else - #define ANALOG_SCALE 0 - #define JITTER_ALPHA 1 - #define ANALOG_MULTIPLIER 1 - #define ANA_FILT(chan) (s_anaFilt[chan]) -#endif - - -#if !defined(SIMU) -uint16_t anaIn(uint8_t chan) -{ -#if defined(VIRTUALINPUTS) - return ANA_FILT(chan); -#elif defined(PCBSKY9X) && !defined(REVA) - static const uint8_t crossAna[]={1,5,7,0,4,6,2,3}; - if (chan == TX_CURRENT) { - return Current_analogue ; - } - volatile uint16_t *p = &s_anaFilt[pgm_read_byte(crossAna+chan)]; - return *p; -#else -#if defined(TELEMETRY_MOD_14051) || defined(TELEMETRY_MOD_14051_SWAPPED) - static const pm_char crossAna[] PROGMEM = {3,1,2,0,4,5,6,0/* shouldn't be used */,TX_VOLTAGE}; -#else - static const pm_char crossAna[] PROGMEM = {3,1,2,0,4,5,6,7}; -#endif -#if defined(FRSKY_STICKS) - volatile uint16_t temp = s_anaFilt[pgm_read_byte(crossAna+chan)]; // volatile saves here 40 bytes; maybe removed for newer AVR when available - if (chan < NUM_STICKS && (g_eeGeneral.stickReverse & (1 << chan))) { - temp = 2048 - temp; - } - return temp; -#else - volatile uint16_t *p = &s_anaFilt[pgm_read_byte(crossAna+chan)]; - return *p; -#endif -#endif -} - -#if defined(CPUARM) -void getADC() -{ - uint16_t temp[NUMBER_ANALOG] = { 0 }; - -#if defined(JITTER_MEASURE) - if (JITTER_MEASURE_ACTIVE() && jitterResetTime < get_tmr10ms()) { - // reset jitter measurement every second - for (uint32_t x=0; x> (3 - ANALOG_SCALE); - -#if defined(VIRTUALINPUTS) && defined(JITTER_FILTER) - // Jitter filter: - // * pass trough any big change directly - // * for small change use Modified moving average (MMA) filter - // - // Explanation: - // - // Normal MMA filter has this formula: - // = ((ALPHA-1)* + )/ALPHA - // - // If calculation is done this way with integer arithmetics, then any small change in - // input signal is lost. One way to combat that, is to rearrange the formula somewhat, - // to store a more precise (larger) number between iterations. The basic idea is to - // store undivided value between iterations. Therefore an new variable is - // used. The new formula becomes: - // = - /ALPHA + - // = /ALPHA (use only when out is needed) - // - // The above formula with a maximum allowed ALPHA value (we are limited by - // the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still had - // some jitter (a value of 1 was observed). The jitter might be bigger on other - // radios. - // - // So another idea is to use larger input values for filtering. So instead of using - // input in a range from 0 to 2047, we use twice larger number (temp[x] is divided less) - // - // This also means that ALPHA must be lowered (remember 16 bit limit), but test results - // have proved that this kind of filtering gives better results. So the recommended values - // for filter are: - // JITTER_FILTER_STRENGTH 4 - // ANALOG_SCALE 1 - // - // Variables mapping: - // * = v - // * = s_anaFilt[x] - uint16_t previous = s_anaFilt[x] / JITTER_ALPHA; - uint16_t diff = (v > previous) ? (v - previous) : (previous - v); - if (diff < 10 * ANALOG_MULTIPLIER) { - // apply jitter filter - s_anaFilt[x] = (s_anaFilt[x] - previous) + v; - } - else -#endif - { - //use unfiltered value - s_anaFilt[x] = v * JITTER_ALPHA; - } - -#if defined(JITTER_MEASURE) - if (JITTER_MEASURE_ACTIVE()) { - avgJitter[x].measure(ANA_FILT(x)); - } -#endif - -#if defined(VIRTUALINPUTS) - #define ANAFILT_MAX (2 * RESX * JITTER_ALPHA * ANALOG_MULTIPLIER - 1) - StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x]; - if (IS_POT_MULTIPOS(x) && IS_MULTIPOS_CALIBRATED(calib)) { - // TODO: consider adding another low pass filter to eliminate multipos switching glitches - uint8_t vShifted = ANA_FILT(x) >> 4; - s_anaFilt[x] = ANAFILT_MAX; - for (uint32_t i=0; icount; i++) { - if (vShifted < calib->steps[i]) { - s_anaFilt[x] = (i * ANAFILT_MAX) / calib->count; - break; - } - } - } -#endif // defined(VIRTUALINPUTS) - } -} -#endif - -#endif // SIMU - -uint8_t g_vbat100mV = 0; -uint16_t lightOffCounter; -uint8_t flashCounter = 0; - -uint16_t sessionTimer; -uint16_t s_timeCumThr; // THR in 1/16 sec -uint16_t s_timeCum16ThrP; // THR% in 1/16 sec - -uint8_t trimsCheckTimer = 0; - -#if defined(CPUARM) -uint8_t trimsDisplayTimer = 0; -uint8_t trimsDisplayMask = 0; -#endif - -void flightReset() -{ - // we don't reset the whole audio here (the tada.wav would be cut, if a prompt is queued before FlightReset, it should be played) - // TODO check if the vario / background music are stopped correctly if switching to a model which doesn't have these functions enabled - - if (!IS_MANUAL_RESET_TIMER(0)) { - timerReset(0); - } - -#if TIMERS > 1 - if (!IS_MANUAL_RESET_TIMER(1)) { - timerReset(1); - } -#endif - -#if TIMERS > 2 - if (!IS_MANUAL_RESET_TIMER(2)) { - timerReset(2); - } -#endif - -#if defined(FRSKY) - telemetryReset(); -#endif - - s_mixer_first_run_done = false; - - START_SILENCE_PERIOD(); - - RESET_THR_TRACE(); -} - -#if defined(THRTRACE) -uint8_t s_traceBuf[MAXTRACE]; -uint16_t s_traceWr; -uint8_t s_cnt_10s; -uint16_t s_cnt_samples_thr_10s; -uint16_t s_sum_samples_thr_10s; -#endif - -void evalTrims() -{ - uint8_t phase = mixerCurrentFlightMode; - for (uint8_t i=0; i throttle trim if applicable - int16_t trim = getTrimValue(phase, i); -#if !defined(VIRTUALINPUTS) - if (i==THR_STICK && g_model.thrTrim) { - int16_t trimMin = g_model.extendedTrims ? TRIM_EXTENDED_MIN : TRIM_MIN; - trim = (((g_model.throttleReversed)?(int32_t)(trim+trimMin):(int32_t)(trim-trimMin)) * (RESX-anas[i])) >> (RESX_SHIFT+1); - } -#endif - if (trimsCheckTimer > 0) { - trim = 0; - } - - trims[i] = trim*2; - } -} - -#if !defined(PCBSTD) -uint8_t mSwitchDuration[1+NUM_ROTARY_ENCODERS] = { 0 }; -#define CFN_PRESSLONG_DURATION 100 -#endif - - -uint8_t s_mixer_first_run_done = false; - -void doMixerCalculations() -{ - static tmr10ms_t lastTMR = 0; - - tmr10ms_t tmr10ms = get_tmr10ms(); - uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); - // handle tick10ms overrun - // correct overflow handling costs a lot of code; happens only each 11 min; - // therefore forget the exact calculation and use only 1 instead; good compromise - -#if !defined(CPUARM) - lastTMR = tmr10ms; -#endif - - getADC(); - -#if defined(PCBTARANIS) - processSbusInput(); -#endif - - getSwitchesPosition(!s_mixer_first_run_done); - -#if defined(CPUARM) - lastTMR = tmr10ms; -#endif - -#if defined(PCBSKY9X) && !defined(REVA) && !defined(SIMU) - Current_analogue = (Current_analogue*31 + s_anaFilt[8] ) >> 5 ; - if (Current_analogue > Current_max) - Current_max = Current_analogue ; -#endif - -#if !defined(CPUARM) - adcPrepareBandgap(); -#endif - - evalMixes(tick10ms); - -#if !defined(CPUARM) - // Bandgap has had plenty of time to settle... - getADC_bandgap(); -#endif - - if (tick10ms) { - -#if !defined(CPUM64) && !defined(ACCURAT_THROTTLE_TIMER) - // code cost is about 16 bytes for higher throttle accuracy for timer - // would not be noticable anyway, because all version up to this change had only 16 steps; - // now it has already 32 steps; this define would increase to 128 steps - #define ACCURAT_THROTTLE_TIMER -#endif - - /* Throttle trace */ - int16_t val; - - if (g_model.thrTraceSrc > NUM_POTS) { - uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; - val = channelOutputs[ch]; - - LimitData *lim = limitAddress(ch); - int16_t gModelMax = LIMIT_MAX_RESX(lim); - int16_t gModelMin = LIMIT_MIN_RESX(lim); - - if (lim->revert) - val = -val + gModelMax; - else - val = val - gModelMin; - -#if defined(PPM_LIMITS_SYMETRICAL) - if (lim->symetrical) { - val -= calc1000toRESX(lim->offset); - } -#endif - - gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default - // usually max is 1024 min is -1024 --> max-min = 2048 full range - -#ifdef ACCURAT_THROTTLE_TIMER - if (gModelMax!=0 && gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs -#else - // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) - gModelMax+=255; // force rounding up --> gModelMax is bigger --> val is smaller - gModelMax >>= (10-2); - - if (gModelMax!=0 && gModelMax!=8) { - val = (val << 3) / gModelMax; // rescaling only needed if Min, Max differs - } -#endif - - if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits - } - else { -#if defined(VIRTUALINPUTS) - val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; -#else - val = RESX + (g_model.thrTraceSrc == 0 ? rawAnas[THR_STICK] : calibratedStick[g_model.thrTraceSrc+NUM_STICKS-1]); -#endif - } - -#if defined(ACCURAT_THROTTLE_TIMER) - val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4) -#else - val >>= (RESX_SHIFT-4); // calibrate it -#endif - - evalTimers(val, tick10ms); - - static uint8_t s_cnt_100ms; - static uint8_t s_cnt_1s; - static uint8_t s_cnt_samples_thr_1s; - static uint16_t s_sum_samples_thr_1s; - - s_cnt_samples_thr_1s++; - s_sum_samples_thr_1s+=val; - - if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec - s_cnt_100ms -= 10; - s_cnt_1s += 1; - - logicalSwitchesTimerTick(); - checkTrainerSignalWarning(); - - if (s_cnt_1s >= 10) { // 1sec - s_cnt_1s -= 10; - sessionTimer += 1; - - struct t_inactivity *ptrInactivity = &inactivity; - FORCE_INDIRECT(ptrInactivity) ; - ptrInactivity->counter++; - if ((((uint8_t)ptrInactivity->counter)&0x07)==0x01 && g_eeGeneral.inactivityTimer && g_vbat100mV>50 && ptrInactivity->counter > ((uint16_t)g_eeGeneral.inactivityTimer*60)) - AUDIO_INACTIVITY(); - -#if defined(AUDIO) - if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1); - if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2); - if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3); -#endif - -#if defined(ACCURAT_THROTTLE_TIMER) - val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; - s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps - if (val) s_timeCumThr += 1; - s_sum_samples_thr_1s>>=2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this -#else - val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; - s_timeCum16ThrP += (val>>1); - if (val) s_timeCumThr += 1; -#endif - -#if defined(THRTRACE) - // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size. - // in case buffer runs out, it wraps around - // resolution for y axis is only 32, therefore no higher value makes sense - s_cnt_samples_thr_10s += s_cnt_samples_thr_1s; - s_sum_samples_thr_10s += s_sum_samples_thr_1s; - - if (++s_cnt_10s >= 10) { // 10s - s_cnt_10s -= 10; - val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s; - s_sum_samples_thr_10s = 0; - s_cnt_samples_thr_10s = 0; - s_traceBuf[s_traceWr % MAXTRACE] = val; - s_traceWr++; - } -#endif - - s_cnt_samples_thr_1s = 0; - s_sum_samples_thr_1s = 0; - } - } - -#if defined(PXX) || defined(DSM2) - static uint8_t countRangecheck = 0; - for (uint8_t i=0; i= 250) { - countRangecheck = 0; - AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP); - } - } - } -#endif - -#if defined(CPUARM) - checkTrims(); -#endif - } - - s_mixer_first_run_done = true; -} - -#if defined(NAVIGATION_STICKS) -uint8_t StickScrollAllowed; -uint8_t StickScrollTimer; -static const pm_uint8_t rate[] PROGMEM = { 0, 0, 100, 40, 16, 7, 3, 1 } ; - -uint8_t calcStickScroll( uint8_t index ) -{ - uint8_t direction; - int8_t value; - - if ( ( g_eeGeneral.stickMode & 1 ) == 0 ) - index ^= 3; - - value = calibratedStick[index] / 128; - direction = value > 0 ? 0x80 : 0; - if (value < 0) - value = -value; // (abs) - if (value > 7) - value = 7; - value = pgm_read_byte(rate+(uint8_t)value); - if (value) - StickScrollTimer = STICK_SCROLL_TIMEOUT; // Seconds - return value | direction; -} -#endif - -void opentxStart() -{ -#if defined(SIMU) - if (main_thread_running == 2) - return; -#endif - - bool display_alerts = (g_eeGeneral.chkSum == evalChkSum()); - -#if defined(GUI) - if (display_alerts) { - doSplash(); - } -#endif - -#if defined(DEBUG_TRACE_BUFFER) - trace_event(trace_start, 0x12345678); -#endif - -#if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU) - for (int i=0; i<500 && !Card_initialized; i++) { - CoTickDelay(1); // 2ms - } -#endif - -#if defined(GUI) - if (display_alerts) { - checkAlarm(); - checkAll(); - } - else { - chainMenu(menuFirstCalib); - } -#endif -} - -#if defined(CPUARM) || defined(CPUM2560) -void opentxClose() -{ - AUDIO_BYE(); - -#if defined(FRSKY) - // TODO needed? telemetryEnd(); -#endif - -#if defined(LUA) - luaClose(); -#endif - -#if defined(SDCARD) - closeLogs(); -#endif - -#if defined(HAPTIC) - hapticOff(); -#endif - - saveTimers(); - -#if defined(CPUARM) - for (int i=0; i value ) - { - repeater = 0; - if ( evt == 0 ) - { - if ( direction ) - { - evt = EVT_KEY_FIRST(KEY_UP); - } - else - { - evt = EVT_KEY_FIRST(KEY_DOWN); - } - } - } - } - else - { - value = calcStickScroll( 3 ); - direction = value & 0x80; - value &= 0x7F; - if ( value ) - { - if ( repeater > value ) - { - repeater = 0; - if ( evt == 0 ) - { - if ( direction ) - { - evt = EVT_KEY_FIRST(KEY_RIGHT); - } - else - { - evt = EVT_KEY_FIRST(KEY_LEFT); - } - } - } - } - } - } - } - else { - StickScrollTimer = 0; // Seconds - } - StickScrollAllowed = 1 ; - return evt; -} -#endif - -void checkBattery() -{ - static uint8_t counter = 0; -#if defined(GUI) && !defined(COLORLCD) - // TODO not the right menu I think ... - if (menuHandlers[menuLevel] == menuGeneralDiagAna) { - g_vbat100mV = 0; - counter = 0; - } -#endif - if (counter-- == 0) { - counter = 10; - int32_t instant_vbat = anaIn(TX_VOLTAGE); -#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) - instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.txVoltageCalibration)/128) * BATT_SCALE; - instant_vbat >>= 11; - instant_vbat += 2; // because of the diode -#elif defined(PCBSKY9X) - instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.txVoltageCalibration)/128) * 4191; - instant_vbat /= 55296; -#elif defined(CPUM2560) - instant_vbat = (instant_vbat*1112 + instant_vbat*g_eeGeneral.txVoltageCalibration + (BandGap<<2)) / (BandGap<<3); -#else - instant_vbat = (instant_vbat*16 + instant_vbat*g_eeGeneral.txVoltageCalibration/8) / BandGap; -#endif - - static uint8_t s_batCheck; - static uint16_t s_batSum; - -#if defined(VOICE) - s_batCheck += 8; -#else - s_batCheck += 32; -#endif - - s_batSum += instant_vbat; - - if (g_vbat100mV == 0) { - g_vbat100mV = instant_vbat; - s_batSum = 0; - s_batCheck = 0; - } -#if defined(VOICE) - else if (!(s_batCheck & 0x3f)) { -#else - else if (s_batCheck == 0) { -#endif - g_vbat100mV = s_batSum / 8; - s_batSum = 0; -#if defined(VOICE) - if (s_batCheck != 0) { - // no alarms - } - else -#endif - if (IS_TXBATT_WARNING() && g_vbat100mV>50) { - AUDIO_TX_BATTERY_LOW(); - } -#if defined(PCBSKY9X) - else if (g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { - AUDIO_TX_TEMP_HIGH(); - } - else if (g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.txCurrentCalibration)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { - AUDIO_TX_MAH_HIGH(); - } -#endif - } - } -} - -#if !defined(SIMU) && !defined(CPUARM) - -volatile uint8_t g_tmr16KHz; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow -ISR(TIMER_16KHZ_VECT, ISR_NOBLOCK) -{ - g_tmr16KHz++; // gruvin: Not 16KHz. Overflows occur at 61.035Hz (1/256th of 15.625KHz) - // to give *16.384ms* intervals. Kind of matters for accuracy elsewhere. ;) - // g_tmr16KHz is used to software-construct a 16-bit timer - // from TIMER-0 (8-bit). See getTmr16KHz, below. -} - -uint16_t getTmr16KHz() -{ - while(1){ - uint8_t hb = g_tmr16KHz; - uint8_t lb = COUNTER_16KHZ; - if(hb-g_tmr16KHz==0) return (hb<<8)|lb; - } -} - -#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) -// Clocks every 128 uS -ISR(TIMER_AUDIO_VECT, ISR_NOBLOCK) -{ - cli(); - PAUSE_AUDIO_INTERRUPT(); // stop reentrance - sei(); - -#if defined(AUDIO) - AUDIO_DRIVER(); -#endif - -#if defined(VOICE) - VOICE_DRIVER(); -#endif - - cli(); - RESUME_AUDIO_INTERRUPT(); - sei(); -} -#endif - -// Clocks every 10ms -ISR(TIMER_10MS_VECT, ISR_NOBLOCK) -{ - // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that - static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed - -#if defined(AUDIO) - AUDIO_HEARTBEAT(); -#endif - -#if defined(BUZZER) - BUZZER_HEARTBEAT(); -#endif - -#if defined(HAPTIC) - HAPTIC_HEARTBEAT(); -#endif - - per10ms(); - - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - TIMER_10MS_COMPVAL += bump; -} - -// Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz -// equating to one count every half millisecond. (2 counts = 1ms). Control channel -// count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), -// corresponding to a PPM signal in the range 0.8ms to 2.2ms (1.5ms at center). -// (The timer is free-running and is thus not reset to zero at each capture interval.) -ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with ISR_NOBLOCK -{ - uint16_t capture=ICR3; - - // Prevent rentrance for this IRQ only - PAUSE_PPMIN_INTERRUPT(); - sei(); // enable other interrupts - - captureTrainerPulses(capture); - - cli(); // disable other interrupts for stack pops before this function's RETI - RESUME_PPMIN_INTERRUPT(); -} -#endif - -#if defined(DSM2_SERIAL) && !defined(CPUARM) -FORCEINLINE void DSM2_USART_vect() -{ - UDR0 = *((uint16_t*)pulses2MHzRPtr); // transmit next byte - - pulses2MHzRPtr += sizeof(uint16_t); - - if (pulses2MHzRPtr == pulses2MHzWPtr) { // if reached end of DSM2 data buffer ... - UCSRB_N(TLM_USART) &= ~(1 << UDRIE_N(TLM_USART)); // disable UDRE interrupt - } -} -#endif - -#if !defined(SIMU) && !defined(CPUARM) - -#if defined (FRSKY) - -FORCEINLINE void FRSKY_USART_vect() -{ - if (frskyTxBufferCount > 0) { - UDR_N(TLM_USART) = frskyTxBuffer[--frskyTxBufferCount]; - } - else { - UCSRB_N(TLM_USART) &= ~(1 << UDRIE_N(TLM_USART)); // disable UDRE interrupt - } -} - -// USART0/1 Transmit Data Register Emtpy ISR -ISR(USART_UDRE_vect_N(TLM_USART)) -{ -#if defined(FRSKY) && defined(DSM2_SERIAL) - if (IS_DSM2_PROTOCOL(g_model.protocol)) { // TODO not s_current_protocol? - DSM2_USART_vect(); - } - else { - FRSKY_USART_vect(); - } -#elif defined(FRSKY) - FRSKY_USART_vect(); -#else - DSM2_USART_vect(); -#endif -} -#endif -#endif - -#if defined(VIRTUALINPUTS) - #define INSTANT_TRIM_MARGIN 10 /* around 1% */ -#else - #define INSTANT_TRIM_MARGIN 15 /* around 1.5% */ -#endif - -void instantTrim() -{ -#if defined(VIRTUALINPUTS) - int16_t anas_0[NUM_INPUTS]; - evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks); - memcpy(anas_0, anas, sizeof(anas_0)); -#endif - - evalInputs(e_perout_mode_notrainer); - - for (uint8_t stick=0; sticksrcRaw-MIXSRC_Rud == stick) { - delta = anas[ed->chn] - anas_0[ed->chn]; - break; - } - } -#else - int16_t delta = anas[stick]; -#endif - if (abs(delta) >= INSTANT_TRIM_MARGIN) { - int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); - setTrimValue(trim_phase, stick, trim); - } - } - } - - storageDirty(EE_MODEL); - AUDIO_WARNING2(); -} - -void copySticksToOffset(uint8_t ch) -{ - pauseMixerCalculations(); - int32_t zero = (int32_t)channelOutputs[ch]; - - evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0); - int32_t val = chans[ch]; - LimitData *ld = limitAddress(ch); - limit_min_max_t lim = LIMIT_MIN(ld); - if (val < 0) { - val = -val; - lim = LIMIT_MIN(ld); - } -#if defined(CPUARM) - zero = (zero*256000 - val*lim) / (1024*256-val); -#else - zero = (zero*25600 - val*lim) / (26214-val); -#endif - ld->offset = (ld->revert ? -zero : zero); - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void copyTrimsToOffset(uint8_t ch) -{ - int16_t zero; - - pauseMixerCalculations(); - - evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims - zero = applyLimits(ch, chans[ch]); - - evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims - - int16_t output = applyLimits(ch, chans[ch]) - zero; - int16_t v = g_model.limitData[ch].offset; - if (g_model.limitData[ch].revert) output = -output; -#if defined(CPUARM) - v += (output * 125) / 128; -#else - v += output; -#endif - g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire - - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void moveTrimsToOffsets() // copy state of 3 primary to subtrim -{ - int16_t zeros[NUM_CHNOUT]; - - pauseMixerCalculations(); - - evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims - for (uint8_t i=0; i q ) { - *p-- = 0x55 ; - } -} - -uint16_t stackAvailable() -{ - unsigned char *p ; - - p = &__bss_end + 1 ; - while ( *p++ == 0x55 ); - return p - &__bss_end ; -} -#endif - -#if defined(CPUM2560) - #define OPENTX_INIT_ARGS const uint8_t mcusr -#elif defined(PCBSTD) - #define OPENTX_INIT_ARGS const uint8_t mcusr -#else - #define OPENTX_INIT_ARGS -#endif - -void opentxInit(OPENTX_INIT_ARGS) -{ -#if defined(DEBUG) && defined(USB_SERIAL) - // CoTickDelay(5000); // 10s -#endif - - TRACE("opentxInit()"); - -#if defined(GUI) - menuHandlers[0] = menuMainView; - #if MENUS_LOCK != 2/*no menus*/ - menuHandlers[1] = menuModelSelect; - #endif -#endif - -#if defined(RTCLOCK) && !defined(COPROCESSOR) - rtcInit(); // RTC must be initialized before rambackupRestore() is called -#endif - -#if !defined(EEPROM) - if (!UNEXPECTED_SHUTDOWN()) { - sdInit(); - } -#endif - -#if defined(COLORLCD) - topbar = new Topbar(&g_model.topbarData); - luaInit(); -#endif - -#if defined(RAMBACKUP) - if (UNEXPECTED_SHUTDOWN()) { - rambackupRestore(); - } - else { - storageReadAll(); - } -#else - storageReadAll(); -#endif - -#if defined(CPUARM) - if (UNEXPECTED_SHUTDOWN()) { - unexpectedShutdown = 1; - } -#endif - -#if defined(PCBTARANIS) - BACKLIGHT_ON(); -#endif - -#if MENUS_LOCK == 1 - getMovedSwitch(); - if (TRIMS_PRESSED() && g_eeGeneral.switchUnlockStates==switches_states) { - readonly = false; - } -#endif - -#if defined(VOICE) - setScaledVolume(g_eeGeneral.speakerVolume+VOLUME_LEVEL_DEF); -#endif - -#if defined(CPUARM) - audioQueue.start(); - setBacklight(g_eeGeneral.backlightBright); -#endif - -#if defined(PCBSKY9X) - // Set ADC gains here - setSticksGain(g_eeGeneral.sticksGain); -#endif - -#if defined(BLUETOOTH) - btInit(); -#endif - -#if defined(COLORLCD) - loadTheme(); -#endif - - if (g_eeGeneral.backlightMode != e_backlight_mode_off) backlightOn(); // on Tx start turn the light on - - if (UNEXPECTED_SHUTDOWN()) { -#if !defined(CPUARM) - // is done above on ARM - unexpectedShutdown = 1; -#endif - } - else { - opentxStart(); - } - -#if defined(CPUARM) || defined(CPUM2560) - if (!g_eeGeneral.unexpectedShutdown) { - g_eeGeneral.unexpectedShutdown = 1; - storageDirty(EE_GENERAL); - } -#endif - -#if defined(GUI) - lcdSetContrast(); -#endif - backlightOn(); - -#if defined(PCBTARANIS) || defined(PCBFLAMENCO) - serial2Init(g_eeGeneral.serial2Mode, MODEL_TELEMETRY_PROTOCOL()); -#endif - -#if defined(PCBSKY9X) && !defined(SIMU) - init_trainer_capture(); -#endif - -#if !defined(CPUARM) - doMixerCalculations(); -#endif - - startPulses(); - - wdt_enable(WDTO_500MS); -} - -#if defined(SIMU) -void * simuMain(void *) -#else -int main() -#endif -{ - // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's - // important to disable it before commencing with system initialisation (or - // we could put a bunch more wdt_reset()s in. But I don't like that approach - // during boot up.) -#if defined(CPUM2560) || defined(CPUM2561) - uint8_t mcusr = MCUSR; // save the WDT (etc) flags - MCUSR = 0; // must be zeroed before disabling the WDT -#elif defined(PCBSTD) - uint8_t mcusr = MCUCSR; - MCUCSR = 0; -#endif -#if defined(PCBTARANIS) - g_eeGeneral.contrast = 30; -#endif - wdt_disable(); - - boardInit(); - -#if defined(GUI) && !defined(PCBTARANIS) && !defined(PCBFLAMENCO) && !defined(PCBHORUS) - // TODO remove this - lcdInit(); -#endif - -#if !defined(SIMU) - stackPaint(); -#endif - -#if defined(GUI) && !defined(PCBTARANIS) - // lcdSetRefVolt(25); -#endif - -#if defined(PCBTARANIS) || defined(PCBHORUS) - drawSplash(); -#endif - - sei(); // interrupts needed now - -#if !defined(CPUARM) && defined(FRSKY) && !defined(DSM2_SERIAL) - telemetryInit(); -#endif - -#if defined(DSM2_SERIAL) && !defined(FRSKY) - DSM2_Init(); -#endif - -#ifdef JETI - JETI_Init(); -#endif - -#ifdef ARDUPILOT - ARDUPILOT_Init(); -#endif - -#ifdef NMEA - NMEA_Init(); -#endif - -#ifdef MAVLINK - MAVLINK_Init(); -#endif - -#ifdef MENU_ROTARY_SW - init_rotary_sw(); -#endif - -#if defined(CPUARM) - tasksStart(); -#else - opentxInit(mcusr); -#if defined(CPUM2560) - uint8_t shutdown_state = 0; -#endif - -#if defined(PCBFLAMENCO) - // TODO not here it's an ARM board ... menuEntryTime = get_tmr10ms() - 200; -#endif - - while (1) { -#if defined(CPUM2560) - if ((shutdown_state=pwrCheck()) > e_power_trainer) - break; -#endif - - perMain(); - - if (heartbeat == HEART_WDT_CHECK) { - wdt_reset(); - heartbeat = 0; - } - } -#endif - -#if defined(CPUM2560) - // Time to switch off - lcdClear(); - displayPopup(STR_SHUTDOWN); - opentxClose(); - lcdClear() ; - lcdRefresh() ; - boardOff(); // Only turn power off if necessary - wdt_disable(); - while(1); // never return from main() - there is no code to return back, if any delays occurs in physical power it does dead loop. -#endif - -#if defined(SIMU) - return NULL; -#endif -} - -#if defined(PWR_BUTTON_DELAY) - -uint32_t pwr_press_time = 0; - -uint32_t pwrPressedDuration() -{ - if (pwr_press_time == 0) { - return 0; - } - else { - return get_tmr10ms() - pwr_press_time; - } -} - -uint32_t pwrCheck() -{ - enum PwrCheckState { - PWR_CHECK_ON, - PWR_CHECK_OFF, - PWR_CHECK_PAUSED, - }; - - static uint8_t pwr_check_state = PWR_CHECK_ON; - - if (pwr_check_state == PWR_CHECK_OFF) { - return e_power_off; - } - else if (pwrPressed()) { - if (pwr_check_state == PWR_CHECK_PAUSED) { - // nothing - } - else if (pwr_press_time == 0) { - pwr_press_time = get_tmr10ms(); - } - else { - if (get_tmr10ms() - pwr_press_time > PWR_PRESS_SHUTDOWN) { -#if defined(SHUTDOWN_CONFIRMATION) - while (1) { - lcdRefreshWait(); - lcdClear(); - POPUP_CONFIRMATION("Confirm Shutdown"); - evt_t evt = getEvent(false); - DISPLAY_WARNING(evt); - lcdRefresh(); - if (warningResult == true) { - pwr_check_state = PWR_CHECK_OFF; - return e_power_off; - } - else if (!warningText) { - // shutdown has been cancelled - pwr_check_state = PWR_CHECK_PAUSED; - return e_power_on; - } - } -#else - haptic.play(15, 3, PLAY_NOW); - pwr_check_state = PWR_CHECK_OFF; - return e_power_off; -#endif - } - else { - drawShutdownBitmap(pwrPressedDuration()); - return e_power_press; - } - } - } - else { - pwr_check_state = PWR_CHECK_ON; - pwr_press_time = 0; - } - - return e_power_on; -} -#endif +/* + * 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" +#include "timers.h" + +RadioData g_eeGeneral; +ModelData g_model; + +#if defined(SDCARD) +Clipboard clipboard; +#endif + +#if defined(PCBTARANIS) +uint8_t modelBitmap[MODEL_BITMAP_SIZE] __DMA; + +bool loadModelBitmap(char * name, uint8_t * bitmap) +{ + uint8_t len = zlen(name, LEN_BITMAP_NAME); + if (len > 0) { + char lfn[] = BITMAPS_PATH "/xxxxxxxxxx.bmp"; + strncpy(lfn+sizeof(BITMAPS_PATH), name, len); + strcpy(lfn+sizeof(BITMAPS_PATH)+len, BITMAPS_EXT); + if (lcdLoadBitmap(bitmap, lfn, MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT)) { + return true; + } + } + + // In all error cases, we set the default logo + memcpy(bitmap, logo_taranis, MODEL_BITMAP_SIZE); + return false; +} +#endif + +#if !defined(CPUARM) +uint8_t g_tmr1Latency_max; +uint8_t g_tmr1Latency_min; +uint16_t lastMixerDuration; +#endif + +uint8_t unexpectedShutdown = 0; + +/* AVR: mixer duration in 1/16ms */ +/* ARM: mixer duration in 0.5us */ +uint16_t maxMixerDuration; + +#if defined(AUDIO) && !defined(CPUARM) +audioQueue audio; +#endif + +uint8_t heartbeat; + +uint8_t stickMode; + +#if defined(OVERRIDE_CHANNEL_FUNCTION) +safetych_t safetyCh[NUM_CHNOUT]; +#endif + +union ReusableBuffer reusableBuffer; + +const pm_uint8_t bchout_ar[] PROGMEM = { + 0x1B, 0x1E, 0x27, 0x2D, 0x36, 0x39, + 0x4B, 0x4E, 0x63, 0x6C, 0x72, 0x78, + 0x87, 0x8D, 0x93, 0x9C, 0xB1, 0xB4, + 0xC6, 0xC9, 0xD2, 0xD8, 0xE1, 0xE4 }; + +uint8_t channel_order(uint8_t x) +{ + return ( ((pgm_read_byte(bchout_ar + g_eeGeneral.templateSetup) >> (6-(x-1) * 2)) & 3 ) + 1 ); +} + +/* +mode1 rud ele thr ail +mode2 rud thr ele ail +mode3 ail ele thr rud +mode4 ail thr ele rud +*/ +const pm_uint8_t modn12x3[] PROGMEM = { + 0, 1, 2, 3, + 0, 2, 1, 3, + 3, 1, 2, 0, + 3, 2, 1, 0 }; + +volatile tmr10ms_t g_tmr10ms; + +#if defined(CPUARM) +volatile uint8_t rtc_count = 0; +uint32_t watchdogTimeout = 0; + +void watchdogSetTimeout(uint32_t timeout) +{ + watchdogTimeout = timeout; +} +#endif + +void per10ms() +{ + g_tmr10ms++; + +#if defined(CPUARM) + if (watchdogTimeout) { + watchdogTimeout -= 1; + wdt_reset(); // Retrigger hardware watchdog + } +#endif + +#if defined(GUI) + if (lightOffCounter) lightOffCounter--; + if (flashCounter) flashCounter--; + if (noHighlightCounter) noHighlightCounter--; +#endif + + if (trimsCheckTimer) trimsCheckTimer--; + if (ppmInputValidityTimer) ppmInputValidityTimer--; + +#if defined(CPUARM) + if (trimsDisplayTimer) + trimsDisplayTimer--; + else + trimsDisplayMask = 0; +#endif + +#if defined(RTCLOCK) + /* Update global Date/Time every 100 per10ms cycles */ + if (++g_ms100 == 100) { + g_rtcTime++; // inc global unix timestamp one second +#if defined(COPROCESSOR) + if (g_rtcTime < 60 || rtc_count<5) { + rtcInit(); + rtc_count++; + } + else { + coprocReadData(true); + } +#endif + g_ms100 = 0; + } +#endif + + readKeysAndTrims(); + +#if defined(ROTARY_ENCODER_NAVIGATION) + if (IS_RE_NAVIGATION_ENABLE()) { + static rotenc_t rePreviousValue; + rotenc_t reNewValue = (g_rotenc[NAVIGATION_RE_IDX()] / ROTARY_ENCODER_GRANULARITY); + int8_t scrollRE = reNewValue - rePreviousValue; + if (scrollRE) { + rePreviousValue = reNewValue; + putEvent(scrollRE < 0 ? EVT_ROTARY_LEFT : EVT_ROTARY_RIGHT); + } + uint8_t evt = s_evt; + if (EVT_KEY_MASK(evt) == BTN_REa + NAVIGATION_RE_IDX()) { + if (IS_KEY_BREAK(evt)) { + putEvent(EVT_ROTARY_BREAK); + } + else if (IS_KEY_LONG(evt)) { + putEvent(EVT_ROTARY_LONG); + } + } + } +#endif + +#if defined(FRSKY) || defined(JETI) + if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol[0])) + telemetryInterrupt10ms(); +#endif + + // These moved here from evalFlightModeMixes() to improve beep trigger reliability. +#if defined(PWM_BACKLIGHT) + if ((g_tmr10ms&0x03) == 0x00) + backlightFade(); // increment or decrement brightness until target brightness is reached +#endif + +#if !defined(AUDIO) + if (mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) AUDIO_MIX_WARNING(1); + if (mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) AUDIO_MIX_WARNING(2); + if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3); +#endif + +#if defined(SDCARD) + sdPoll10ms(); +#endif + + heartbeat |= HEART_TIMER_10MS; +} + +FlightModeData *flightModeAddress(uint8_t idx) +{ + return &g_model.flightModeData[idx]; +} + +ExpoData *expoAddress(uint8_t idx ) +{ + return &g_model.expoData[idx]; +} + +MixData *mixAddress(uint8_t idx) +{ + return &g_model.mixData[idx]; +} + +LimitData *limitAddress(uint8_t idx) +{ + return &g_model.limitData[idx]; +} + +#if defined(CPUM64) +void memclear(void *ptr, uint8_t size) +{ + memset(ptr, 0, size); +} +#endif + +void memswap(void * a, void * b, uint8_t size) +{ + uint8_t * x = (uint8_t *)a; + uint8_t * y = (uint8_t *)b; + uint8_t temp ; + + while (size--) { + temp = *x; + *x++ = *y; + *y++ = temp; + } +} + +void generalDefault() +{ + memclear(&g_eeGeneral, sizeof(g_eeGeneral)); + g_eeGeneral.version = EEPROM_VER; + g_eeGeneral.variant = EEPROM_VARIANT; + +#if !defined(PCBHORUS) + g_eeGeneral.contrast = 25; +#endif + +#if defined(PCBFLAMENCO) + g_eeGeneral.vBatWarn = 33; + g_eeGeneral.vBatMin = -60; // 0 is 9.0V + g_eeGeneral.vBatMax = -78; // 0 is 12.0V +#elif defined(PCBHORUS) + g_eeGeneral.potsConfig = 0x05; // S1 and S2 = pots with detent + g_eeGeneral.slidersConfig = 0x03; // LS and RS = sliders with detent +#elif defined(PCBTARANIS) + g_eeGeneral.potsConfig = 0x05; // S1 and S2 = pots with detent + g_eeGeneral.slidersConfig = 0x03; // LS and RS = sliders with detent +#endif + +#if defined(PCBTARANIS) || defined(PCBHORUS) + g_eeGeneral.switchConfig = 0x00007bff; // 6x3POS, 1x2POS, 1xTOGGLE +#endif + +#if defined(PCBTARANIS) && defined(REV9E) + // NI-MH 9.6V + g_eeGeneral.vBatWarn = 87; + g_eeGeneral.vBatMin = -5; + g_eeGeneral.vBatMax = -5; +#elif defined(PCBTARANIS) + // NI-MH 7.2V + g_eeGeneral.vBatWarn = 65; + g_eeGeneral.vBatMin = -30; + g_eeGeneral.vBatMax = -40; +#else + g_eeGeneral.vBatWarn = 90; +#endif + +#if defined(DEFAULT_MODE) + g_eeGeneral.stickMode = DEFAULT_MODE-1; +#endif + +#if defined(PCBFLAMENCO) + g_eeGeneral.templateSetup = 21; /* AETR */ +#elif defined(PCBTARANIS) + g_eeGeneral.templateSetup = 17; /* TAER */ +#endif + +#if defined(PCBFLAMENCO) + g_eeGeneral.inactivityTimer = 50; +#elif !defined(CPUM64) + g_eeGeneral.backlightMode = e_backlight_mode_all; + g_eeGeneral.lightAutoOff = 2; + g_eeGeneral.inactivityTimer = 10; +#endif + +#if defined(CPUARM) + g_eeGeneral.wavVolume = 2; + g_eeGeneral.backgroundVolume = 1; +#endif + +#if defined(CPUARM) + for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; + expo->curve.type = CURVE_REF_EXPO; + expo->chn = i; + expo->weight = 100; + expo->mode = 3; // TODO constant +#if defined(TRANSLATIONS_CZ) + for (int c=0; c<4; c++) { + g_model.inputNames[i][c] = char2idx(STR_INPUTNAMES[1+4*(stick_index-1)+c]); + } + g_model.inputNames[i][4] = '\0'; +#else + for (int c=0; c<3; c++) { + g_model.inputNames[i][c] = char2idx(STR_VSRCRAW[2+4*stick_index+c]); + } + g_model.inputNames[i][3] = '\0'; +#endif + } + storageDirty(EE_MODEL); +} +#endif + +#if defined(TEMPLATES) +inline void applyDefaultTemplate() +{ + applyTemplate(TMPL_SIMPLE_4CH); // calls storageDirty internally +} +#else +void applyDefaultTemplate() +{ +#if defined(VIRTUALINPUTS) + defaultInputs(); // calls storageDirty internally +#else + storageDirty(EE_MODEL); +#endif + + for (int i=0; idestCh = i; + mix->weight = 100; +#if defined(VIRTUALINPUTS) + mix->srcRaw = i+1; +#else + mix->srcRaw = MIXSRC_Rud - 1 + channel_order(i+1); +#endif + } +} +#endif + +#if defined(CPUARM) && defined(EEPROM) +void checkModelIdUnique(uint8_t index, uint8_t module) +{ + uint8_t modelId = g_model.header.modelId[module]; + if (modelId != 0) { + for (uint8_t i=0; icreate(&g_model.screenData[0].layoutData); + strcpy(g_model.screenData[0].layoutName, "Layout2P1"); + extern const WidgetFactory * defaultWidget; + customScreens[0]->createWidget(0, defaultWidget); +#endif +} + +#if defined(VIRTUALINPUTS) +bool isInputRecursive(int index) +{ + ExpoData * line = expoAddress(0); + for (int i=0; ichn > index) + break; + else if (line->chn < index) + continue; + else if (line->srcRaw >= MIXSRC_FIRST_LOGICAL_SWITCH) + return true; + } + return false; +} +#endif + +#if defined(AUTOSOURCE) +int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) +{ + int8_t result = 0; + static tmr10ms_t s_move_last_time = 0; + +#if defined(VIRTUALINPUTS) + static int16_t inputsStates[MAX_INPUTS]; + if (min <= MIXSRC_FIRST_INPUT) { + for (uint8_t i=0; i 512) { + if (!isInputRecursive(i)) { + result = MIXSRC_FIRST_INPUT+i; + break; + } + } + } + } +#endif + + static int16_t sourcesStates[NUM_STICKS+NUM_POTS]; + if (result == 0) { + for (uint8_t i=0; i 512) { + result = MIXSRC_Rud+i; + break; + } + } + } + + bool recent = ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10); + if (recent) { + result = 0; + } + + if (result || recent) { +#if defined(VIRTUALINPUTS) + memcpy(inputsStates, anas, sizeof(inputsStates)); +#endif + memcpy(sourcesStates, calibratedStick, sizeof(sourcesStates)); + } + + s_move_last_time = get_tmr10ms(); + return result; +} +#endif + +#if defined(FLIGHT_MODES) +uint8_t getFlightMode() +{ + for (uint8_t i=1; iswtch && getSwitch(phase->swtch)) { + return i; + } + } + return 0; +} +#endif + +trim_t getRawTrimValue(uint8_t phase, uint8_t idx) +{ + FlightModeData *p = flightModeAddress(phase); +#if defined(PCBSTD) + return (((trim_t)p->trim[idx]) << 2) + ((p->trim_ext >> (2*idx)) & 0x03); +#else + return p->trim[idx]; +#endif +} + +int getTrimValue(uint8_t phase, uint8_t idx) +{ +#if defined(VIRTUALINPUTS) + int result = 0; + for (uint8_t i=0; i> 1; + if (p == phase || phase == 0) { + return result + v.value; + } + else { + phase = p; + if (v.mode % 2 != 0) { + result += v.value; + } + } + } + } + return 0; +#else + return getRawTrimValue(getTrimFlightPhase(phase, idx), idx); +#endif +} + +#if defined(VIRTUALINPUTS) +bool setTrimValue(uint8_t phase, uint8_t idx, int trim) +{ + for (uint8_t i=0; itrim[idx]; + if (v.mode == TRIM_MODE_NONE) + return false; + unsigned int p = v.mode >> 1; + if (p == phase || phase == 0) { + v.value = trim; + break; + } + else if (v.mode % 2 == 0) { + phase = p; + } + else { + v.value = limit(TRIM_EXTENDED_MIN, trim - getTrimValue(p, idx), TRIM_EXTENDED_MAX); + break; + } + } + storageDirty(EE_MODEL); + return true; +} +#else +void setTrimValue(uint8_t phase, uint8_t idx, int trim) +{ +#if defined(PCBSTD) + FlightModeData *p = flightModeAddress(phase); + p->trim[idx] = (int8_t)(trim >> 2); + idx <<= 1; + p->trim_ext = (p->trim_ext & ~(0x03 << idx)) + (((trim & 0x03) << idx)); +#else + FlightModeData *p = flightModeAddress(phase); + p->trim[idx] = trim; +#endif + storageDirty(EE_MODEL); +} +#endif + +#if !defined(VIRTUALINPUTS) +uint8_t getTrimFlightPhase(uint8_t phase, uint8_t idx) +{ + for (uint8_t i=0; i= phase) result++; + phase = result; + } + return 0; +} +#endif + +#if defined(ROTARY_ENCODERS) +uint8_t getRotaryEncoderFlightPhase(uint8_t idx) +{ + uint8_t phase = mixerCurrentFlightMode; + for (uint8_t i=0; irotaryEncoders[idx]; + if (value <= ROTARY_ENCODER_MAX) return phase; + uint8_t result = value-ROTARY_ENCODER_MAX-1; + if (result >= phase) result++; + phase = result; + } + return 0; +} + +int16_t getRotaryEncoder(uint8_t idx) +{ + return flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]; +} + +void incRotaryEncoder(uint8_t idx, int8_t inc) +{ + g_rotenc[idx] += inc; + int16_t *value = &(flightModeAddress(getRotaryEncoderFlightPhase(idx))->rotaryEncoders[idx]); + *value = limit((int16_t)-1024, (int16_t)(*value + (inc * 8)), (int16_t)+1024); + storageDirty(EE_MODEL); +} +#endif + +#if defined(GVARS) + +#if defined(PCBSTD) + #define SET_GVAR_VALUE(idx, phase, value) \ + (GVAR_VALUE(idx, phase) = value, storageDirty(EE_MODEL)) +#else + #define SET_GVAR_VALUE(idx, phase, value) \ + GVAR_VALUE(idx, phase) = value; \ + storageDirty(EE_MODEL); \ + if (g_model.gvars[idx].popup) { \ + gvarLastChanged = idx; \ + gvarDisplayTimer = GVAR_DISPLAY_TIME; \ + } +#endif + +#if defined(PCBSTD) +int16_t getGVarFieldValue(int16_t x, int16_t min, int16_t max) +{ + if (GV_IS_GV_VALUE(x, min, max)) { + int8_t idx = GV_INDEX_CALCULATION(x, max); + int8_t mul = 1; + + if (idx < 0) { + idx = -1-idx; + mul = -1; + } + + x = GVAR_VALUE(idx, -1) * mul; + } + + return limit(min, x, max); +} + +void setGVarValue(uint8_t idx, int8_t value) +{ + if (GVAR_VALUE(idx, -1) != value) { + SET_GVAR_VALUE(idx, -1, value); + } +} +#else +uint8_t gvarDisplayTimer = 0; +uint8_t gvarLastChanged = 0; + +uint8_t getGVarFlightMode(uint8_t fm, uint8_t gv) // TODO change params order to be consistent! +{ + for (uint8_t i=0; i= fm) result++; + fm = result; + } + return 0; +} + +int16_t getGVarValue(int8_t gv, int8_t fm) +{ + int8_t mul = 1; + if (gv < 0) { + gv = -1-gv; + mul = -1; + } + return GVAR_VALUE(gv, getGVarFlightMode(fm, gv)) * mul; +} + +int32_t getGVarValuePrec1(int8_t gv, int8_t fm) +{ + int8_t mul; + uint8_t prec = g_model.gvars[gv].prec; + if (prec == 0) + mul = 10; + else + mul = 1; + + if (gv < 0) { + gv = -1-gv; + mul = -mul; + } + return GVAR_VALUE(gv, getGVarFlightMode(fm, gv)) * mul; +} + +void setGVarValue(uint8_t gv, int16_t value, int8_t fm) +{ + fm = getGVarFlightMode(fm, gv); + if (GVAR_VALUE(gv, fm) != value) { + SET_GVAR_VALUE(gv, fm, value); + } +} + +int16_t getGVarFieldValue(int16_t val, int16_t min, int16_t max, int8_t fm) +{ + if (GV_IS_GV_VALUE(val, min, max)) { + int8_t gv = GV_INDEX_CALCULATION(val, max); + val = getGVarValue(gv, fm); + } + return limit(min, val, max); +} + +int32_t getGVarFieldValuePrec1(int16_t val, int16_t min, int16_t max, int8_t fm) +{ + if (GV_IS_GV_VALUE(val, min, max)) { + int8_t gv = GV_INDEX_CALCULATION(val, max); + val = getGVarValuePrec1(gv, fm); + } + else { + val *= 10; + } + return limit(min*10, val, max*10); +} +#endif + +#endif + +#if defined(CPUARM) +getvalue_t convert16bitsTelemValue(source_t channel, ls_telemetry_value_t value) +{ + return value; +} + +getvalue_t convert8bitsTelemValue(source_t channel, ls_telemetry_value_t value) +{ + return value; +} + +#if defined(FRSKY) +ls_telemetry_value_t minTelemValue(source_t channel) +{ + return 0; +} + +ls_telemetry_value_t maxTelemValue(source_t channel) +{ + return 30000; +} +#endif + +ls_telemetry_value_t max8bitsTelemValue(source_t channel) +{ + return 30000; +} + +#elif defined(FRSKY) + +/* +ls_telemetry_value_t minTelemValue(uint8_t channel) +{ + switch (channel) { + case TELEM_TIMER1: + case TELEM_TIMER2: + return -3600; + case TELEM_ALT: + case TELEM_MIN_ALT: + case TELEM_MAX_ALT: + case TELEM_GPSALT: + return -500; + case TELEM_T1: + case TELEM_MAX_T1: + case TELEM_T2: + case TELEM_MAX_T2: + return -30; + case TELEM_ACCx: + case TELEM_ACCy: + case TELEM_ACCz: + return -1000; + case TELEM_VSPEED: + return -3000; + default: + return 0; + } +} +*/ +ls_telemetry_value_t maxTelemValue(uint8_t channel) +{ + switch (channel) { + case TELEM_FUEL: + case TELEM_RSSI_TX: + case TELEM_RSSI_RX: + return 100; + case TELEM_HDG: + return 180; + default: + return 255; + } +} +#endif + +#if !defined(CPUARM) +getvalue_t convert8bitsTelemValue(uint8_t channel, ls_telemetry_value_t value) +{ + getvalue_t result; + switch (channel) { + case TELEM_TIMER1: + case TELEM_TIMER2: + result = value * 5; + break; +#if defined(FRSKY) + case TELEM_ALT: + case TELEM_GPSALT: + case TELEM_MAX_ALT: + case TELEM_MIN_ALT: + result = value * 8 - 500; + break; + case TELEM_RPM: + case TELEM_MAX_RPM: + result = value * 50; + break; + case TELEM_T1: + case TELEM_T2: + case TELEM_MAX_T1: + case TELEM_MAX_T2: + result = (getvalue_t)value - 30; + break; + case TELEM_CELL: + case TELEM_HDG: + case TELEM_SPEED: + case TELEM_MAX_SPEED: + result = value * 2; + break; + case TELEM_ASPEED: + case TELEM_MAX_ASPEED: + result = value * 20; + break; + case TELEM_DIST: + case TELEM_MAX_DIST: + result = value * 8; + break; + case TELEM_CURRENT: + case TELEM_POWER: + case TELEM_MAX_CURRENT: + case TELEM_MAX_POWER: + result = value * 5; + break; + case TELEM_CONSUMPTION: + result = value * 100; + break; + case TELEM_VSPEED: + result = ((getvalue_t)value - 125) * 10; + break; +#endif + default: + result = value; + break; + } + return result; +} +#endif + +#if defined(FRSKY)&& !defined(CPUARM) +FORCEINLINE void convertUnit(getvalue_t & val, uint8_t & unit) +{ + if (IS_IMPERIAL_ENABLE()) { + if (unit == UNIT_TEMPERATURE) { + val += 18; + val *= 115; + val >>= 6; + } + if (unit == UNIT_DIST) { + // m to ft *105/32 + val = val * 3 + (val >> 2) + (val >> 5); + } + if (unit == UNIT_FEET) { + unit = UNIT_DIST; + } + if (unit == UNIT_KTS) { + // kts to mph + unit = UNIT_SPEED; + val = (val * 23) / 20; + } + } + else { + if (unit == UNIT_KTS) { + // kts to km/h + unit = UNIT_SPEED; +#if defined(CPUARM) + val = (val * 1852) / 1000; +#else + val = (val * 50) / 27; +#endif + } + } + + if (unit == UNIT_HDG) { + unit = UNIT_TEMPERATURE; + } +} +#endif + +#define INAC_STICKS_SHIFT 6 +#define INAC_SWITCHES_SHIFT 8 +bool inputsMoved() +{ + uint8_t sum = 0; + for (uint8_t i=0; i> INAC_STICKS_SHIFT; + for (uint8_t i=0; i> INAC_SWITCHES_SHIFT; + + if (abs((int8_t)(sum-inactivity.sum)) > 1) { + inactivity.sum = sum; + return true; + } + else { + return false; + } +} + +void checkBacklight() +{ + static uint8_t tmr10ms ; + +#if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION) + rotencPoll(); +#endif + + uint8_t x = g_blinkTmr10ms; + if (tmr10ms != x) { + tmr10ms = x; + if (inputsMoved()) { + inactivity.counter = 0; + if (g_eeGeneral.backlightMode & e_backlight_mode_sticks) + backlightOn(); + } + + bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || lightOffCounter || isFunctionActive(FUNCTION_BACKLIGHT)); + if (flashCounter) backlightOn = !backlightOn; + if (backlightOn) + BACKLIGHT_ON(); + else + BACKLIGHT_OFF(); + +#if defined(PCBSTD) && defined(VOICE) && !defined(SIMU) + Voice.voice_process() ; +#endif + } +} + +#if defined(PCBFLAMENCO) +void checkUsbChip() +{ + uint8_t reg = i2cReadBQ24195(0x00); + if (reg & 0x80) { + i2cWriteBQ24195(0x00, reg & 0x7F); + } +} +#endif + +void doLoopCommonActions() +{ + checkBacklight(); + +#if defined(PCBFLAMENCO) + checkUsbChip(); +#endif +} + +void backlightOn() +{ + lightOffCounter = ((uint16_t)g_eeGeneral.lightAutoOff*250) << 1; +} + +#if MENUS_LOCK == 1 +bool readonly = true; +bool readonlyUnlocked() +{ + if (readonly) { + POPUP_WARNING(STR_MODS_FORBIDDEN); + return false; + } + else { + return true; + } +} +#endif + +#if defined(SPLASH) +void doSplash() +{ +#if defined(PWR_BUTTON_DELAY) + bool refresh = false; +#endif + + if (SPLASH_NEEDED()) { +#if !defined(PCBTARANIS) && !defined(PCBHORUS) + drawSplash(); +#endif + +#if !defined(CPUARM) + AUDIO_TADA(); +#endif + +#if defined(PCBSTD) + lcdSetContrast(); +#elif !defined(PCBHORUS) && !defined(PCBTARANIS) + tmr10ms_t curTime = get_tmr10ms() + 10; + uint8_t contrast = 10; + lcdSetRefVolt(contrast); +#endif + + getADC(); // init ADC array + + inputsMoved(); + + tmr10ms_t tgtime = get_tmr10ms() + SPLASH_TIMEOUT; + + while (tgtime > get_tmr10ms()) { +#if defined(SIMU) + SIMU_SLEEP(1); +#elif defined(CPUARM) + CoTickDelay(1); +#endif + + getADC(); + +#if defined(FSPLASH) + // Splash is forced, we can't skip it + if (!(g_eeGeneral.splashMode & 0x04)) { +#endif + + if (keyDown() || inputsMoved()) return; + +#if defined(FSPLASH) + } +#endif + +#if defined(PWR_BUTTON_DELAY) + uint32_t pwr_check = pwrCheck(); + if (pwr_check == e_power_off) { + break; + } + else if (pwr_check == e_power_press) { + refresh = true; + } + else if (pwr_check == e_power_on && refresh) { + drawSplash(); + refresh = false; + } +#else + if (pwrCheck() == e_power_off) { + return; + } +#endif + +#if !defined(PCBHORUS) && !defined(PCBTARANIS) && !defined(PCBSTD) + if (curTime < get_tmr10ms()) { + curTime += 10; + if (contrast < g_eeGeneral.contrast) { + contrast += 1; + lcdSetRefVolt(contrast); + } + } +#endif + + doLoopCommonActions(); + } + } +} +#else +#define Splash() +#define doSplash() +#endif + +#if defined(PCBTARANIS) +void checkFailsafe() +{ + for (int i=0; iNUM_POTS)) ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1; + // throttle channel is either the stick according stick mode (already handled in evalInputs) + // or P1 to P3; + // in case an output channel is choosen as throttle source (thrTraceSrc>NUM_POTS) we assume the throttle stick is the input + // no other information available at the moment, and good enough to my option (otherwise too much exceptions...) + +#if defined(MODULE_ALWAYS_SEND_PULSES) + int16_t v = calibratedStick[thrchn]; + if (v<=THRCHK_DEADBAND-1024 || g_model.disableThrottleWarning || pwrCheck()==e_power_off || keyDown()) { + startupWarningState = STARTUP_WARNING_THROTTLE+1; + } + else { + calibratedStick[thrchn] = -1024; +#if !defined(VIRTUALINPUTS) + if (thrchn < NUM_STICKS) { + rawAnas[thrchn] = anas[thrchn] = calibratedStick[thrchn]; + } +#endif + MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); + } +#else + if (g_model.disableThrottleWarning) { + return; + } + + getADC(); + + evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job + + int16_t v = calibratedStick[thrchn]; + if (v <= THRCHK_DEADBAND-1024) { + return; // prevent warning if throttle input OK + } + + // first - display warning; also deletes inputs if any have been before + LED_ERROR_BEGIN(); + MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_THROTTLE_ALERT); + +#if defined(PWR_BUTTON_DELAY) + bool refresh = false; +#endif + + while (1) { + + SIMU_SLEEP(1); + + getADC(); + + evalInputs(e_perout_mode_notrainer); // let do evalInputs do the job + + v = calibratedStick[thrchn]; + +#if defined(PWR_BUTTON_DELAY) + uint32_t pwr_check = pwrCheck(); + if (pwr_check == e_power_off) { + break; + } + else if (pwr_check == e_power_press) { + refresh = true; + } + else if (pwr_check == e_power_on && refresh) { + MESSAGE(STR_THROTTLEWARN, STR_THROTTLENOTIDLE, STR_PRESSANYKEYTOSKIP, AU_NONE); + refresh = false; + } +#else + if (pwrCheck() == e_power_off) { + break; + } +#endif + + if (keyDown() || v <= THRCHK_DEADBAND-1024) { + break; + } + + doLoopCommonActions(); + + wdt_reset(); + } +#endif + + LED_ERROR_END(); +} + +void checkAlarm() // added by Gohst +{ + if (g_eeGeneral.disableAlarmWarning) { + return; + } + + if (IS_SOUND_OFF()) { + ALERT(STR_ALARMSWARN, STR_ALARMSDISABLED, AU_ERROR); + } +} + +void alert(const pm_char * t, const pm_char * s MESSAGE_SOUND_ARG) +{ + LED_ERROR_BEGIN(); + + MESSAGE(t, s, STR_PRESSANYKEY, sound); + +#if defined(PWR_BUTTON_DELAY) + bool refresh = false; +#endif + + while(1) { + SIMU_SLEEP(1); + + if (keyDown()) break; // wait for key release + + doLoopCommonActions(); + + wdt_reset(); + +#if defined(PWR_BUTTON_DELAY) + uint32_t pwr_check = pwrCheck(); + if (pwr_check == e_power_off) { + boardOff(); + } + else if (pwr_check == e_power_press) { + refresh = true; + } + else if (pwr_check == e_power_on && refresh) { + MESSAGE(t, s, STR_PRESSANYKEY, AU_NONE); + refresh = false; + } +#else + if (pwrCheck() == e_power_off) { + boardOff(); // turn power off now + } +#endif + } + + LED_ERROR_END(); +} + +#if defined(GVARS) +int8_t trimGvar[NUM_STICKS] = { -1, -1, -1, -1 }; +#endif + +#if defined(CPUARM) +void checkTrims() +{ + evt_t event = getEvent(true); + if (event && !IS_KEY_BREAK(event)) { + int8_t k = EVT_KEY_MASK(event) - TRM_BASE; +#else +uint8_t checkTrim(uint8_t event) +{ + int8_t k = EVT_KEY_MASK(event) - TRM_BASE; + if (k>=0 && k<8 && !IS_KEY_BREAK(event)) { +#endif + // LH_DWN LH_UP LV_DWN LV_UP RV_DWN RV_UP RH_DWN RH_UP + uint8_t idx = CONVERT_MODE((uint8_t)k/2); + uint8_t phase; + int before; + bool thro; + +#if defined(CPUARM) + trimsDisplayTimer = 200; // 2 seconds + trimsDisplayMask |= (1<=mark && beforemark))) { + after = mark; + beepTrim = (mark == 0 ? 1 : 2); + } + } + + if ((beforeTRIM_MAX) || (before>after && after TRIM_EXTENDED_MAX) { + after = TRIM_EXTENDED_MAX; + } + +#if defined(GVARS) + if (TRIM_REUSED(idx)) { + SET_GVAR_VALUE(trimGvar[idx], phase, after); + } + else +#endif + { +#if defined(VIRTUALINPUTS) + if (!setTrimValue(phase, idx, after)) { + // we don't play a beep, so we exit now the function + return; + } +#else + setTrimValue(phase, idx, after); +#endif + } + +#if defined(AUDIO) + // toneFreq higher/lower according to trim position + // limit the frequency, range -125 to 125 = toneFreq: 19 to 101 + if (after > TRIM_MAX) + after = TRIM_MAX; + if (after < TRIM_MIN) + after = TRIM_MIN; +#if defined(CPUARM) + after <<= 3; + after += 120*16; +#else + after >>= 2; + after += 60; +#endif +#endif + + if (beepTrim) { + if (beepTrim == 1) { + AUDIO_TRIM_MIDDLE(after); + pauseEvents(event); + } + else { + AUDIO_TRIM_END(after); + killEvents(event); + } + } + else { + AUDIO_TRIM(event, after); + } +#if !defined(CPUARM) + return 0; +#endif + } +#if !defined(CPUARM) + return event; +#endif +} + +#if !defined(SIMU) +uint16_t s_anaFilt[NUMBER_ANALOG]; +#endif + +#if defined(SIMU) +uint16_t BandGap = 225; +#elif defined(CPUM2560) +// #define STARTADCONV (ADCSRA = (1< rawJitter[NUMBER_ANALOG]; + JitterMeter avgJitter[NUMBER_ANALOG]; + tmr10ms_t jitterResetTime = 0; + #if defined(PCBTARANIS) + #define JITTER_MEASURE_ACTIVE() (menuHandlers[menuLevel] == menuGeneralDiagAna) + #elif defined(CLI) + #define JITTER_MEASURE_ACTIVE() (1) + #endif +#endif + + +#if defined(VIRTUALINPUTS) && defined(JITTER_FILTER) + #define JITTER_FILTER_STRENGTH 4 // tune this value, bigger value - more filtering (range: 1-5) (see explanation below) + #define ANALOG_SCALE 1 // tune this value, bigger value - more filtering (range: 0-3) (see explanation below) + + #define JITTER_ALPHA (1< 32) + #error "JITTER_FILTER_STRENGTH and ANALOG_SCALE are too big, their summ should be <= 5 !!!" + #endif +#else + #define ANALOG_SCALE 0 + #define JITTER_ALPHA 1 + #define ANALOG_MULTIPLIER 1 + #define ANA_FILT(chan) (s_anaFilt[chan]) +#endif + + +#if !defined(SIMU) +uint16_t anaIn(uint8_t chan) +{ +#if defined(VIRTUALINPUTS) + return ANA_FILT(chan); +#elif defined(PCBSKY9X) && !defined(REVA) + static const uint8_t crossAna[]={1,5,7,0,4,6,2,3}; + if (chan == TX_CURRENT) { + return Current_analogue ; + } + volatile uint16_t *p = &s_anaFilt[pgm_read_byte(crossAna+chan)]; + return *p; +#else +#if defined(TELEMETRY_MOD_14051) || defined(TELEMETRY_MOD_14051_SWAPPED) + static const pm_char crossAna[] PROGMEM = {3,1,2,0,4,5,6,0/* shouldn't be used */,TX_VOLTAGE}; +#else + static const pm_char crossAna[] PROGMEM = {3,1,2,0,4,5,6,7}; +#endif +#if defined(FRSKY_STICKS) + volatile uint16_t temp = s_anaFilt[pgm_read_byte(crossAna+chan)]; // volatile saves here 40 bytes; maybe removed for newer AVR when available + if (chan < NUM_STICKS && (g_eeGeneral.stickReverse & (1 << chan))) { + temp = 2048 - temp; + } + return temp; +#else + volatile uint16_t *p = &s_anaFilt[pgm_read_byte(crossAna+chan)]; + return *p; +#endif +#endif +} + +#if defined(CPUARM) +void getADC() +{ + uint16_t temp[NUMBER_ANALOG] = { 0 }; + +#if defined(JITTER_MEASURE) + if (JITTER_MEASURE_ACTIVE() && jitterResetTime < get_tmr10ms()) { + // reset jitter measurement every second + for (uint32_t x=0; x> (3 - ANALOG_SCALE); + +#if defined(VIRTUALINPUTS) && defined(JITTER_FILTER) + // Jitter filter: + // * pass trough any big change directly + // * for small change use Modified moving average (MMA) filter + // + // Explanation: + // + // Normal MMA filter has this formula: + // = ((ALPHA-1)* + )/ALPHA + // + // If calculation is done this way with integer arithmetics, then any small change in + // input signal is lost. One way to combat that, is to rearrange the formula somewhat, + // to store a more precise (larger) number between iterations. The basic idea is to + // store undivided value between iterations. Therefore an new variable is + // used. The new formula becomes: + // = - /ALPHA + + // = /ALPHA (use only when out is needed) + // + // The above formula with a maximum allowed ALPHA value (we are limited by + // the 16 bit s_anaFilt[]) was tested on the radio. The resulting signal still had + // some jitter (a value of 1 was observed). The jitter might be bigger on other + // radios. + // + // So another idea is to use larger input values for filtering. So instead of using + // input in a range from 0 to 2047, we use twice larger number (temp[x] is divided less) + // + // This also means that ALPHA must be lowered (remember 16 bit limit), but test results + // have proved that this kind of filtering gives better results. So the recommended values + // for filter are: + // JITTER_FILTER_STRENGTH 4 + // ANALOG_SCALE 1 + // + // Variables mapping: + // * = v + // * = s_anaFilt[x] + uint16_t previous = s_anaFilt[x] / JITTER_ALPHA; + uint16_t diff = (v > previous) ? (v - previous) : (previous - v); + if (diff < 10 * ANALOG_MULTIPLIER) { + // apply jitter filter + s_anaFilt[x] = (s_anaFilt[x] - previous) + v; + } + else +#endif + { + //use unfiltered value + s_anaFilt[x] = v * JITTER_ALPHA; + } + +#if defined(JITTER_MEASURE) + if (JITTER_MEASURE_ACTIVE()) { + avgJitter[x].measure(ANA_FILT(x)); + } +#endif + +#if defined(VIRTUALINPUTS) + #define ANAFILT_MAX (2 * RESX * JITTER_ALPHA * ANALOG_MULTIPLIER - 1) + StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[x]; + if (IS_POT_MULTIPOS(x) && IS_MULTIPOS_CALIBRATED(calib)) { + // TODO: consider adding another low pass filter to eliminate multipos switching glitches + uint8_t vShifted = ANA_FILT(x) >> 4; + s_anaFilt[x] = ANAFILT_MAX; + for (uint32_t i=0; icount; i++) { + if (vShifted < calib->steps[i]) { + s_anaFilt[x] = (i * ANAFILT_MAX) / calib->count; + break; + } + } + } +#endif // defined(VIRTUALINPUTS) + } +} +#endif + +#endif // SIMU + +uint8_t g_vbat100mV = 0; +uint16_t lightOffCounter; +uint8_t flashCounter = 0; + +uint16_t sessionTimer; +uint16_t s_timeCumThr; // THR in 1/16 sec +uint16_t s_timeCum16ThrP; // THR% in 1/16 sec + +uint8_t trimsCheckTimer = 0; + +#if defined(CPUARM) +uint8_t trimsDisplayTimer = 0; +uint8_t trimsDisplayMask = 0; +#endif + +void flightReset() +{ + // we don't reset the whole audio here (the tada.wav would be cut, if a prompt is queued before FlightReset, it should be played) + // TODO check if the vario / background music are stopped correctly if switching to a model which doesn't have these functions enabled + + if (!IS_MANUAL_RESET_TIMER(0)) { + timerReset(0); + } + +#if TIMERS > 1 + if (!IS_MANUAL_RESET_TIMER(1)) { + timerReset(1); + } +#endif + +#if TIMERS > 2 + if (!IS_MANUAL_RESET_TIMER(2)) { + timerReset(2); + } +#endif + +#if defined(FRSKY) + telemetryReset(); +#endif + + s_mixer_first_run_done = false; + + START_SILENCE_PERIOD(); + + RESET_THR_TRACE(); +} + +#if defined(THRTRACE) +uint8_t s_traceBuf[MAXTRACE]; +uint16_t s_traceWr; +uint8_t s_cnt_10s; +uint16_t s_cnt_samples_thr_10s; +uint16_t s_sum_samples_thr_10s; +#endif + +void evalTrims() +{ + uint8_t phase = mixerCurrentFlightMode; + for (uint8_t i=0; i throttle trim if applicable + int16_t trim = getTrimValue(phase, i); +#if !defined(VIRTUALINPUTS) + if (i==THR_STICK && g_model.thrTrim) { + int16_t trimMin = g_model.extendedTrims ? TRIM_EXTENDED_MIN : TRIM_MIN; + trim = (((g_model.throttleReversed)?(int32_t)(trim+trimMin):(int32_t)(trim-trimMin)) * (RESX-anas[i])) >> (RESX_SHIFT+1); + } +#endif + if (trimsCheckTimer > 0) { + trim = 0; + } + + trims[i] = trim*2; + } +} + +#if !defined(PCBSTD) +uint8_t mSwitchDuration[1+NUM_ROTARY_ENCODERS] = { 0 }; +#define CFN_PRESSLONG_DURATION 100 +#endif + + +uint8_t s_mixer_first_run_done = false; + +void doMixerCalculations() +{ + static tmr10ms_t lastTMR = 0; + + tmr10ms_t tmr10ms = get_tmr10ms(); + uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); + // handle tick10ms overrun + // correct overflow handling costs a lot of code; happens only each 11 min; + // therefore forget the exact calculation and use only 1 instead; good compromise + +#if !defined(CPUARM) + lastTMR = tmr10ms; +#endif + + getADC(); + +#if defined(PCBTARANIS) + processSbusInput(); +#endif + + getSwitchesPosition(!s_mixer_first_run_done); + +#if defined(CPUARM) + lastTMR = tmr10ms; +#endif + +#if defined(PCBSKY9X) && !defined(REVA) && !defined(SIMU) + Current_analogue = (Current_analogue*31 + s_anaFilt[8] ) >> 5 ; + if (Current_analogue > Current_max) + Current_max = Current_analogue ; +#endif + +#if !defined(CPUARM) + adcPrepareBandgap(); +#endif + + evalMixes(tick10ms); + +#if !defined(CPUARM) + // Bandgap has had plenty of time to settle... + getADC_bandgap(); +#endif + + if (tick10ms) { + +#if !defined(CPUM64) && !defined(ACCURAT_THROTTLE_TIMER) + // code cost is about 16 bytes for higher throttle accuracy for timer + // would not be noticable anyway, because all version up to this change had only 16 steps; + // now it has already 32 steps; this define would increase to 128 steps + #define ACCURAT_THROTTLE_TIMER +#endif + + /* Throttle trace */ + int16_t val; + + if (g_model.thrTraceSrc > NUM_POTS) { + uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; + val = channelOutputs[ch]; + + LimitData *lim = limitAddress(ch); + int16_t gModelMax = LIMIT_MAX_RESX(lim); + int16_t gModelMin = LIMIT_MIN_RESX(lim); + + if (lim->revert) + val = -val + gModelMax; + else + val = val - gModelMin; + +#if defined(PPM_LIMITS_SYMETRICAL) + if (lim->symetrical) { + val -= calc1000toRESX(lim->offset); + } +#endif + + gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default + // usually max is 1024 min is -1024 --> max-min = 2048 full range + +#ifdef ACCURAT_THROTTLE_TIMER + if (gModelMax!=0 && gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // rescaling only needed if Min, Max differs +#else + // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) + gModelMax+=255; // force rounding up --> gModelMax is bigger --> val is smaller + gModelMax >>= (10-2); + + if (gModelMax!=0 && gModelMax!=8) { + val = (val << 3) / gModelMax; // rescaling only needed if Min, Max differs + } +#endif + + if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits + } + else { +#if defined(VIRTUALINPUTS) + val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; +#else + val = RESX + (g_model.thrTraceSrc == 0 ? rawAnas[THR_STICK] : calibratedStick[g_model.thrTraceSrc+NUM_STICKS-1]); +#endif + } + +#if defined(ACCURAT_THROTTLE_TIMER) + val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4) +#else + val >>= (RESX_SHIFT-4); // calibrate it +#endif + + evalTimers(val, tick10ms); + + static uint8_t s_cnt_100ms; + static uint8_t s_cnt_1s; + static uint8_t s_cnt_samples_thr_1s; + static uint16_t s_sum_samples_thr_1s; + + s_cnt_samples_thr_1s++; + s_sum_samples_thr_1s+=val; + + if ((s_cnt_100ms += tick10ms) >= 10) { // 0.1sec + s_cnt_100ms -= 10; + s_cnt_1s += 1; + + logicalSwitchesTimerTick(); + checkTrainerSignalWarning(); + + if (s_cnt_1s >= 10) { // 1sec + s_cnt_1s -= 10; + sessionTimer += 1; + + struct t_inactivity *ptrInactivity = &inactivity; + FORCE_INDIRECT(ptrInactivity) ; + ptrInactivity->counter++; + if ((((uint8_t)ptrInactivity->counter)&0x07)==0x01 && g_eeGeneral.inactivityTimer && g_vbat100mV>50 && ptrInactivity->counter > ((uint16_t)g_eeGeneral.inactivityTimer*60)) + AUDIO_INACTIVITY(); + +#if defined(AUDIO) + if (mixWarning & 1) if ((sessionTimer&0x03)==0) AUDIO_MIX_WARNING(1); + if (mixWarning & 2) if ((sessionTimer&0x03)==1) AUDIO_MIX_WARNING(2); + if (mixWarning & 4) if ((sessionTimer&0x03)==2) AUDIO_MIX_WARNING(3); +#endif + +#if defined(ACCURAT_THROTTLE_TIMER) + val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; + s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps + if (val) s_timeCumThr += 1; + s_sum_samples_thr_1s>>=2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this +#else + val = s_sum_samples_thr_1s / s_cnt_samples_thr_1s; + s_timeCum16ThrP += (val>>1); + if (val) s_timeCumThr += 1; +#endif + +#if defined(THRTRACE) + // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size. + // in case buffer runs out, it wraps around + // resolution for y axis is only 32, therefore no higher value makes sense + s_cnt_samples_thr_10s += s_cnt_samples_thr_1s; + s_sum_samples_thr_10s += s_sum_samples_thr_1s; + + if (++s_cnt_10s >= 10) { // 10s + s_cnt_10s -= 10; + val = s_sum_samples_thr_10s / s_cnt_samples_thr_10s; + s_sum_samples_thr_10s = 0; + s_cnt_samples_thr_10s = 0; + s_traceBuf[s_traceWr % MAXTRACE] = val; + s_traceWr++; + } +#endif + + s_cnt_samples_thr_1s = 0; + s_sum_samples_thr_1s = 0; + } + } + +#if defined(PXX) || defined(DSM2) + static uint8_t countRangecheck = 0; + for (uint8_t i=0; i= 250) { + countRangecheck = 0; + AUDIO_PLAY(AU_SPECIAL_SOUND_CHEEP); + } + } + } +#endif + +#if defined(CPUARM) + checkTrims(); +#endif + } + + s_mixer_first_run_done = true; +} + +#if defined(NAVIGATION_STICKS) +uint8_t StickScrollAllowed; +uint8_t StickScrollTimer; +static const pm_uint8_t rate[] PROGMEM = { 0, 0, 100, 40, 16, 7, 3, 1 } ; + +uint8_t calcStickScroll( uint8_t index ) +{ + uint8_t direction; + int8_t value; + + if ( ( g_eeGeneral.stickMode & 1 ) == 0 ) + index ^= 3; + + value = calibratedStick[index] / 128; + direction = value > 0 ? 0x80 : 0; + if (value < 0) + value = -value; // (abs) + if (value > 7) + value = 7; + value = pgm_read_byte(rate+(uint8_t)value); + if (value) + StickScrollTimer = STICK_SCROLL_TIMEOUT; // Seconds + return value | direction; +} +#endif + +void opentxStart() +{ +#if defined(SIMU) + if (main_thread_running == 2) + return; +#endif + + bool display_alerts = (g_eeGeneral.chkSum == evalChkSum()); + +#if defined(GUI) + if (display_alerts) { + doSplash(); + } +#endif + +#if defined(DEBUG_TRACE_BUFFER) + trace_event(trace_start, 0x12345678); +#endif + +#if defined(PCBSKY9X) && defined(SDCARD) && !defined(SIMU) + for (int i=0; i<500 && !Card_initialized; i++) { + CoTickDelay(1); // 2ms + } +#endif + +#if defined(GUI) + if (display_alerts) { + checkAlarm(); + checkAll(); + } + else { + chainMenu(menuFirstCalib); + } +#endif +} + +#if defined(CPUARM) || defined(CPUM2560) +void opentxClose() +{ + AUDIO_BYE(); + +#if defined(FRSKY) + // TODO needed? telemetryEnd(); +#endif + +#if defined(LUA) + luaClose(); +#endif + +#if defined(SDCARD) + closeLogs(); +#endif + +#if defined(HAPTIC) + hapticOff(); +#endif + + saveTimers(); + +#if defined(CPUARM) + for (int i=0; i value ) + { + repeater = 0; + if ( evt == 0 ) + { + if ( direction ) + { + evt = EVT_KEY_FIRST(KEY_UP); + } + else + { + evt = EVT_KEY_FIRST(KEY_DOWN); + } + } + } + } + else + { + value = calcStickScroll( 3 ); + direction = value & 0x80; + value &= 0x7F; + if ( value ) + { + if ( repeater > value ) + { + repeater = 0; + if ( evt == 0 ) + { + if ( direction ) + { + evt = EVT_KEY_FIRST(KEY_RIGHT); + } + else + { + evt = EVT_KEY_FIRST(KEY_LEFT); + } + } + } + } + } + } + } + else { + StickScrollTimer = 0; // Seconds + } + StickScrollAllowed = 1 ; + return evt; +} +#endif + +void checkBattery() +{ + static uint8_t counter = 0; +#if defined(GUI) && !defined(COLORLCD) + // TODO not the right menu I think ... + if (menuHandlers[menuLevel] == menuGeneralDiagAna) { + g_vbat100mV = 0; + counter = 0; + } +#endif + if (counter-- == 0) { + counter = 10; + int32_t instant_vbat = anaIn(TX_VOLTAGE); +#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) + instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.txVoltageCalibration)/128) * BATT_SCALE; + instant_vbat >>= 11; + instant_vbat += 2; // because of the diode +#elif defined(PCBSKY9X) + instant_vbat = (instant_vbat + instant_vbat*(g_eeGeneral.txVoltageCalibration)/128) * 4191; + instant_vbat /= 55296; +#elif defined(CPUM2560) + instant_vbat = (instant_vbat*1112 + instant_vbat*g_eeGeneral.txVoltageCalibration + (BandGap<<2)) / (BandGap<<3); +#else + instant_vbat = (instant_vbat*16 + instant_vbat*g_eeGeneral.txVoltageCalibration/8) / BandGap; +#endif + + static uint8_t s_batCheck; + static uint16_t s_batSum; + +#if defined(VOICE) + s_batCheck += 8; +#else + s_batCheck += 32; +#endif + + s_batSum += instant_vbat; + + if (g_vbat100mV == 0) { + g_vbat100mV = instant_vbat; + s_batSum = 0; + s_batCheck = 0; + } +#if defined(VOICE) + else if (!(s_batCheck & 0x3f)) { +#else + else if (s_batCheck == 0) { +#endif + g_vbat100mV = s_batSum / 8; + s_batSum = 0; +#if defined(VOICE) + if (s_batCheck != 0) { + // no alarms + } + else +#endif + if (IS_TXBATT_WARNING() && g_vbat100mV>50) { + AUDIO_TX_BATTERY_LOW(); + } +#if defined(PCBSKY9X) + else if (g_eeGeneral.temperatureWarn && getTemperature() >= g_eeGeneral.temperatureWarn) { + AUDIO_TX_TEMP_HIGH(); + } + else if (g_eeGeneral.mAhWarn && (g_eeGeneral.mAhUsed + Current_used * (488 + g_eeGeneral.txCurrentCalibration)/8192/36) / 500 >= g_eeGeneral.mAhWarn) { + AUDIO_TX_MAH_HIGH(); + } +#endif + } + } +} + +#if !defined(SIMU) && !defined(CPUARM) + +volatile uint8_t g_tmr16KHz; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow +ISR(TIMER_16KHZ_VECT, ISR_NOBLOCK) +{ + g_tmr16KHz++; // gruvin: Not 16KHz. Overflows occur at 61.035Hz (1/256th of 15.625KHz) + // to give *16.384ms* intervals. Kind of matters for accuracy elsewhere. ;) + // g_tmr16KHz is used to software-construct a 16-bit timer + // from TIMER-0 (8-bit). See getTmr16KHz, below. +} + +uint16_t getTmr16KHz() +{ + while(1){ + uint8_t hb = g_tmr16KHz; + uint8_t lb = COUNTER_16KHZ; + if(hb-g_tmr16KHz==0) return (hb<<8)|lb; + } +} + +#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) +// Clocks every 128 uS +ISR(TIMER_AUDIO_VECT, ISR_NOBLOCK) +{ + cli(); + PAUSE_AUDIO_INTERRUPT(); // stop reentrance + sei(); + +#if defined(AUDIO) + AUDIO_DRIVER(); +#endif + +#if defined(VOICE) + VOICE_DRIVER(); +#endif + + cli(); + RESUME_AUDIO_INTERRUPT(); + sei(); +} +#endif + +// Clocks every 10ms +ISR(TIMER_10MS_VECT, ISR_NOBLOCK) +{ + // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that + static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed + +#if defined(AUDIO) + AUDIO_HEARTBEAT(); +#endif + +#if defined(BUZZER) + BUZZER_HEARTBEAT(); +#endif + +#if defined(HAPTIC) + HAPTIC_HEARTBEAT(); +#endif + + per10ms(); + + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + TIMER_10MS_COMPVAL += bump; +} + +// Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz +// equating to one count every half millisecond. (2 counts = 1ms). Control channel +// count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), +// corresponding to a PPM signal in the range 0.8ms to 2.2ms (1.5ms at center). +// (The timer is free-running and is thus not reset to zero at each capture interval.) +ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with ISR_NOBLOCK +{ + uint16_t capture=ICR3; + + // Prevent rentrance for this IRQ only + PAUSE_PPMIN_INTERRUPT(); + sei(); // enable other interrupts + + captureTrainerPulses(capture); + + cli(); // disable other interrupts for stack pops before this function's RETI + RESUME_PPMIN_INTERRUPT(); +} +#endif + +#if defined(DSM2_SERIAL) && !defined(CPUARM) +FORCEINLINE void DSM2_USART_vect() +{ + UDR0 = *((uint16_t*)pulses2MHzRPtr); // transmit next byte + + pulses2MHzRPtr += sizeof(uint16_t); + + if (pulses2MHzRPtr == pulses2MHzWPtr) { // if reached end of DSM2 data buffer ... + UCSRB_N(TLM_USART) &= ~(1 << UDRIE_N(TLM_USART)); // disable UDRE interrupt + } +} +#endif + +#if !defined(SIMU) && !defined(CPUARM) + +#if defined (FRSKY) + +FORCEINLINE void FRSKY_USART_vect() +{ + if (frskyTxBufferCount > 0) { + UDR_N(TLM_USART) = frskyTxBuffer[--frskyTxBufferCount]; + } + else { + UCSRB_N(TLM_USART) &= ~(1 << UDRIE_N(TLM_USART)); // disable UDRE interrupt + } +} + +// USART0/1 Transmit Data Register Emtpy ISR +ISR(USART_UDRE_vect_N(TLM_USART)) +{ +#if defined(FRSKY) && defined(DSM2_SERIAL) + if (IS_DSM2_PROTOCOL(g_model.protocol)) { // TODO not s_current_protocol? + DSM2_USART_vect(); + } + else { + FRSKY_USART_vect(); + } +#elif defined(FRSKY) + FRSKY_USART_vect(); +#else + DSM2_USART_vect(); +#endif +} +#endif +#endif + +#if defined(VIRTUALINPUTS) + #define INSTANT_TRIM_MARGIN 10 /* around 1% */ +#else + #define INSTANT_TRIM_MARGIN 15 /* around 1.5% */ +#endif + +void instantTrim() +{ +#if defined(VIRTUALINPUTS) + int16_t anas_0[NUM_INPUTS]; + evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks); + memcpy(anas_0, anas, sizeof(anas_0)); +#endif + + evalInputs(e_perout_mode_notrainer); + + for (uint8_t stick=0; sticksrcRaw-MIXSRC_Rud == stick) { + delta = anas[ed->chn] - anas_0[ed->chn]; + break; + } + } +#else + int16_t delta = anas[stick]; +#endif + if (abs(delta) >= INSTANT_TRIM_MARGIN) { + int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); + setTrimValue(trim_phase, stick, trim); + } + } + } + + storageDirty(EE_MODEL); + AUDIO_WARNING2(); +} + +void copySticksToOffset(uint8_t ch) +{ + pauseMixerCalculations(); + int32_t zero = (int32_t)channelOutputs[ch]; + + evalFlightModeMixes(e_perout_mode_nosticks+e_perout_mode_notrainer, 0); + int32_t val = chans[ch]; + LimitData *ld = limitAddress(ch); + limit_min_max_t lim = LIMIT_MIN(ld); + if (val < 0) { + val = -val; + lim = LIMIT_MIN(ld); + } +#if defined(CPUARM) + zero = (zero*256000 - val*lim) / (1024*256-val); +#else + zero = (zero*25600 - val*lim) / (26214-val); +#endif + ld->offset = (ld->revert ? -zero : zero); + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +void copyTrimsToOffset(uint8_t ch) +{ + int16_t zero; + + pauseMixerCalculations(); + + evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims + zero = applyLimits(ch, chans[ch]); + + evalFlightModeMixes(e_perout_mode_noinput-e_perout_mode_notrims, 0); // do output loop - only trims + + int16_t output = applyLimits(ch, chans[ch]) - zero; + int16_t v = g_model.limitData[ch].offset; + if (g_model.limitData[ch].revert) output = -output; +#if defined(CPUARM) + v += (output * 125) / 128; +#else + v += output; +#endif + g_model.limitData[ch].offset = limit((int16_t)-1000, (int16_t)v, (int16_t)1000); // make sure the offset doesn't go haywire + + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +void moveTrimsToOffsets() // copy state of 3 primary to subtrim +{ + int16_t zeros[NUM_CHNOUT]; + + pauseMixerCalculations(); + + evalFlightModeMixes(e_perout_mode_noinput, 0); // do output loop - zero input sticks and trims + for (uint8_t i=0; i q ) { + *p-- = 0x55 ; + } +} + +uint16_t stackAvailable() +{ + unsigned char *p ; + + p = &__bss_end + 1 ; + while ( *p++ == 0x55 ); + return p - &__bss_end ; +} +#endif + +#if defined(CPUM2560) + #define OPENTX_INIT_ARGS const uint8_t mcusr +#elif defined(PCBSTD) + #define OPENTX_INIT_ARGS const uint8_t mcusr +#else + #define OPENTX_INIT_ARGS +#endif + +void opentxInit(OPENTX_INIT_ARGS) +{ +#if defined(DEBUG) && defined(USB_SERIAL) + // CoTickDelay(5000); // 10s +#endif + + TRACE("opentxInit()"); + +#if defined(GUI) + menuHandlers[0] = menuMainView; + #if MENUS_LOCK != 2/*no menus*/ + menuHandlers[1] = menuModelSelect; + #endif +#endif + +#if defined(RTCLOCK) && !defined(COPROCESSOR) + rtcInit(); // RTC must be initialized before rambackupRestore() is called +#endif + +#if !defined(EEPROM) + if (!UNEXPECTED_SHUTDOWN()) { + sdInit(); + } +#endif + +#if defined(COLORLCD) + topbar = new Topbar(&g_model.topbarData); + luaInit(); +#endif + +#if defined(RAMBACKUP) + if (UNEXPECTED_SHUTDOWN()) { + rambackupRestore(); + } + else { + storageReadAll(); + } +#else + storageReadAll(); +#endif + +#if defined(CPUARM) + if (UNEXPECTED_SHUTDOWN()) { + unexpectedShutdown = 1; + } +#endif + +#if defined(PCBTARANIS) + BACKLIGHT_ON(); +#endif + +#if MENUS_LOCK == 1 + getMovedSwitch(); + if (TRIMS_PRESSED() && g_eeGeneral.switchUnlockStates==switches_states) { + readonly = false; + } +#endif + +#if defined(VOICE) + setScaledVolume(g_eeGeneral.speakerVolume+VOLUME_LEVEL_DEF); +#endif + +#if defined(CPUARM) + audioQueue.start(); + setBacklight(g_eeGeneral.backlightBright); +#endif + +#if defined(PCBSKY9X) + // Set ADC gains here + setSticksGain(g_eeGeneral.sticksGain); +#endif + +#if defined(BLUETOOTH) + btInit(); +#endif + +#if defined(COLORLCD) + loadTheme(); +#endif + + if (g_eeGeneral.backlightMode != e_backlight_mode_off) backlightOn(); // on Tx start turn the light on + + if (UNEXPECTED_SHUTDOWN()) { +#if !defined(CPUARM) + // is done above on ARM + unexpectedShutdown = 1; +#endif + } + else { + opentxStart(); + } + +#if defined(CPUARM) || defined(CPUM2560) + if (!g_eeGeneral.unexpectedShutdown) { + g_eeGeneral.unexpectedShutdown = 1; + storageDirty(EE_GENERAL); + } +#endif + +#if defined(GUI) + lcdSetContrast(); +#endif + backlightOn(); + +#if defined(PCBTARANIS) || defined(PCBFLAMENCO) + serial2Init(g_eeGeneral.serial2Mode, MODEL_TELEMETRY_PROTOCOL()); +#endif + +#if defined(PCBSKY9X) && !defined(SIMU) + init_trainer_capture(); +#endif + +#if !defined(CPUARM) + doMixerCalculations(); +#endif + + startPulses(); + + wdt_enable(WDTO_500MS); +} + +#if defined(SIMU) +void * simuMain(void *) +#else +int main() +#endif +{ + // G: The WDT remains active after a WDT reset -- at maximum clock speed. So it's + // important to disable it before commencing with system initialisation (or + // we could put a bunch more wdt_reset()s in. But I don't like that approach + // during boot up.) +#if defined(CPUM2560) || defined(CPUM2561) + uint8_t mcusr = MCUSR; // save the WDT (etc) flags + MCUSR = 0; // must be zeroed before disabling the WDT +#elif defined(PCBSTD) + uint8_t mcusr = MCUCSR; + MCUCSR = 0; +#endif +#if defined(PCBTARANIS) + g_eeGeneral.contrast = 30; +#endif + wdt_disable(); + + boardInit(); + +#if defined(GUI) && !defined(PCBTARANIS) && !defined(PCBFLAMENCO) && !defined(PCBHORUS) + // TODO remove this + lcdInit(); +#endif + +#if !defined(SIMU) + stackPaint(); +#endif + +#if defined(GUI) && !defined(PCBTARANIS) + // lcdSetRefVolt(25); +#endif + +#if defined(PCBTARANIS) || defined(PCBHORUS) + drawSplash(); +#endif + + sei(); // interrupts needed now + +#if !defined(CPUARM) && defined(FRSKY) && !defined(DSM2_SERIAL) + telemetryInit(); +#endif + +#if defined(DSM2_SERIAL) && !defined(FRSKY) + DSM2_Init(); +#endif + +#ifdef JETI + JETI_Init(); +#endif + +#ifdef ARDUPILOT + ARDUPILOT_Init(); +#endif + +#ifdef NMEA + NMEA_Init(); +#endif + +#ifdef MAVLINK + MAVLINK_Init(); +#endif + +#ifdef MENU_ROTARY_SW + init_rotary_sw(); +#endif + +#if defined(CPUARM) + tasksStart(); +#else + opentxInit(mcusr); +#if defined(CPUM2560) + uint8_t shutdown_state = 0; +#endif + +#if defined(PCBFLAMENCO) + // TODO not here it's an ARM board ... menuEntryTime = get_tmr10ms() - 200; +#endif + + while (1) { +#if defined(CPUM2560) + if ((shutdown_state=pwrCheck()) > e_power_trainer) + break; +#endif + + perMain(); + + if (heartbeat == HEART_WDT_CHECK) { + wdt_reset(); + heartbeat = 0; + } + } +#endif + +#if defined(CPUM2560) + // Time to switch off + lcdClear(); + displayPopup(STR_SHUTDOWN); + opentxClose(); + lcdClear() ; + lcdRefresh() ; + boardOff(); // Only turn power off if necessary + wdt_disable(); + while(1); // never return from main() - there is no code to return back, if any delays occurs in physical power it does dead loop. +#endif + +#if defined(SIMU) + return NULL; +#endif +} + +#if defined(PWR_BUTTON_DELAY) + +uint32_t pwr_press_time = 0; + +uint32_t pwrPressedDuration() +{ + if (pwr_press_time == 0) { + return 0; + } + else { + return get_tmr10ms() - pwr_press_time; + } +} + +uint32_t pwrCheck() +{ + enum PwrCheckState { + PWR_CHECK_ON, + PWR_CHECK_OFF, + PWR_CHECK_PAUSED, + }; + + static uint8_t pwr_check_state = PWR_CHECK_ON; + + if (pwr_check_state == PWR_CHECK_OFF) { + return e_power_off; + } + else if (pwrPressed()) { + if (pwr_check_state == PWR_CHECK_PAUSED) { + // nothing + } + else if (pwr_press_time == 0) { + pwr_press_time = get_tmr10ms(); + } + else { + if (get_tmr10ms() - pwr_press_time > PWR_PRESS_SHUTDOWN) { +#if defined(SHUTDOWN_CONFIRMATION) + while (1) { + lcdRefreshWait(); + lcdClear(); + POPUP_CONFIRMATION("Confirm Shutdown"); + evt_t evt = getEvent(false); + DISPLAY_WARNING(evt); + lcdRefresh(); + if (warningResult == true) { + pwr_check_state = PWR_CHECK_OFF; + return e_power_off; + } + else if (!warningText) { + // shutdown has been cancelled + pwr_check_state = PWR_CHECK_PAUSED; + return e_power_on; + } + } +#else + haptic.play(15, 3, PLAY_NOW); + pwr_check_state = PWR_CHECK_OFF; + return e_power_off; +#endif + } + else { + drawShutdownBitmap(pwrPressedDuration()); + return e_power_press; + } + } + } + else { + pwr_check_state = PWR_CHECK_ON; + pwr_press_time = 0; + } + + return e_power_on; +} +#endif diff --git a/radio/src/rtc.cpp b/radio/src/rtc.cpp index fca58e564..ee3702fea 100644 --- a/radio/src/rtc.cpp +++ b/radio/src/rtc.cpp @@ -18,471 +18,471 @@ * GNU General Public License for more details. */ -#include -#include "opentx.h" - -extern void rtcdriver_settime(struct gtm * t); - -#define LEAP_SECONDS_POSSIBLE 0 - -/* Shift A right by B bits portably, by dividing A by 2**B and - truncating towards minus infinity. A and B should be free of side - effects, and B should be in the range 0 <= B <= INT_BITS - 2, where - INT_BITS is the number of useful bits in an int. GNU code can - assume that INT_BITS is at least 32. - - ISO C99 says that A >> B is implementation-defined if A < 0. Some - implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift - right in the usual way when A < 0, so SHR falls back on division if - ordinary A >> B doesn't seem to be the usual signed shift. */ -#define SHR(a, b) (-1 >> 1 == -1 ? (a) >> (b) : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) - -/* The extra casts in the following macros work around compiler bugs, - e.g., in Cray C 5.0.3.0. */ - -/* True if the arithmetic type T is an integer type. bool counts as - an integer. */ -#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) - -/* True if negative values of the signed integer type T use two's - complement, ones' complement, or signed magnitude representation, - respectively. Much GNU code assumes two's complement, but some - people like to be portable to all possible C hosts. */ -#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) -#define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0) -#define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1) - -/* True if the arithmetic type T is signed. */ -#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) - -/* The maximum and minimum values for the integer type T. These - macros have undefined behavior if T is signed and has padding bits. - If this is a problem for you, please let us know how to fix it for - your host. */ -#define TYPE_MINIMUM(t) \ - ((t) (! TYPE_SIGNED (t) \ - ? (t) 0 \ - : TYPE_SIGNED_MAGNITUDE (t) \ - ? ~ (t) 0 \ - : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))) -#define TYPE_MAXIMUM(t) \ - ((t) (! TYPE_SIGNED (t) \ - ? (t) -1 \ - : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))) - -#ifndef TIME_T_MIN -# define TIME_T_MIN TYPE_MINIMUM (gtime_t) -#endif -#ifndef TIME_T_MAX -# define TIME_T_MAX TYPE_MAXIMUM (gtime_t) -#endif -#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1) - -/* Verify a requirement at compile-time (unlike assert, which is runtime). */ -#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } - -verify (gtime_t_is_integer, TYPE_IS_INTEGER (gtime_t)); -verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int)); -/* The code also assumes that signed integer overflow silently wraps - around, but this assumption can't be stated without causing a - diagnostic on some hosts. */ - -#define EPOCH_YEAR 1970 -#define TM_YEAR_BASE 1900 -verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0); - -/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */ -static inline int -leapyear (long int year) -{ - /* Don't add YEAR to TM_YEAR_BASE, as that might overflow. - Also, work even if YEAR is negative. */ - return - ((year & 3) == 0 - && (year % 100 != 0 - || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3))); -} - -const unsigned short int __mon_yday[2][13] = - { - /* Normal years. */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - /* Leap years. */ - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } - }; - -/* Compute the `struct tm' representation of *T, - offset OFFSET seconds east of UTC, - and store year, yday, mon, mday, wday, hour, min, sec into *TP. - Return nonzero if successful. */ -int -__offtime ( - gtime_t *t, - long int offset, - struct gtm *tp) -{ - long int days, rem, y; - const unsigned short int *ip; - - days = *t / SECS_PER_DAY; - rem = *t % SECS_PER_DAY; - rem += offset; - while (rem < 0) - { - rem += SECS_PER_DAY; - --days; - } - while (rem >= (long int)SECS_PER_DAY) - { - rem -= SECS_PER_DAY; - ++days; - } - tp->tm_hour = rem / SECS_PER_HOUR; - rem %= SECS_PER_HOUR; - tp->tm_min = rem / 60; - tp->tm_sec = rem % 60; - /* January 1, 1970 was a Thursday. */ - tp->tm_wday = (4 + days) % 7; - if (tp->tm_wday < 0) - tp->tm_wday += 7; - y = 1970; - -#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) -#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) - - while (days < 0 || days >= (leapyear (y) ? 366 : 365)) - { - /* Guess a corrected year, assuming 365 days per year. */ - long int yg = y + days / 365 - (days % 365 < 0); - - /* Adjust DAYS and Y to match the guessed year. */ - days -= ((yg - y) * 365 - + LEAPS_THRU_END_OF (yg - 1) - - LEAPS_THRU_END_OF (y - 1)); - y = yg; - } - tp->tm_year = y - 1900; - if (tp->tm_year != y - 1900) - { - /* The year cannot be represented due to overflow. */ - // __set_errno (EOVERFLOW); - return 0; - } - tp->tm_yday = days; - ip = __mon_yday[leapyear(y)]; - for (y = 11; days < (long int) ip[y]; --y) - continue; - days -= ip[y]; - tp->tm_mon = y; - tp->tm_mday = days + 1; - return 1; -} - -/* time_r function implementations */ -// G: No time zones in our implementation so just do the converion from gtime_t to struct tm -struct gtm * -__localtime_r (gtime_t * t, struct gtm * tp) -{ - __offtime(t, 0, tp); - return tp; -} - - -/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) - - (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks - were not adjusted between the time stamps. - - The YEAR values uses the same numbering as TP->tm_year. Values - need not be in the usual range. However, YEAR1 must not be less - than 2 * INT_MIN or greater than 2 * INT_MAX. - - The result may overflow. It is the caller's responsibility to - detect overflow. */ - -static inline gtime_t -ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1, - int year0, int yday0, int hour0, int min0, int sec0) -{ - verify (C99_integer_division, -1 / 2 == 0); - verify (long_int_year_and_yday_are_wide_enough, - INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX); - - /* Compute intervening leap days correctly even if year is negative. - Take care to avoid integer overflow here. */ - int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3); - int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3); - int a100 = a4 / 25 - (a4 % 25 < 0); - int b100 = b4 / 25 - (b4 % 25 < 0); - int a400 = SHR (a100, 2); - int b400 = SHR (b100, 2); - int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); - - /* Compute the desired time in gtime_t precision. Overflow might - occur here. */ - gtime_t tyear1 = year1; - gtime_t years = tyear1 - year0; - gtime_t days = 365 * years + yday1 - yday0 + intervening_leap_days; - gtime_t hours = 24 * days + hour1 - hour0; - gtime_t minutes = 60 * hours + min1 - min0; - gtime_t seconds = 60 * minutes + sec1 - sec0; - return seconds; -} - -/* Return a gtime_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), - assuming that *T corresponds to *TP and that no clock adjustments - occurred between *TP and the desired time. - If TP is null, return a value not equal to *T; this avoids false matches. - If overflow occurs, yield the minimal or maximal value, except do not - yield a value equal to *T. */ -static gtime_t -guess_time_tm (long int year, long int yday, int hour, int min, int sec, - gtime_t *t, struct gtm *tp) -{ - if (tp) - { - gtime_t d = ydhms_diff (year, yday, hour, min, sec, - tp->tm_year, tp->tm_yday, - tp->tm_hour, tp->tm_min, tp->tm_sec); - gtime_t t1 = *t + d; - if ((t1 < *t) == (TYPE_SIGNED (gtime_t) ? d < 0 : TIME_T_MAX / 2 < d)) - return t1; - } - - /* Overflow occurred one way or another. Return the nearest result - that is actually in range, except don't report a zero difference - if the actual difference is nonzero, as that would cause a false - match; and don't oscillate between two values, as that would - confuse the spring-forward gap detector. */ - return (*t < TIME_T_MIDPOINT - ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN) - : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX)); -} - -/* Use CONVERT to convert *T to a broken down time in *TP. - If *T is out of range for conversion, adjust it so that - it is the nearest in-range value and then convert that. */ -static struct gtm * -ranged_convert (struct gtm *(*convert) (gtime_t *, struct gtm *), - gtime_t *t, struct gtm *tp) -{ - struct gtm *r = convert (t, tp); - - if (!r && *t) - { - gtime_t bad = *t; - gtime_t ok = 0; - - /* BAD is a known unconvertible gtime_t, and OK is a known good one. - Use binary search to narrow the range between BAD and OK until - they differ by 1. */ - while (bad != ok + (bad < 0 ? -1 : 1)) - { - gtime_t mid = *t = (bad < 0 - ? bad + ((ok - bad) >> 1) - : ok + ((bad - ok) >> 1)); - r = convert (t, tp); - if (r) - ok = mid; - else - bad = mid; - } - - if (!r && ok) - { - /* The last conversion attempt failed; - revert to the most recent successful attempt. */ - *t = ok; - r = convert (t, tp); - } - } - - return r; -} - -/* Convert *TP to a gtime_t value, inverting - the monotonic and mostly-unit-linear conversion function CONVERT. - Use *OFFSET to keep track of a guess at the offset of the result, - compared to what the result would be for UTC without leap seconds. - If *OFFSET's guess is correct, only one CONVERT call is needed. - This function is external because it is used also by timegm.c. */ -gtime_t -__mktime_internal (struct gtm *tp, - struct gtm *(*convert) (gtime_t *, struct gtm *), - gtime_t *offset) -{ - gtime_t t, gt, t0, t1, t2; - struct gtm tm; - - /* The maximum number of probes (calls to CONVERT) should be enough - to handle any combinations of time zone rule changes, solar time, - leap seconds, and oscillations around a spring-forward gap. - POSIX.1 prohibits leap seconds, but some hosts have them anyway. */ - int remaining_probes = 6; - - /* Time requested. Copy it in case CONVERT modifies *TP; this can - occur if TP is localtime's returned value and CONVERT is localtime. */ - int sec = tp->tm_sec; - int min = tp->tm_min; - int hour = tp->tm_hour; - int mday = tp->tm_mday; - int mon = tp->tm_mon; - int year_requested = tp->tm_year; - - /* Ensure that mon is in range, and set year accordingly. */ - int mon_remainder = mon % 12; - int negative_mon_remainder = mon_remainder < 0; - int mon_years = mon / 12 - negative_mon_remainder; - long int lyear_requested = year_requested; - long int year = lyear_requested + mon_years; - - /* The other values need not be in range: - the remaining code handles minor overflows correctly, - assuming int and gtime_t arithmetic wraps around. - Major overflows are caught at the end. */ - - /* Calculate day of year from year, month, and day of month. - The result need not be in range. */ - int mon_yday = ((__mon_yday[leapyear (year)] - [mon_remainder + 12 * negative_mon_remainder]) - - 1); - long int lmday = mday; - long int yday = mon_yday + lmday; - - gtime_t guessed_offset = *offset; - - int sec_requested = sec; - -/* - if (LEAP_SECONDS_POSSIBLE) - { - // Handle out-of-range seconds specially, - // since ydhms_tm_diff assumes every minute has 60 seconds. - if (sec < 0) - sec = 0; - if (59 < sec) - sec = 59; - } -*/ - - /* Invert CONVERT by probing. First assume the same offset as last - time. */ - - t0 = ydhms_diff (year, yday, hour, min, sec, - EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset); - - if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) - { - /* gtime_t isn't large enough to rule out overflows, so check - for major overflows. A gross check suffices, since if t0 - has overflowed, it is off by a multiple of TIME_T_MAX - - TIME_T_MIN + 1. So ignore any component of the difference - that is bounded by a small value. */ - - /* Approximate log base 2 of the number of time units per - biennium. A biennium is 2 years; use this unit instead of - years to avoid integer overflow. For example, 2 average - Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds, - which is 63113904 seconds, and rint (log2 (63113904)) is - 26. */ - int ALOG2_SECONDS_PER_BIENNIUM = 26; - int ALOG2_MINUTES_PER_BIENNIUM = 20; - int ALOG2_HOURS_PER_BIENNIUM = 14; - int ALOG2_DAYS_PER_BIENNIUM = 10; - int LOG2_YEARS_PER_BIENNIUM = 1; - - int approx_requested_biennia = - (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM) - - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM) - + SHR (mday, ALOG2_DAYS_PER_BIENNIUM) - + SHR (hour, ALOG2_HOURS_PER_BIENNIUM) - + SHR (min, ALOG2_MINUTES_PER_BIENNIUM) - + (LEAP_SECONDS_POSSIBLE - ? 0 - : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM))); - - int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM); - int diff = approx_biennia - approx_requested_biennia; - int abs_diff = diff < 0 ? - diff : diff; - - /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously - gives a positive value of 715827882. Setting a variable - first then doing math on it seems to work. - (ghazi@caip.rutgers.edu) */ - gtime_t time_t_max = TIME_T_MAX; - gtime_t time_t_min = TIME_T_MIN; - gtime_t overflow_threshold = - (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM; - - if (overflow_threshold < abs_diff) - { - /* Overflow occurred. Try repairing it; this might work if - the time zone offset is enough to undo the overflow. */ - gtime_t repaired_t0 = -1 - t0; - approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); - diff = approx_biennia - approx_requested_biennia; - abs_diff = diff < 0 ? - diff : diff; - if (overflow_threshold < abs_diff) - return -1; - guessed_offset += repaired_t0 - t0; - t0 = repaired_t0; - } - } - - /* Repeatedly use the error to improve the guess. */ - - for (t = t1 = t2 = t0; - (gt = guess_time_tm (year, yday, hour, min, sec, &t, - ranged_convert (convert, &t, &tm)), - t != gt); - t1 = t2, t2 = t, t = gt) - if (t == t1 && t != t2) - goto offset_found; - else if (--remaining_probes == 0) - return -1; - - offset_found: - *offset = guessed_offset + t - t0; - - if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec) - { - /* Adjust time to reflect the tm_sec requested, not the normalized value. - Also, repair any damage from a false match due to a leap second. */ - int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec; - t1 = t + sec_requested; - t2 = t1 + sec_adjustment; - if (((t1 < t) != (sec_requested < 0)) - | ((t2 < t1) != (sec_adjustment < 0)) - | ! convert (&t2, &tm)) - return -1; - t = t2; - } - - *tp = tm; - return t; -} - -/* Convert *TP to a gtime_t value. */ -gtime_t -gmktime (struct gtm *tp) -{ - // no time zone stuff. Just do the math ;) - static gtime_t localtime_offset; - return __mktime_internal (tp, __localtime_r, &localtime_offset); -} - -/* Fill a (struct tm) TP* from a given gtime_t time stamp */ -gtime_t -filltm(gtime_t *t, struct gtm *tp) -{ - return __offtime(t, 0, tp); -} - -gtime_t g_rtcTime; -uint8_t g_ms100 = 0; // global to allow time set function to reset to zero - -void gettime(struct gtm * tm) -{ - filltm(&g_rtcTime, tm); // create a struct tm date/time structure from global unix time stamp -} - - +#include +#include "opentx.h" + +extern void rtcdriver_settime(struct gtm * t); + +#define LEAP_SECONDS_POSSIBLE 0 + +/* Shift A right by B bits portably, by dividing A by 2**B and + truncating towards minus infinity. A and B should be free of side + effects, and B should be in the range 0 <= B <= INT_BITS - 2, where + INT_BITS is the number of useful bits in an int. GNU code can + assume that INT_BITS is at least 32. + + ISO C99 says that A >> B is implementation-defined if A < 0. Some + implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift + right in the usual way when A < 0, so SHR falls back on division if + ordinary A >> B doesn't seem to be the usual signed shift. */ +#define SHR(a, b) (-1 >> 1 == -1 ? (a) >> (b) : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) + +/* The extra casts in the following macros work around compiler bugs, + e.g., in Cray C 5.0.3.0. */ + +/* True if the arithmetic type T is an integer type. bool counts as + an integer. */ +#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) + +/* True if negative values of the signed integer type T use two's + complement, ones' complement, or signed magnitude representation, + respectively. Much GNU code assumes two's complement, but some + people like to be portable to all possible C hosts. */ +#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) +#define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0) +#define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1) + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : TYPE_SIGNED_MAGNITUDE (t) \ + ? ~ (t) 0 \ + : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))) + +#ifndef TIME_T_MIN +# define TIME_T_MIN TYPE_MINIMUM (gtime_t) +#endif +#ifndef TIME_T_MAX +# define TIME_T_MAX TYPE_MAXIMUM (gtime_t) +#endif +#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1) + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +verify (gtime_t_is_integer, TYPE_IS_INTEGER (gtime_t)); +verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int)); +/* The code also assumes that signed integer overflow silently wraps + around, but this assumption can't be stated without causing a + diagnostic on some hosts. */ + +#define EPOCH_YEAR 1970 +#define TM_YEAR_BASE 1900 +verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0); + +/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */ +static inline int +leapyear (long int year) +{ + /* Don't add YEAR to TM_YEAR_BASE, as that might overflow. + Also, work even if YEAR is negative. */ + return + ((year & 3) == 0 + && (year % 100 != 0 + || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3))); +} + +const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + +/* Compute the `struct tm' representation of *T, + offset OFFSET seconds east of UTC, + and store year, yday, mon, mday, wday, hour, min, sec into *TP. + Return nonzero if successful. */ +int +__offtime ( + gtime_t *t, + long int offset, + struct gtm *tp) +{ + long int days, rem, y; + const unsigned short int *ip; + + days = *t / SECS_PER_DAY; + rem = *t % SECS_PER_DAY; + rem += offset; + while (rem < 0) + { + rem += SECS_PER_DAY; + --days; + } + while (rem >= (long int)SECS_PER_DAY) + { + rem -= SECS_PER_DAY; + ++days; + } + tp->tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + tp->tm_min = rem / 60; + tp->tm_sec = rem % 60; + /* January 1, 1970 was a Thursday. */ + tp->tm_wday = (4 + days) % 7; + if (tp->tm_wday < 0) + tp->tm_wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (leapyear (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + tp->tm_year = y - 1900; + if (tp->tm_year != y - 1900) + { + /* The year cannot be represented due to overflow. */ + // __set_errno (EOVERFLOW); + return 0; + } + tp->tm_yday = days; + ip = __mon_yday[leapyear(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + tp->tm_mon = y; + tp->tm_mday = days + 1; + return 1; +} + +/* time_r function implementations */ +// G: No time zones in our implementation so just do the converion from gtime_t to struct tm +struct gtm * +__localtime_r (gtime_t * t, struct gtm * tp) +{ + __offtime(t, 0, tp); + return tp; +} + + +/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) - + (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks + were not adjusted between the time stamps. + + The YEAR values uses the same numbering as TP->tm_year. Values + need not be in the usual range. However, YEAR1 must not be less + than 2 * INT_MIN or greater than 2 * INT_MAX. + + The result may overflow. It is the caller's responsibility to + detect overflow. */ + +static inline gtime_t +ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1, + int year0, int yday0, int hour0, int min0, int sec0) +{ + verify (C99_integer_division, -1 / 2 == 0); + verify (long_int_year_and_yday_are_wide_enough, + INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX); + + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid integer overflow here. */ + int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3); + int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = SHR (a100, 2); + int b400 = SHR (b100, 2); + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + + /* Compute the desired time in gtime_t precision. Overflow might + occur here. */ + gtime_t tyear1 = year1; + gtime_t years = tyear1 - year0; + gtime_t days = 365 * years + yday1 - yday0 + intervening_leap_days; + gtime_t hours = 24 * days + hour1 - hour0; + gtime_t minutes = 60 * hours + min1 - min0; + gtime_t seconds = 60 * minutes + sec1 - sec0; + return seconds; +} + +/* Return a gtime_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), + assuming that *T corresponds to *TP and that no clock adjustments + occurred between *TP and the desired time. + If TP is null, return a value not equal to *T; this avoids false matches. + If overflow occurs, yield the minimal or maximal value, except do not + yield a value equal to *T. */ +static gtime_t +guess_time_tm (long int year, long int yday, int hour, int min, int sec, + gtime_t *t, struct gtm *tp) +{ + if (tp) + { + gtime_t d = ydhms_diff (year, yday, hour, min, sec, + tp->tm_year, tp->tm_yday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + gtime_t t1 = *t + d; + if ((t1 < *t) == (TYPE_SIGNED (gtime_t) ? d < 0 : TIME_T_MAX / 2 < d)) + return t1; + } + + /* Overflow occurred one way or another. Return the nearest result + that is actually in range, except don't report a zero difference + if the actual difference is nonzero, as that would cause a false + match; and don't oscillate between two values, as that would + confuse the spring-forward gap detector. */ + return (*t < TIME_T_MIDPOINT + ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN) + : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX)); +} + +/* Use CONVERT to convert *T to a broken down time in *TP. + If *T is out of range for conversion, adjust it so that + it is the nearest in-range value and then convert that. */ +static struct gtm * +ranged_convert (struct gtm *(*convert) (gtime_t *, struct gtm *), + gtime_t *t, struct gtm *tp) +{ + struct gtm *r = convert (t, tp); + + if (!r && *t) + { + gtime_t bad = *t; + gtime_t ok = 0; + + /* BAD is a known unconvertible gtime_t, and OK is a known good one. + Use binary search to narrow the range between BAD and OK until + they differ by 1. */ + while (bad != ok + (bad < 0 ? -1 : 1)) + { + gtime_t mid = *t = (bad < 0 + ? bad + ((ok - bad) >> 1) + : ok + ((bad - ok) >> 1)); + r = convert (t, tp); + if (r) + ok = mid; + else + bad = mid; + } + + if (!r && ok) + { + /* The last conversion attempt failed; + revert to the most recent successful attempt. */ + *t = ok; + r = convert (t, tp); + } + } + + return r; +} + +/* Convert *TP to a gtime_t value, inverting + the monotonic and mostly-unit-linear conversion function CONVERT. + Use *OFFSET to keep track of a guess at the offset of the result, + compared to what the result would be for UTC without leap seconds. + If *OFFSET's guess is correct, only one CONVERT call is needed. + This function is external because it is used also by timegm.c. */ +gtime_t +__mktime_internal (struct gtm *tp, + struct gtm *(*convert) (gtime_t *, struct gtm *), + gtime_t *offset) +{ + gtime_t t, gt, t0, t1, t2; + struct gtm tm; + + /* The maximum number of probes (calls to CONVERT) should be enough + to handle any combinations of time zone rule changes, solar time, + leap seconds, and oscillations around a spring-forward gap. + POSIX.1 prohibits leap seconds, but some hosts have them anyway. */ + int remaining_probes = 6; + + /* Time requested. Copy it in case CONVERT modifies *TP; this can + occur if TP is localtime's returned value and CONVERT is localtime. */ + int sec = tp->tm_sec; + int min = tp->tm_min; + int hour = tp->tm_hour; + int mday = tp->tm_mday; + int mon = tp->tm_mon; + int year_requested = tp->tm_year; + + /* Ensure that mon is in range, and set year accordingly. */ + int mon_remainder = mon % 12; + int negative_mon_remainder = mon_remainder < 0; + int mon_years = mon / 12 - negative_mon_remainder; + long int lyear_requested = year_requested; + long int year = lyear_requested + mon_years; + + /* The other values need not be in range: + the remaining code handles minor overflows correctly, + assuming int and gtime_t arithmetic wraps around. + Major overflows are caught at the end. */ + + /* Calculate day of year from year, month, and day of month. + The result need not be in range. */ + int mon_yday = ((__mon_yday[leapyear (year)] + [mon_remainder + 12 * negative_mon_remainder]) + - 1); + long int lmday = mday; + long int yday = mon_yday + lmday; + + gtime_t guessed_offset = *offset; + + int sec_requested = sec; + +/* + if (LEAP_SECONDS_POSSIBLE) + { + // Handle out-of-range seconds specially, + // since ydhms_tm_diff assumes every minute has 60 seconds. + if (sec < 0) + sec = 0; + if (59 < sec) + sec = 59; + } +*/ + + /* Invert CONVERT by probing. First assume the same offset as last + time. */ + + t0 = ydhms_diff (year, yday, hour, min, sec, + EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset); + + if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) + { + /* gtime_t isn't large enough to rule out overflows, so check + for major overflows. A gross check suffices, since if t0 + has overflowed, it is off by a multiple of TIME_T_MAX - + TIME_T_MIN + 1. So ignore any component of the difference + that is bounded by a small value. */ + + /* Approximate log base 2 of the number of time units per + biennium. A biennium is 2 years; use this unit instead of + years to avoid integer overflow. For example, 2 average + Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds, + which is 63113904 seconds, and rint (log2 (63113904)) is + 26. */ + int ALOG2_SECONDS_PER_BIENNIUM = 26; + int ALOG2_MINUTES_PER_BIENNIUM = 20; + int ALOG2_HOURS_PER_BIENNIUM = 14; + int ALOG2_DAYS_PER_BIENNIUM = 10; + int LOG2_YEARS_PER_BIENNIUM = 1; + + int approx_requested_biennia = + (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM) + - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM) + + SHR (mday, ALOG2_DAYS_PER_BIENNIUM) + + SHR (hour, ALOG2_HOURS_PER_BIENNIUM) + + SHR (min, ALOG2_MINUTES_PER_BIENNIUM) + + (LEAP_SECONDS_POSSIBLE + ? 0 + : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM))); + + int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM); + int diff = approx_biennia - approx_requested_biennia; + int abs_diff = diff < 0 ? - diff : diff; + + /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously + gives a positive value of 715827882. Setting a variable + first then doing math on it seems to work. + (ghazi@caip.rutgers.edu) */ + gtime_t time_t_max = TIME_T_MAX; + gtime_t time_t_min = TIME_T_MIN; + gtime_t overflow_threshold = + (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM; + + if (overflow_threshold < abs_diff) + { + /* Overflow occurred. Try repairing it; this might work if + the time zone offset is enough to undo the overflow. */ + gtime_t repaired_t0 = -1 - t0; + approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); + diff = approx_biennia - approx_requested_biennia; + abs_diff = diff < 0 ? - diff : diff; + if (overflow_threshold < abs_diff) + return -1; + guessed_offset += repaired_t0 - t0; + t0 = repaired_t0; + } + } + + /* Repeatedly use the error to improve the guess. */ + + for (t = t1 = t2 = t0; + (gt = guess_time_tm (year, yday, hour, min, sec, &t, + ranged_convert (convert, &t, &tm)), + t != gt); + t1 = t2, t2 = t, t = gt) + if (t == t1 && t != t2) + goto offset_found; + else if (--remaining_probes == 0) + return -1; + + offset_found: + *offset = guessed_offset + t - t0; + + if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec) + { + /* Adjust time to reflect the tm_sec requested, not the normalized value. + Also, repair any damage from a false match due to a leap second. */ + int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec; + t1 = t + sec_requested; + t2 = t1 + sec_adjustment; + if (((t1 < t) != (sec_requested < 0)) + | ((t2 < t1) != (sec_adjustment < 0)) + | ! convert (&t2, &tm)) + return -1; + t = t2; + } + + *tp = tm; + return t; +} + +/* Convert *TP to a gtime_t value. */ +gtime_t +gmktime (struct gtm *tp) +{ + // no time zone stuff. Just do the math ;) + static gtime_t localtime_offset; + return __mktime_internal (tp, __localtime_r, &localtime_offset); +} + +/* Fill a (struct tm) TP* from a given gtime_t time stamp */ +gtime_t +filltm(gtime_t *t, struct gtm *tp) +{ + return __offtime(t, 0, tp); +} + +gtime_t g_rtcTime; +uint8_t g_ms100 = 0; // global to allow time set function to reset to zero + +void gettime(struct gtm * tm) +{ + filltm(&g_rtcTime, tm); // create a struct tm date/time structure from global unix time stamp +} + + diff --git a/radio/src/serial.cpp b/radio/src/serial.cpp index f0773b921..668357046 100644 --- a/radio/src/serial.cpp +++ b/radio/src/serial.cpp @@ -18,40 +18,40 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "serial.h" -#include -#include - -#define PRINTF_BUFFER_SIZE 128 - -void serialPutc(char c) -{ -#if defined(USB_SERIAL) - usbSerialPutc(c); -#else - serial2Putc(c); -#endif -} - -void serialPrintf(const char * format, ...) -{ - va_list arglist; - char tmp[PRINTF_BUFFER_SIZE+1]; - - va_start(arglist, format); - vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist); - tmp[PRINTF_BUFFER_SIZE] = '\0'; - va_end(arglist); - - const char *t = tmp; - while (*t) { - serialPutc(*t++); - } -} - -void serialCrlf() -{ - serialPutc('\r'); - serialPutc('\n'); -} +#include "opentx.h" +#include "serial.h" +#include +#include + +#define PRINTF_BUFFER_SIZE 128 + +void serialPutc(char c) +{ +#if defined(USB_SERIAL) + usbSerialPutc(c); +#else + serial2Putc(c); +#endif +} + +void serialPrintf(const char * format, ...) +{ + va_list arglist; + char tmp[PRINTF_BUFFER_SIZE+1]; + + va_start(arglist, format); + vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist); + tmp[PRINTF_BUFFER_SIZE] = '\0'; + va_end(arglist); + + const char *t = tmp; + while (*t) { + serialPutc(*t++); + } +} + +void serialCrlf() +{ + serialPutc('\r'); + serialPutc('\n'); +} diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index b737620d5..6bf470b1a 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -1,1114 +1,1114 @@ -/* - * 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" - -#define CS_LAST_VALUE_INIT -32768 - -#if defined(PCBFLAMENCO) - #define SWITCH_WARNING_LIST_INTERVAL 20 -#elif defined(PCBHORUS) - #define SWITCH_WARNING_LIST_X WARNING_LINE_X - #define SWITCH_WARNING_LIST_Y WARNING_LINE_Y+3*FH - #define SWITCH_WARNING_LIST_INTERVAL 35 -#elif defined(PCBTARANIS) - #define SWITCH_WARNING_LIST_X 60 - #define SWITCH_WARNING_LIST_Y 4*FH+3 -#else -#endif - -#if defined(CPUARM) - -enum LogicalSwitchContextState { - SWITCH_START, - SWITCH_DELAY, - SWITCH_ENABLE -}; - -PACK(typedef struct { - uint8_t state:1; - uint8_t timerState:2; - uint8_t spare:5; - uint8_t timer; - int16_t lastValue; -}) LogicalSwitchContext; - -PACK(typedef struct { - LogicalSwitchContext lsw[NUM_LOGICAL_SWITCH]; -}) LogicalSwitchesFlightModeContext; -LogicalSwitchesFlightModeContext lswFm[MAX_FLIGHT_MODES]; - -#define LS_LAST_VALUE(fm, idx) lswFm[fm].lsw[idx].lastValue - -#else - -int16_t lsLastValue[NUM_LOGICAL_SWITCH]; -#define LS_LAST_VALUE(fm, idx) lsLastValue[idx] - -volatile GETSWITCH_RECURSIVE_TYPE s_last_switch_used = 0; -volatile GETSWITCH_RECURSIVE_TYPE s_last_switch_value = 0; - -#endif - -#if defined(PCBFLAMENCO) -tmr10ms_t potsLastposStart[1]; -uint8_t potsPos[1]; -tmr10ms_t switchesMidposStart[2]; -uint64_t switchesPos = 0; -div_t switchInfo(int switchPosition) -{ - const div_t infos[] = { - { 0, 0 }, { 0, 1 }, { 0, 2 }, - { 1, 0 }, { 1, 1 }, - { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 }, { 2, 4 }, { 2, 5 }, - { 4, 0 }, { 4, 1 }, - { 5, 0 }, { 5, 1 }, { 5, 2 }, - }; - return infos[switchPosition-SWSRC_FIRST_SWITCH]; -} - -uint32_t check2PosSwitchPosition(EnumKeys sw) -{ - uint32_t result; - uint32_t index; - - if (switchState(sw)) - index = sw - SW_SA0; - else - index = sw - SW_SA0 + 1; - - result = ((uint32_t)1 << index); - - if (!(switchesPos & result)) { - PLAY_SWITCH_MOVED(index); - } - - return result; -} - -uint32_t check3PosSwitchPosition(int idx, EnumKeys sw, bool startup) -{ - uint32_t result; - uint32_t index; - - if (switchState(sw)) { - index = sw - SW_SA0; - result = (1 << index); - switchesMidposStart[idx] = 0; - } - else if (switchState(EnumKeys(sw+2))) { - index = sw - SW_SA0 + 2; - result = (1 << index); - switchesMidposStart[idx] = 0; - } - else if (startup || (switchesPos & (1 << (sw - SW_SA0 + 1))) || g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (switchesMidposStart[idx] && (tmr10ms_t)(get_tmr10ms() - switchesMidposStart[idx]) > SWITCHES_DELAY())) { - index = sw - SW_SA0 + 1; - result = (1 << index); - switchesMidposStart[idx] = 0; - } - else { - index = sw - SW_SA0 + 1; - if (!switchesMidposStart[idx]) { - switchesMidposStart[idx] = get_tmr10ms(); - } - result = (switchesPos & (0x7 << (sw - SW_SA0))); - } - - if (!(switchesPos & result)) { - PLAY_SWITCH_MOVED(index); - } - - return result; -} - -#define CHECK_2POS(sw) newPos |= check2PosSwitchPosition(sw ## 0) -#define CHECK_3POS(idx, sw) newPos |= check3PosSwitchPosition(idx, sw ## 0, startup) - -void getSwitchesPosition(bool startup) -{ - uint64_t newPos = 0; - CHECK_3POS(0, SW_SA); - CHECK_2POS(SW_SB); - CHECK_2POS(SW_SE); - CHECK_3POS(1, SW_SF); - switchesPos = newPos; - - int i = 0; - StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+i]; - if (calib->count>0 && calib->countcount); - uint8_t previousPos = potsPos[i] >> 4; - uint8_t previousStoredPos = potsPos[i] & 0x0F; - if (startup) { - potsPos[i] = (pos << 4) | pos; - } - else if (pos != previousPos) { - potsLastposStart[i] = get_tmr10ms(); - potsPos[i] = (pos << 4) | previousStoredPos; - } - else if (g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (tmr10ms_t)(get_tmr10ms() - potsLastposStart[i]) > SWITCHES_DELAY()) { - potsLastposStart[i] = 0; - potsPos[i] = (pos << 4) | pos; - if (previousStoredPos != pos) { - PLAY_SWITCH_MOVED(SWSRC_LAST_SWITCH+i*XPOTS_MULTIPOS_COUNT+pos); - } - } - } -} -#endif - -#if defined(PCBTARANIS) || defined(PCBHORUS) -#if defined(REV9E) -tmr10ms_t switchesMidposStart[16]; -#else -tmr10ms_t switchesMidposStart[6]; // TODO constant -#endif -uint64_t switchesPos = 0; -tmr10ms_t potsLastposStart[NUM_XPOTS]; -uint8_t potsPos[NUM_XPOTS]; - -#define SWITCH_POSITION(sw) (switchesPos & ((MASK_CFN_TYPE)1<<(sw))) -#define POT_POSITION(sw) ((potsPos[(sw)/XPOTS_MULTIPOS_COUNT] & 0x0f) == ((sw) % XPOTS_MULTIPOS_COUNT)) - -div_t switchInfo(int switchPosition) -{ - return div(switchPosition-SWSRC_FIRST_SWITCH, 3); -} - -uint64_t check2PosSwitchPosition(EnumKeys sw) -{ - uint64_t result; - uint32_t index; - - if (switchState(sw)) - index = sw - SW_SA0; - else - index = sw - SW_SA0 + 2; - - result = ((uint64_t)1 << index); - - if (!(switchesPos & result)) { - PLAY_SWITCH_MOVED(index); - } - - return result; -} - -uint64_t check3PosSwitchPosition(uint8_t idx, EnumKeys sw, bool startup) -{ - uint64_t result; - uint32_t index; - - if (switchState(sw)) { - index = sw - SW_SA0; - result = ((MASK_CFN_TYPE)1 << index); - switchesMidposStart[idx] = 0; - } - else if (switchState(EnumKeys(sw+2))) { - index = sw - SW_SA0 + 2; - result = ((MASK_CFN_TYPE)1 << index); - switchesMidposStart[idx] = 0; - } - else { - index = sw - SW_SA0 + 1; - if (startup || SWITCH_POSITION(index) || g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (switchesMidposStart[idx] && (tmr10ms_t)(get_tmr10ms() - switchesMidposStart[idx]) > SWITCHES_DELAY())) { - result = ((MASK_CFN_TYPE)1 << index); - switchesMidposStart[idx] = 0; - } - else { - result = (switchesPos & ((MASK_CFN_TYPE)0x7 << (sw - SW_SA0))); - if (!switchesMidposStart[idx]) { - switchesMidposStart[idx] = get_tmr10ms(); - } - } - } - - if (!(switchesPos & result)) { - PLAY_SWITCH_MOVED(index); - } - - return result; -} - -#define CHECK_2POS(sw) newPos |= check2PosSwitchPosition(sw ## 0) -#define CHECK_3POS(idx, sw) newPos |= check3PosSwitchPosition(idx, sw ## 0, startup) - -void getSwitchesPosition(bool startup) -{ - uint64_t newPos = 0; - CHECK_3POS(0, SW_SA); - CHECK_3POS(1, SW_SB); - CHECK_3POS(2, SW_SC); - CHECK_3POS(3, SW_SD); - CHECK_3POS(4, SW_SE); - CHECK_2POS(SW_SF); - CHECK_3POS(5, SW_SG); - CHECK_2POS(SW_SH); - -#if defined(REV9E) - CHECK_3POS(6, SW_SI); - CHECK_3POS(7, SW_SJ); - CHECK_3POS(8, SW_SK); - CHECK_3POS(9, SW_SL); - CHECK_3POS(10, SW_SM); - CHECK_3POS(11, SW_SN); - CHECK_3POS(12, SW_SO); - CHECK_3POS(13, SW_SP); - CHECK_3POS(14, SW_SQ); - CHECK_3POS(15, SW_SR); -#endif - - switchesPos = newPos; - - for (int i=0; icount); - uint8_t previousPos = potsPos[i] >> 4; - uint8_t previousStoredPos = potsPos[i] & 0x0F; - if (startup) { - potsPos[i] = (pos << 4) | pos; - } - else if (pos != previousPos) { - potsLastposStart[i] = get_tmr10ms(); - potsPos[i] = (pos << 4) | previousStoredPos; - } - else if (g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (tmr10ms_t)(get_tmr10ms() - potsLastposStart[i]) > SWITCHES_DELAY()) { - potsLastposStart[i] = 0; - potsPos[i] = (pos << 4) | pos; - if (previousStoredPos != pos) { - PLAY_SWITCH_MOVED(SWSRC_LAST_SWITCH+i*XPOTS_MULTIPOS_COUNT+pos); - } - } - } - } - } -} - - -getvalue_t getValueForLogicalSwitch(mixsrc_t i) -{ - getvalue_t result = getValue(i); - if (i>=MIXSRC_FIRST_INPUT && i<=MIXSRC_LAST_INPUT) { - int8_t trimIdx = virtualInputsTrims[i-MIXSRC_FIRST_INPUT]; - if (trimIdx >= 0) { - int16_t trim = trims[trimIdx]; - if (trimIdx == THR_STICK && g_model.throttleReversed) - result -= trim; - else - result += trim; - } - } - return result; -} -#else - #define getValueForLogicalSwitch(i) getValue(i) -#endif - -PACK(typedef struct { - uint8_t state; - uint8_t last; -}) ls_sticky_struct; - -PACK(typedef struct { - uint16_t state:1; - uint16_t duration:15; -}) ls_stay_struct; - -bool getLogicalSwitch(uint8_t idx) -{ - LogicalSwitchData * ls = lswAddress(idx); - bool result; - -#if defined(CPUARM) - swsrc_t s = ls->andsw; -#else - uint8_t s = ls->andsw; - if (s > SWSRC_LAST_SWITCH) { - s += SWSRC_SW1-SWSRC_LAST_SWITCH-1; - } -#endif - - if (ls->func == LS_FUNC_NONE || (s && !getSwitch(s))) { -#if defined(CPUARM) - if (ls->func != LS_FUNC_STICKY && ls->func != LS_FUNC_EDGE ) { -#else - if (ls->func != LS_FUNC_STICKY) { -#endif - // AND switch must not affect STICKY and EDGE processing - LS_LAST_VALUE(mixerCurrentFlightMode, idx) = CS_LAST_VALUE_INIT; - } - result = false; - } - else if ((s=lswFamily(ls->func)) == LS_FAMILY_BOOL) { - bool res1 = getSwitch(ls->v1); - bool res2 = getSwitch(ls->v2); - switch (ls->func) { - case LS_FUNC_AND: - result = (res1 && res2); - break; - case LS_FUNC_OR: - result = (res1 || res2); - break; - // case LS_FUNC_XOR: - default: - result = (res1 ^ res2); - break; - } - } - else if (s == LS_FAMILY_TIMER) { - result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) <= 0); - } - else if (s == LS_FAMILY_STICKY) { - result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0)); - } -#if defined(CPUARM) - else if (s == LS_FAMILY_EDGE) { - result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0)); - } -#endif - else { - getvalue_t x = getValueForLogicalSwitch(ls->v1); - getvalue_t y; - if (s == LS_FAMILY_COMP) { - y = getValueForLogicalSwitch(ls->v2); - - switch (ls->func) { - case LS_FUNC_EQUAL: - result = (x==y); - break; - case LS_FUNC_GREATER: - result = (x>y); - break; - default: - result = (xv1; -#if defined(FRSKY) - // Telemetry - if (v1 >= MIXSRC_FIRST_TELEM) { -#if defined(CPUARM) - if (!TELEMETRY_STREAMING() || IS_FAI_FORBIDDEN(v1-1)) { -#else - if ((!TELEMETRY_STREAMING() && v1 >= MIXSRC_FIRST_TELEM+TELEM_FIRST_STREAMED_VALUE-1) || IS_FAI_FORBIDDEN(v1-1)) { -#endif - result = false; - goto DurationAndDelayProcessing; - } - - y = convertLswTelemValue(ls); - -#if defined(GAUGES) && !defined(CPUARM) - // Fill the telemetry bars threshold array - if (s == LS_FAMILY_OFS) { - uint8_t idx = v1-MIXSRC_FIRST_TELEM+1-TELEM_ALT; - if (idx < THLD_MAX) { - FILL_THRESHOLD(idx, ls->v2); - } - } -#endif - - } - else if (v1 >= MIXSRC_GVAR1) { - y = ls->v2; - } - else { - y = calc100toRESX(ls->v2); - } -#else - if (v1 >= MIXSRC_FIRST_TELEM) { - y = (int16_t)3 * (128+ls->v2); // it's a Timer - } - else if (v1 >= MIXSRC_GVAR1) { - y = ls->v2; // it's a GVAR - } - else { - y = calc100toRESX(ls->v2); - } -#endif - - switch (ls->func) { -#if defined(CPUARM) - case LS_FUNC_VEQUAL: - result = (x==y); - break; -#endif - case LS_FUNC_VALMOSTEQUAL: -#if defined(GVARS) - if (v1 >= MIXSRC_GVAR1 && v1 <= MIXSRC_LAST_GVAR) - result = (x==y); - else -#endif - result = (abs(x-y) < (1024 / STICK_TOLERANCE)); - break; - case LS_FUNC_VPOS: - result = (x>y); - break; - case LS_FUNC_VNEG: - result = (xy); - break; - case LS_FUNC_ANEG: - result = (abs(x)func == LS_FUNC_DIFFEGREATER) { - if (y >= 0) { - result = (diff >= y); - if (diff < 0) - update = true; - } - else { - result = (diff <= y); - if (diff > 0) - update = true; - } - } - else { - result = (abs(diff) >= y); - } - if (result || update) { - LS_LAST_VALUE(mixerCurrentFlightMode, idx) = x; - } - break; - } - } - } - } - -#if defined(FRSKY) -DurationAndDelayProcessing: -#endif - -#if defined(CPUARM) - if (ls->delay || ls->duration) { - LogicalSwitchContext &context = lswFm[mixerCurrentFlightMode].lsw[idx]; - if (result) { - if (context.timerState == SWITCH_START) { - // set delay timer - context.timerState = SWITCH_DELAY; - context.timer = (ls->func == LS_FUNC_EDGE ? 0 : ls->delay); - } - - if (context.timerState == SWITCH_DELAY) { - if (context.timer) { - result = false; // return false while delay timer running - } - else { - // set duration timer - context.timerState = SWITCH_ENABLE; - context.timer = ls->duration; - } - } - - if (context.timerState == SWITCH_ENABLE) { - result = (ls->duration==0 || context.timer>0); // return false after duration timer runs out - if (!result && ls->func == LS_FUNC_STICKY) { - ls_sticky_struct & lastValue = (ls_sticky_struct &)context.lastValue; - lastValue.state = 0; - } - } - } - else if (context.timerState == SWITCH_ENABLE && ls->duration > 0 && context.timer > 0) { - result = true; - } - else { - context.timerState = SWITCH_START; - context.timer = 0; - } - } -#endif - - return result; -} - -#if defined(CPUARM) -bool getSwitch(swsrc_t swtch, uint8_t flags) -#else -bool getSwitch(swsrc_t swtch) -#endif -{ - bool result; - - if (swtch == SWSRC_NONE) - return true; - - uint8_t cs_idx = abs(swtch); - - if (cs_idx == SWSRC_ONE) { - result = !s_mixer_first_run_done; - } - else if (cs_idx == SWSRC_ON) { - result = true; - } - else if (cs_idx <= SWSRC_LAST_SWITCH) { -#if defined(PCBTARANIS) || defined(PCBHORUS) // TODO || defined(PCBFLAMENCO) - if (flags & GETSWITCH_MIDPOS_DELAY) - result = SWITCH_POSITION(cs_idx-SWSRC_FIRST_SWITCH); - else - result = switchState((EnumKeys)(SW_BASE+cs_idx-SWSRC_FIRST_SWITCH)); -#else - result = switchState((EnumKeys)(SW_BASE+cs_idx-SWSRC_FIRST_SWITCH)); -#endif - -#if defined(MODULE_ALWAYS_SEND_PULSES) - if (startupWarningState < STARTUP_WARNING_DONE) { - // if throttle or switch warning is currently active, ignore actual stick position and use wanted values - if (cs_idx <= 3) { - if (!(g_model.switchWarningEnable & 1)) { // ID1 to ID3 is just one bit in switchWarningEnable - result = (cs_idx)==((g_model.switchWarningState & 3)+1); // overwrite result with desired value - } - } - else if (!(g_model.switchWarningEnable & (1<<(cs_idx-3)))) { - // current switch should not be ignored for warning - result = g_model.switchWarningState & (1<<(cs_idx-2)); // overwrite result with desired value - } - } -#endif - } -#if NUM_XPOTS > 0 - else if (cs_idx <= SWSRC_LAST_MULTIPOS_SWITCH) { - result = POT_POSITION(cs_idx-SWSRC_FIRST_MULTIPOS_SWITCH); - } -#endif - else if (cs_idx <= SWSRC_LAST_TRIM) { - uint8_t idx = cs_idx - SWSRC_FIRST_TRIM; - idx = (CONVERT_MODE(idx/2) << 1) + (idx & 1); - result = trimDown(idx); - } -#if ROTARY_ENCODERS > 0 - else if (cs_idx == SWSRC_REa) { - result = REA_DOWN(); - } -#endif -#if ROTARY_ENCODERS > 1 - else if (cs_idx == SWSRC_REb) { - result = REB_DOWN(); - } -#endif -#if defined(CPUARM) - else if (cs_idx >= SWSRC_FIRST_SENSOR) { - result = !telemetryItems[cs_idx-SWSRC_FIRST_SENSOR].isOld(); - } - else if (cs_idx == SWSRC_TELEMETRY_STREAMING) { - result = TELEMETRY_STREAMING(); - } - else if (cs_idx >= SWSRC_FIRST_FLIGHT_MODE) { -#if defined(FLIGHT_MODES) - uint8_t idx = cs_idx - SWSRC_FIRST_FLIGHT_MODE; - if (flags & GETSWITCH_MIDPOS_DELAY) - result = (idx == flightModeTransitionLast); - else - result = (idx == mixerCurrentFlightMode); -#else - result = false; -#endif - } -#endif - else { - cs_idx -= SWSRC_FIRST_LOGICAL_SWITCH; -#if defined(CPUARM) - result = lswFm[mixerCurrentFlightMode].lsw[cs_idx].state; -#else - GETSWITCH_RECURSIVE_TYPE mask = ((GETSWITCH_RECURSIVE_TYPE)1 << cs_idx); - if (s_last_switch_used & mask) { - result = (s_last_switch_value & mask); - } - else { - s_last_switch_used |= mask; - result = getLogicalSwitch(cs_idx); - if (result) { - s_last_switch_value |= mask; - } - else { - s_last_switch_value &= ~mask; - } - } -#endif - } - - return swtch > 0 ? result : !result; -} - -#if defined(CPUARM) -/** - @brief Calculates new state of logical switches for mixerCurrentFlightMode -*/ -void evalLogicalSwitches(bool isCurrentPhase) -{ - for (unsigned int idx=0; idx> (i*3); - uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024; - if (prev != next) { - switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*3)); - if (i == 0) - result = 1+next; - else if (i == 1) - result = 1+(3*1)+(next!=0); - else if (i == 3) - result = 12+(next!=0); - else if (i == 4) - result = 14+next; - else - result = 0; - } - } -#endif -#elif defined(PCBTARANIS) || defined(PCBHORUS) - for (int i=0; i> (i*2); - uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024; - if (prev != next) { - switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*2)); - result = 1+(3*i)+next; - } - } - } -#else - // return delivers 1 to 3 for ID1 to ID3 - // 4..8 for all other switches if changed to true - // -4..-8 for all other switches if changed to false - // 9 for Trainer switch if changed to true; Change to false is ignored - swarnstate_t mask = 0x80; - for (uint8_t i=NUM_PSWITCH; i>1; i--) { - bool prev; - prev = (switches_states & mask); - // don't use getSwitch here to always get the proper value, even getSwitch manipulates - bool next = switchState((EnumKeys)(SW_BASE+i-1)); - if (prev != next) { - if (((i3)) || next==true) - result = next ? i : -i; - if (i<=3 && result==0) result = 1; - switches_states ^= mask; - } - mask >>= 1; - } -#endif - - if ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10) - result = 0; - - s_move_last_time = get_tmr10ms(); - return result; -} - -#if defined(GUI) -void checkSwitches() -{ -#if defined(MODULE_ALWAYS_SEND_PULSES) - static swarnstate_t last_bad_switches = 0xff; -#else - swarnstate_t last_bad_switches = 0xff; -#endif - swarnstate_t states = g_model.switchWarningState; - -#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) - uint8_t bad_pots = 0, last_bad_pots = 0xff; -#endif - -#if !defined(MODULE_ALWAYS_SEND_PULSES) - while (1) { - -#if defined(TELEMETRY_MOD_14051) || defined(TELEMETRY_MOD_14051_SWAPPED) -// FIXME: One getADC() call only reads one 14051 MUX input. To have all switch states updated, we need to call it MUX_MAX+1 times. -#define GETADC_COUNT (MUX_MAX+1) -#elif defined(PCBTARANIS) || defined(PCBFLAMENCO) -#define GETADC_COUNT 1 -#endif -#ifdef GETADC_COUNT - for (uint8_t i=0; i> (3*i)) & 0x07); - if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) { - warn = true; - } - } - } -#elif defined(PCBTARANIS) - for (int i=0; i 1)) { - warn = true; - bad_pots |= (1<> (3*i)) & 0x07); - if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) { - if (++numWarnings < 6) { - // LcdFlags attr = ((states & mask) == (switches_states & mask)) ? TEXT_COLOR : ALARM_COLOR; - LcdFlags attr = ALARM_COLOR; - putsSwitches(x, y, SWSRC_FIRST_SWITCH+i*3+state-1, attr); - x += SWITCH_WARNING_LIST_INTERVAL; - } - else if (numWarnings == 6) { - lcdDrawText(x, y, "...", ALARM_COLOR); - } - } - } -#else - if (SWITCH_WARNING_ALLOWED(i) && !(g_model.switchWarningEnable & (1<> (i*2)]; - putsMixerSource(x, y, MIXSRC_FIRST_SWITCH+i, attr); - lcdDrawChar(lcdNextPos, y, c, attr); - x = lcdNextPos + 3; - } - else if (numWarnings == 7) { - lcdDrawText(x, y, "...", 0); - } - } - } -#endif - } - - if (g_model.potsWarnMode) { - if (y == 4*FH+3) { - y = 6*FH-2; - x = 60; - } - for (int i=0; i 1) { -#if defined(COLORLCD) - char s[8]; - // TODO add an helper - strncpy(s, &STR_VSRCRAW[1+(NUM_STICKS+1+i)*STR_VSRCRAW[0]], STR_VSRCRAW[0]); - s[int(STR_VSRCRAW[0])] = '\0'; -#else - lcdDrawTextAtIndex(x, y, STR_VSRCRAW, NUM_STICKS+1+i, INVERS); - if (IS_POT(POT1+i)) - lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? 126 : 127, INVERS); - else - lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? '\300' : '\301', INVERS); -#endif -#if defined(COLORLCD) - if (++numWarnings < 6) { - lcdDrawText(x, y, s, ALARM_COLOR); - } - else if (numWarnings == 6) { - lcdDrawText(x, y, "...", ALARM_COLOR); - } - x += 20; -#else - x = lcdNextPos + 3; -#endif - } - - } - } - } - last_bad_pots = bad_pots; -#else - if (last_bad_switches != switches_states) { - MESSAGE(STR_SWITCHWARN, NULL, STR_PRESSANYKEYTOSKIP, last_bad_switches == 0xff ? AU_SWITCH_ALERT : AU_NONE); - uint8_t x = 2; - for (uint8_t i=0; i0?(i+3):(states&0x3)+1), attr); - x += 3*FW+FW/2; - } -#endif - - lcdRefresh(); - lcdSetContrast(); - clearKeyEvents(); - - last_bad_switches = switches_states; - } - -#if defined(MODULE_ALWAYS_SEND_PULSES) - if (pwrCheck()==e_power_off || keyDown()) { - startupWarningState = STARTUP_WARNING_SWITCHES+1; - last_bad_switches = 0xff; - } -#else - if (pwrCheck()==e_power_off || keyDown()) break; - - doLoopCommonActions(); - - wdt_reset(); - - SIMU_SLEEP(1); - } -#endif - - LED_ERROR_END(); -} -#endif // GUI - -void logicalSwitchesTimerTick() -{ -#if defined(CPUARM) - for (uint8_t fm=0; fmfunc == LS_FUNC_TIMER) { - int16_t *lastValue = &LS_LAST_VALUE(fm, i); - if (*lastValue == 0 || *lastValue == CS_LAST_VALUE_INIT) { - *lastValue = -lswTimerValue(ls->v1); - } - else if (*lastValue < 0) { - if (++(*lastValue) == 0) - *lastValue = lswTimerValue(ls->v2); - } - else { // if (*lastValue > 0) - *lastValue -= 1; - } - } - else if (ls->func == LS_FUNC_STICKY) { - ls_sticky_struct & lastValue = (ls_sticky_struct &)LS_LAST_VALUE(fm, i); - bool before = lastValue.last & 0x01; - if (lastValue.state) { - bool now = getSwitch(ls->v2); - if (now != before) { - lastValue.last ^= 1; - if (!before) { - lastValue.state = 0; - } - } - } - else { - bool now = getSwitch(ls->v1); - if (before != now) { - lastValue.last ^= 1; - if (!before) { - lastValue.state = 1; - } - } - } - } -#if defined(CPUARM) - else if (ls->func == LS_FUNC_EDGE) { - ls_stay_struct & lastValue = (ls_stay_struct &)LS_LAST_VALUE(fm, i); - // if this ls was reset by the logicalSwitchesReset() the lastValue will be set to CS_LAST_VALUE_INIT(0x8000) - // when it is unpacked into ls_stay_struct the lastValue.duration will have a value of 0x4000 - // this will produce an instant true for edge logical switch if the second parameter is big enough. - // So we reset it here. - if (LS_LAST_VALUE(fm, i) == CS_LAST_VALUE_INIT) { - lastValue.duration = 0; - } - lastValue.state = false; - bool state = getSwitch(ls->v1); - if (state) { - if (ls->v3 == -1 && lastValue.duration == lswTimerValue(ls->v2)) - lastValue.state = true; - if (lastValue.duration < 1000) - lastValue.duration++; - } - else { - if (lastValue.duration > lswTimerValue(ls->v2) && (ls->v3 == 0 || lastValue.duration <= lswTimerValue(ls->v2+ls->v3))) - lastValue.state = true; - lastValue.duration = 0; - } - } - - // decrement delay/duration timer - LogicalSwitchContext &context = lswFm[fm].lsw[i]; - if (context.timer) { - context.timer--; - } -#endif - } -#if defined(CPUARM) - } -#endif -} - -LogicalSwitchData * lswAddress(uint8_t idx) -{ - return &g_model.logicalSw[idx]; -} - -uint8_t lswFamily(uint8_t func) -{ - if (func <= LS_FUNC_ANEG) - return LS_FAMILY_OFS; - else if (func <= LS_FUNC_XOR) - return LS_FAMILY_BOOL; -#if defined(CPUARM) - else if (func == LS_FUNC_EDGE) - return LS_FAMILY_EDGE; -#endif - else if (func <= LS_FUNC_LESS) - return LS_FAMILY_COMP; - else if (func <= LS_FUNC_ADIFFEGREATER) - return LS_FAMILY_DIFF; - else - return LS_FAMILY_TIMER+func-LS_FUNC_TIMER; -} - -int16_t lswTimerValue(delayval_t val) -{ - return (val < -109 ? 129+val : (val < 7 ? (113+val)*5 : (53+val)*10)); -} - -void logicalSwitchesReset() -{ -#if defined(CPUARM) - flightModeTransitionLast = 255; - memset(lswFm, 0, sizeof(lswFm)); -#else - s_last_switch_value = 0; -#endif - -#if defined(CPUARM) - for (uint8_t fm=0; fmv1 - MIXSRC_FIRST_TELEM + 1, ls->v2); -#else - if (lswFamily(ls->func)==LS_FAMILY_OFS) - val = convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128+ls->v2); - else - val = convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128+ls->v2) - convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128); -#endif - return val; -} - -#if defined(CPUARM) -void logicalSwitchesCopyState(uint8_t src, uint8_t dst) -{ - lswFm[dst] = lswFm[src]; -} -#endif +/* + * 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" + +#define CS_LAST_VALUE_INIT -32768 + +#if defined(PCBFLAMENCO) + #define SWITCH_WARNING_LIST_INTERVAL 20 +#elif defined(PCBHORUS) + #define SWITCH_WARNING_LIST_X WARNING_LINE_X + #define SWITCH_WARNING_LIST_Y WARNING_LINE_Y+3*FH + #define SWITCH_WARNING_LIST_INTERVAL 35 +#elif defined(PCBTARANIS) + #define SWITCH_WARNING_LIST_X 60 + #define SWITCH_WARNING_LIST_Y 4*FH+3 +#else +#endif + +#if defined(CPUARM) + +enum LogicalSwitchContextState { + SWITCH_START, + SWITCH_DELAY, + SWITCH_ENABLE +}; + +PACK(typedef struct { + uint8_t state:1; + uint8_t timerState:2; + uint8_t spare:5; + uint8_t timer; + int16_t lastValue; +}) LogicalSwitchContext; + +PACK(typedef struct { + LogicalSwitchContext lsw[NUM_LOGICAL_SWITCH]; +}) LogicalSwitchesFlightModeContext; +LogicalSwitchesFlightModeContext lswFm[MAX_FLIGHT_MODES]; + +#define LS_LAST_VALUE(fm, idx) lswFm[fm].lsw[idx].lastValue + +#else + +int16_t lsLastValue[NUM_LOGICAL_SWITCH]; +#define LS_LAST_VALUE(fm, idx) lsLastValue[idx] + +volatile GETSWITCH_RECURSIVE_TYPE s_last_switch_used = 0; +volatile GETSWITCH_RECURSIVE_TYPE s_last_switch_value = 0; + +#endif + +#if defined(PCBFLAMENCO) +tmr10ms_t potsLastposStart[1]; +uint8_t potsPos[1]; +tmr10ms_t switchesMidposStart[2]; +uint64_t switchesPos = 0; +div_t switchInfo(int switchPosition) +{ + const div_t infos[] = { + { 0, 0 }, { 0, 1 }, { 0, 2 }, + { 1, 0 }, { 1, 1 }, + { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 }, { 2, 4 }, { 2, 5 }, + { 4, 0 }, { 4, 1 }, + { 5, 0 }, { 5, 1 }, { 5, 2 }, + }; + return infos[switchPosition-SWSRC_FIRST_SWITCH]; +} + +uint32_t check2PosSwitchPosition(EnumKeys sw) +{ + uint32_t result; + uint32_t index; + + if (switchState(sw)) + index = sw - SW_SA0; + else + index = sw - SW_SA0 + 1; + + result = ((uint32_t)1 << index); + + if (!(switchesPos & result)) { + PLAY_SWITCH_MOVED(index); + } + + return result; +} + +uint32_t check3PosSwitchPosition(int idx, EnumKeys sw, bool startup) +{ + uint32_t result; + uint32_t index; + + if (switchState(sw)) { + index = sw - SW_SA0; + result = (1 << index); + switchesMidposStart[idx] = 0; + } + else if (switchState(EnumKeys(sw+2))) { + index = sw - SW_SA0 + 2; + result = (1 << index); + switchesMidposStart[idx] = 0; + } + else if (startup || (switchesPos & (1 << (sw - SW_SA0 + 1))) || g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (switchesMidposStart[idx] && (tmr10ms_t)(get_tmr10ms() - switchesMidposStart[idx]) > SWITCHES_DELAY())) { + index = sw - SW_SA0 + 1; + result = (1 << index); + switchesMidposStart[idx] = 0; + } + else { + index = sw - SW_SA0 + 1; + if (!switchesMidposStart[idx]) { + switchesMidposStart[idx] = get_tmr10ms(); + } + result = (switchesPos & (0x7 << (sw - SW_SA0))); + } + + if (!(switchesPos & result)) { + PLAY_SWITCH_MOVED(index); + } + + return result; +} + +#define CHECK_2POS(sw) newPos |= check2PosSwitchPosition(sw ## 0) +#define CHECK_3POS(idx, sw) newPos |= check3PosSwitchPosition(idx, sw ## 0, startup) + +void getSwitchesPosition(bool startup) +{ + uint64_t newPos = 0; + CHECK_3POS(0, SW_SA); + CHECK_2POS(SW_SB); + CHECK_2POS(SW_SE); + CHECK_3POS(1, SW_SF); + switchesPos = newPos; + + int i = 0; + StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+i]; + if (calib->count>0 && calib->countcount); + uint8_t previousPos = potsPos[i] >> 4; + uint8_t previousStoredPos = potsPos[i] & 0x0F; + if (startup) { + potsPos[i] = (pos << 4) | pos; + } + else if (pos != previousPos) { + potsLastposStart[i] = get_tmr10ms(); + potsPos[i] = (pos << 4) | previousStoredPos; + } + else if (g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (tmr10ms_t)(get_tmr10ms() - potsLastposStart[i]) > SWITCHES_DELAY()) { + potsLastposStart[i] = 0; + potsPos[i] = (pos << 4) | pos; + if (previousStoredPos != pos) { + PLAY_SWITCH_MOVED(SWSRC_LAST_SWITCH+i*XPOTS_MULTIPOS_COUNT+pos); + } + } + } +} +#endif + +#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(REV9E) +tmr10ms_t switchesMidposStart[16]; +#else +tmr10ms_t switchesMidposStart[6]; // TODO constant +#endif +uint64_t switchesPos = 0; +tmr10ms_t potsLastposStart[NUM_XPOTS]; +uint8_t potsPos[NUM_XPOTS]; + +#define SWITCH_POSITION(sw) (switchesPos & ((MASK_CFN_TYPE)1<<(sw))) +#define POT_POSITION(sw) ((potsPos[(sw)/XPOTS_MULTIPOS_COUNT] & 0x0f) == ((sw) % XPOTS_MULTIPOS_COUNT)) + +div_t switchInfo(int switchPosition) +{ + return div(switchPosition-SWSRC_FIRST_SWITCH, 3); +} + +uint64_t check2PosSwitchPosition(EnumKeys sw) +{ + uint64_t result; + uint32_t index; + + if (switchState(sw)) + index = sw - SW_SA0; + else + index = sw - SW_SA0 + 2; + + result = ((uint64_t)1 << index); + + if (!(switchesPos & result)) { + PLAY_SWITCH_MOVED(index); + } + + return result; +} + +uint64_t check3PosSwitchPosition(uint8_t idx, EnumKeys sw, bool startup) +{ + uint64_t result; + uint32_t index; + + if (switchState(sw)) { + index = sw - SW_SA0; + result = ((MASK_CFN_TYPE)1 << index); + switchesMidposStart[idx] = 0; + } + else if (switchState(EnumKeys(sw+2))) { + index = sw - SW_SA0 + 2; + result = ((MASK_CFN_TYPE)1 << index); + switchesMidposStart[idx] = 0; + } + else { + index = sw - SW_SA0 + 1; + if (startup || SWITCH_POSITION(index) || g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (switchesMidposStart[idx] && (tmr10ms_t)(get_tmr10ms() - switchesMidposStart[idx]) > SWITCHES_DELAY())) { + result = ((MASK_CFN_TYPE)1 << index); + switchesMidposStart[idx] = 0; + } + else { + result = (switchesPos & ((MASK_CFN_TYPE)0x7 << (sw - SW_SA0))); + if (!switchesMidposStart[idx]) { + switchesMidposStart[idx] = get_tmr10ms(); + } + } + } + + if (!(switchesPos & result)) { + PLAY_SWITCH_MOVED(index); + } + + return result; +} + +#define CHECK_2POS(sw) newPos |= check2PosSwitchPosition(sw ## 0) +#define CHECK_3POS(idx, sw) newPos |= check3PosSwitchPosition(idx, sw ## 0, startup) + +void getSwitchesPosition(bool startup) +{ + uint64_t newPos = 0; + CHECK_3POS(0, SW_SA); + CHECK_3POS(1, SW_SB); + CHECK_3POS(2, SW_SC); + CHECK_3POS(3, SW_SD); + CHECK_3POS(4, SW_SE); + CHECK_2POS(SW_SF); + CHECK_3POS(5, SW_SG); + CHECK_2POS(SW_SH); + +#if defined(REV9E) + CHECK_3POS(6, SW_SI); + CHECK_3POS(7, SW_SJ); + CHECK_3POS(8, SW_SK); + CHECK_3POS(9, SW_SL); + CHECK_3POS(10, SW_SM); + CHECK_3POS(11, SW_SN); + CHECK_3POS(12, SW_SO); + CHECK_3POS(13, SW_SP); + CHECK_3POS(14, SW_SQ); + CHECK_3POS(15, SW_SR); +#endif + + switchesPos = newPos; + + for (int i=0; icount); + uint8_t previousPos = potsPos[i] >> 4; + uint8_t previousStoredPos = potsPos[i] & 0x0F; + if (startup) { + potsPos[i] = (pos << 4) | pos; + } + else if (pos != previousPos) { + potsLastposStart[i] = get_tmr10ms(); + potsPos[i] = (pos << 4) | previousStoredPos; + } + else if (g_eeGeneral.switchesDelay==SWITCHES_DELAY_NONE || (tmr10ms_t)(get_tmr10ms() - potsLastposStart[i]) > SWITCHES_DELAY()) { + potsLastposStart[i] = 0; + potsPos[i] = (pos << 4) | pos; + if (previousStoredPos != pos) { + PLAY_SWITCH_MOVED(SWSRC_LAST_SWITCH+i*XPOTS_MULTIPOS_COUNT+pos); + } + } + } + } + } +} + + +getvalue_t getValueForLogicalSwitch(mixsrc_t i) +{ + getvalue_t result = getValue(i); + if (i>=MIXSRC_FIRST_INPUT && i<=MIXSRC_LAST_INPUT) { + int8_t trimIdx = virtualInputsTrims[i-MIXSRC_FIRST_INPUT]; + if (trimIdx >= 0) { + int16_t trim = trims[trimIdx]; + if (trimIdx == THR_STICK && g_model.throttleReversed) + result -= trim; + else + result += trim; + } + } + return result; +} +#else + #define getValueForLogicalSwitch(i) getValue(i) +#endif + +PACK(typedef struct { + uint8_t state; + uint8_t last; +}) ls_sticky_struct; + +PACK(typedef struct { + uint16_t state:1; + uint16_t duration:15; +}) ls_stay_struct; + +bool getLogicalSwitch(uint8_t idx) +{ + LogicalSwitchData * ls = lswAddress(idx); + bool result; + +#if defined(CPUARM) + swsrc_t s = ls->andsw; +#else + uint8_t s = ls->andsw; + if (s > SWSRC_LAST_SWITCH) { + s += SWSRC_SW1-SWSRC_LAST_SWITCH-1; + } +#endif + + if (ls->func == LS_FUNC_NONE || (s && !getSwitch(s))) { +#if defined(CPUARM) + if (ls->func != LS_FUNC_STICKY && ls->func != LS_FUNC_EDGE ) { +#else + if (ls->func != LS_FUNC_STICKY) { +#endif + // AND switch must not affect STICKY and EDGE processing + LS_LAST_VALUE(mixerCurrentFlightMode, idx) = CS_LAST_VALUE_INIT; + } + result = false; + } + else if ((s=lswFamily(ls->func)) == LS_FAMILY_BOOL) { + bool res1 = getSwitch(ls->v1); + bool res2 = getSwitch(ls->v2); + switch (ls->func) { + case LS_FUNC_AND: + result = (res1 && res2); + break; + case LS_FUNC_OR: + result = (res1 || res2); + break; + // case LS_FUNC_XOR: + default: + result = (res1 ^ res2); + break; + } + } + else if (s == LS_FAMILY_TIMER) { + result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) <= 0); + } + else if (s == LS_FAMILY_STICKY) { + result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0)); + } +#if defined(CPUARM) + else if (s == LS_FAMILY_EDGE) { + result = (LS_LAST_VALUE(mixerCurrentFlightMode, idx) & (1<<0)); + } +#endif + else { + getvalue_t x = getValueForLogicalSwitch(ls->v1); + getvalue_t y; + if (s == LS_FAMILY_COMP) { + y = getValueForLogicalSwitch(ls->v2); + + switch (ls->func) { + case LS_FUNC_EQUAL: + result = (x==y); + break; + case LS_FUNC_GREATER: + result = (x>y); + break; + default: + result = (xv1; +#if defined(FRSKY) + // Telemetry + if (v1 >= MIXSRC_FIRST_TELEM) { +#if defined(CPUARM) + if (!TELEMETRY_STREAMING() || IS_FAI_FORBIDDEN(v1-1)) { +#else + if ((!TELEMETRY_STREAMING() && v1 >= MIXSRC_FIRST_TELEM+TELEM_FIRST_STREAMED_VALUE-1) || IS_FAI_FORBIDDEN(v1-1)) { +#endif + result = false; + goto DurationAndDelayProcessing; + } + + y = convertLswTelemValue(ls); + +#if defined(GAUGES) && !defined(CPUARM) + // Fill the telemetry bars threshold array + if (s == LS_FAMILY_OFS) { + uint8_t idx = v1-MIXSRC_FIRST_TELEM+1-TELEM_ALT; + if (idx < THLD_MAX) { + FILL_THRESHOLD(idx, ls->v2); + } + } +#endif + + } + else if (v1 >= MIXSRC_GVAR1) { + y = ls->v2; + } + else { + y = calc100toRESX(ls->v2); + } +#else + if (v1 >= MIXSRC_FIRST_TELEM) { + y = (int16_t)3 * (128+ls->v2); // it's a Timer + } + else if (v1 >= MIXSRC_GVAR1) { + y = ls->v2; // it's a GVAR + } + else { + y = calc100toRESX(ls->v2); + } +#endif + + switch (ls->func) { +#if defined(CPUARM) + case LS_FUNC_VEQUAL: + result = (x==y); + break; +#endif + case LS_FUNC_VALMOSTEQUAL: +#if defined(GVARS) + if (v1 >= MIXSRC_GVAR1 && v1 <= MIXSRC_LAST_GVAR) + result = (x==y); + else +#endif + result = (abs(x-y) < (1024 / STICK_TOLERANCE)); + break; + case LS_FUNC_VPOS: + result = (x>y); + break; + case LS_FUNC_VNEG: + result = (xy); + break; + case LS_FUNC_ANEG: + result = (abs(x)func == LS_FUNC_DIFFEGREATER) { + if (y >= 0) { + result = (diff >= y); + if (diff < 0) + update = true; + } + else { + result = (diff <= y); + if (diff > 0) + update = true; + } + } + else { + result = (abs(diff) >= y); + } + if (result || update) { + LS_LAST_VALUE(mixerCurrentFlightMode, idx) = x; + } + break; + } + } + } + } + +#if defined(FRSKY) +DurationAndDelayProcessing: +#endif + +#if defined(CPUARM) + if (ls->delay || ls->duration) { + LogicalSwitchContext &context = lswFm[mixerCurrentFlightMode].lsw[idx]; + if (result) { + if (context.timerState == SWITCH_START) { + // set delay timer + context.timerState = SWITCH_DELAY; + context.timer = (ls->func == LS_FUNC_EDGE ? 0 : ls->delay); + } + + if (context.timerState == SWITCH_DELAY) { + if (context.timer) { + result = false; // return false while delay timer running + } + else { + // set duration timer + context.timerState = SWITCH_ENABLE; + context.timer = ls->duration; + } + } + + if (context.timerState == SWITCH_ENABLE) { + result = (ls->duration==0 || context.timer>0); // return false after duration timer runs out + if (!result && ls->func == LS_FUNC_STICKY) { + ls_sticky_struct & lastValue = (ls_sticky_struct &)context.lastValue; + lastValue.state = 0; + } + } + } + else if (context.timerState == SWITCH_ENABLE && ls->duration > 0 && context.timer > 0) { + result = true; + } + else { + context.timerState = SWITCH_START; + context.timer = 0; + } + } +#endif + + return result; +} + +#if defined(CPUARM) +bool getSwitch(swsrc_t swtch, uint8_t flags) +#else +bool getSwitch(swsrc_t swtch) +#endif +{ + bool result; + + if (swtch == SWSRC_NONE) + return true; + + uint8_t cs_idx = abs(swtch); + + if (cs_idx == SWSRC_ONE) { + result = !s_mixer_first_run_done; + } + else if (cs_idx == SWSRC_ON) { + result = true; + } + else if (cs_idx <= SWSRC_LAST_SWITCH) { +#if defined(PCBTARANIS) || defined(PCBHORUS) // TODO || defined(PCBFLAMENCO) + if (flags & GETSWITCH_MIDPOS_DELAY) + result = SWITCH_POSITION(cs_idx-SWSRC_FIRST_SWITCH); + else + result = switchState((EnumKeys)(SW_BASE+cs_idx-SWSRC_FIRST_SWITCH)); +#else + result = switchState((EnumKeys)(SW_BASE+cs_idx-SWSRC_FIRST_SWITCH)); +#endif + +#if defined(MODULE_ALWAYS_SEND_PULSES) + if (startupWarningState < STARTUP_WARNING_DONE) { + // if throttle or switch warning is currently active, ignore actual stick position and use wanted values + if (cs_idx <= 3) { + if (!(g_model.switchWarningEnable & 1)) { // ID1 to ID3 is just one bit in switchWarningEnable + result = (cs_idx)==((g_model.switchWarningState & 3)+1); // overwrite result with desired value + } + } + else if (!(g_model.switchWarningEnable & (1<<(cs_idx-3)))) { + // current switch should not be ignored for warning + result = g_model.switchWarningState & (1<<(cs_idx-2)); // overwrite result with desired value + } + } +#endif + } +#if NUM_XPOTS > 0 + else if (cs_idx <= SWSRC_LAST_MULTIPOS_SWITCH) { + result = POT_POSITION(cs_idx-SWSRC_FIRST_MULTIPOS_SWITCH); + } +#endif + else if (cs_idx <= SWSRC_LAST_TRIM) { + uint8_t idx = cs_idx - SWSRC_FIRST_TRIM; + idx = (CONVERT_MODE(idx/2) << 1) + (idx & 1); + result = trimDown(idx); + } +#if ROTARY_ENCODERS > 0 + else if (cs_idx == SWSRC_REa) { + result = REA_DOWN(); + } +#endif +#if ROTARY_ENCODERS > 1 + else if (cs_idx == SWSRC_REb) { + result = REB_DOWN(); + } +#endif +#if defined(CPUARM) + else if (cs_idx >= SWSRC_FIRST_SENSOR) { + result = !telemetryItems[cs_idx-SWSRC_FIRST_SENSOR].isOld(); + } + else if (cs_idx == SWSRC_TELEMETRY_STREAMING) { + result = TELEMETRY_STREAMING(); + } + else if (cs_idx >= SWSRC_FIRST_FLIGHT_MODE) { +#if defined(FLIGHT_MODES) + uint8_t idx = cs_idx - SWSRC_FIRST_FLIGHT_MODE; + if (flags & GETSWITCH_MIDPOS_DELAY) + result = (idx == flightModeTransitionLast); + else + result = (idx == mixerCurrentFlightMode); +#else + result = false; +#endif + } +#endif + else { + cs_idx -= SWSRC_FIRST_LOGICAL_SWITCH; +#if defined(CPUARM) + result = lswFm[mixerCurrentFlightMode].lsw[cs_idx].state; +#else + GETSWITCH_RECURSIVE_TYPE mask = ((GETSWITCH_RECURSIVE_TYPE)1 << cs_idx); + if (s_last_switch_used & mask) { + result = (s_last_switch_value & mask); + } + else { + s_last_switch_used |= mask; + result = getLogicalSwitch(cs_idx); + if (result) { + s_last_switch_value |= mask; + } + else { + s_last_switch_value &= ~mask; + } + } +#endif + } + + return swtch > 0 ? result : !result; +} + +#if defined(CPUARM) +/** + @brief Calculates new state of logical switches for mixerCurrentFlightMode +*/ +void evalLogicalSwitches(bool isCurrentPhase) +{ + for (unsigned int idx=0; idx> (i*3); + uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024; + if (prev != next) { + switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*3)); + if (i == 0) + result = 1+next; + else if (i == 1) + result = 1+(3*1)+(next!=0); + else if (i == 3) + result = 12+(next!=0); + else if (i == 4) + result = 14+next; + else + result = 0; + } + } +#endif +#elif defined(PCBTARANIS) || defined(PCBHORUS) + for (int i=0; i> (i*2); + uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024; + if (prev != next) { + switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*2)); + result = 1+(3*i)+next; + } + } + } +#else + // return delivers 1 to 3 for ID1 to ID3 + // 4..8 for all other switches if changed to true + // -4..-8 for all other switches if changed to false + // 9 for Trainer switch if changed to true; Change to false is ignored + swarnstate_t mask = 0x80; + for (uint8_t i=NUM_PSWITCH; i>1; i--) { + bool prev; + prev = (switches_states & mask); + // don't use getSwitch here to always get the proper value, even getSwitch manipulates + bool next = switchState((EnumKeys)(SW_BASE+i-1)); + if (prev != next) { + if (((i3)) || next==true) + result = next ? i : -i; + if (i<=3 && result==0) result = 1; + switches_states ^= mask; + } + mask >>= 1; + } +#endif + + if ((tmr10ms_t)(get_tmr10ms() - s_move_last_time) > 10) + result = 0; + + s_move_last_time = get_tmr10ms(); + return result; +} + +#if defined(GUI) +void checkSwitches() +{ +#if defined(MODULE_ALWAYS_SEND_PULSES) + static swarnstate_t last_bad_switches = 0xff; +#else + swarnstate_t last_bad_switches = 0xff; +#endif + swarnstate_t states = g_model.switchWarningState; + +#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) + uint8_t bad_pots = 0, last_bad_pots = 0xff; +#endif + +#if !defined(MODULE_ALWAYS_SEND_PULSES) + while (1) { + +#if defined(TELEMETRY_MOD_14051) || defined(TELEMETRY_MOD_14051_SWAPPED) +// FIXME: One getADC() call only reads one 14051 MUX input. To have all switch states updated, we need to call it MUX_MAX+1 times. +#define GETADC_COUNT (MUX_MAX+1) +#elif defined(PCBTARANIS) || defined(PCBFLAMENCO) +#define GETADC_COUNT 1 +#endif +#ifdef GETADC_COUNT + for (uint8_t i=0; i> (3*i)) & 0x07); + if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) { + warn = true; + } + } + } +#elif defined(PCBTARANIS) + for (int i=0; i 1)) { + warn = true; + bad_pots |= (1<> (3*i)) & 0x07); + if (state && state-1 != ((switches_states >> (i*2)) & 0x03)) { + if (++numWarnings < 6) { + // LcdFlags attr = ((states & mask) == (switches_states & mask)) ? TEXT_COLOR : ALARM_COLOR; + LcdFlags attr = ALARM_COLOR; + putsSwitches(x, y, SWSRC_FIRST_SWITCH+i*3+state-1, attr); + x += SWITCH_WARNING_LIST_INTERVAL; + } + else if (numWarnings == 6) { + lcdDrawText(x, y, "...", ALARM_COLOR); + } + } + } +#else + if (SWITCH_WARNING_ALLOWED(i) && !(g_model.switchWarningEnable & (1<> (i*2)]; + putsMixerSource(x, y, MIXSRC_FIRST_SWITCH+i, attr); + lcdDrawChar(lcdNextPos, y, c, attr); + x = lcdNextPos + 3; + } + else if (numWarnings == 7) { + lcdDrawText(x, y, "...", 0); + } + } + } +#endif + } + + if (g_model.potsWarnMode) { + if (y == 4*FH+3) { + y = 6*FH-2; + x = 60; + } + for (int i=0; i 1) { +#if defined(COLORLCD) + char s[8]; + // TODO add an helper + strncpy(s, &STR_VSRCRAW[1+(NUM_STICKS+1+i)*STR_VSRCRAW[0]], STR_VSRCRAW[0]); + s[int(STR_VSRCRAW[0])] = '\0'; +#else + lcdDrawTextAtIndex(x, y, STR_VSRCRAW, NUM_STICKS+1+i, INVERS); + if (IS_POT(POT1+i)) + lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? 126 : 127, INVERS); + else + lcdDrawChar(lcdNextPos, y, g_model.potsWarnPosition[i] > GET_LOWRES_POT_POSITION(i) ? '\300' : '\301', INVERS); +#endif +#if defined(COLORLCD) + if (++numWarnings < 6) { + lcdDrawText(x, y, s, ALARM_COLOR); + } + else if (numWarnings == 6) { + lcdDrawText(x, y, "...", ALARM_COLOR); + } + x += 20; +#else + x = lcdNextPos + 3; +#endif + } + + } + } + } + last_bad_pots = bad_pots; +#else + if (last_bad_switches != switches_states) { + MESSAGE(STR_SWITCHWARN, NULL, STR_PRESSANYKEYTOSKIP, last_bad_switches == 0xff ? AU_SWITCH_ALERT : AU_NONE); + uint8_t x = 2; + for (uint8_t i=0; i0?(i+3):(states&0x3)+1), attr); + x += 3*FW+FW/2; + } +#endif + + lcdRefresh(); + lcdSetContrast(); + clearKeyEvents(); + + last_bad_switches = switches_states; + } + +#if defined(MODULE_ALWAYS_SEND_PULSES) + if (pwrCheck()==e_power_off || keyDown()) { + startupWarningState = STARTUP_WARNING_SWITCHES+1; + last_bad_switches = 0xff; + } +#else + if (pwrCheck()==e_power_off || keyDown()) break; + + doLoopCommonActions(); + + wdt_reset(); + + SIMU_SLEEP(1); + } +#endif + + LED_ERROR_END(); +} +#endif // GUI + +void logicalSwitchesTimerTick() +{ +#if defined(CPUARM) + for (uint8_t fm=0; fmfunc == LS_FUNC_TIMER) { + int16_t *lastValue = &LS_LAST_VALUE(fm, i); + if (*lastValue == 0 || *lastValue == CS_LAST_VALUE_INIT) { + *lastValue = -lswTimerValue(ls->v1); + } + else if (*lastValue < 0) { + if (++(*lastValue) == 0) + *lastValue = lswTimerValue(ls->v2); + } + else { // if (*lastValue > 0) + *lastValue -= 1; + } + } + else if (ls->func == LS_FUNC_STICKY) { + ls_sticky_struct & lastValue = (ls_sticky_struct &)LS_LAST_VALUE(fm, i); + bool before = lastValue.last & 0x01; + if (lastValue.state) { + bool now = getSwitch(ls->v2); + if (now != before) { + lastValue.last ^= 1; + if (!before) { + lastValue.state = 0; + } + } + } + else { + bool now = getSwitch(ls->v1); + if (before != now) { + lastValue.last ^= 1; + if (!before) { + lastValue.state = 1; + } + } + } + } +#if defined(CPUARM) + else if (ls->func == LS_FUNC_EDGE) { + ls_stay_struct & lastValue = (ls_stay_struct &)LS_LAST_VALUE(fm, i); + // if this ls was reset by the logicalSwitchesReset() the lastValue will be set to CS_LAST_VALUE_INIT(0x8000) + // when it is unpacked into ls_stay_struct the lastValue.duration will have a value of 0x4000 + // this will produce an instant true for edge logical switch if the second parameter is big enough. + // So we reset it here. + if (LS_LAST_VALUE(fm, i) == CS_LAST_VALUE_INIT) { + lastValue.duration = 0; + } + lastValue.state = false; + bool state = getSwitch(ls->v1); + if (state) { + if (ls->v3 == -1 && lastValue.duration == lswTimerValue(ls->v2)) + lastValue.state = true; + if (lastValue.duration < 1000) + lastValue.duration++; + } + else { + if (lastValue.duration > lswTimerValue(ls->v2) && (ls->v3 == 0 || lastValue.duration <= lswTimerValue(ls->v2+ls->v3))) + lastValue.state = true; + lastValue.duration = 0; + } + } + + // decrement delay/duration timer + LogicalSwitchContext &context = lswFm[fm].lsw[i]; + if (context.timer) { + context.timer--; + } +#endif + } +#if defined(CPUARM) + } +#endif +} + +LogicalSwitchData * lswAddress(uint8_t idx) +{ + return &g_model.logicalSw[idx]; +} + +uint8_t lswFamily(uint8_t func) +{ + if (func <= LS_FUNC_ANEG) + return LS_FAMILY_OFS; + else if (func <= LS_FUNC_XOR) + return LS_FAMILY_BOOL; +#if defined(CPUARM) + else if (func == LS_FUNC_EDGE) + return LS_FAMILY_EDGE; +#endif + else if (func <= LS_FUNC_LESS) + return LS_FAMILY_COMP; + else if (func <= LS_FUNC_ADIFFEGREATER) + return LS_FAMILY_DIFF; + else + return LS_FAMILY_TIMER+func-LS_FUNC_TIMER; +} + +int16_t lswTimerValue(delayval_t val) +{ + return (val < -109 ? 129+val : (val < 7 ? (113+val)*5 : (53+val)*10)); +} + +void logicalSwitchesReset() +{ +#if defined(CPUARM) + flightModeTransitionLast = 255; + memset(lswFm, 0, sizeof(lswFm)); +#else + s_last_switch_value = 0; +#endif + +#if defined(CPUARM) + for (uint8_t fm=0; fmv1 - MIXSRC_FIRST_TELEM + 1, ls->v2); +#else + if (lswFamily(ls->func)==LS_FAMILY_OFS) + val = convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128+ls->v2); + else + val = convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128+ls->v2) - convert8bitsTelemValue(ls->v1 - MIXSRC_FIRST_TELEM + 1, 128); +#endif + return val; +} + +#if defined(CPUARM) +void logicalSwitchesCopyState(uint8_t src, uint8_t dst) +{ + lswFm[dst] = lswFm[src]; +} +#endif diff --git a/radio/src/tasks_arm.cpp b/radio/src/tasks_arm.cpp index 99236dace..b1a32584a 100644 --- a/radio/src/tasks_arm.cpp +++ b/radio/src/tasks_arm.cpp @@ -1,209 +1,209 @@ -/* - * 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" - -OS_TID menusTaskId; -// menus stack must be aligned to 8 bytes otherwise printf for %f does not work! -TaskStack _ALIGNED(8) menusStack; - -OS_TID mixerTaskId; -TaskStack mixerStack; - -OS_TID audioTaskId; -TaskStack audioStack; - -#if defined(BLUETOOTH) -OS_TID btTaskId; -TaskStack bluetoothStack; -#endif - -OS_MutexID audioMutex; -OS_MutexID mixerMutex; - -enum TaskIndex { - MENU_TASK_INDEX, - MIXER_TASK_INDEX, - AUDIO_TASK_INDEX, - CLI_TASK_INDEX, - BLUETOOTH_TASK_INDEX, - TASK_INDEX_COUNT, - MAIN_TASK_INDEX = 255 -}; - -template -void TaskStack::paint() -{ - for (uint32_t i=0; i maxMixerDuration) maxMixerDuration = t0 ; - } - - CoTickDelay(1); // 2ms for now - } -} - -#define MENU_TASK_PERIOD_TICKS 10 // 20ms - -void menusTask(void * pdata) -{ - opentxInit(); - -#if defined(PWR_BUTTON_DELAY) - while (1) { - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - break; - } - else if (pwr_check == e_power_press) { - CoTickDelay(MENU_TASK_PERIOD_TICKS); - continue; - } -#else - while (pwrCheck() != e_power_off) { -#endif - U64 start = CoGetOSTime(); - perMain(); - // TODO remove completely massstorage from sky9x firmware - U32 runtime = (U32)(CoGetOSTime() - start); - // deduct the thread run-time from the wait, if run-time was more than - // desired period, then skip the wait all together - if (runtime < MENU_TASK_PERIOD_TICKS) { - CoTickDelay(MENU_TASK_PERIOD_TICKS - runtime); - } - -#if defined(SIMU) - if (main_thread_running == 0) - return; -#endif - } - -#if defined(PCBTARANIS) && defined(REV9E) - toplcdOff(); -#endif - - BACKLIGHT_OFF(); - -#if defined(PCBHORUS) - ledOff(); -#endif - -#if defined(COLORLCD) || defined(PCBTARANIS) - drawSleepBitmap(); -#else - lcdClear(); - displayPopup(STR_SHUTDOWN); -#endif - - opentxClose(); - boardOff(); // Only turn power off if necessary -} - -extern void audioTask(void* pdata); - -void tasksStart() -{ - CoInitOS(); - -#if defined(CLI) - cliStart(); -#endif - -#if defined(BLUETOOTH) - btTaskId = CoCreateTask(btTask, NULL, 15, &bluetoothStack.stack[BLUETOOTH_STACK_SIZE-1], BLUETOOTH_STACK_SIZE); -#endif - - mixerTaskId = CoCreateTask(mixerTask, NULL, 5, &mixerStack.stack[MIXER_STACK_SIZE-1], MIXER_STACK_SIZE); - menusTaskId = CoCreateTask(menusTask, NULL, 10, &menusStack.stack[MENUS_STACK_SIZE-1], MENUS_STACK_SIZE); -#if !defined(SIMU) - // TODO move the SIMU audio in this task - audioTaskId = CoCreateTask(audioTask, NULL, 7, &audioStack.stack[AUDIO_STACK_SIZE-1], AUDIO_STACK_SIZE); -#endif - audioMutex = CoCreateMutex(); - mixerMutex = CoCreateMutex(); - - CoStartOS(); -} +/* + * 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" + +OS_TID menusTaskId; +// menus stack must be aligned to 8 bytes otherwise printf for %f does not work! +TaskStack _ALIGNED(8) menusStack; + +OS_TID mixerTaskId; +TaskStack mixerStack; + +OS_TID audioTaskId; +TaskStack audioStack; + +#if defined(BLUETOOTH) +OS_TID btTaskId; +TaskStack bluetoothStack; +#endif + +OS_MutexID audioMutex; +OS_MutexID mixerMutex; + +enum TaskIndex { + MENU_TASK_INDEX, + MIXER_TASK_INDEX, + AUDIO_TASK_INDEX, + CLI_TASK_INDEX, + BLUETOOTH_TASK_INDEX, + TASK_INDEX_COUNT, + MAIN_TASK_INDEX = 255 +}; + +template +void TaskStack::paint() +{ + for (uint32_t i=0; i maxMixerDuration) maxMixerDuration = t0 ; + } + + CoTickDelay(1); // 2ms for now + } +} + +#define MENU_TASK_PERIOD_TICKS 10 // 20ms + +void menusTask(void * pdata) +{ + opentxInit(); + +#if defined(PWR_BUTTON_DELAY) + while (1) { + uint32_t pwr_check = pwrCheck(); + if (pwr_check == e_power_off) { + break; + } + else if (pwr_check == e_power_press) { + CoTickDelay(MENU_TASK_PERIOD_TICKS); + continue; + } +#else + while (pwrCheck() != e_power_off) { +#endif + U64 start = CoGetOSTime(); + perMain(); + // TODO remove completely massstorage from sky9x firmware + U32 runtime = (U32)(CoGetOSTime() - start); + // deduct the thread run-time from the wait, if run-time was more than + // desired period, then skip the wait all together + if (runtime < MENU_TASK_PERIOD_TICKS) { + CoTickDelay(MENU_TASK_PERIOD_TICKS - runtime); + } + +#if defined(SIMU) + if (main_thread_running == 0) + return; +#endif + } + +#if defined(PCBTARANIS) && defined(REV9E) + toplcdOff(); +#endif + + BACKLIGHT_OFF(); + +#if defined(PCBHORUS) + ledOff(); +#endif + +#if defined(COLORLCD) || defined(PCBTARANIS) + drawSleepBitmap(); +#else + lcdClear(); + displayPopup(STR_SHUTDOWN); +#endif + + opentxClose(); + boardOff(); // Only turn power off if necessary +} + +extern void audioTask(void* pdata); + +void tasksStart() +{ + CoInitOS(); + +#if defined(CLI) + cliStart(); +#endif + +#if defined(BLUETOOTH) + btTaskId = CoCreateTask(btTask, NULL, 15, &bluetoothStack.stack[BLUETOOTH_STACK_SIZE-1], BLUETOOTH_STACK_SIZE); +#endif + + mixerTaskId = CoCreateTask(mixerTask, NULL, 5, &mixerStack.stack[MIXER_STACK_SIZE-1], MIXER_STACK_SIZE); + menusTaskId = CoCreateTask(menusTask, NULL, 10, &menusStack.stack[MENUS_STACK_SIZE-1], MENUS_STACK_SIZE); +#if !defined(SIMU) + // TODO move the SIMU audio in this task + audioTaskId = CoCreateTask(audioTask, NULL, 7, &audioStack.stack[AUDIO_STACK_SIZE-1], AUDIO_STACK_SIZE); +#endif + audioMutex = CoCreateMutex(); + mixerMutex = CoCreateMutex(); + + CoStartOS(); +} diff --git a/radio/src/templates.cpp b/radio/src/templates.cpp index b5a50f0b8..7fb91280c 100644 --- a/radio/src/templates.cpp +++ b/radio/src/templates.cpp @@ -18,241 +18,241 @@ * GNU General Public License for more details. */ -/* - * - * ============================================================ - * Templates file - * - * eccpm - * crow - * throttle cut - * flaperon - * elevon - * v-tail - * throttle hold - * Aileron Differential - * Spoilers - * Snap Roll - * ELE->Flap - * Flap->ELE - * - */ - -#include "opentx.h" - -#if defined(PCBTARANIS) - #pragma message("Templates with virtual inputs (FrSky Taranis) are not implemented!") -#endif - -MixData* setDest(uint8_t dch, uint8_t src, bool clear=false) -{ - uint8_t i = 0; - MixData * mix; - - while (1) { - mix = mixAddress(i); - if (mix->srcRaw && mix->destCh <= dch) { - if (clear && mix->destCh == dch) { - deleteExpoMix(0, i); - } - else { - if (++i==MAX_MIXERS) { - // TODO should return null pointer but needs to be tested then - mix = mixAddress(0); - break; - } - } - } - else { - break; - } - } - - memmove(mix+1, mix, (MAX_MIXERS-(i+1))*sizeof(MixData) ); - memclear(mix, sizeof(MixData)); - mix->destCh = dch; - mix->srcRaw = src; - mix->weight = 100; - return mix; -} - -void mixSetWeight(MixData* md, int8_t weight) -{ - u_int8int16_t tmp; - tmp.word=weight; - MD_UNION_TO_WEIGHT(tmp,md); - // MD_SETWEIGHT(md,weight); doesn't matter here in code cost compiler optimizes this anyway -} - -#if defined(PCBTARANIS) - #define TMPL_INPUT(x) (MIXSRC_FIRST_INPUT+channel_order(x)-1) -#else - #define clearInputs() - #define defaultInputs() - #define TMPL_INPUT(x) (MIXSRC_Rud+x-1) -#endif - -void clearMixes() -{ - memset(g_model.mixData, 0, sizeof(g_model.mixData)); // clear all mixes -} - -void clearCurves() -{ - memclear(g_model.curves, sizeof(g_model.curves) + sizeof(g_model.points)); // clear all curves -} - -void setCurve(uint8_t c, const pm_int8_t ar[]) -{ - int8_t * cv = curveAddress(c); - for (uint8_t i=0; i<5; i++) { - cv[i] = pgm_read_byte(&ar[i]); - } -} - -void setLogicalSwitch(uint8_t idx, uint8_t func, int8_t v1, int8_t v2) -{ - LogicalSwitchData *cs = lswAddress(idx-1); - cs->func = func; - cs->v1 = v1; - cs->v2 = v2; -} - -const pm_int8_t heli_ar1[] PROGMEM = {-100, 20, 30, 70, 90}; -const pm_int8_t heli_ar2[] PROGMEM = {80, 70, 60, 70, 100}; -const pm_int8_t heli_ar3[] PROGMEM = {100, 90, 80, 90, 100}; -const pm_int8_t heli_ar4[] PROGMEM = {-30, -15, 0, 50, 100}; -const pm_int8_t heli_ar5[] PROGMEM = {-100, -50, 0, 50, 100}; - -void applyTemplate(uint8_t idx) -{ - MixData *md; - - //CC(STK) -> vSTK - //ICC(vSTK) -> STK -#define ICC(x) icc[(x)-1] - uint8_t icc[4] = {0}; - for (uint8_t i=0; i<4; i++) { //generate inverse array - for(uint8_t j=0; j<4; j++) - if(CC(i+1)==j+MIXSRC_Rud) icc[j]=i; - } - - switch (idx) { - case TMPL_CLEAR_MIXES: - case TMPL_SIMPLE_4CH: - case TMPL_HELI_SETUP: - clearMixes(); - break; - } - - switch (idx) { - // Simple 4-Ch - case TMPL_SIMPLE_4CH: - defaultInputs(); - setDest(ICC(STK_RUD), TMPL_INPUT(STK_RUD)); - setDest(ICC(STK_ELE), TMPL_INPUT(STK_ELE)); - setDest(ICC(STK_THR), TMPL_INPUT(STK_THR)); - setDest(ICC(STK_AIL), TMPL_INPUT(STK_AIL)); - break; - - // Sticky-T-Cut - case TMPL_STI_THR_CUT: - md=setDest(ICC(STK_THR), MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_SWC; md->mltpx=MLTPX_REP; - md=setDest(13, MIXSRC_CH14); // md->weight= 100; done by setDest anyway - md=setDest(13, MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_SWB; md->mltpx=MLTPX_REP; - md=setDest(13, MIXSRC_MAX); /* md->weight= 100;*/ md->swtch=SWSRC_THR; md->mltpx=MLTPX_REP; - setLogicalSwitch(11, LS_FUNC_VNEG, STK_THR, -99); - setLogicalSwitch(12, LS_FUNC_VPOS, MIXSRC_CH14, 0); - break; - - // V-Tail - case TMPL_V_TAIL: - defaultInputs(); - setDest(ICC(STK_RUD), TMPL_INPUT(STK_RUD), true); - md=setDest(ICC(STK_RUD), TMPL_INPUT(STK_ELE)); mixSetWeight(md, -100); - setDest(ICC(STK_ELE), TMPL_INPUT(STK_RUD), true); - setDest(ICC(STK_ELE), TMPL_INPUT(STK_ELE)); - break; - - // Elevon\\Delta - case TMPL_ELEVON_DELTA: - defaultInputs(); - setDest(ICC(STK_ELE), MIXSRC_Ele, true); - setDest(ICC(STK_ELE), MIXSRC_Ail); - setDest(ICC(STK_AIL), MIXSRC_Ele, true); - md=setDest(ICC(STK_AIL), MIXSRC_Ail); mixSetWeight(md, -100); - break; - - // eCCPM - case TMPL_ECCPM: - md=setDest(ICC(STK_ELE), MIXSRC_Ele, true); md->weight= 72; - md=setDest(ICC(STK_ELE), MIXSRC_Thr); md->weight= 55; - md=setDest(ICC(STK_AIL), MIXSRC_Ele, true); mixSetWeight(md, -36); - md=setDest(ICC(STK_AIL), MIXSRC_Ail); md->weight= 62; - md=setDest(ICC(STK_AIL), MIXSRC_Thr); md->weight= 55; - md=setDest(5, MIXSRC_Ele, true); mixSetWeight(md, -36); - md=setDest(5, MIXSRC_Ail); mixSetWeight(md, -62); - md=setDest(5, MIXSRC_Thr); md->weight= 55; - break; - - // Heli Setup - case TMPL_HELI_SETUP: - clearCurves(); - - //Set up Mixes - // 3 cyclic channels - md=setDest(0, MIXSRC_CYC1); // md->weight=100; - md=setDest(1, MIXSRC_CYC2); // md->weight=100; - md=setDest(2, MIXSRC_CYC3); // md->weight=100; - - // rudder - md=setDest(3, MIXSRC_Rud); // md->weight=100; - - // throttle -#if defined(PCBTARANIS) - // TODO -#else - md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID0; mixSetCurve(md, 0); md->carryTrim=TRIM_OFF; - md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID1; mixSetCurve(md, 1); md->carryTrim=TRIM_OFF; - md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID2; mixSetCurve(md, 2); md->carryTrim=TRIM_OFF; -#endif - md=setDest(4, MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_THR; md->mltpx=MLTPX_REP; - - // gyro gain - md=setDest(5, MIXSRC_MAX); md->weight= 30; md->swtch=-SWSRC_GEA; - md=setDest(5, MIXSRC_MAX); mixSetWeight(md, -30); md->swtch= SWSRC_GEA; - - // collective -#if defined(PCBTARANIS) - // TODO -#else - md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID0; mixSetCurve(md, 3); md->carryTrim=TRIM_OFF; - md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID1; mixSetCurve(md, 4); md->carryTrim=TRIM_OFF; - md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID2; mixSetCurve(md, 5); md->carryTrim=TRIM_OFF; -#endif - - g_model.swashR.collectiveSource = MIXSRC_CH11; - g_model.swashR.type = SWASH_TYPE_120; - - // curves - setCurve(0, heli_ar1); - setCurve(1, heli_ar2); - setCurve(2, heli_ar3); - setCurve(3, heli_ar4); - setCurve(4, heli_ar5); - setCurve(5, heli_ar5); - break; - - // Servo Test - case TMPL_SERVO_TEST: - md=setDest(NUM_CHNOUT-1, MIXSRC_SW1, true); md->weight=110; md->mltpx=MLTPX_ADD; md->delayUp = 6; md->delayDown = 6; md->speedUp = 8; md->speedDown = 8; - setLogicalSwitch(1, LS_FUNC_VNEG, MIXSRC_LAST_CH, 0); - break; - - default: - break; - - } - - storageDirty(EE_MODEL); -} +/* + * + * ============================================================ + * Templates file + * + * eccpm + * crow + * throttle cut + * flaperon + * elevon + * v-tail + * throttle hold + * Aileron Differential + * Spoilers + * Snap Roll + * ELE->Flap + * Flap->ELE + * + */ + +#include "opentx.h" + +#if defined(PCBTARANIS) + #pragma message("Templates with virtual inputs (FrSky Taranis) are not implemented!") +#endif + +MixData* setDest(uint8_t dch, uint8_t src, bool clear=false) +{ + uint8_t i = 0; + MixData * mix; + + while (1) { + mix = mixAddress(i); + if (mix->srcRaw && mix->destCh <= dch) { + if (clear && mix->destCh == dch) { + deleteExpoMix(0, i); + } + else { + if (++i==MAX_MIXERS) { + // TODO should return null pointer but needs to be tested then + mix = mixAddress(0); + break; + } + } + } + else { + break; + } + } + + memmove(mix+1, mix, (MAX_MIXERS-(i+1))*sizeof(MixData) ); + memclear(mix, sizeof(MixData)); + mix->destCh = dch; + mix->srcRaw = src; + mix->weight = 100; + return mix; +} + +void mixSetWeight(MixData* md, int8_t weight) +{ + u_int8int16_t tmp; + tmp.word=weight; + MD_UNION_TO_WEIGHT(tmp,md); + // MD_SETWEIGHT(md,weight); doesn't matter here in code cost compiler optimizes this anyway +} + +#if defined(PCBTARANIS) + #define TMPL_INPUT(x) (MIXSRC_FIRST_INPUT+channel_order(x)-1) +#else + #define clearInputs() + #define defaultInputs() + #define TMPL_INPUT(x) (MIXSRC_Rud+x-1) +#endif + +void clearMixes() +{ + memset(g_model.mixData, 0, sizeof(g_model.mixData)); // clear all mixes +} + +void clearCurves() +{ + memclear(g_model.curves, sizeof(g_model.curves) + sizeof(g_model.points)); // clear all curves +} + +void setCurve(uint8_t c, const pm_int8_t ar[]) +{ + int8_t * cv = curveAddress(c); + for (uint8_t i=0; i<5; i++) { + cv[i] = pgm_read_byte(&ar[i]); + } +} + +void setLogicalSwitch(uint8_t idx, uint8_t func, int8_t v1, int8_t v2) +{ + LogicalSwitchData *cs = lswAddress(idx-1); + cs->func = func; + cs->v1 = v1; + cs->v2 = v2; +} + +const pm_int8_t heli_ar1[] PROGMEM = {-100, 20, 30, 70, 90}; +const pm_int8_t heli_ar2[] PROGMEM = {80, 70, 60, 70, 100}; +const pm_int8_t heli_ar3[] PROGMEM = {100, 90, 80, 90, 100}; +const pm_int8_t heli_ar4[] PROGMEM = {-30, -15, 0, 50, 100}; +const pm_int8_t heli_ar5[] PROGMEM = {-100, -50, 0, 50, 100}; + +void applyTemplate(uint8_t idx) +{ + MixData *md; + + //CC(STK) -> vSTK + //ICC(vSTK) -> STK +#define ICC(x) icc[(x)-1] + uint8_t icc[4] = {0}; + for (uint8_t i=0; i<4; i++) { //generate inverse array + for(uint8_t j=0; j<4; j++) + if(CC(i+1)==j+MIXSRC_Rud) icc[j]=i; + } + + switch (idx) { + case TMPL_CLEAR_MIXES: + case TMPL_SIMPLE_4CH: + case TMPL_HELI_SETUP: + clearMixes(); + break; + } + + switch (idx) { + // Simple 4-Ch + case TMPL_SIMPLE_4CH: + defaultInputs(); + setDest(ICC(STK_RUD), TMPL_INPUT(STK_RUD)); + setDest(ICC(STK_ELE), TMPL_INPUT(STK_ELE)); + setDest(ICC(STK_THR), TMPL_INPUT(STK_THR)); + setDest(ICC(STK_AIL), TMPL_INPUT(STK_AIL)); + break; + + // Sticky-T-Cut + case TMPL_STI_THR_CUT: + md=setDest(ICC(STK_THR), MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_SWC; md->mltpx=MLTPX_REP; + md=setDest(13, MIXSRC_CH14); // md->weight= 100; done by setDest anyway + md=setDest(13, MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_SWB; md->mltpx=MLTPX_REP; + md=setDest(13, MIXSRC_MAX); /* md->weight= 100;*/ md->swtch=SWSRC_THR; md->mltpx=MLTPX_REP; + setLogicalSwitch(11, LS_FUNC_VNEG, STK_THR, -99); + setLogicalSwitch(12, LS_FUNC_VPOS, MIXSRC_CH14, 0); + break; + + // V-Tail + case TMPL_V_TAIL: + defaultInputs(); + setDest(ICC(STK_RUD), TMPL_INPUT(STK_RUD), true); + md=setDest(ICC(STK_RUD), TMPL_INPUT(STK_ELE)); mixSetWeight(md, -100); + setDest(ICC(STK_ELE), TMPL_INPUT(STK_RUD), true); + setDest(ICC(STK_ELE), TMPL_INPUT(STK_ELE)); + break; + + // Elevon\\Delta + case TMPL_ELEVON_DELTA: + defaultInputs(); + setDest(ICC(STK_ELE), MIXSRC_Ele, true); + setDest(ICC(STK_ELE), MIXSRC_Ail); + setDest(ICC(STK_AIL), MIXSRC_Ele, true); + md=setDest(ICC(STK_AIL), MIXSRC_Ail); mixSetWeight(md, -100); + break; + + // eCCPM + case TMPL_ECCPM: + md=setDest(ICC(STK_ELE), MIXSRC_Ele, true); md->weight= 72; + md=setDest(ICC(STK_ELE), MIXSRC_Thr); md->weight= 55; + md=setDest(ICC(STK_AIL), MIXSRC_Ele, true); mixSetWeight(md, -36); + md=setDest(ICC(STK_AIL), MIXSRC_Ail); md->weight= 62; + md=setDest(ICC(STK_AIL), MIXSRC_Thr); md->weight= 55; + md=setDest(5, MIXSRC_Ele, true); mixSetWeight(md, -36); + md=setDest(5, MIXSRC_Ail); mixSetWeight(md, -62); + md=setDest(5, MIXSRC_Thr); md->weight= 55; + break; + + // Heli Setup + case TMPL_HELI_SETUP: + clearCurves(); + + //Set up Mixes + // 3 cyclic channels + md=setDest(0, MIXSRC_CYC1); // md->weight=100; + md=setDest(1, MIXSRC_CYC2); // md->weight=100; + md=setDest(2, MIXSRC_CYC3); // md->weight=100; + + // rudder + md=setDest(3, MIXSRC_Rud); // md->weight=100; + + // throttle +#if defined(PCBTARANIS) + // TODO +#else + md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID0; mixSetCurve(md, 0); md->carryTrim=TRIM_OFF; + md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID1; mixSetCurve(md, 1); md->carryTrim=TRIM_OFF; + md=setDest(4, MIXSRC_Thr); md->swtch=SWSRC_ID2; mixSetCurve(md, 2); md->carryTrim=TRIM_OFF; +#endif + md=setDest(4, MIXSRC_MAX); mixSetWeight(md, -100); md->swtch=SWSRC_THR; md->mltpx=MLTPX_REP; + + // gyro gain + md=setDest(5, MIXSRC_MAX); md->weight= 30; md->swtch=-SWSRC_GEA; + md=setDest(5, MIXSRC_MAX); mixSetWeight(md, -30); md->swtch= SWSRC_GEA; + + // collective +#if defined(PCBTARANIS) + // TODO +#else + md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID0; mixSetCurve(md, 3); md->carryTrim=TRIM_OFF; + md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID1; mixSetCurve(md, 4); md->carryTrim=TRIM_OFF; + md=setDest(10, MIXSRC_Thr); /*md->weight= 100;*/ md->swtch=SWSRC_ID2; mixSetCurve(md, 5); md->carryTrim=TRIM_OFF; +#endif + + g_model.swashR.collectiveSource = MIXSRC_CH11; + g_model.swashR.type = SWASH_TYPE_120; + + // curves + setCurve(0, heli_ar1); + setCurve(1, heli_ar2); + setCurve(2, heli_ar3); + setCurve(3, heli_ar4); + setCurve(4, heli_ar5); + setCurve(5, heli_ar5); + break; + + // Servo Test + case TMPL_SERVO_TEST: + md=setDest(NUM_CHNOUT-1, MIXSRC_SW1, true); md->weight=110; md->mltpx=MLTPX_ADD; md->delayUp = 6; md->delayDown = 6; md->speedUp = 8; md->speedDown = 8; + setLogicalSwitch(1, LS_FUNC_VNEG, MIXSRC_LAST_CH, 0); + break; + + default: + break; + + } + + storageDirty(EE_MODEL); +} diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 9912c63e7..5a78c6151 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -1,717 +1,717 @@ -/* - * 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" - -#define ISTR(x) LEN_##x TR_##x - -// The non-0-terminated-strings -const pm_char STR_OPEN9X[] PROGMEM = - ISTR(OFFON) - ISTR(MMMINV) - ISTR(NCHANNELS) -#if !defined(GRAPHICS) - ISTR(VBEEPLEN) -#endif - ISTR(VBEEPMODE) -#if defined(ROTARY_ENCODERS) - ISTR(VRENAVIG) -#endif -#if defined(ROTARY_ENCODER_NAVIGATION) - ISTR(VRENCODERS) -#endif - ISTR(TRNMODE) - ISTR(TRNCHN) -#if defined(PCBTARANIS) || defined(PCBHORUS) - ISTR(UART3MODES) - ISTR(SWTYPES) - ISTR(POTTYPES) - ISTR(SLIDERTYPES) -#endif - ISTR(VTRIMINC) -#if defined(CPUARM) - ISTR(VDISPLAYTRIMS) -#endif - ISTR(RETA123) - ISTR(VPROTOS) - ISTR(POSNEG) -#if defined(PCBSKY9X) && defined(REVX) - ISTR(VOUTPUT_TYPE) -#endif - ISTR(VBLMODE) - ISTR(VCURVEFUNC) - ISTR(VMLTPX) - ISTR(VMLTPX2) - ISTR(VMIXTRIMS) - ISTR(VCSWFUNC) - ISTR(VFSWFUNC) - ISTR(VFSWRESET) - ISTR(FUNCSOUNDS) - ISTR(VTELEMCHNS) -#if defined(FRSKY) || defined(CPUARM) - ISTR(VTELEMUNIT) - ISTR(VALARM) - ISTR(VALARMFN) - ISTR(VTELPROTO) - ISTR(GPSFORMAT) - ISTR(AMPSRC) - ISTR(VARIOSRC) - ISTR(VTELEMSCREENTYPE) -#endif -#if defined(TEMPLATES) - ISTR(VTEMPLATES) -#endif -#if defined(HELI) - ISTR(VSWASHTYPE) -#endif - ISTR(VKEYS) - ISTR(VSWITCHES) - ISTR(VSRCRAW) -#if defined(TRANSLATIONS_CZ) && defined(CPUARM) - ISTR(INPUTNAMES) -#endif - ISTR(VTMRMODES) -#if defined(CPUM2560) || defined(CPUARM) - ISTR(DATETIME) - ISTR(VPERSISTENT) -#endif -#if defined(CPUARM) - ISTR(VLCD) - ISTR(VUNITSSYSTEM) - ISTR(VBEEPCOUNTDOWN) - ISTR(VVARIOCENTER) -#endif -#if defined(PXX) || defined(CPUARM) - ISTR(COUNTRYCODES) - ISTR(VFAILSAFE) -#endif -#if defined(CPUARM) - ISTR(VTRAINERMODES) - ISTR(TARANIS_PROTOCOLS) - ISTR(XJT_PROTOCOLS) - ISTR(DSM_PROTOCOLS) - ISTR(VOLTSRC) - ISTR(CURVE_TYPES) - ISTR(VSENSORTYPES) - ISTR(VFORMULAS) - ISTR(VPREC) - ISTR(VCELLINDEX) -#endif -#if defined(MAVLINK) - ISTR(MAVLINK_BAUDS) - ISTR(MAVLINK_AC_MODES) - ISTR(MAVLINK_AP_MODES) -#endif - ; - -// The 0-terminated-strings -const pm_char STR_POPUPS[] PROGMEM = TR_POPUPS; -#if !defined(OFS_EXIT) - const pm_char STR_EXIT[] PROGMEM = TR_EXIT; -#endif -const pm_char STR_MENUWHENDONE[] PROGMEM = TR_MENUWHENDONE; -const pm_char STR_FREE[] PROGMEM = TR_FREE; -const pm_char STR_DELETEMODEL[] PROGMEM = TR_DELETEMODEL; -const pm_char STR_COPYINGMODEL[] PROGMEM = TR_COPYINGMODEL; -const pm_char STR_MOVINGMODEL[] PROGMEM = TR_MOVINGMODEL; -const pm_char STR_LOADINGMODEL[] PROGMEM = TR_LOADINGMODEL; -const pm_char STR_NAME[] PROGMEM = TR_NAME; -const pm_char STR_BITMAP[] PROGMEM = TR_BITMAP; -const pm_char STR_TIMER[] PROGMEM = TR_TIMER; -const pm_char STR_ELIMITS[] PROGMEM = TR_ELIMITS; -const pm_char STR_ETRIMS[] PROGMEM = TR_ETRIMS; -const pm_char STR_TRIMINC[] PROGMEM = TR_TRIMINC; -const pm_char STR_DISPLAY_TRIMS[] PROGMEM = TR_DISPLAY_TRIMS; -const pm_char STR_TTRACE[] PROGMEM = TR_TTRACE; -const pm_char STR_TTRIM[] PROGMEM = TR_TTRIM; -const pm_char STR_BEEPCTR[] PROGMEM = TR_BEEPCTR; -const pm_char STR_USE_GLOBAL_FUNCS[] PROGMEM = TR_USE_GLOBAL_FUNCS; -#if defined(PCBSKY9X) && defined(REVX) - const pm_char STR_OUTPUT_TYPE[] PROGMEM = TR_OUTPUT_TYPE; -#endif -const pm_char STR_PROTO[] PROGMEM = TR_PROTO; -const pm_char STR_PPMFRAME[] PROGMEM = TR_PPMFRAME; -const pm_char STR_MS[] PROGMEM = TR_MS; -const pm_char STR_SWITCH[] PROGMEM = TR_SWITCH; -const pm_char STR_TRIMS[] PROGMEM = TR_TRIMS; -const pm_char STR_FADEIN[] PROGMEM = TR_FADEIN; -const pm_char STR_FADEOUT[] PROGMEM = TR_FADEOUT; -const pm_char STR_DEFAULT[] PROGMEM = TR_DEFAULT; -const pm_char STR_CHECKTRIMS[] PROGMEM = TR_CHECKTRIMS; -#ifdef HELI -const pm_char STR_SWASHTYPE[] PROGMEM = TR_SWASHTYPE; -const pm_char STR_COLLECTIVE[] PROGMEM = TR_COLLECTIVE; -const pm_char STR_AILERON[] PROGMEM = TR_AILERON; -const pm_char STR_ELEVATOR[] PROGMEM = TR_ELEVATOR; -const pm_char STR_SWASHRING[] PROGMEM = TR_SWASHRING; -const pm_char STR_ELEDIRECTION[] PROGMEM = TR_ELEDIRECTION; -const pm_char STR_AILDIRECTION[] PROGMEM = TR_AILDIRECTION; -const pm_char STR_COLDIRECTION[] PROGMEM = TR_COLDIRECTION; -#endif -const pm_char STR_MODE[] PROGMEM = TR_MODE; -#if defined(AUDIO) && defined(BUZZER) -const pm_char STR_SPEAKER[] PROGMEM = TR_SPEAKER; -const pm_char STR_BUZZER[] PROGMEM = TR_BUZZER; -#endif -const pm_char STR_NOFREEEXPO[] PROGMEM = TR_NOFREEEXPO; -const pm_char STR_NOFREEMIXER[] PROGMEM = TR_NOFREEMIXER; -const pm_char STR_SOURCE[] PROGMEM = TR_SOURCE; -const pm_char STR_WEIGHT[] PROGMEM = TR_WEIGHT; -const pm_char STR_EXPO[] PROGMEM = TR_EXPO; -const pm_char STR_SIDE[] PROGMEM = TR_SIDE; -const pm_char STR_DIFFERENTIAL[] PROGMEM = TR_DIFFERENTIAL; -const pm_char STR_OFFSET[] PROGMEM = TR_OFFSET; -const pm_char STR_TRIM[] PROGMEM = TR_TRIM; -const pm_char STR_DREX[] PROGMEM = TR_DREX; -const pm_char STR_CURVE[] PROGMEM = TR_CURVE; -const pm_char STR_FLMODE[] PROGMEM = TR_FLMODE; -const pm_char STR_MIXWARNING[] PROGMEM = TR_MIXWARNING; -const pm_char STR_OFF[] PROGMEM = TR_OFF; -const pm_char STR_MULTPX[] PROGMEM = TR_MULTPX; -const pm_char STR_DELAYDOWN[] PROGMEM = TR_DELAYDOWN; -const pm_char STR_DELAYUP[] PROGMEM = TR_DELAYUP; -const pm_char STR_SLOWDOWN[] PROGMEM = TR_SLOWDOWN; -const pm_char STR_SLOWUP[] PROGMEM = TR_SLOWUP; -const pm_char STR_MIXER[] PROGMEM = TR_MIXER; -const pm_char STR_CV[] PROGMEM = TR_CV; -const pm_char STR_GV[] PROGMEM = TR_GV; -const pm_char STR_ACHANNEL[] PROGMEM = TR_ACHANNEL; -const pm_char STR_RANGE[] PROGMEM = TR_RANGE; -const pm_char STR_CENTER[] PROGMEM = TR_CENTER; -const pm_char STR_BAR[] PROGMEM = TR_BAR; -const pm_char STR_ALARM[] PROGMEM = TR_ALARM; -const pm_char STR_USRDATA[] PROGMEM = TR_USRDATA; -const pm_char STR_BLADES[] PROGMEM = TR_BLADES; -const pm_char STR_SCREEN[] PROGMEM = TR_SCREEN; -const pm_char STR_SOUND_LABEL[] PROGMEM = TR_SOUND_LABEL; -const pm_char STR_LENGTH[] PROGMEM = TR_LENGTH; -#if defined(CPUARM) -const pm_char STR_BEEP_LENGTH[] PROGMEM = TR_BEEP_LENGTH; -#endif -#if defined(AUDIO) -const pm_char STR_SPKRPITCH[] PROGMEM = TR_SPKRPITCH; -#endif -#if defined(HAPTIC) -const pm_char STR_HAPTIC_LABEL[] PROGMEM = TR_HAPTIC_LABEL; -const pm_char STR_HAPTICSTRENGTH[] PROGMEM = TR_HAPTICSTRENGTH; -#endif -const pm_char STR_CONTRAST[] PROGMEM = TR_CONTRAST; -const pm_char STR_ALARMS_LABEL[] PROGMEM = TR_ALARMS_LABEL; -#if defined(BATTGRAPH) || defined(CPUARM) -const pm_char STR_BATTERY_RANGE[] PROGMEM = TR_BATTERY_RANGE; -#endif -const pm_char STR_BATTERYWARNING[] PROGMEM = TR_BATTERYWARNING; -const pm_char STR_INACTIVITYALARM[] PROGMEM = TR_INACTIVITYALARM; -const pm_char STR_MEMORYWARNING[] PROGMEM = TR_MEMORYWARNING; -const pm_char STR_ALARMWARNING[] PROGMEM = TR_ALARMWARNING; -#if defined(ROTARY_ENCODERS) -const pm_char STR_RENAVIG[] PROGMEM = TR_RENAVIG; -#endif -const pm_char STR_THROTTLEREVERSE[] PROGMEM = TR_THROTTLEREVERSE; -const pm_char STR_TIMER_NAME[] PROGMEM = TR_TIMER_NAME; -const pm_char STR_MINUTEBEEP[] PROGMEM = TR_MINUTEBEEP; -const pm_char STR_BEEPCOUNTDOWN[] PROGMEM = TR_BEEPCOUNTDOWN; -const pm_char STR_PERSISTENT[] PROGMEM = TR_PERSISTENT; -const pm_char STR_BACKLIGHT_LABEL[] PROGMEM = TR_BACKLIGHT_LABEL; -const pm_char STR_BLDELAY[] PROGMEM = TR_BLDELAY; - -#if defined(PWM_BACKLIGHT) -const pm_char STR_BLONBRIGHTNESS[] PROGMEM = TR_BLONBRIGHTNESS; -const pm_char STR_BLOFFBRIGHTNESS[] PROGMEM = TR_BLOFFBRIGHTNESS; -#endif - -const pm_char STR_SPLASHSCREEN[] PROGMEM = TR_SPLASHSCREEN; -const pm_char STR_THROTTLEWARNING[] PROGMEM = TR_THROTTLEWARNING; -const pm_char STR_SWITCHWARNING[] PROGMEM = TR_SWITCHWARNING; -#ifdef FRSKY -const pm_char STR_TIMEZONE[] PROGMEM = TR_TIMEZONE; -const pm_char STR_ADJUST_RTC[] PROGMEM = TR_ADJUST_RTC; -const pm_char STR_GPS[] PROGMEM = TR_GPS; -const pm_char STR_GPSCOORD[] PROGMEM = TR_GPSCOORD; -const pm_char STR_VARIO[] PROGMEM = TR_VARIO; -const pm_char STR_PITCH_AT_ZERO[] PROGMEM = TR_PITCH_AT_ZERO; -const pm_char STR_PITCH_AT_MAX[] PROGMEM = TR_PITCH_AT_MAX; -const pm_char STR_REPEAT_AT_ZERO[] PROGMEM = TR_REPEAT_AT_ZERO; -#endif -const pm_char STR_RXCHANNELORD[] PROGMEM = TR_RXCHANNELORD; -const pm_char STR_STICKS[] PROGMEM = TR_STICKS; -const pm_char STR_POTS[] PROGMEM = TR_POTS; -const pm_char STR_SWITCHES[] PROGMEM = TR_SWITCHES; -const pm_char STR_SWITCHES_DELAY[] PROGMEM = TR_SWITCHES_DELAY; -const pm_char STR_SLAVE[] PROGMEM = TR_SLAVE; -const pm_char STR_MODESRC[] PROGMEM = TR_MODESRC; -const pm_char STR_MULTIPLIER[] PROGMEM = TR_MULTIPLIER; -const pm_char STR_CAL[] PROGMEM = TR_CAL; -const pm_char STR_VTRIM[] PROGMEM = TR_VTRIM; -const pm_char STR_BG[] PROGMEM = TR_BG; -const pm_char STR_MENUTOSTART[] PROGMEM = TR_MENUTOSTART; -const pm_char STR_SETMIDPOINT[] PROGMEM = TR_SETMIDPOINT; -const pm_char STR_MOVESTICKSPOTS[] PROGMEM = TR_MOVESTICKSPOTS; -const pm_char STR_RXBATT[] PROGMEM = TR_RXBATT; -const pm_char STR_TX[] PROGMEM = TR_TXnRX; -const pm_char STR_ACCEL[] PROGMEM = TR_ACCEL; -const pm_char STR_NODATA[] PROGMEM = TR_NODATA; -const pm_char STR_TOTTM1TM2THRTHP[] PROGMEM = TR_TOTTM1TM2THRTHP; -const pm_char STR_TMR1LATMAXUS[] PROGMEM = TR_TMR1LATMAXUS; -const pm_char STR_TMR1LATMINUS[] PROGMEM = TR_TMR1LATMINUS; -const pm_char STR_TMR1JITTERUS[] PROGMEM = TR_TMR1JITTERUS; -const pm_char STR_TMIXMAXMS[] PROGMEM = TR_TMIXMAXMS; -const pm_char STR_FREESTACKMINB[] PROGMEM = TR_FREESTACKMINB; -const pm_char STR_MENUTORESET[] PROGMEM = TR_MENUTORESET; -const pm_char STR_PPM_TRAINER[] PROGMEM = TR_PPM_TRAINER; -const pm_char STR_CH[] PROGMEM = TR_CH; -const pm_char STR_MODEL[] PROGMEM = TR_MODEL; -const pm_char STR_FP[] PROGMEM = TR_FP; -const pm_char STR_MIX[] PROGMEM = TR_MIX; -const pm_char STR_ALERT[] PROGMEM = TR_ALERT; -const pm_char STR_PRESSANYKEYTOSKIP[] PROGMEM = TR_PRESSANYKEYTOSKIP; -const pm_char STR_THROTTLENOTIDLE[] PROGMEM = TR_THROTTLENOTIDLE; -const pm_char STR_ALARMSDISABLED[] PROGMEM = TR_ALARMSDISABLED; -const pm_char STR_PRESSANYKEY[] PROGMEM = TR_PRESSANYKEY; -const pm_char STR_TRIMS2OFFSETS[] PROGMEM = TR_TRIMS2OFFSETS; -const pm_char STR_MENURADIOSETUP[] PROGMEM = TR_MENURADIOSETUP; - -#if defined(EEPROM) -const pm_char STR_BAD_RADIO_DATA[] PROGMEM = TR_BADEEPROMDATA; -const pm_char STR_STORAGE_FORMAT[] PROGMEM = TR_EEPROMFORMATTING; -const pm_char STR_STORAGE_WARNING[] PROGMEM = TR_EEPROMWARN; -const pm_char STR_EEPROM_CONVERTING[] PROGMEM = TR_EEPROM_CONVERTING; -const pm_char STR_EEPROMLOWMEM[] PROGMEM = TR_EEPROMLOWMEM; -const pm_char STR_EEPROMOVERFLOW[] PROGMEM = TR_EEPROMOVERFLOW; -#else -const pm_char STR_BAD_RADIO_DATA[] PROGMEM = TR_BAD_RADIO_DATA; -const pm_char STR_STORAGE_WARNING[] PROGMEM = TR_STORAGE_WARNING; -const pm_char STR_STORAGE_FORMAT[] PROGMEM = TR_STORAGE_FORMAT; -#endif - -#if defined(FAS_OFFSET) || !defined(CPUM64) -const pm_char STR_FAS_OFFSET[] PROGMEM = TR_FAS_OFFSET; -#endif - -#if defined(CPUM2560) || defined(CPUARM) -const pm_char STR_MENUDATEANDTIME[] PROGMEM = TR_MENUDATEANDTIME; -#endif - -const pm_char STR_MENUTRAINER[] PROGMEM = TR_MENUTRAINER; -const pm_char STR_MENUGLOBALFUNCS[] PROGMEM = TR_MENUGLOBALFUNCS; -const pm_char STR_MENUVERSION[] PROGMEM = TR_MENUVERSION; -const pm_char STR_MENUDIAG[] PROGMEM = TR_MENUDIAG; -const pm_char STR_MENUANA[] PROGMEM = TR_MENUANA; -const pm_char STR_MENUCALIBRATION[] PROGMEM = TR_MENUCALIBRATION; - -const pm_char STR_MENUMODELSEL[] PROGMEM = TR_MENUMODELSEL; -const pm_char STR_MENUSETUP[] PROGMEM = TR_MENUSETUP; -const pm_char STR_MENUFLIGHTPHASE[] PROGMEM = TR_MENUFLIGHTPHASE; -const pm_char STR_MENUFLIGHTPHASES[] PROGMEM = TR_MENUFLIGHTPHASES; - -#ifdef HELI -const pm_char STR_MENUHELISETUP[] PROGMEM = TR_MENUHELISETUP; -#endif - -const pm_char STR_MENUINPUTS[] PROGMEM = TR_MENUINPUTS; -const pm_char STR_MENULIMITS[] PROGMEM = TR_MENULIMITS; -const pm_char STR_MENUCURVES[] PROGMEM = TR_MENUCURVES; -const pm_char STR_MENUCURVE[] PROGMEM = TR_MENUCURVE; -const pm_char STR_MENULOGICALSWITCH[] PROGMEM = TR_MENULOGICALSWITCH; -const pm_char STR_MENULOGICALSWITCHES[] PROGMEM = TR_MENULOGICALSWITCHES; -const pm_char STR_MENUCUSTOMFUNC[] PROGMEM = TR_MENUCUSTOMFUNC; - -#if defined(LUA) -const pm_char STR_MENUCUSTOMSCRIPTS[] PROGMEM = TR_MENUCUSTOMSCRIPTS; -#endif - -#if defined(FRSKY) -const pm_char STR_MENUTELEMETRY[] PROGMEM = TR_MENUTELEMETRY; -const pm_char STR_LIMIT[] PROGMEM = TR_LIMIT; -#endif - -#if defined(TEMPLATES) -const pm_char STR_MENUTEMPLATES[] PROGMEM = TR_MENUTEMPLATES; -#endif - -const pm_char STR_MENUSTAT[] PROGMEM = TR_MENUSTAT; -const pm_char STR_MENUDEBUG[] PROGMEM = TR_MENUDEBUG; -const pm_char STR_MENUGLOBALVARS[] PROGMEM = TR_MENUGLOBALVARS; - -#if defined(DSM2) || defined(PXX) -const pm_char STR_RXNUM[] PROGMEM = TR_RXNUM; -#endif - -#if defined(PXX) || defined(CPUARM) -const pm_char STR_SYNCMENU[] PROGMEM = TR_SYNCMENU; -const pm_char STR_INTERNALRF[] PROGMEM = TR_INTERNALRF; -const pm_char STR_EXTERNALRF[] PROGMEM = TR_EXTERNALRF; -const pm_char STR_COUNTRYCODE[] PROGMEM = TR_COUNTRYCODE; -const pm_char STR_FAILSAFE[] PROGMEM = TR_FAILSAFE; -const pm_char STR_FAILSAFESET[] PROGMEM = TR_FAILSAFESET; -const pm_char STR_MENUSENSOR[] PROGMEM = TR_MENUSENSOR; -const pm_char STR_SENSOR[] PROGMEM = TR_SENSOR; -#endif - -const pm_char STR_INVERT_THR[] PROGMEM = TR_INVERT_THR; -const pm_char STR_AND_SWITCH[] PROGMEM = TR_AND_SWITCH; -const pm_char STR_SF[] PROGMEM = TR_SF; -const pm_char STR_GF[] PROGMEM = TR_GF; - -#if defined(FRSKY_HUB) -const pm_char STR_MINRSSI[] PROGMEM = TR_MINRSSI; -const pm_char STR_LATITUDE[] PROGMEM = TR_LATITUDE; -const pm_char STR_LONGITUDE[] PROGMEM = TR_LONGITUDE; -#endif - -#if defined(CPUARM) || defined(CPUM2560) -const pm_char STR_SHUTDOWN[] PROGMEM = TR_SHUTDOWN; -const pm_char STR_SAVEMODEL[] PROGMEM = TR_SAVEMODEL; -#endif - -#if defined(PCBTARANIS) && defined(REV9E) -const pm_char STR_POWEROFF[] PROGMEM = TR_POWEROFF; -#endif - -const pm_char STR_BATT_CALIB[] PROGMEM = TR_BATT_CALIB; - -#if defined(CPUARM) || defined(FRSKY) -const pm_char STR_VOLTAGE[] PROGMEM = TR_VOLTAGE; -const pm_char STR_CURRENT[] PROGMEM = TR_CURRENT; -#endif - -#if defined(CPUARM) -const pm_char STR_CURRENT_CALIB[] PROGMEM = TR_CURRENT_CALIB; -const pm_char STR_UNITSSYSTEM[] PROGMEM = TR_UNITSSYSTEM; -const pm_char STR_VOICELANG[] PROGMEM = TR_VOICELANG; -const pm_char STR_MODELIDUSED[] PROGMEM = TR_MODELIDUSED; -const pm_char STR_BEEP_VOLUME[] PROGMEM = INDENT TR_BEEP_VOLUME; -const pm_char STR_WAV_VOLUME[] PROGMEM = INDENT TR_WAV_VOLUME; -const pm_char STR_VARIO_VOLUME[] PROGMEM = INDENT TR_VARIO_VOLUME; -const pm_char STR_BG_VOLUME[] PROGMEM = INDENT TR_BG_VOLUME; -const pm_char STR_PERSISTENT_MAH[] PROGMEM = TR_PERSISTENT_MAH; -#endif - -#if defined(NAVIGATION_MENUS) -const pm_char STR_SELECT_MODEL[] PROGMEM = TR_SELECT_MODEL; -const pm_char STR_CREATE_CATEGORY[] PROGMEM = TR_CREATE_CATEGORY; -const pm_char STR_RENAME_CATEGORY[] PROGMEM = TR_RENAME_CATEGORY; -const pm_char STR_DELETE_CATEGORY[] PROGMEM = TR_DELETE_CATEGORY; -const pm_char STR_CREATE_MODEL[] PROGMEM = TR_CREATE_MODEL; -const pm_char STR_DUPLICATE_MODEL[] PROGMEM = TR_DUPLICATE_MODEL; +/* + * 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" + +#define ISTR(x) LEN_##x TR_##x + +// The non-0-terminated-strings +const pm_char STR_OPEN9X[] PROGMEM = + ISTR(OFFON) + ISTR(MMMINV) + ISTR(NCHANNELS) +#if !defined(GRAPHICS) + ISTR(VBEEPLEN) +#endif + ISTR(VBEEPMODE) +#if defined(ROTARY_ENCODERS) + ISTR(VRENAVIG) +#endif +#if defined(ROTARY_ENCODER_NAVIGATION) + ISTR(VRENCODERS) +#endif + ISTR(TRNMODE) + ISTR(TRNCHN) +#if defined(PCBTARANIS) || defined(PCBHORUS) + ISTR(UART3MODES) + ISTR(SWTYPES) + ISTR(POTTYPES) + ISTR(SLIDERTYPES) +#endif + ISTR(VTRIMINC) +#if defined(CPUARM) + ISTR(VDISPLAYTRIMS) +#endif + ISTR(RETA123) + ISTR(VPROTOS) + ISTR(POSNEG) +#if defined(PCBSKY9X) && defined(REVX) + ISTR(VOUTPUT_TYPE) +#endif + ISTR(VBLMODE) + ISTR(VCURVEFUNC) + ISTR(VMLTPX) + ISTR(VMLTPX2) + ISTR(VMIXTRIMS) + ISTR(VCSWFUNC) + ISTR(VFSWFUNC) + ISTR(VFSWRESET) + ISTR(FUNCSOUNDS) + ISTR(VTELEMCHNS) +#if defined(FRSKY) || defined(CPUARM) + ISTR(VTELEMUNIT) + ISTR(VALARM) + ISTR(VALARMFN) + ISTR(VTELPROTO) + ISTR(GPSFORMAT) + ISTR(AMPSRC) + ISTR(VARIOSRC) + ISTR(VTELEMSCREENTYPE) +#endif +#if defined(TEMPLATES) + ISTR(VTEMPLATES) +#endif +#if defined(HELI) + ISTR(VSWASHTYPE) +#endif + ISTR(VKEYS) + ISTR(VSWITCHES) + ISTR(VSRCRAW) +#if defined(TRANSLATIONS_CZ) && defined(CPUARM) + ISTR(INPUTNAMES) +#endif + ISTR(VTMRMODES) +#if defined(CPUM2560) || defined(CPUARM) + ISTR(DATETIME) + ISTR(VPERSISTENT) +#endif +#if defined(CPUARM) + ISTR(VLCD) + ISTR(VUNITSSYSTEM) + ISTR(VBEEPCOUNTDOWN) + ISTR(VVARIOCENTER) +#endif +#if defined(PXX) || defined(CPUARM) + ISTR(COUNTRYCODES) + ISTR(VFAILSAFE) +#endif +#if defined(CPUARM) + ISTR(VTRAINERMODES) + ISTR(TARANIS_PROTOCOLS) + ISTR(XJT_PROTOCOLS) + ISTR(DSM_PROTOCOLS) + ISTR(VOLTSRC) + ISTR(CURVE_TYPES) + ISTR(VSENSORTYPES) + ISTR(VFORMULAS) + ISTR(VPREC) + ISTR(VCELLINDEX) +#endif +#if defined(MAVLINK) + ISTR(MAVLINK_BAUDS) + ISTR(MAVLINK_AC_MODES) + ISTR(MAVLINK_AP_MODES) +#endif + ; + +// The 0-terminated-strings +const pm_char STR_POPUPS[] PROGMEM = TR_POPUPS; +#if !defined(OFS_EXIT) + const pm_char STR_EXIT[] PROGMEM = TR_EXIT; +#endif +const pm_char STR_MENUWHENDONE[] PROGMEM = TR_MENUWHENDONE; +const pm_char STR_FREE[] PROGMEM = TR_FREE; +const pm_char STR_DELETEMODEL[] PROGMEM = TR_DELETEMODEL; +const pm_char STR_COPYINGMODEL[] PROGMEM = TR_COPYINGMODEL; +const pm_char STR_MOVINGMODEL[] PROGMEM = TR_MOVINGMODEL; +const pm_char STR_LOADINGMODEL[] PROGMEM = TR_LOADINGMODEL; +const pm_char STR_NAME[] PROGMEM = TR_NAME; +const pm_char STR_BITMAP[] PROGMEM = TR_BITMAP; +const pm_char STR_TIMER[] PROGMEM = TR_TIMER; +const pm_char STR_ELIMITS[] PROGMEM = TR_ELIMITS; +const pm_char STR_ETRIMS[] PROGMEM = TR_ETRIMS; +const pm_char STR_TRIMINC[] PROGMEM = TR_TRIMINC; +const pm_char STR_DISPLAY_TRIMS[] PROGMEM = TR_DISPLAY_TRIMS; +const pm_char STR_TTRACE[] PROGMEM = TR_TTRACE; +const pm_char STR_TTRIM[] PROGMEM = TR_TTRIM; +const pm_char STR_BEEPCTR[] PROGMEM = TR_BEEPCTR; +const pm_char STR_USE_GLOBAL_FUNCS[] PROGMEM = TR_USE_GLOBAL_FUNCS; +#if defined(PCBSKY9X) && defined(REVX) + const pm_char STR_OUTPUT_TYPE[] PROGMEM = TR_OUTPUT_TYPE; +#endif +const pm_char STR_PROTO[] PROGMEM = TR_PROTO; +const pm_char STR_PPMFRAME[] PROGMEM = TR_PPMFRAME; +const pm_char STR_MS[] PROGMEM = TR_MS; +const pm_char STR_SWITCH[] PROGMEM = TR_SWITCH; +const pm_char STR_TRIMS[] PROGMEM = TR_TRIMS; +const pm_char STR_FADEIN[] PROGMEM = TR_FADEIN; +const pm_char STR_FADEOUT[] PROGMEM = TR_FADEOUT; +const pm_char STR_DEFAULT[] PROGMEM = TR_DEFAULT; +const pm_char STR_CHECKTRIMS[] PROGMEM = TR_CHECKTRIMS; +#ifdef HELI +const pm_char STR_SWASHTYPE[] PROGMEM = TR_SWASHTYPE; +const pm_char STR_COLLECTIVE[] PROGMEM = TR_COLLECTIVE; +const pm_char STR_AILERON[] PROGMEM = TR_AILERON; +const pm_char STR_ELEVATOR[] PROGMEM = TR_ELEVATOR; +const pm_char STR_SWASHRING[] PROGMEM = TR_SWASHRING; +const pm_char STR_ELEDIRECTION[] PROGMEM = TR_ELEDIRECTION; +const pm_char STR_AILDIRECTION[] PROGMEM = TR_AILDIRECTION; +const pm_char STR_COLDIRECTION[] PROGMEM = TR_COLDIRECTION; +#endif +const pm_char STR_MODE[] PROGMEM = TR_MODE; +#if defined(AUDIO) && defined(BUZZER) +const pm_char STR_SPEAKER[] PROGMEM = TR_SPEAKER; +const pm_char STR_BUZZER[] PROGMEM = TR_BUZZER; +#endif +const pm_char STR_NOFREEEXPO[] PROGMEM = TR_NOFREEEXPO; +const pm_char STR_NOFREEMIXER[] PROGMEM = TR_NOFREEMIXER; +const pm_char STR_SOURCE[] PROGMEM = TR_SOURCE; +const pm_char STR_WEIGHT[] PROGMEM = TR_WEIGHT; +const pm_char STR_EXPO[] PROGMEM = TR_EXPO; +const pm_char STR_SIDE[] PROGMEM = TR_SIDE; +const pm_char STR_DIFFERENTIAL[] PROGMEM = TR_DIFFERENTIAL; +const pm_char STR_OFFSET[] PROGMEM = TR_OFFSET; +const pm_char STR_TRIM[] PROGMEM = TR_TRIM; +const pm_char STR_DREX[] PROGMEM = TR_DREX; +const pm_char STR_CURVE[] PROGMEM = TR_CURVE; +const pm_char STR_FLMODE[] PROGMEM = TR_FLMODE; +const pm_char STR_MIXWARNING[] PROGMEM = TR_MIXWARNING; +const pm_char STR_OFF[] PROGMEM = TR_OFF; +const pm_char STR_MULTPX[] PROGMEM = TR_MULTPX; +const pm_char STR_DELAYDOWN[] PROGMEM = TR_DELAYDOWN; +const pm_char STR_DELAYUP[] PROGMEM = TR_DELAYUP; +const pm_char STR_SLOWDOWN[] PROGMEM = TR_SLOWDOWN; +const pm_char STR_SLOWUP[] PROGMEM = TR_SLOWUP; +const pm_char STR_MIXER[] PROGMEM = TR_MIXER; +const pm_char STR_CV[] PROGMEM = TR_CV; +const pm_char STR_GV[] PROGMEM = TR_GV; +const pm_char STR_ACHANNEL[] PROGMEM = TR_ACHANNEL; +const pm_char STR_RANGE[] PROGMEM = TR_RANGE; +const pm_char STR_CENTER[] PROGMEM = TR_CENTER; +const pm_char STR_BAR[] PROGMEM = TR_BAR; +const pm_char STR_ALARM[] PROGMEM = TR_ALARM; +const pm_char STR_USRDATA[] PROGMEM = TR_USRDATA; +const pm_char STR_BLADES[] PROGMEM = TR_BLADES; +const pm_char STR_SCREEN[] PROGMEM = TR_SCREEN; +const pm_char STR_SOUND_LABEL[] PROGMEM = TR_SOUND_LABEL; +const pm_char STR_LENGTH[] PROGMEM = TR_LENGTH; +#if defined(CPUARM) +const pm_char STR_BEEP_LENGTH[] PROGMEM = TR_BEEP_LENGTH; +#endif +#if defined(AUDIO) +const pm_char STR_SPKRPITCH[] PROGMEM = TR_SPKRPITCH; +#endif +#if defined(HAPTIC) +const pm_char STR_HAPTIC_LABEL[] PROGMEM = TR_HAPTIC_LABEL; +const pm_char STR_HAPTICSTRENGTH[] PROGMEM = TR_HAPTICSTRENGTH; +#endif +const pm_char STR_CONTRAST[] PROGMEM = TR_CONTRAST; +const pm_char STR_ALARMS_LABEL[] PROGMEM = TR_ALARMS_LABEL; +#if defined(BATTGRAPH) || defined(CPUARM) +const pm_char STR_BATTERY_RANGE[] PROGMEM = TR_BATTERY_RANGE; +#endif +const pm_char STR_BATTERYWARNING[] PROGMEM = TR_BATTERYWARNING; +const pm_char STR_INACTIVITYALARM[] PROGMEM = TR_INACTIVITYALARM; +const pm_char STR_MEMORYWARNING[] PROGMEM = TR_MEMORYWARNING; +const pm_char STR_ALARMWARNING[] PROGMEM = TR_ALARMWARNING; +#if defined(ROTARY_ENCODERS) +const pm_char STR_RENAVIG[] PROGMEM = TR_RENAVIG; +#endif +const pm_char STR_THROTTLEREVERSE[] PROGMEM = TR_THROTTLEREVERSE; +const pm_char STR_TIMER_NAME[] PROGMEM = TR_TIMER_NAME; +const pm_char STR_MINUTEBEEP[] PROGMEM = TR_MINUTEBEEP; +const pm_char STR_BEEPCOUNTDOWN[] PROGMEM = TR_BEEPCOUNTDOWN; +const pm_char STR_PERSISTENT[] PROGMEM = TR_PERSISTENT; +const pm_char STR_BACKLIGHT_LABEL[] PROGMEM = TR_BACKLIGHT_LABEL; +const pm_char STR_BLDELAY[] PROGMEM = TR_BLDELAY; + +#if defined(PWM_BACKLIGHT) +const pm_char STR_BLONBRIGHTNESS[] PROGMEM = TR_BLONBRIGHTNESS; +const pm_char STR_BLOFFBRIGHTNESS[] PROGMEM = TR_BLOFFBRIGHTNESS; +#endif + +const pm_char STR_SPLASHSCREEN[] PROGMEM = TR_SPLASHSCREEN; +const pm_char STR_THROTTLEWARNING[] PROGMEM = TR_THROTTLEWARNING; +const pm_char STR_SWITCHWARNING[] PROGMEM = TR_SWITCHWARNING; +#ifdef FRSKY +const pm_char STR_TIMEZONE[] PROGMEM = TR_TIMEZONE; +const pm_char STR_ADJUST_RTC[] PROGMEM = TR_ADJUST_RTC; +const pm_char STR_GPS[] PROGMEM = TR_GPS; +const pm_char STR_GPSCOORD[] PROGMEM = TR_GPSCOORD; +const pm_char STR_VARIO[] PROGMEM = TR_VARIO; +const pm_char STR_PITCH_AT_ZERO[] PROGMEM = TR_PITCH_AT_ZERO; +const pm_char STR_PITCH_AT_MAX[] PROGMEM = TR_PITCH_AT_MAX; +const pm_char STR_REPEAT_AT_ZERO[] PROGMEM = TR_REPEAT_AT_ZERO; +#endif +const pm_char STR_RXCHANNELORD[] PROGMEM = TR_RXCHANNELORD; +const pm_char STR_STICKS[] PROGMEM = TR_STICKS; +const pm_char STR_POTS[] PROGMEM = TR_POTS; +const pm_char STR_SWITCHES[] PROGMEM = TR_SWITCHES; +const pm_char STR_SWITCHES_DELAY[] PROGMEM = TR_SWITCHES_DELAY; +const pm_char STR_SLAVE[] PROGMEM = TR_SLAVE; +const pm_char STR_MODESRC[] PROGMEM = TR_MODESRC; +const pm_char STR_MULTIPLIER[] PROGMEM = TR_MULTIPLIER; +const pm_char STR_CAL[] PROGMEM = TR_CAL; +const pm_char STR_VTRIM[] PROGMEM = TR_VTRIM; +const pm_char STR_BG[] PROGMEM = TR_BG; +const pm_char STR_MENUTOSTART[] PROGMEM = TR_MENUTOSTART; +const pm_char STR_SETMIDPOINT[] PROGMEM = TR_SETMIDPOINT; +const pm_char STR_MOVESTICKSPOTS[] PROGMEM = TR_MOVESTICKSPOTS; +const pm_char STR_RXBATT[] PROGMEM = TR_RXBATT; +const pm_char STR_TX[] PROGMEM = TR_TXnRX; +const pm_char STR_ACCEL[] PROGMEM = TR_ACCEL; +const pm_char STR_NODATA[] PROGMEM = TR_NODATA; +const pm_char STR_TOTTM1TM2THRTHP[] PROGMEM = TR_TOTTM1TM2THRTHP; +const pm_char STR_TMR1LATMAXUS[] PROGMEM = TR_TMR1LATMAXUS; +const pm_char STR_TMR1LATMINUS[] PROGMEM = TR_TMR1LATMINUS; +const pm_char STR_TMR1JITTERUS[] PROGMEM = TR_TMR1JITTERUS; +const pm_char STR_TMIXMAXMS[] PROGMEM = TR_TMIXMAXMS; +const pm_char STR_FREESTACKMINB[] PROGMEM = TR_FREESTACKMINB; +const pm_char STR_MENUTORESET[] PROGMEM = TR_MENUTORESET; +const pm_char STR_PPM_TRAINER[] PROGMEM = TR_PPM_TRAINER; +const pm_char STR_CH[] PROGMEM = TR_CH; +const pm_char STR_MODEL[] PROGMEM = TR_MODEL; +const pm_char STR_FP[] PROGMEM = TR_FP; +const pm_char STR_MIX[] PROGMEM = TR_MIX; +const pm_char STR_ALERT[] PROGMEM = TR_ALERT; +const pm_char STR_PRESSANYKEYTOSKIP[] PROGMEM = TR_PRESSANYKEYTOSKIP; +const pm_char STR_THROTTLENOTIDLE[] PROGMEM = TR_THROTTLENOTIDLE; +const pm_char STR_ALARMSDISABLED[] PROGMEM = TR_ALARMSDISABLED; +const pm_char STR_PRESSANYKEY[] PROGMEM = TR_PRESSANYKEY; +const pm_char STR_TRIMS2OFFSETS[] PROGMEM = TR_TRIMS2OFFSETS; +const pm_char STR_MENURADIOSETUP[] PROGMEM = TR_MENURADIOSETUP; + +#if defined(EEPROM) +const pm_char STR_BAD_RADIO_DATA[] PROGMEM = TR_BADEEPROMDATA; +const pm_char STR_STORAGE_FORMAT[] PROGMEM = TR_EEPROMFORMATTING; +const pm_char STR_STORAGE_WARNING[] PROGMEM = TR_EEPROMWARN; +const pm_char STR_EEPROM_CONVERTING[] PROGMEM = TR_EEPROM_CONVERTING; +const pm_char STR_EEPROMLOWMEM[] PROGMEM = TR_EEPROMLOWMEM; +const pm_char STR_EEPROMOVERFLOW[] PROGMEM = TR_EEPROMOVERFLOW; +#else +const pm_char STR_BAD_RADIO_DATA[] PROGMEM = TR_BAD_RADIO_DATA; +const pm_char STR_STORAGE_WARNING[] PROGMEM = TR_STORAGE_WARNING; +const pm_char STR_STORAGE_FORMAT[] PROGMEM = TR_STORAGE_FORMAT; +#endif + +#if defined(FAS_OFFSET) || !defined(CPUM64) +const pm_char STR_FAS_OFFSET[] PROGMEM = TR_FAS_OFFSET; +#endif + +#if defined(CPUM2560) || defined(CPUARM) +const pm_char STR_MENUDATEANDTIME[] PROGMEM = TR_MENUDATEANDTIME; +#endif + +const pm_char STR_MENUTRAINER[] PROGMEM = TR_MENUTRAINER; +const pm_char STR_MENUGLOBALFUNCS[] PROGMEM = TR_MENUGLOBALFUNCS; +const pm_char STR_MENUVERSION[] PROGMEM = TR_MENUVERSION; +const pm_char STR_MENUDIAG[] PROGMEM = TR_MENUDIAG; +const pm_char STR_MENUANA[] PROGMEM = TR_MENUANA; +const pm_char STR_MENUCALIBRATION[] PROGMEM = TR_MENUCALIBRATION; + +const pm_char STR_MENUMODELSEL[] PROGMEM = TR_MENUMODELSEL; +const pm_char STR_MENUSETUP[] PROGMEM = TR_MENUSETUP; +const pm_char STR_MENUFLIGHTPHASE[] PROGMEM = TR_MENUFLIGHTPHASE; +const pm_char STR_MENUFLIGHTPHASES[] PROGMEM = TR_MENUFLIGHTPHASES; + +#ifdef HELI +const pm_char STR_MENUHELISETUP[] PROGMEM = TR_MENUHELISETUP; +#endif + +const pm_char STR_MENUINPUTS[] PROGMEM = TR_MENUINPUTS; +const pm_char STR_MENULIMITS[] PROGMEM = TR_MENULIMITS; +const pm_char STR_MENUCURVES[] PROGMEM = TR_MENUCURVES; +const pm_char STR_MENUCURVE[] PROGMEM = TR_MENUCURVE; +const pm_char STR_MENULOGICALSWITCH[] PROGMEM = TR_MENULOGICALSWITCH; +const pm_char STR_MENULOGICALSWITCHES[] PROGMEM = TR_MENULOGICALSWITCHES; +const pm_char STR_MENUCUSTOMFUNC[] PROGMEM = TR_MENUCUSTOMFUNC; + +#if defined(LUA) +const pm_char STR_MENUCUSTOMSCRIPTS[] PROGMEM = TR_MENUCUSTOMSCRIPTS; +#endif + +#if defined(FRSKY) +const pm_char STR_MENUTELEMETRY[] PROGMEM = TR_MENUTELEMETRY; +const pm_char STR_LIMIT[] PROGMEM = TR_LIMIT; +#endif + +#if defined(TEMPLATES) +const pm_char STR_MENUTEMPLATES[] PROGMEM = TR_MENUTEMPLATES; +#endif + +const pm_char STR_MENUSTAT[] PROGMEM = TR_MENUSTAT; +const pm_char STR_MENUDEBUG[] PROGMEM = TR_MENUDEBUG; +const pm_char STR_MENUGLOBALVARS[] PROGMEM = TR_MENUGLOBALVARS; + +#if defined(DSM2) || defined(PXX) +const pm_char STR_RXNUM[] PROGMEM = TR_RXNUM; +#endif + +#if defined(PXX) || defined(CPUARM) +const pm_char STR_SYNCMENU[] PROGMEM = TR_SYNCMENU; +const pm_char STR_INTERNALRF[] PROGMEM = TR_INTERNALRF; +const pm_char STR_EXTERNALRF[] PROGMEM = TR_EXTERNALRF; +const pm_char STR_COUNTRYCODE[] PROGMEM = TR_COUNTRYCODE; +const pm_char STR_FAILSAFE[] PROGMEM = TR_FAILSAFE; +const pm_char STR_FAILSAFESET[] PROGMEM = TR_FAILSAFESET; +const pm_char STR_MENUSENSOR[] PROGMEM = TR_MENUSENSOR; +const pm_char STR_SENSOR[] PROGMEM = TR_SENSOR; +#endif + +const pm_char STR_INVERT_THR[] PROGMEM = TR_INVERT_THR; +const pm_char STR_AND_SWITCH[] PROGMEM = TR_AND_SWITCH; +const pm_char STR_SF[] PROGMEM = TR_SF; +const pm_char STR_GF[] PROGMEM = TR_GF; + +#if defined(FRSKY_HUB) +const pm_char STR_MINRSSI[] PROGMEM = TR_MINRSSI; +const pm_char STR_LATITUDE[] PROGMEM = TR_LATITUDE; +const pm_char STR_LONGITUDE[] PROGMEM = TR_LONGITUDE; +#endif + +#if defined(CPUARM) || defined(CPUM2560) +const pm_char STR_SHUTDOWN[] PROGMEM = TR_SHUTDOWN; +const pm_char STR_SAVEMODEL[] PROGMEM = TR_SAVEMODEL; +#endif + +#if defined(PCBTARANIS) && defined(REV9E) +const pm_char STR_POWEROFF[] PROGMEM = TR_POWEROFF; +#endif + +const pm_char STR_BATT_CALIB[] PROGMEM = TR_BATT_CALIB; + +#if defined(CPUARM) || defined(FRSKY) +const pm_char STR_VOLTAGE[] PROGMEM = TR_VOLTAGE; +const pm_char STR_CURRENT[] PROGMEM = TR_CURRENT; +#endif + +#if defined(CPUARM) +const pm_char STR_CURRENT_CALIB[] PROGMEM = TR_CURRENT_CALIB; +const pm_char STR_UNITSSYSTEM[] PROGMEM = TR_UNITSSYSTEM; +const pm_char STR_VOICELANG[] PROGMEM = TR_VOICELANG; +const pm_char STR_MODELIDUSED[] PROGMEM = TR_MODELIDUSED; +const pm_char STR_BEEP_VOLUME[] PROGMEM = INDENT TR_BEEP_VOLUME; +const pm_char STR_WAV_VOLUME[] PROGMEM = INDENT TR_WAV_VOLUME; +const pm_char STR_VARIO_VOLUME[] PROGMEM = INDENT TR_VARIO_VOLUME; +const pm_char STR_BG_VOLUME[] PROGMEM = INDENT TR_BG_VOLUME; +const pm_char STR_PERSISTENT_MAH[] PROGMEM = TR_PERSISTENT_MAH; +#endif + +#if defined(NAVIGATION_MENUS) +const pm_char STR_SELECT_MODEL[] PROGMEM = TR_SELECT_MODEL; +const pm_char STR_CREATE_CATEGORY[] PROGMEM = TR_CREATE_CATEGORY; +const pm_char STR_RENAME_CATEGORY[] PROGMEM = TR_RENAME_CATEGORY; +const pm_char STR_DELETE_CATEGORY[] PROGMEM = TR_DELETE_CATEGORY; +const pm_char STR_CREATE_MODEL[] PROGMEM = TR_CREATE_MODEL; +const pm_char STR_DUPLICATE_MODEL[] PROGMEM = TR_DUPLICATE_MODEL; const pm_char STR_CHANGE_CATEGORY[] PROGMEM = TR_CHANGE_CATEGORY; -const pm_char STR_COPY_MODEL[] PROGMEM = TR_COPY_MODEL; -const pm_char STR_MOVE_MODEL[] PROGMEM = TR_MOVE_MODEL; -const pm_char STR_DELETE_MODEL[] PROGMEM = TR_DELETE_MODEL; -const pm_char STR_EDIT[] PROGMEM = TR_EDIT; -const pm_char STR_INSERT_BEFORE[] PROGMEM = TR_INSERT_BEFORE; -const pm_char STR_INSERT_AFTER[] PROGMEM = TR_INSERT_AFTER; -const pm_char STR_COPY[] PROGMEM = TR_COPY; -const pm_char STR_MOVE[] PROGMEM = TR_MOVE; -const pm_char STR_PASTE[] PROGMEM = TR_PASTE; -const pm_char STR_INSERT[] PROGMEM = TR_INSERT; -const pm_char STR_DELETE[] PROGMEM = TR_DELETE; -const pm_char STR_RESET_FLIGHT[] PROGMEM = TR_RESET_FLIGHT; -const pm_char STR_RESET_TIMER1[] PROGMEM = TR_RESET_TIMER1; -const pm_char STR_RESET_TIMER2[] PROGMEM = TR_RESET_TIMER2; -const pm_char STR_RESET_TIMER3[] PROGMEM = TR_RESET_TIMER3; -const pm_char STR_RESET_TELEMETRY[] PROGMEM = TR_RESET_TELEMETRY; -const pm_char STR_STATISTICS[] PROGMEM = TR_STATISTICS; -const pm_char STR_ABOUT_US[] PROGMEM = TR_ABOUT_US; -const pm_char STR_SETUP_SCREENS[] PROGMEM = TR_SETUP_SCREENS; -#endif - -const pm_char STR_RESET_BTN[] PROGMEM = TR_RESET_BTN; - -#if defined(SDCARD) -const pm_char STR_BACKUP_MODEL[] PROGMEM = TR_BACKUP_MODEL; -const pm_char STR_RESTORE_MODEL[] PROGMEM = TR_RESTORE_MODEL; -const pm_char STR_SDCARD_ERROR[] PROGMEM = TR_SDCARD_ERROR; -const pm_char STR_NO_SDCARD[] PROGMEM = TR_NO_SDCARD; -const pm_char STR_SDCARD_FULL[] PROGMEM = TR_SDCARD_FULL; -const pm_char STR_INCOMPATIBLE[] PROGMEM = TR_INCOMPATIBLE; -const pm_char STR_LOGS_PATH[] PROGMEM = LOGS_PATH; -const pm_char STR_LOGS_EXT[] PROGMEM = LOGS_EXT; -const pm_char STR_MODELS_PATH[] PROGMEM = MODELS_PATH; -const pm_char STR_MODELS_EXT[] PROGMEM = MODELS_EXT; -#endif - -const pm_char STR_WARNING[] PROGMEM = TR_WARNING; -const pm_char STR_THROTTLEWARN[] PROGMEM = TR_THROTTLEWARN; -const pm_char STR_ALARMSWARN[] PROGMEM = TR_ALARMSWARN; -const pm_char STR_SWITCHWARN[] PROGMEM = TR_SWITCHWARN; -const pm_char STR_FAILSAFEWARN[] PROGMEM = TR_FAILSAFEWARN; -const pm_char STR_NO_FAILSAFE[] PROGMEM = TR_NO_FAILSAFE; -const pm_char STR_KEYSTUCK[] PROGMEM = TR_KEYSTUCK; - -const pm_char STR_SPEAKER_VOLUME[] PROGMEM = TR_SPEAKER_VOLUME; -const pm_char STR_LCD[] PROGMEM = TR_LCD; -const pm_char STR_BRIGHTNESS[] PROGMEM = TR_BRIGHTNESS; -const pm_char STR_CPU_TEMP[] PROGMEM = TR_CPU_TEMP; -const pm_char STR_CPU_CURRENT[] PROGMEM = TR_CPU_CURRENT; -const pm_char STR_CPU_MAH[] PROGMEM = TR_CPU_MAH; -const pm_char STR_COPROC[] PROGMEM = TR_COPROC; -const pm_char STR_COPROC_TEMP[] PROGMEM = TR_COPROC_TEMP; -const pm_char STR_TEMPWARNING[] PROGMEM = TR_TEMPWARNING; -const pm_char STR_CAPAWARNING[] PROGMEM = TR_CAPAWARNING; -const pm_char STR_FUNC[] PROGMEM = TR_FUNC; -const pm_char STR_V1[] PROGMEM = TR_V1; -const pm_char STR_V2[] PROGMEM = TR_V2; -const pm_char STR_DURATION[] PROGMEM = TR_DURATION; -const pm_char STR_DELAY[] PROGMEM = TR_DELAY; -const pm_char STR_SD_CARD[] PROGMEM = TR_SD_CARD; -const pm_char STR_SDHC_CARD[] PROGMEM = TR_SDHC_CARD; -const pm_char STR_NO_SOUNDS_ON_SD[] PROGMEM = TR_NO_SOUNDS_ON_SD; -const pm_char STR_NO_MODELS_ON_SD[] PROGMEM = TR_NO_MODELS_ON_SD; -const pm_char STR_NO_BITMAPS_ON_SD[] PROGMEM = TR_NO_BITMAPS_ON_SD; -const pm_char STR_NO_SCRIPTS_ON_SD[] PROGMEM = TR_NO_SCRIPTS_ON_SD; -const pm_char STR_SCRIPT_SYNTAX_ERROR[] PROGMEM = TR_SCRIPT_SYNTAX_ERROR; -const pm_char STR_SCRIPT_PANIC[] PROGMEM = TR_SCRIPT_PANIC; -const pm_char STR_SCRIPT_KILLED[] PROGMEM = TR_SCRIPT_KILLED; -const pm_char STR_SCRIPT_ERROR[] PROGMEM = TR_SCRIPT_ERROR; -const pm_char STR_PLAY_FILE[] PROGMEM = TR_PLAY_FILE; -const pm_char STR_ASSIGN_BITMAP[] PROGMEM = TR_ASSIGN_BITMAP; -const pm_char STR_EXECUTE_FILE[] PROGMEM = TR_EXECUTE_FILE; -const pm_char STR_DELETE_FILE[] PROGMEM = TR_DELETE_FILE; -const pm_char STR_COPY_FILE[] PROGMEM = TR_COPY_FILE; -const pm_char STR_RENAME_FILE[] PROGMEM = TR_RENAME_FILE; -const pm_char STR_SD_INFO[] PROGMEM = TR_SD_INFO; -const pm_char STR_SD_FORMAT[] PROGMEM = TR_SD_FORMAT; -const pm_char STR_REMOVED[] PROGMEM = TR_REMOVED; -const pm_char STR_NA[] PROGMEM = TR_NA; -const pm_char STR_HARDWARE[] PROGMEM = TR_HARDWARE; -const pm_char STR_FORMATTING[] PROGMEM = TR_FORMATTING; -const pm_char STR_TEMP_CALIB[] PROGMEM = TR_TEMP_CALIB; -const pm_char STR_TIME[] PROGMEM = TR_TIME; -const pm_char STR_BAUDRATE[] PROGMEM = TR_BAUDRATE; -const pm_char STR_SD_INFO_TITLE[] PROGMEM = TR_SD_INFO_TITLE; -const pm_char STR_SD_TYPE[] PROGMEM = TR_SD_TYPE; -const pm_char STR_SD_SPEED[] PROGMEM = TR_SD_SPEED; -const pm_char STR_SD_SECTORS[] PROGMEM = TR_SD_SECTORS; -const pm_char STR_SD_SIZE[] PROGMEM = TR_SD_SIZE; -const pm_char STR_TYPE[] PROGMEM = TR_TYPE; -const pm_char STR_GLOBAL_VARS[] PROGMEM = TR_GLOBAL_VARS; -const pm_char STR_GVARS[] PROGMEM = TR_GVARS; -const pm_char STR_GLOBAL_VAR[] PROGMEM = TR_GLOBAL_VAR; -const pm_char STR_OWN[] PROGMEM = TR_OWN; -const pm_char STR_ROTARY_ENCODER[] PROGMEM = TR_ROTARY_ENCODER; -const pm_char STR_DATE[] PROGMEM = TR_DATE; -const pm_char STR_CHANNELS_MONITOR[] PROGMEM = TR_CHANNELS_MONITOR; -const pm_char STR_MIXERS_MONITOR[] PROGMEM = TR_MIXERS_MONITOR; -const pm_char STR_PATH_TOO_LONG[] PROGMEM = TR_PATH_TOO_LONG; -const pm_char STR_VIEW_TEXT[] PROGMEM = TR_VIEW_TEXT; -const pm_char STR_FLASH_BOOTLOADER[] PROGMEM = TR_FLASH_BOOTLOADER; -const pm_char STR_FLASH_INTERNAL_MODULE[] PROGMEM = TR_FLASH_INTERNAL_MODULE; -const pm_char STR_FLASH_EXTERNAL_DEVICE[] PROGMEM = TR_FLASH_EXTERNAL_DEVICE; -const pm_char STR_WRITING[] PROGMEM = TR_WRITING; -const pm_char STR_CONFIRM_FORMAT[] PROGMEM = TR_CONFIRM_FORMAT; -const pm_char STR_EEBACKUP[] PROGMEM = TR_EEBACKUP; -const pm_char STR_FACTORYRESET[] PROGMEM = TR_FACTORYRESET; -const pm_char STR_CONFIRMRESET[] PROGMEM = TR_CONFIRMRESET; -const pm_char STR_TOO_MANY_LUA_SCRIPTS[] PROGMEM = TR_TO_MANY_LUA_SCRIPTS; -const pm_char STR_BLCOLOR[] PROGMEM = TR_BLCOLOR; - -#if LCD_W >= 212 - const pm_char STR_MODELNAME[] PROGMEM = TR_MODELNAME; - const pm_char STR_PHASENAME[] PROGMEM = TR_PHASENAME; - const pm_char STR_MIXNAME[] PROGMEM = TR_MIXNAME; - const pm_char STR_INPUTNAME[] PROGMEM = TR_INPUTNAME; - const pm_char STR_EXPONAME[] PROGMEM = TR_EXPONAME; -#endif - -#if LCD_W >= 212 - const char * const STR_PHASES_HEADERS[] = TR_PHASES_HEADERS; - const char * const STR_LIMITS_HEADERS[] = TR_LIMITS_HEADERS; - const char * const STR_CSW_HEADERS[] = TR_CSW_HEADERS; -#endif - -#if defined(CPUARM) - const pm_char STR_TRAINER[] PROGMEM = TR_TRAINER; - const pm_char STR_MODULE_BIND[] PROGMEM = TR_MODULE_BIND; - const pm_char STR_CHANNELRANGE[] PROGMEM = TR_CHANNELRANGE; - const pm_char STR_SET[] PROGMEM = TR_SET; - const pm_char STR_PREFLIGHT[] PROGMEM = TR_PREFLIGHT; - const pm_char STR_CHECKLIST[] PROGMEM = TR_CHECKLIST; - const pm_char STR_VIEW_NOTES[] PROGMEM = TR_VIEW_NOTES; -const pm_char STR_MODEL_SELECT[] PROGMEM = TR_MODEL_SELECT; - const pm_char STR_RESET_SUBMENU[] PROGMEM = TR_RESET_SUBMENU; - const pm_char STR_LOWALARM[] PROGMEM = TR_LOWALARM; - const pm_char STR_CRITICALALARM[] PROGMEM = TR_CRITICALALARM; - const pm_char STR_TELEMETRY_TYPE[] PROGMEM = TR_TELEMETRY_TYPE; - const pm_char STR_TELEMETRY_SENSORS[] PROGMEM = TR_TELEMETRY_SENSORS; - const pm_char STR_VALUE[] PROGMEM = TR_VALUE; - const pm_char STR_TOPLCDTIMER[] PROGMEM = TR_TOPLCDTIMER; - const pm_char STR_UNIT[] PROGMEM = TR_UNIT; - const pm_char STR_TELEMETRY_NEWSENSOR[] PROGMEM = TR_TELEMETRY_NEWSENSOR; - const pm_char STR_ID[] PROGMEM = TR_ID; - const pm_char STR_PRECISION[] PROGMEM = TR_PRECISION; - const pm_char STR_RATIO[] PROGMEM = TR_RATIO; - const pm_char STR_FORMULA[] PROGMEM = TR_FORMULA; - const pm_char STR_CELLINDEX[] PROGMEM = TR_CELLINDEX; - const pm_char STR_LOGS[] PROGMEM = TR_LOGS; - const pm_char STR_OPTIONS[] PROGMEM = TR_OPTIONS; - const pm_char STR_ALTSENSOR[] PROGMEM = TR_ALTSENSOR; - const pm_char STR_CELLSENSOR[] PROGMEM = TR_CELLSENSOR; - const pm_char STR_GPSSENSOR[] PROGMEM = TR_GPSSENSOR; - const pm_char STR_CURRENTSENSOR[] PROGMEM = TR_CURRENTSENSOR; - const pm_char STR_AUTOOFFSET[] PROGMEM = TR_AUTOOFFSET; - const pm_char STR_ONLYPOSITIVE[] PROGMEM = TR_ONLYPOSITIVE; - const pm_char STR_FILTER[] PROGMEM = TR_FILTER; - const pm_char STR_TELEMETRYFULL[] PROGMEM = TR_TELEMETRYFULL; - const pm_char STR_INVERTED_SERIAL[] PROGMEM = TR_INVERTED_SERIAL; - const pm_char STR_IGNORE_INSTANCE[] PROGMEM = TR_IGNORE_INSTANCE; - const pm_char STR_DISCOVER_SENSORS[] PROGMEM = TR_DISCOVER_SENSORS; - const pm_char STR_STOP_DISCOVER_SENSORS[] PROGMEM = TR_STOP_DISCOVER_SENSORS; - const pm_char STR_DELETE_ALL_SENSORS[] PROGMEM = TR_DELETE_ALL_SENSORS; - const pm_char STR_CONFIRMDELETE[] PROGMEM = TR_CONFIRMDELETE; +const pm_char STR_COPY_MODEL[] PROGMEM = TR_COPY_MODEL; +const pm_char STR_MOVE_MODEL[] PROGMEM = TR_MOVE_MODEL; +const pm_char STR_DELETE_MODEL[] PROGMEM = TR_DELETE_MODEL; +const pm_char STR_EDIT[] PROGMEM = TR_EDIT; +const pm_char STR_INSERT_BEFORE[] PROGMEM = TR_INSERT_BEFORE; +const pm_char STR_INSERT_AFTER[] PROGMEM = TR_INSERT_AFTER; +const pm_char STR_COPY[] PROGMEM = TR_COPY; +const pm_char STR_MOVE[] PROGMEM = TR_MOVE; +const pm_char STR_PASTE[] PROGMEM = TR_PASTE; +const pm_char STR_INSERT[] PROGMEM = TR_INSERT; +const pm_char STR_DELETE[] PROGMEM = TR_DELETE; +const pm_char STR_RESET_FLIGHT[] PROGMEM = TR_RESET_FLIGHT; +const pm_char STR_RESET_TIMER1[] PROGMEM = TR_RESET_TIMER1; +const pm_char STR_RESET_TIMER2[] PROGMEM = TR_RESET_TIMER2; +const pm_char STR_RESET_TIMER3[] PROGMEM = TR_RESET_TIMER3; +const pm_char STR_RESET_TELEMETRY[] PROGMEM = TR_RESET_TELEMETRY; +const pm_char STR_STATISTICS[] PROGMEM = TR_STATISTICS; +const pm_char STR_ABOUT_US[] PROGMEM = TR_ABOUT_US; +const pm_char STR_SETUP_SCREENS[] PROGMEM = TR_SETUP_SCREENS; +#endif + +const pm_char STR_RESET_BTN[] PROGMEM = TR_RESET_BTN; + +#if defined(SDCARD) +const pm_char STR_BACKUP_MODEL[] PROGMEM = TR_BACKUP_MODEL; +const pm_char STR_RESTORE_MODEL[] PROGMEM = TR_RESTORE_MODEL; +const pm_char STR_SDCARD_ERROR[] PROGMEM = TR_SDCARD_ERROR; +const pm_char STR_NO_SDCARD[] PROGMEM = TR_NO_SDCARD; +const pm_char STR_SDCARD_FULL[] PROGMEM = TR_SDCARD_FULL; +const pm_char STR_INCOMPATIBLE[] PROGMEM = TR_INCOMPATIBLE; +const pm_char STR_LOGS_PATH[] PROGMEM = LOGS_PATH; +const pm_char STR_LOGS_EXT[] PROGMEM = LOGS_EXT; +const pm_char STR_MODELS_PATH[] PROGMEM = MODELS_PATH; +const pm_char STR_MODELS_EXT[] PROGMEM = MODELS_EXT; +#endif + +const pm_char STR_WARNING[] PROGMEM = TR_WARNING; +const pm_char STR_THROTTLEWARN[] PROGMEM = TR_THROTTLEWARN; +const pm_char STR_ALARMSWARN[] PROGMEM = TR_ALARMSWARN; +const pm_char STR_SWITCHWARN[] PROGMEM = TR_SWITCHWARN; +const pm_char STR_FAILSAFEWARN[] PROGMEM = TR_FAILSAFEWARN; +const pm_char STR_NO_FAILSAFE[] PROGMEM = TR_NO_FAILSAFE; +const pm_char STR_KEYSTUCK[] PROGMEM = TR_KEYSTUCK; + +const pm_char STR_SPEAKER_VOLUME[] PROGMEM = TR_SPEAKER_VOLUME; +const pm_char STR_LCD[] PROGMEM = TR_LCD; +const pm_char STR_BRIGHTNESS[] PROGMEM = TR_BRIGHTNESS; +const pm_char STR_CPU_TEMP[] PROGMEM = TR_CPU_TEMP; +const pm_char STR_CPU_CURRENT[] PROGMEM = TR_CPU_CURRENT; +const pm_char STR_CPU_MAH[] PROGMEM = TR_CPU_MAH; +const pm_char STR_COPROC[] PROGMEM = TR_COPROC; +const pm_char STR_COPROC_TEMP[] PROGMEM = TR_COPROC_TEMP; +const pm_char STR_TEMPWARNING[] PROGMEM = TR_TEMPWARNING; +const pm_char STR_CAPAWARNING[] PROGMEM = TR_CAPAWARNING; +const pm_char STR_FUNC[] PROGMEM = TR_FUNC; +const pm_char STR_V1[] PROGMEM = TR_V1; +const pm_char STR_V2[] PROGMEM = TR_V2; +const pm_char STR_DURATION[] PROGMEM = TR_DURATION; +const pm_char STR_DELAY[] PROGMEM = TR_DELAY; +const pm_char STR_SD_CARD[] PROGMEM = TR_SD_CARD; +const pm_char STR_SDHC_CARD[] PROGMEM = TR_SDHC_CARD; +const pm_char STR_NO_SOUNDS_ON_SD[] PROGMEM = TR_NO_SOUNDS_ON_SD; +const pm_char STR_NO_MODELS_ON_SD[] PROGMEM = TR_NO_MODELS_ON_SD; +const pm_char STR_NO_BITMAPS_ON_SD[] PROGMEM = TR_NO_BITMAPS_ON_SD; +const pm_char STR_NO_SCRIPTS_ON_SD[] PROGMEM = TR_NO_SCRIPTS_ON_SD; +const pm_char STR_SCRIPT_SYNTAX_ERROR[] PROGMEM = TR_SCRIPT_SYNTAX_ERROR; +const pm_char STR_SCRIPT_PANIC[] PROGMEM = TR_SCRIPT_PANIC; +const pm_char STR_SCRIPT_KILLED[] PROGMEM = TR_SCRIPT_KILLED; +const pm_char STR_SCRIPT_ERROR[] PROGMEM = TR_SCRIPT_ERROR; +const pm_char STR_PLAY_FILE[] PROGMEM = TR_PLAY_FILE; +const pm_char STR_ASSIGN_BITMAP[] PROGMEM = TR_ASSIGN_BITMAP; +const pm_char STR_EXECUTE_FILE[] PROGMEM = TR_EXECUTE_FILE; +const pm_char STR_DELETE_FILE[] PROGMEM = TR_DELETE_FILE; +const pm_char STR_COPY_FILE[] PROGMEM = TR_COPY_FILE; +const pm_char STR_RENAME_FILE[] PROGMEM = TR_RENAME_FILE; +const pm_char STR_SD_INFO[] PROGMEM = TR_SD_INFO; +const pm_char STR_SD_FORMAT[] PROGMEM = TR_SD_FORMAT; +const pm_char STR_REMOVED[] PROGMEM = TR_REMOVED; +const pm_char STR_NA[] PROGMEM = TR_NA; +const pm_char STR_HARDWARE[] PROGMEM = TR_HARDWARE; +const pm_char STR_FORMATTING[] PROGMEM = TR_FORMATTING; +const pm_char STR_TEMP_CALIB[] PROGMEM = TR_TEMP_CALIB; +const pm_char STR_TIME[] PROGMEM = TR_TIME; +const pm_char STR_BAUDRATE[] PROGMEM = TR_BAUDRATE; +const pm_char STR_SD_INFO_TITLE[] PROGMEM = TR_SD_INFO_TITLE; +const pm_char STR_SD_TYPE[] PROGMEM = TR_SD_TYPE; +const pm_char STR_SD_SPEED[] PROGMEM = TR_SD_SPEED; +const pm_char STR_SD_SECTORS[] PROGMEM = TR_SD_SECTORS; +const pm_char STR_SD_SIZE[] PROGMEM = TR_SD_SIZE; +const pm_char STR_TYPE[] PROGMEM = TR_TYPE; +const pm_char STR_GLOBAL_VARS[] PROGMEM = TR_GLOBAL_VARS; +const pm_char STR_GVARS[] PROGMEM = TR_GVARS; +const pm_char STR_GLOBAL_VAR[] PROGMEM = TR_GLOBAL_VAR; +const pm_char STR_OWN[] PROGMEM = TR_OWN; +const pm_char STR_ROTARY_ENCODER[] PROGMEM = TR_ROTARY_ENCODER; +const pm_char STR_DATE[] PROGMEM = TR_DATE; +const pm_char STR_CHANNELS_MONITOR[] PROGMEM = TR_CHANNELS_MONITOR; +const pm_char STR_MIXERS_MONITOR[] PROGMEM = TR_MIXERS_MONITOR; +const pm_char STR_PATH_TOO_LONG[] PROGMEM = TR_PATH_TOO_LONG; +const pm_char STR_VIEW_TEXT[] PROGMEM = TR_VIEW_TEXT; +const pm_char STR_FLASH_BOOTLOADER[] PROGMEM = TR_FLASH_BOOTLOADER; +const pm_char STR_FLASH_INTERNAL_MODULE[] PROGMEM = TR_FLASH_INTERNAL_MODULE; +const pm_char STR_FLASH_EXTERNAL_DEVICE[] PROGMEM = TR_FLASH_EXTERNAL_DEVICE; +const pm_char STR_WRITING[] PROGMEM = TR_WRITING; +const pm_char STR_CONFIRM_FORMAT[] PROGMEM = TR_CONFIRM_FORMAT; +const pm_char STR_EEBACKUP[] PROGMEM = TR_EEBACKUP; +const pm_char STR_FACTORYRESET[] PROGMEM = TR_FACTORYRESET; +const pm_char STR_CONFIRMRESET[] PROGMEM = TR_CONFIRMRESET; +const pm_char STR_TOO_MANY_LUA_SCRIPTS[] PROGMEM = TR_TO_MANY_LUA_SCRIPTS; +const pm_char STR_BLCOLOR[] PROGMEM = TR_BLCOLOR; + +#if LCD_W >= 212 + const pm_char STR_MODELNAME[] PROGMEM = TR_MODELNAME; + const pm_char STR_PHASENAME[] PROGMEM = TR_PHASENAME; + const pm_char STR_MIXNAME[] PROGMEM = TR_MIXNAME; + const pm_char STR_INPUTNAME[] PROGMEM = TR_INPUTNAME; + const pm_char STR_EXPONAME[] PROGMEM = TR_EXPONAME; +#endif + +#if LCD_W >= 212 + const char * const STR_PHASES_HEADERS[] = TR_PHASES_HEADERS; + const char * const STR_LIMITS_HEADERS[] = TR_LIMITS_HEADERS; + const char * const STR_CSW_HEADERS[] = TR_CSW_HEADERS; +#endif + +#if defined(CPUARM) + const pm_char STR_TRAINER[] PROGMEM = TR_TRAINER; + const pm_char STR_MODULE_BIND[] PROGMEM = TR_MODULE_BIND; + const pm_char STR_CHANNELRANGE[] PROGMEM = TR_CHANNELRANGE; + const pm_char STR_SET[] PROGMEM = TR_SET; + const pm_char STR_PREFLIGHT[] PROGMEM = TR_PREFLIGHT; + const pm_char STR_CHECKLIST[] PROGMEM = TR_CHECKLIST; + const pm_char STR_VIEW_NOTES[] PROGMEM = TR_VIEW_NOTES; +const pm_char STR_MODEL_SELECT[] PROGMEM = TR_MODEL_SELECT; + const pm_char STR_RESET_SUBMENU[] PROGMEM = TR_RESET_SUBMENU; + const pm_char STR_LOWALARM[] PROGMEM = TR_LOWALARM; + const pm_char STR_CRITICALALARM[] PROGMEM = TR_CRITICALALARM; + const pm_char STR_TELEMETRY_TYPE[] PROGMEM = TR_TELEMETRY_TYPE; + const pm_char STR_TELEMETRY_SENSORS[] PROGMEM = TR_TELEMETRY_SENSORS; + const pm_char STR_VALUE[] PROGMEM = TR_VALUE; + const pm_char STR_TOPLCDTIMER[] PROGMEM = TR_TOPLCDTIMER; + const pm_char STR_UNIT[] PROGMEM = TR_UNIT; + const pm_char STR_TELEMETRY_NEWSENSOR[] PROGMEM = TR_TELEMETRY_NEWSENSOR; + const pm_char STR_ID[] PROGMEM = TR_ID; + const pm_char STR_PRECISION[] PROGMEM = TR_PRECISION; + const pm_char STR_RATIO[] PROGMEM = TR_RATIO; + const pm_char STR_FORMULA[] PROGMEM = TR_FORMULA; + const pm_char STR_CELLINDEX[] PROGMEM = TR_CELLINDEX; + const pm_char STR_LOGS[] PROGMEM = TR_LOGS; + const pm_char STR_OPTIONS[] PROGMEM = TR_OPTIONS; + const pm_char STR_ALTSENSOR[] PROGMEM = TR_ALTSENSOR; + const pm_char STR_CELLSENSOR[] PROGMEM = TR_CELLSENSOR; + const pm_char STR_GPSSENSOR[] PROGMEM = TR_GPSSENSOR; + const pm_char STR_CURRENTSENSOR[] PROGMEM = TR_CURRENTSENSOR; + const pm_char STR_AUTOOFFSET[] PROGMEM = TR_AUTOOFFSET; + const pm_char STR_ONLYPOSITIVE[] PROGMEM = TR_ONLYPOSITIVE; + const pm_char STR_FILTER[] PROGMEM = TR_FILTER; + const pm_char STR_TELEMETRYFULL[] PROGMEM = TR_TELEMETRYFULL; + const pm_char STR_INVERTED_SERIAL[] PROGMEM = TR_INVERTED_SERIAL; + const pm_char STR_IGNORE_INSTANCE[] PROGMEM = TR_IGNORE_INSTANCE; + const pm_char STR_DISCOVER_SENSORS[] PROGMEM = TR_DISCOVER_SENSORS; + const pm_char STR_STOP_DISCOVER_SENSORS[] PROGMEM = TR_STOP_DISCOVER_SENSORS; + const pm_char STR_DELETE_ALL_SENSORS[] PROGMEM = TR_DELETE_ALL_SENSORS; + const pm_char STR_CONFIRMDELETE[] PROGMEM = TR_CONFIRMDELETE; const pm_char STR_SELECT_WIDGET[] PROGMEM = TR_SELECT_WIDGET; const pm_char STR_REMOVE_WIDGET[] PROGMEM = TR_REMOVE_WIDGET; const pm_char STR_WIDGET_SETTINGS[] PROGMEM = TR_WIDGET_SETTINGS; const pm_char STR_REMOVE_SCREEN[] PROGMEM = TR_REMOVE_SCREEN; -#endif - -#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) - const pm_char STR_BYTES[] PROGMEM = TR_BYTES; - const pm_char STR_ANTENNAPROBLEM[] PROGMEM = TR_ANTENNAPROBLEM; - const pm_char STR_MODULE[] PROGMEM = TR_MODULE; - const pm_char STR_ENABLE_POPUP[] PROGMEM = TR_ENABLE_POPUP; - const pm_char STR_DISABLE_POPUP[] PROGMEM = TR_DISABLE_POPUP; -const pm_char STR_POPUP[] PROGMEM = TR_POPUP; -const pm_char STR_MIN[] PROGMEM = TR_MIN; -const pm_char STR_MAX[] PROGMEM = TR_MAX; - const pm_char STR_CURVE_PRESET[] PROGMEM = TR_CURVE_PRESET; - const pm_char STR_PRESET[] PROGMEM = TR_PRESET; - const pm_char STR_MIRROR[] PROGMEM = TR_MIRROR; - const pm_char STR_CLEAR[] PROGMEM = TR_CLEAR; - const pm_char STR_RESET[] PROGMEM = TR_RESET; - const pm_char STR_COUNT[] PROGMEM = TR_COUNT; - const pm_char STR_PT[] PROGMEM = TR_PT; - const pm_char STR_PTS[] PROGMEM = TR_PTS; - const pm_char STR_SMOOTH[] PROGMEM = TR_SMOOTH; - const pm_char STR_COPY_STICKS_TO_OFS[] PROGMEM = TR_COPY_STICKS_TO_OFS; - const pm_char STR_COPY_TRIMS_TO_OFS[] PROGMEM = TR_COPY_TRIMS_TO_OFS; - const pm_char STR_INCDEC[] PROGMEM = TR_INCDEC; - const pm_char STR_GLOBALVAR[] PROGMEM = TR_GLOBALVAR; - const pm_char STR_MIXSOURCE[] PROGMEM = TR_MIXSOURCE; - const pm_char STR_CONSTANT[] PROGMEM = TR_CONSTANT; - const pm_char STR_TOP_BAR[] PROGMEM = TR_TOP_BAR; - const pm_char STR_ALTITUDE[] PROGMEM = TR_ALTITUDE; - const pm_char STR_SCALE[] PROGMEM = TR_SCALE; - const pm_char STR_VIEW_CHANNELS[] PROGMEM = TR_VIEW_CHANNELS; - const pm_char STR_POTWARNING[] PROGMEM = TR_POTWARNING; - const pm_char STR_UART3MODE[] PROGMEM = TR_UART3MODE; - const pm_char STR_THROTTLE_LABEL[] PROGMEM = TR_THROTTLE_LABEL; - const pm_char STR_SCRIPT[] PROGMEM = TR_SCRIPT; - const pm_char STR_INPUTS[] PROGMEM = TR_INPUTS; - const pm_char STR_OUTPUTS[] PROGMEM = TR_OUTPUTS; - const pm_char STR_MENU_INPUTS[] PROGMEM = TR_MENU_INPUTS; - const pm_char STR_MENU_LUA[] PROGMEM = TR_MENU_LUA; - const pm_char STR_MENU_STICKS[] PROGMEM = TR_MENU_STICKS; - const pm_char STR_MENU_POTS[] PROGMEM = TR_MENU_POTS; - const pm_char STR_MENU_MAX[] PROGMEM = TR_MENU_MAX; - const pm_char STR_MENU_HELI[] PROGMEM = TR_MENU_HELI; - const pm_char STR_MENU_TRIMS[] PROGMEM = TR_MENU_TRIMS; - const pm_char STR_MENU_SWITCHES[] PROGMEM = TR_MENU_SWITCHES; -const pm_char STR_MENU_LOGICAL_SWITCHES[] PROGMEM = TR_MENU_LOGICAL_SWITCHES; - const pm_char STR_MENU_TRAINER[] PROGMEM = TR_MENU_TRAINER; - const pm_char STR_MENU_CHANNELS[] PROGMEM = TR_MENU_CHANNELS; - const pm_char STR_MENU_GVARS[] PROGMEM = TR_MENU_GVARS; - const pm_char STR_MENU_TELEMETRY[] PROGMEM = TR_MENU_TELEMETRY; -const pm_char STR_MENU_OTHER[] PROGMEM = TR_MENU_OTHER; -const pm_char STR_MENU_INVERT[] PROGMEM = TR_MENU_INVERT; -#endif - -#if MENUS_LOCK == 1 - const pm_char STR_UNLOCKED[] PROGMEM = TR_UNLOCKED; - const pm_char STR_MODS_FORBIDDEN[] PROGMEM = TR_MODS_FORBIDDEN; -#endif - -#if defined(PCBTARANIS) || defined(DSM2) - const pm_char STR_MODULE_RANGE[] PROGMEM = TR_MODULE_RANGE; -#endif - -#if defined(MAVLINK) - const pm_char STR_MAVLINK_RC_RSSI_SCALE_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_SCALE_LABEL; - const pm_char STR_MAVLINK_PC_RSSI_EN_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_EN_LABEL; - const pm_char STR_MAVMENUSETUP_TITLE[] PROGMEM = TR_MAVMENUSETUP_TITLE; - const pm_char STR_MAVLINK_BAUD_LABEL[] PROGMEM = TR_MAVLINK_BAUD_LABEL; - const pm_char STR_MAVLINK_INFOS[] PROGMEM = TR_MAVLINK_INFOS; - const pm_char STR_MAVLINK_MODE[] PROGMEM = TR_MAVLINK_MODE; - const pm_char STR_MAVLINK_CUR_MODE[] PROGMEM = TR_MAVLINK_CUR_MODE; - const pm_char STR_MAVLINK_ARMED[] PROGMEM = TR_MAVLINK_ARMED; - const pm_char STR_MAVLINK_BAT_MENU_TITLE[] PROGMEM = TR_MAVLINK_BAT_MENU_TITLE; - const pm_char STR_MAVLINK_BATTERY_LABEL[] PROGMEM = TR_MAVLINK_BATTERY_LABEL; - const pm_char STR_MAVLINK_RC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_LABEL; - const pm_char STR_MAVLINK_PC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_LABEL; - const pm_char STR_MAVLINK_NAV_MENU_TITLE[] PROGMEM = TR_MAVLINK_NAV_MENU_TITLE; - const pm_char STR_MAVLINK_COURSE[] PROGMEM = TR_MAVLINK_COURSE; - const pm_char STR_MAVLINK_HEADING[] PROGMEM = TR_MAVLINK_HEADING; - const pm_char STR_MAVLINK_BEARING[] PROGMEM = TR_MAVLINK_BEARING; - const pm_char STR_MAVLINK_ALTITUDE[] PROGMEM = TR_MAVLINK_ALTITUDE; - const pm_char STR_MAVLINK_GPS[] PROGMEM = TR_MAVLINK_GPS; - const pm_char STR_MAVLINK_NO_FIX[] PROGMEM = TR_MAVLINK_NO_FIX; - const pm_char STR_MAVLINK_SAT[] PROGMEM = TR_MAVLINK_SAT; - const pm_char STR_MAVLINK_HDOP[] PROGMEM = TR_MAVLINK_HDOP; - const pm_char STR_MAVLINK_LAT[] PROGMEM = TR_MAVLINK_LAT; - const pm_char STR_MAVLINK_LON[] PROGMEM = TR_MAVLINK_LON; -#endif - -#if !defined(CPUM64) - const pm_char STR_ABOUTUS[] PROGMEM = TR_ABOUTUS; - const pm_char STR_ABOUT_OPENTX_1[] PROGMEM = TR_ABOUT_OPENTX_1; - const pm_char STR_ABOUT_OPENTX_2[] PROGMEM = TR_ABOUT_OPENTX_2; - const pm_char STR_ABOUT_OPENTX_3[] PROGMEM = TR_ABOUT_OPENTX_3; - const pm_char STR_ABOUT_OPENTX_4[] PROGMEM = TR_ABOUT_OPENTX_4; - const pm_char STR_ABOUT_OPENTX_5[] PROGMEM = TR_ABOUT_OPENTX_5; - - const pm_char STR_ABOUT_BERTRAND_1[] PROGMEM = TR_ABOUT_BERTRAND_1; - const pm_char STR_ABOUT_BERTRAND_2[] PROGMEM = TR_ABOUT_BERTRAND_2; - const pm_char STR_ABOUT_BERTRAND_3[] PROGMEM = TR_ABOUT_BERTRAND_3; - - const pm_char STR_ABOUT_MIKE_1[] PROGMEM = TR_ABOUT_MIKE_1; - const pm_char STR_ABOUT_MIKE_2[] PROGMEM = TR_ABOUT_MIKE_2; - const pm_char STR_ABOUT_MIKE_3[] PROGMEM = TR_ABOUT_MIKE_3; - const pm_char STR_ABOUT_MIKE_4[] PROGMEM = TR_ABOUT_MIKE_4; - - const pm_char STR_ABOUT_ROMOLO_1[] PROGMEM = TR_ABOUT_ROMOLO_1; - const pm_char STR_ABOUT_ROMOLO_2[] PROGMEM = TR_ABOUT_ROMOLO_2; - const pm_char STR_ABOUT_ROMOLO_3[] PROGMEM = TR_ABOUT_ROMOLO_3; - - const pm_char STR_ABOUT_ANDRE_1[] PROGMEM = TR_ABOUT_ANDRE_1; - const pm_char STR_ABOUT_ANDRE_2[] PROGMEM = TR_ABOUT_ANDRE_2; - const pm_char STR_ABOUT_ANDRE_3[] PROGMEM = TR_ABOUT_ANDRE_3; - - const pm_char STR_ABOUT_ROB_1[] PROGMEM = TR_ABOUT_ROB_1; - const pm_char STR_ABOUT_ROB_2[] PROGMEM = TR_ABOUT_ROB_2; - - const pm_char STR_ABOUT_MARTIN_1[] PROGMEM = TR_ABOUT_MARTIN_1; - const pm_char STR_ABOUT_MARTIN_2[] PROGMEM = TR_ABOUT_MARTIN_2; - - const pm_char STR_ABOUT_KJELL_1[] PROGMEM = TR_ABOUT_KJELL_1; - const pm_char STR_ABOUT_KJELL_2[] PROGMEM = TR_ABOUT_KJELL_2; - const pm_char STR_ABOUT_KJELL_3[] PROGMEM = TR_ABOUT_KJELL_3; - const pm_char STR_ABOUT_KJELL_4[] PROGMEM = TR_ABOUT_KJELL_4; - - const pm_char STR_ABOUT_HARDWARE_1[] PROGMEM = TR_ABOUT_HARDWARE_1; - const pm_char STR_ABOUT_HARDWARE_2[] PROGMEM = TR_ABOUT_HARDWARE_2; - const pm_char STR_ABOUT_HARDWARE_3[] PROGMEM = TR_ABOUT_HARDWARE_3; - - const pm_char STR_ABOUT_PARENTS_1[] PROGMEM = TR_ABOUT_PARENTS_1; - const pm_char STR_ABOUT_PARENTS_2[] PROGMEM = TR_ABOUT_PARENTS_2; - const pm_char STR_ABOUT_PARENTS_3[] PROGMEM = TR_ABOUT_PARENTS_3; - const pm_char STR_ABOUT_PARENTS_4[] PROGMEM = TR_ABOUT_PARENTS_4; -#endif - - +#endif + +#if defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBHORUS) + const pm_char STR_BYTES[] PROGMEM = TR_BYTES; + const pm_char STR_ANTENNAPROBLEM[] PROGMEM = TR_ANTENNAPROBLEM; + const pm_char STR_MODULE[] PROGMEM = TR_MODULE; + const pm_char STR_ENABLE_POPUP[] PROGMEM = TR_ENABLE_POPUP; + const pm_char STR_DISABLE_POPUP[] PROGMEM = TR_DISABLE_POPUP; +const pm_char STR_POPUP[] PROGMEM = TR_POPUP; +const pm_char STR_MIN[] PROGMEM = TR_MIN; +const pm_char STR_MAX[] PROGMEM = TR_MAX; + const pm_char STR_CURVE_PRESET[] PROGMEM = TR_CURVE_PRESET; + const pm_char STR_PRESET[] PROGMEM = TR_PRESET; + const pm_char STR_MIRROR[] PROGMEM = TR_MIRROR; + const pm_char STR_CLEAR[] PROGMEM = TR_CLEAR; + const pm_char STR_RESET[] PROGMEM = TR_RESET; + const pm_char STR_COUNT[] PROGMEM = TR_COUNT; + const pm_char STR_PT[] PROGMEM = TR_PT; + const pm_char STR_PTS[] PROGMEM = TR_PTS; + const pm_char STR_SMOOTH[] PROGMEM = TR_SMOOTH; + const pm_char STR_COPY_STICKS_TO_OFS[] PROGMEM = TR_COPY_STICKS_TO_OFS; + const pm_char STR_COPY_TRIMS_TO_OFS[] PROGMEM = TR_COPY_TRIMS_TO_OFS; + const pm_char STR_INCDEC[] PROGMEM = TR_INCDEC; + const pm_char STR_GLOBALVAR[] PROGMEM = TR_GLOBALVAR; + const pm_char STR_MIXSOURCE[] PROGMEM = TR_MIXSOURCE; + const pm_char STR_CONSTANT[] PROGMEM = TR_CONSTANT; + const pm_char STR_TOP_BAR[] PROGMEM = TR_TOP_BAR; + const pm_char STR_ALTITUDE[] PROGMEM = TR_ALTITUDE; + const pm_char STR_SCALE[] PROGMEM = TR_SCALE; + const pm_char STR_VIEW_CHANNELS[] PROGMEM = TR_VIEW_CHANNELS; + const pm_char STR_POTWARNING[] PROGMEM = TR_POTWARNING; + const pm_char STR_UART3MODE[] PROGMEM = TR_UART3MODE; + const pm_char STR_THROTTLE_LABEL[] PROGMEM = TR_THROTTLE_LABEL; + const pm_char STR_SCRIPT[] PROGMEM = TR_SCRIPT; + const pm_char STR_INPUTS[] PROGMEM = TR_INPUTS; + const pm_char STR_OUTPUTS[] PROGMEM = TR_OUTPUTS; + const pm_char STR_MENU_INPUTS[] PROGMEM = TR_MENU_INPUTS; + const pm_char STR_MENU_LUA[] PROGMEM = TR_MENU_LUA; + const pm_char STR_MENU_STICKS[] PROGMEM = TR_MENU_STICKS; + const pm_char STR_MENU_POTS[] PROGMEM = TR_MENU_POTS; + const pm_char STR_MENU_MAX[] PROGMEM = TR_MENU_MAX; + const pm_char STR_MENU_HELI[] PROGMEM = TR_MENU_HELI; + const pm_char STR_MENU_TRIMS[] PROGMEM = TR_MENU_TRIMS; + const pm_char STR_MENU_SWITCHES[] PROGMEM = TR_MENU_SWITCHES; +const pm_char STR_MENU_LOGICAL_SWITCHES[] PROGMEM = TR_MENU_LOGICAL_SWITCHES; + const pm_char STR_MENU_TRAINER[] PROGMEM = TR_MENU_TRAINER; + const pm_char STR_MENU_CHANNELS[] PROGMEM = TR_MENU_CHANNELS; + const pm_char STR_MENU_GVARS[] PROGMEM = TR_MENU_GVARS; + const pm_char STR_MENU_TELEMETRY[] PROGMEM = TR_MENU_TELEMETRY; +const pm_char STR_MENU_OTHER[] PROGMEM = TR_MENU_OTHER; +const pm_char STR_MENU_INVERT[] PROGMEM = TR_MENU_INVERT; +#endif + +#if MENUS_LOCK == 1 + const pm_char STR_UNLOCKED[] PROGMEM = TR_UNLOCKED; + const pm_char STR_MODS_FORBIDDEN[] PROGMEM = TR_MODS_FORBIDDEN; +#endif + +#if defined(PCBTARANIS) || defined(DSM2) + const pm_char STR_MODULE_RANGE[] PROGMEM = TR_MODULE_RANGE; +#endif + +#if defined(MAVLINK) + const pm_char STR_MAVLINK_RC_RSSI_SCALE_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_SCALE_LABEL; + const pm_char STR_MAVLINK_PC_RSSI_EN_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_EN_LABEL; + const pm_char STR_MAVMENUSETUP_TITLE[] PROGMEM = TR_MAVMENUSETUP_TITLE; + const pm_char STR_MAVLINK_BAUD_LABEL[] PROGMEM = TR_MAVLINK_BAUD_LABEL; + const pm_char STR_MAVLINK_INFOS[] PROGMEM = TR_MAVLINK_INFOS; + const pm_char STR_MAVLINK_MODE[] PROGMEM = TR_MAVLINK_MODE; + const pm_char STR_MAVLINK_CUR_MODE[] PROGMEM = TR_MAVLINK_CUR_MODE; + const pm_char STR_MAVLINK_ARMED[] PROGMEM = TR_MAVLINK_ARMED; + const pm_char STR_MAVLINK_BAT_MENU_TITLE[] PROGMEM = TR_MAVLINK_BAT_MENU_TITLE; + const pm_char STR_MAVLINK_BATTERY_LABEL[] PROGMEM = TR_MAVLINK_BATTERY_LABEL; + const pm_char STR_MAVLINK_RC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_RC_RSSI_LABEL; + const pm_char STR_MAVLINK_PC_RSSI_LABEL[] PROGMEM = TR_MAVLINK_PC_RSSI_LABEL; + const pm_char STR_MAVLINK_NAV_MENU_TITLE[] PROGMEM = TR_MAVLINK_NAV_MENU_TITLE; + const pm_char STR_MAVLINK_COURSE[] PROGMEM = TR_MAVLINK_COURSE; + const pm_char STR_MAVLINK_HEADING[] PROGMEM = TR_MAVLINK_HEADING; + const pm_char STR_MAVLINK_BEARING[] PROGMEM = TR_MAVLINK_BEARING; + const pm_char STR_MAVLINK_ALTITUDE[] PROGMEM = TR_MAVLINK_ALTITUDE; + const pm_char STR_MAVLINK_GPS[] PROGMEM = TR_MAVLINK_GPS; + const pm_char STR_MAVLINK_NO_FIX[] PROGMEM = TR_MAVLINK_NO_FIX; + const pm_char STR_MAVLINK_SAT[] PROGMEM = TR_MAVLINK_SAT; + const pm_char STR_MAVLINK_HDOP[] PROGMEM = TR_MAVLINK_HDOP; + const pm_char STR_MAVLINK_LAT[] PROGMEM = TR_MAVLINK_LAT; + const pm_char STR_MAVLINK_LON[] PROGMEM = TR_MAVLINK_LON; +#endif + +#if !defined(CPUM64) + const pm_char STR_ABOUTUS[] PROGMEM = TR_ABOUTUS; + const pm_char STR_ABOUT_OPENTX_1[] PROGMEM = TR_ABOUT_OPENTX_1; + const pm_char STR_ABOUT_OPENTX_2[] PROGMEM = TR_ABOUT_OPENTX_2; + const pm_char STR_ABOUT_OPENTX_3[] PROGMEM = TR_ABOUT_OPENTX_3; + const pm_char STR_ABOUT_OPENTX_4[] PROGMEM = TR_ABOUT_OPENTX_4; + const pm_char STR_ABOUT_OPENTX_5[] PROGMEM = TR_ABOUT_OPENTX_5; + + const pm_char STR_ABOUT_BERTRAND_1[] PROGMEM = TR_ABOUT_BERTRAND_1; + const pm_char STR_ABOUT_BERTRAND_2[] PROGMEM = TR_ABOUT_BERTRAND_2; + const pm_char STR_ABOUT_BERTRAND_3[] PROGMEM = TR_ABOUT_BERTRAND_3; + + const pm_char STR_ABOUT_MIKE_1[] PROGMEM = TR_ABOUT_MIKE_1; + const pm_char STR_ABOUT_MIKE_2[] PROGMEM = TR_ABOUT_MIKE_2; + const pm_char STR_ABOUT_MIKE_3[] PROGMEM = TR_ABOUT_MIKE_3; + const pm_char STR_ABOUT_MIKE_4[] PROGMEM = TR_ABOUT_MIKE_4; + + const pm_char STR_ABOUT_ROMOLO_1[] PROGMEM = TR_ABOUT_ROMOLO_1; + const pm_char STR_ABOUT_ROMOLO_2[] PROGMEM = TR_ABOUT_ROMOLO_2; + const pm_char STR_ABOUT_ROMOLO_3[] PROGMEM = TR_ABOUT_ROMOLO_3; + + const pm_char STR_ABOUT_ANDRE_1[] PROGMEM = TR_ABOUT_ANDRE_1; + const pm_char STR_ABOUT_ANDRE_2[] PROGMEM = TR_ABOUT_ANDRE_2; + const pm_char STR_ABOUT_ANDRE_3[] PROGMEM = TR_ABOUT_ANDRE_3; + + const pm_char STR_ABOUT_ROB_1[] PROGMEM = TR_ABOUT_ROB_1; + const pm_char STR_ABOUT_ROB_2[] PROGMEM = TR_ABOUT_ROB_2; + + const pm_char STR_ABOUT_MARTIN_1[] PROGMEM = TR_ABOUT_MARTIN_1; + const pm_char STR_ABOUT_MARTIN_2[] PROGMEM = TR_ABOUT_MARTIN_2; + + const pm_char STR_ABOUT_KJELL_1[] PROGMEM = TR_ABOUT_KJELL_1; + const pm_char STR_ABOUT_KJELL_2[] PROGMEM = TR_ABOUT_KJELL_2; + const pm_char STR_ABOUT_KJELL_3[] PROGMEM = TR_ABOUT_KJELL_3; + const pm_char STR_ABOUT_KJELL_4[] PROGMEM = TR_ABOUT_KJELL_4; + + const pm_char STR_ABOUT_HARDWARE_1[] PROGMEM = TR_ABOUT_HARDWARE_1; + const pm_char STR_ABOUT_HARDWARE_2[] PROGMEM = TR_ABOUT_HARDWARE_2; + const pm_char STR_ABOUT_HARDWARE_3[] PROGMEM = TR_ABOUT_HARDWARE_3; + + const pm_char STR_ABOUT_PARENTS_1[] PROGMEM = TR_ABOUT_PARENTS_1; + const pm_char STR_ABOUT_PARENTS_2[] PROGMEM = TR_ABOUT_PARENTS_2; + const pm_char STR_ABOUT_PARENTS_3[] PROGMEM = TR_ABOUT_PARENTS_3; + const pm_char STR_ABOUT_PARENTS_4[] PROGMEM = TR_ABOUT_PARENTS_4; +#endif + + diff --git a/radio/src/vario.cpp b/radio/src/vario.cpp index 43c1f4dbc..67f195e4e 100644 --- a/radio/src/vario.cpp +++ b/radio/src/vario.cpp @@ -18,139 +18,139 @@ * GNU General Public License for more details. */ -#include "opentx.h" - - -#if defined(CPUARM) - -void varioWakeup() -{ - if (isFunctionActive(FUNCTION_VARIO)) { - int varioFreq, varioDuration, varioPause=0; - uint8_t varioFlags; - - int verticalSpeed = 0; - if (g_model.frsky.varioSource) { - uint8_t item = g_model.frsky.varioSource-1; - if (item < MAX_SENSORS) { - verticalSpeed = telemetryItems[item].value * g_model.telemetrySensors[item].getPrecMultiplier(); - } - } - - int varioCenterMin = (int)g_model.frsky.varioCenterMin * 10 - 50; - int varioCenterMax = (int)g_model.frsky.varioCenterMax * 10 + 50; - int varioMax = (10+(int)g_model.frsky.varioMax) * 100; - int varioMin = (-10+(int)g_model.frsky.varioMin) * 100; - - if (verticalSpeed > varioMax) - verticalSpeed = varioMax; - else if (verticalSpeed < varioMin) - verticalSpeed = varioMin; - - if (verticalSpeed <= varioCenterMin) { - varioFreq = VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10) - (((VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10)-((VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10))/2)) * (verticalSpeed-varioCenterMin)) / varioMin); - varioDuration = 80; // continuous beep: we will enter again here before the tone ends - varioFlags = PLAY_BACKGROUND|PLAY_NOW; - } - else if (verticalSpeed >= varioCenterMax || !g_model.frsky.varioCenterSilent) { - varioFreq = VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10) + (((VARIO_FREQUENCY_RANGE+(g_eeGeneral.varioRange*10)) * (verticalSpeed-varioCenterMin)) / varioMax); - int varioPeriod = VARIO_REPEAT_MAX + ((VARIO_REPEAT_ZERO+(g_eeGeneral.varioRepeat*10)-VARIO_REPEAT_MAX) * (varioMax-verticalSpeed) * (varioMax-verticalSpeed)) / ((varioMax-varioCenterMin) * (varioMax-varioCenterMin)); - if (verticalSpeed >= varioCenterMax || varioCenterMin == varioCenterMax) - varioDuration = varioPeriod / 5; - else - varioDuration = varioPeriod * (85 - (((verticalSpeed-varioCenterMin) * 25) / (varioCenterMax-varioCenterMin))) / 100; - varioPause = varioPeriod - varioDuration; - varioFlags = PLAY_BACKGROUND; - } - else { - return; - } - - AUDIO_VARIO(varioFreq, varioDuration, varioPause, varioFlags); - } -} - -#elif defined(FRSKY) - -void varioWakeup() -{ - static tmr10ms_t s_varioTmr; - tmr10ms_t tmr10ms = get_tmr10ms(); - - if (isFunctionActive(FUNCTION_VARIO)) { -#if defined(AUDIO) - cli(); - int16_t verticalSpeed = frskyData.hub.varioSpeed; - sei(); - -#if defined(PCBSTD) - int16_t varioCenterMax = (int16_t)g_model.frsky.varioCenterMax * 10 + 50; - if (verticalSpeed >= varioCenterMax) { - verticalSpeed = verticalSpeed - varioCenterMax; - int16_t varioMax = (10+(int16_t)g_model.frsky.varioMax) * 100; - if (verticalSpeed > varioMax) verticalSpeed = varioMax; - verticalSpeed = (verticalSpeed * 10) / ((varioMax-varioCenterMax) / 100); - - if ((int16_t)(s_varioTmr-tmr10ms) < 0) { - uint8_t varioFreq = (verticalSpeed * 10 + 16000) >> 8; - uint8_t varioDuration = (1600 - verticalSpeed) / 100; - s_varioTmr = tmr10ms + (varioDuration*2); - AUDIO_VARIO(varioFreq, varioDuration); - } - } -#else - int varioCenterMin = (int)g_model.frsky.varioCenterMin * 10 - 50; - int varioCenterMax = (int)g_model.frsky.varioCenterMax * 10 + 50; - int varioMax = (10+(int)g_model.frsky.varioMax) * 100; - int varioMin = (-10+(int)g_model.frsky.varioMin) * 100; - - if (verticalSpeed < varioCenterMin || (verticalSpeed > varioCenterMax && (int16_t)(s_varioTmr - tmr10ms) < 0)) { - if (verticalSpeed > varioMax) - verticalSpeed = varioMax; - else if (verticalSpeed < varioMin) - verticalSpeed = varioMin; - - uint8_t varioFreq, varioDuration; - if (verticalSpeed > 0) { - varioFreq = (verticalSpeed * 4 + 8000) >> 7; - varioDuration = (8000 - verticalSpeed * 5) / 100; - } - else { - varioFreq = (verticalSpeed * 3 + 8000) >> 7; - varioDuration = 20; - } - s_varioTmr = tmr10ms + (varioDuration/2); - AUDIO_VARIO(varioFreq, varioDuration); - } -#endif - -#elif defined(BUZZER) // && !defined(AUDIO) - - int8_t verticalSpeed = limit((int16_t)-100, (int16_t)(frskyData.hub.varioSpeed/10), (int16_t)+100); - - uint16_t interval; - if (verticalSpeed == 0) { - interval = 300; - } - else { - if (verticalSpeed < 0) { - verticalSpeed = -verticalSpeed; - warble = 1; - } - interval = (uint8_t)200 / verticalSpeed; - } - if (g_tmr10ms - s_varioTmr > interval) { - s_varioTmr = g_tmr10ms; - if (warble) - AUDIO_VARIO_DOWN(); - else - AUDIO_VARIO_UP(); - } -#endif - } - else { - s_varioTmr = tmr10ms; - } -} - -#endif +#include "opentx.h" + + +#if defined(CPUARM) + +void varioWakeup() +{ + if (isFunctionActive(FUNCTION_VARIO)) { + int varioFreq, varioDuration, varioPause=0; + uint8_t varioFlags; + + int verticalSpeed = 0; + if (g_model.frsky.varioSource) { + uint8_t item = g_model.frsky.varioSource-1; + if (item < MAX_SENSORS) { + verticalSpeed = telemetryItems[item].value * g_model.telemetrySensors[item].getPrecMultiplier(); + } + } + + int varioCenterMin = (int)g_model.frsky.varioCenterMin * 10 - 50; + int varioCenterMax = (int)g_model.frsky.varioCenterMax * 10 + 50; + int varioMax = (10+(int)g_model.frsky.varioMax) * 100; + int varioMin = (-10+(int)g_model.frsky.varioMin) * 100; + + if (verticalSpeed > varioMax) + verticalSpeed = varioMax; + else if (verticalSpeed < varioMin) + verticalSpeed = varioMin; + + if (verticalSpeed <= varioCenterMin) { + varioFreq = VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10) - (((VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10)-((VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10))/2)) * (verticalSpeed-varioCenterMin)) / varioMin); + varioDuration = 80; // continuous beep: we will enter again here before the tone ends + varioFlags = PLAY_BACKGROUND|PLAY_NOW; + } + else if (verticalSpeed >= varioCenterMax || !g_model.frsky.varioCenterSilent) { + varioFreq = VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch*10) + (((VARIO_FREQUENCY_RANGE+(g_eeGeneral.varioRange*10)) * (verticalSpeed-varioCenterMin)) / varioMax); + int varioPeriod = VARIO_REPEAT_MAX + ((VARIO_REPEAT_ZERO+(g_eeGeneral.varioRepeat*10)-VARIO_REPEAT_MAX) * (varioMax-verticalSpeed) * (varioMax-verticalSpeed)) / ((varioMax-varioCenterMin) * (varioMax-varioCenterMin)); + if (verticalSpeed >= varioCenterMax || varioCenterMin == varioCenterMax) + varioDuration = varioPeriod / 5; + else + varioDuration = varioPeriod * (85 - (((verticalSpeed-varioCenterMin) * 25) / (varioCenterMax-varioCenterMin))) / 100; + varioPause = varioPeriod - varioDuration; + varioFlags = PLAY_BACKGROUND; + } + else { + return; + } + + AUDIO_VARIO(varioFreq, varioDuration, varioPause, varioFlags); + } +} + +#elif defined(FRSKY) + +void varioWakeup() +{ + static tmr10ms_t s_varioTmr; + tmr10ms_t tmr10ms = get_tmr10ms(); + + if (isFunctionActive(FUNCTION_VARIO)) { +#if defined(AUDIO) + cli(); + int16_t verticalSpeed = frskyData.hub.varioSpeed; + sei(); + +#if defined(PCBSTD) + int16_t varioCenterMax = (int16_t)g_model.frsky.varioCenterMax * 10 + 50; + if (verticalSpeed >= varioCenterMax) { + verticalSpeed = verticalSpeed - varioCenterMax; + int16_t varioMax = (10+(int16_t)g_model.frsky.varioMax) * 100; + if (verticalSpeed > varioMax) verticalSpeed = varioMax; + verticalSpeed = (verticalSpeed * 10) / ((varioMax-varioCenterMax) / 100); + + if ((int16_t)(s_varioTmr-tmr10ms) < 0) { + uint8_t varioFreq = (verticalSpeed * 10 + 16000) >> 8; + uint8_t varioDuration = (1600 - verticalSpeed) / 100; + s_varioTmr = tmr10ms + (varioDuration*2); + AUDIO_VARIO(varioFreq, varioDuration); + } + } +#else + int varioCenterMin = (int)g_model.frsky.varioCenterMin * 10 - 50; + int varioCenterMax = (int)g_model.frsky.varioCenterMax * 10 + 50; + int varioMax = (10+(int)g_model.frsky.varioMax) * 100; + int varioMin = (-10+(int)g_model.frsky.varioMin) * 100; + + if (verticalSpeed < varioCenterMin || (verticalSpeed > varioCenterMax && (int16_t)(s_varioTmr - tmr10ms) < 0)) { + if (verticalSpeed > varioMax) + verticalSpeed = varioMax; + else if (verticalSpeed < varioMin) + verticalSpeed = varioMin; + + uint8_t varioFreq, varioDuration; + if (verticalSpeed > 0) { + varioFreq = (verticalSpeed * 4 + 8000) >> 7; + varioDuration = (8000 - verticalSpeed * 5) / 100; + } + else { + varioFreq = (verticalSpeed * 3 + 8000) >> 7; + varioDuration = 20; + } + s_varioTmr = tmr10ms + (varioDuration/2); + AUDIO_VARIO(varioFreq, varioDuration); + } +#endif + +#elif defined(BUZZER) // && !defined(AUDIO) + + int8_t verticalSpeed = limit((int16_t)-100, (int16_t)(frskyData.hub.varioSpeed/10), (int16_t)+100); + + uint16_t interval; + if (verticalSpeed == 0) { + interval = 300; + } + else { + if (verticalSpeed < 0) { + verticalSpeed = -verticalSpeed; + warble = 1; + } + interval = (uint8_t)200 / verticalSpeed; + } + if (g_tmr10ms - s_varioTmr > interval) { + s_varioTmr = g_tmr10ms; + if (warble) + AUDIO_VARIO_DOWN(); + else + AUDIO_VARIO_UP(); + } +#endif + } + else { + s_varioTmr = tmr10ms; + } +} + +#endif