From be496b66b60ac63fa56054a8edef179724acab08 Mon Sep 17 00:00:00 2001 From: 3djc <5167938+3djc@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:08:05 +0100 Subject: [PATCH] feat: add support for RadioMaster TX16S MK3 (#7017) Co-authored-by: philmoz Co-authored-by: 3djc <3djc@gh.com> Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug-report.yml | 1 + .github/workflows/build_fw.yml | 2 + .github/workflows/nightly.yml | 1 + companion/src/firmwares/boardjson.cpp | 15 + companion/src/firmwares/boards.cpp | 30 +- companion/src/firmwares/boards.h | 15 +- companion/src/firmwares/generalsettings.cpp | 2 + .../src/firmwares/opentx/opentxinterface.cpp | 10 +- fw.json | 1 + radio/src/CMakeLists.txt | 12 +- radio/src/boards/rm-h750/audio_driver.cpp | 2 +- radio/src/boards/rm-h750/backlight_driver.cpp | 8 +- radio/src/boards/rm-h750/bsp_io.cpp | 2 +- radio/src/boards/rm-h750/lcd_driver_800.cpp | 275 +++++ radio/src/boards/rm-h750/lcd_driver_800.h | 46 + .../src/boards/rm-h750/luminosity_sensor.cpp | 51 + radio/src/boards/rm-h750/luminosity_sensor.h | 28 + radio/src/boards/rm-h750/sdram_driver.cpp | 8 + radio/src/boards/rm-h750/system_clock.c | 6 +- radio/src/dataconstants.h | 9 +- radio/src/datastructs.h | 4 +- radio/src/edgetx.cpp | 14 + radio/src/functions.cpp | 4 + .../gui/colorlcd/controls/sourcechoice.cpp | 11 +- .../gui/colorlcd/libui/list_line_button.cpp | 10 +- .../src/gui/colorlcd/libui/list_line_button.h | 6 +- radio/src/gui/colorlcd/lv_conf.h | 13 +- radio/src/gui/colorlcd/model/curveedit.cpp | 4 + .../colorlcd/model/model_logical_switches.cpp | 2 +- radio/src/gui/colorlcd/model/model_mixes.cpp | 9 +- .../src/gui/common/stdlcd/draw_functions.cpp | 5 + radio/src/gui/gui_common.cpp | 7 +- radio/src/hal/adc_driver.h | 1 + radio/src/lua/api_colorlcd_lvgl.cpp | 2 +- radio/src/lua/api_general.cpp | 4 + radio/src/mixer.cpp | 10 + radio/src/storage/yaml/CMakeLists.txt | 2 + radio/src/storage/yaml/yaml_datastructs.cpp | 2 + .../yaml/yaml_datastructs_tx16smk3.cpp | 1042 +++++++++++++++++ radio/src/strhelpers.cpp | 8 +- radio/src/targets/pl18/CMakeLists.txt | 2 +- radio/src/targets/simu/adc_driver.cpp | 2 + radio/src/targets/simu/backlight_driver.cpp | 2 + radio/src/targets/tx16smk3/CMakeLists.txt | 212 ++++ radio/src/targets/tx16smk3/bsp_io.h | 55 + radio/src/targets/tx16smk3/hal.h | 624 ++++++++++ radio/src/targets/tx16smk3/key_driver.cpp | 93 ++ radio/src/targets/tx16smk3/usb_descriptor.h | 29 + .../src/tests/images/color/bitmap_800x480.png | Bin 0 -> 30848 bytes .../tests/images/color/clipping_800x480.png | Bin 0 -> 12200 bytes .../src/tests/images/color/lines_800x480.png | Bin 0 -> 12883 bytes .../src/tests/images/color/masks_800x480.png | Bin 0 -> 31962 bytes .../images/color/primitives_EN_800x480.png | Bin 0 -> 19763 bytes .../images/color/transparency_EN_800x480.png | Bin 0 -> 18798 bytes .../src/tests/images/color/vline_800x480.png | Bin 0 -> 12297 bytes radio/src/translations/i18n/cn.h | 1 + radio/src/translations/i18n/cz.h | 1 + radio/src/translations/i18n/da.h | 1 + radio/src/translations/i18n/de.h | 1 + radio/src/translations/i18n/en.h | 1 + radio/src/translations/i18n/es.h | 1 + radio/src/translations/i18n/fi.h | 1 + radio/src/translations/i18n/fr.h | 1 + radio/src/translations/i18n/he.h | 1 + radio/src/translations/i18n/it.h | 1 + radio/src/translations/i18n/jp.h | 1 + radio/src/translations/i18n/ko.h | 1 + radio/src/translations/i18n/nl.h | 1 + radio/src/translations/i18n/pl.h | 1 + radio/src/translations/i18n/pt.h | 1 + radio/src/translations/i18n/ru.h | 1 + radio/src/translations/i18n/se.h | 1 + radio/src/translations/i18n/tw.h | 1 + radio/src/translations/i18n/ua.h | 1 + radio/src/translations/sim_string_list.h | 1 + radio/src/translations/string_list.h | 1 + radio/util/hw_defs/hal_adc.py | 7 +- radio/util/hw_defs/hal_adc_inputs.jinja | 12 +- radio/util/hw_defs/hal_keys.py | 2 +- radio/util/hw_defs/legacy_names.py | 67 ++ radio/util/hw_defs/pot_config.py | 8 + radio/util/hw_defs/switch_config.py | 20 + tools/build-common.sh | 3 + tools/build-companion.sh | 2 +- tools/generate-hw-defs.sh | 2 +- tools/generate-yaml.sh | 2 +- 86 files changed, 2789 insertions(+), 60 deletions(-) create mode 100644 radio/src/boards/rm-h750/lcd_driver_800.cpp create mode 100644 radio/src/boards/rm-h750/lcd_driver_800.h create mode 100644 radio/src/boards/rm-h750/luminosity_sensor.cpp create mode 100644 radio/src/boards/rm-h750/luminosity_sensor.h create mode 100644 radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp create mode 100644 radio/src/targets/tx16smk3/CMakeLists.txt create mode 100644 radio/src/targets/tx16smk3/bsp_io.h create mode 100644 radio/src/targets/tx16smk3/hal.h create mode 100644 radio/src/targets/tx16smk3/key_driver.cpp create mode 100644 radio/src/targets/tx16smk3/usb_descriptor.h create mode 100644 radio/src/tests/images/color/bitmap_800x480.png create mode 100644 radio/src/tests/images/color/clipping_800x480.png create mode 100644 radio/src/tests/images/color/lines_800x480.png create mode 100644 radio/src/tests/images/color/masks_800x480.png create mode 100644 radio/src/tests/images/color/primitives_EN_800x480.png create mode 100644 radio/src/tests/images/color/transparency_EN_800x480.png create mode 100644 radio/src/tests/images/color/vline_800x480.png diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index a9d1f78f1..862df89ff 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -128,6 +128,7 @@ body: - RadioMaster TX12 / TX12MK2 - RadioMaster TX15 - RadioMaster TX16S / TX16SMK2 + - RadioMaster TX16SMK3 - RadioMaster Zorro - Other (Please specify below) validations: diff --git a/.github/workflows/build_fw.yml b/.github/workflows/build_fw.yml index 29d2c2b55..18d9166c8 100644 --- a/.github/workflows/build_fw.yml +++ b/.github/workflows/build_fw.yml @@ -33,6 +33,7 @@ jobs: - x9dp2019 - tx15 - tx16s + - tx16smk3 - nv14 - el18 - pl18 @@ -95,6 +96,7 @@ jobs: - tx12mk2;boxer;gx12 - tx15 - tx16s + - tx16smk3 - f16 - v12;v14;v16 - x10;x10express diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7a9bd7bde..4c537a65f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -26,6 +26,7 @@ jobs: - t20;t20v2;t14 - tx12mk2;boxer;gx12 - tx16s + - tx16smk3 - f16 - v16 - x10;x10express diff --git a/companion/src/firmwares/boardjson.cpp b/companion/src/firmwares/boardjson.cpp index a02d0325b..c581003c5 100644 --- a/companion/src/firmwares/boardjson.cpp +++ b/companion/src/firmwares/boardjson.cpp @@ -117,6 +117,21 @@ void BoardJson::afterLoadFixups(Board::Type board, InputsTable * inputs, Switche } } + if (IS_RADIOMASTER_TX16SMK3(board)) { + if (getInputIndex(inputs, "LIGHT", Board::LVT_TAG) < 0) { + InputDefn defn; + defn.type = AIT_FLEX; + defn.tag = "LIGHT"; + defn.name = "Ambient light"; + defn.shortName = "Light"; + defn.flexType = FLEX_POT; + defn.inverted = false; + defn.cfgYaml = Board::LVT_TAG; + defn.refYaml = Board::LVT_TAG; // non-default + inputs->insert(inputs->end(), defn); + } + } + // Flex switches are not listed in json file for these radios int count = IS_RADIOMASTER_TX16S(board) || IS_RADIOMASTER_MT12(board) ? 2 : 0; diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 27d954cf4..178c14ca2 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -137,6 +137,8 @@ uint32_t Boards::getFourCC(Type board) return 0x3878746F; case BOARD_RADIOMASTER_TX15: return 0x4978746F; + case BOARD_RADIOMASTER_TX16SMK3: + return 0x4978746F; case BOARD_RADIOMASTER_TX12: return 0x4178746F; case BOARD_RADIOMASTER_TX12_MK2: @@ -214,6 +216,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX16SMK3: case BOARD_RADIOMASTER_TX15: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: @@ -276,6 +279,7 @@ int Boards::getFlashSize(Type board) case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX16SMK3: case BOARD_RADIOMASTER_TX15: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: @@ -387,27 +391,25 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 1; case LcdHeight: - if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) + if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_RADIOMASTER_TX16SMK3(board)) return 480; + else if (IS_FAMILY_HORUS_OR_T16(board)) + return 272; else if (IS_FAMILY_PL18(board) || IS_JUMPER_T15(board) || IS_JUMPER_T15PRO(board) || IS_FLYSKY_ST16(board) || IS_RADIOMASTER_TX15(board)) return 320; else if (IS_FLYSKY_PA01(board)) return 240; - else if (IS_FAMILY_HORUS_OR_T16(board)) - return 272; else return 64; case LcdWidth: - if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PA01(board)) + if (IS_RADIOMASTER_TX16SMK3(board)) + return 800; + else if (IS_FAMILY_HORUS_OR_T16(board) || IS_RADIOMASTER_TX15(board) || IS_FAMILY_PL18(board) || IS_FLYSKY_ST16(board)) + return 480; + else if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PA01(board)) return 320; - else if (IS_FAMILY_PL18(board) || IS_FLYSKY_ST16(board)) - return 480; - else if (IS_FAMILY_HORUS_OR_T16(board) || IS_RADIOMASTER_TX15(board)) - return 480; - else if (IS_TARANIS_SMALL(board)) - return 128; - else if (IS_TARANIS(board)) + else if (IS_TARANIS(board) && !IS_TARANIS_SMALL(board)) return 212; else return 128; @@ -427,7 +429,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return IS_RADIOMASTER_MT12(board); case FunctionSwitchColors: - return IS_RADIOMASTER_GX12(board) || IS_FLYSKY_ST16(board) || IS_FLYSKY_PA01(board) || IS_RADIOMASTER_TX15(board); + return IS_RADIOMASTER_GX12(board) || IS_FLYSKY_ST16(board) || IS_FLYSKY_PA01(board) || IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_TX16SMK3(board); default: return getBoardJson(board)->getCapability(capability); @@ -659,6 +661,8 @@ QString Boards::getBoardName(Board::Type board) return "Radiomaster TX12 Mark II"; case BOARD_RADIOMASTER_TX16S: return "Radiomaster TX16S"; + case BOARD_RADIOMASTER_TX16SMK3: + return "Radiomaster TX16SMK3"; case BOARD_RADIOMASTER_TX15: return "Radiomaster TX15"; case BOARD_RADIOMASTER_ZORRO: @@ -799,6 +803,7 @@ int Boards::getDefaultInternalModules(Board::Type board) case BOARD_HELLORADIOSKY_V14: case BOARD_HELLORADIOSKY_V16: case BOARD_RADIOMASTER_TX15: + case BOARD_RADIOMASTER_TX16SMK3: case BOARD_IFLIGHT_COMMANDO8: case BOARD_JUMPER_BUMBLEBEE: case BOARD_JUMPER_T12MAX: @@ -866,6 +871,7 @@ void Boards::getBattRange(Board::Type board, int& vmin, int& vmax, unsigned int& case BOARD_X10: case BOARD_X10_EXPRESS: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX16SMK3: case BOARD_RADIOMASTER_TX15: case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index d20c5d470..7d595a2dc 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -65,6 +65,7 @@ namespace Board { BOARD_JUMPER_T15PRO, BOARD_JUMPER_T16, BOARD_RADIOMASTER_TX16S, + BOARD_RADIOMASTER_TX16SMK3, BOARD_RADIOMASTER_TX15, BOARD_JUMPER_T18, BOARD_JUMPER_T20, @@ -572,6 +573,11 @@ inline bool IS_RADIOMASTER_TX16S(Board::Type board) return board == Board::BOARD_RADIOMASTER_TX16S; } +inline bool IS_RADIOMASTER_TX16SMK3(Board::Type board) +{ + return board == Board::BOARD_RADIOMASTER_TX16SMK3; +} + inline bool IS_RADIOMASTER_TX15(Board::Type board) { return board == Board::BOARD_RADIOMASTER_TX15; @@ -634,13 +640,14 @@ inline bool IS_HELLORADIOSKY_V16(Board::Type board) inline bool IS_FAMILY_T16(Board::Type board) { - return board == Board::BOARD_JUMPER_T15 || + return board == Board::BOARD_FATFISH_F16 || + board == Board::BOARD_HELLORADIOSKY_V16 || + board == Board::BOARD_JUMPER_T15 || board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_JUMPER_T18 || + board == Board::BOARD_RADIOMASTER_TX15 || board == Board::BOARD_RADIOMASTER_TX16S || - board == Board::BOARD_FATFISH_F16 || - board == Board::BOARD_HELLORADIOSKY_V16 || - board == Board::BOARD_RADIOMASTER_TX15; + board == Board::BOARD_RADIOMASTER_TX16SMK3; } inline bool IS_FAMILY_T12(Board::Type board) diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index 287a2d8ec..388efde40 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -213,6 +213,8 @@ void GeneralSettings::init() strcpy(bluetoothName, "st16"); else if (IS_RADIOMASTER_TX15(board)) strcpy(bluetoothName, "tx15"); + else if (IS_RADIOMASTER_TX16SMK3(board)) + strcpy(bluetoothName, "tx16smk3"); else if (IS_FAMILY_HORUS_OR_T16(board)) strcpy(bluetoothName, "horus"); else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board)) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 9468940a6..b3cdf7a3c 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -329,7 +329,8 @@ int OpenTxFirmware::getCapability(::Capability capability) IS_RADIOMASTER_BOXER(board) || IS_RADIOMASTER_GX12(board) || IS_RADIOMASTER_MT12(board) || IS_RADIOMASTER_POCKET(board) || IS_RADIOMASTER_TX12(board) || IS_RADIOMASTER_TX12_MK2(board) || IS_RADIOMASTER_TX16S(board) || IS_RADIOMASTER_ZORRO(board) || IS_RADIOMASTER_TX15(board) || IS_JUMPER_T15PRO(board) || - IS_FLYSKY_PA01(board) || IS_FLYSKY_ST16(board)); + IS_FLYSKY_PA01(board) || IS_FLYSKY_ST16(board) || + IS_RADIOMASTER_TX16SMK3(board)); case HasSoftwareSerialPower: return IS_RADIOMASTER_TX16S(board); case HasIntModuleMulti: @@ -852,6 +853,13 @@ void registerOpenTxFirmwares() firmware->addOption("flyskygimbals", Firmware::tr("Support hardware mod: FlySky Paladin EV Gimbals")); registerOpenTxFirmware(firmware); + /* Radiomaster TX16SMK3 board */ + firmware = new OpenTxFirmware(FIRMWAREID("tx16smk3"), Firmware::tr("Radiomaster TX16SMK3"), BOARD_RADIOMASTER_TX16SMK3); + addOpenTxFrskyOptions(firmware); + addOpenTxRfOptions(firmware, FLEX); + firmware->addOptionsGroup({opt_bt, opt_internal_gps}); + registerOpenTxFirmware(firmware); + /* Radiomaster Zorro board */ firmware = new OpenTxFirmware(FIRMWAREID("zorro"), QCoreApplication::translate("Firmware", "Radiomaster Zorro"), Board::BOARD_RADIOMASTER_ZORRO); addOpenTxCommonOptions(firmware); diff --git a/fw.json b/fw.json index 57b586d08..2baa51da6 100644 --- a/fw.json +++ b/fw.json @@ -38,6 +38,7 @@ ["RadioMaster TX12MK2", "tx12mk2-"], ["RadioMaster TX15", "tx15-"], ["RadioMaster TX16S", "tx16s-"], + ["RadioMaster TX16SMK3", "tx16smk3-"], ["RadioMaster Zorro","zorro-"] ], "changelog": "- Major initial bug fixes" diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index c1a92c103..d4daae156 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -1,7 +1,7 @@ include(CMakeForceCompiler) include(Bitmaps) -set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S PL18 T15PRO ST16 PA01 TX15) +set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S PL18 T15PRO ST16 PA01 TX15 TX16SMK3) set(RADIO_LANGUAGES CN CZ DA DE EN ES FI FR HE HU IT JP KO NL PL PT RU SE SK TW UA) set(TTS_LANGUAGES CN CZ DA DE EN ES FI FR HE HU IT JP KO NL PL PT RU SE SK TW UA) @@ -99,6 +99,8 @@ elseif(PCB STREQUAL PA01) include(targets/pa01/CMakeLists.txt) elseif(PCB STREQUAL TX15) include(targets/tx15/CMakeLists.txt) +elseif(PCB STREQUAL TX16SMK3) + include(targets/tx16smk3/CMakeLists.txt) elseif(PCB STREQUAL STM32H7S78_DK) include(targets/stm32h7s78-dk/CMakeLists.txt) elseif(PCB STREQUAL X9E OR PCB STREQUAL X9D+ OR PCB STREQUAL X9D OR PCB STREQUAL X7 OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES OR PCB STREQUAL XLITE OR PCB STREQUAL XLITES) @@ -424,6 +426,14 @@ if(DISABLE_MCUCHECK) add_definitions(-DDISABLE_MCUCHECK) endif() +if(SIZE_TARGET_MEM_DEFINE MATCHES 32768) + add_definitions(-DSDRAM_32M) +elseif(SIZE_TARGET_MEM_DEFINE MATCHES 16384) + add_definitions(-DSDRAM_16M) +else(SIZE_TARGET_MEM_DEFINE MATCHES 8192) + add_definitions(-DSDRAM_8M) +endif() + set(SRC ${SRC} edgetx.cpp diff --git a/radio/src/boards/rm-h750/audio_driver.cpp b/radio/src/boards/rm-h750/audio_driver.cpp index 4435223b4..ee83c1fc8 100644 --- a/radio/src/boards/rm-h750/audio_driver.cpp +++ b/radio/src/boards/rm-h750/audio_driver.cpp @@ -184,7 +184,7 @@ extern "C" void DMA1_Stream4_IRQHandler(void) if (stm32_dma_check_ht_flag(I2S_DMA, I2S_DMA_Stream)) { audio_update_dma_buffer(0); } - + if (stm32_dma_check_tc_flag(I2S_DMA, I2S_DMA_Stream)) { audio_update_dma_buffer(1); } diff --git a/radio/src/boards/rm-h750/backlight_driver.cpp b/radio/src/boards/rm-h750/backlight_driver.cpp index 5452b87a3..772af46c9 100644 --- a/radio/src/boards/rm-h750/backlight_driver.cpp +++ b/radio/src/boards/rm-h750/backlight_driver.cpp @@ -29,6 +29,12 @@ #include "globals.h" +typedef void (*lcdSpiInitFucPtr)(void); +typedef unsigned int LcdReadIDFucPtr( void ); + +extern lcdSpiInitFucPtr lcdOffFunction; +extern lcdSpiInitFucPtr lcdOnFunction; + static const stm32_pulse_timer_t _bl_timer = { .GPIO = (gpio_t)BACKLIGHT_GPIO, .GPIO_Alternate = BACKLIGHT_GPIO_AF, @@ -51,7 +57,7 @@ void backlightLowInit() void backlightInit() { - stm32_pulse_init(&_bl_timer, 1000000); + stm32_pulse_init(&_bl_timer, 100000); stm32_pulse_config_output(&_bl_timer, true, LL_TIM_OCMODE_PWM1, 100); LL_TIM_SetAutoReload(_bl_timer.TIMx, 100); LL_TIM_EnableCounter(_bl_timer.TIMx); diff --git a/radio/src/boards/rm-h750/bsp_io.cpp b/radio/src/boards/rm-h750/bsp_io.cpp index c1e59fdc0..df4261950 100644 --- a/radio/src/boards/rm-h750/bsp_io.cpp +++ b/radio/src/boards/rm-h750/bsp_io.cpp @@ -202,7 +202,7 @@ static SwitchHwPos _get_switch_pos(uint8_t idx) } else if (!def->Pin_low) { // 2POS switch - if ((state & def->Pin_high) == 0) { + if ((state & def->Pin_high) != 0) { pos = SWITCH_HW_DOWN; } } else { diff --git a/radio/src/boards/rm-h750/lcd_driver_800.cpp b/radio/src/boards/rm-h750/lcd_driver_800.cpp new file mode 100644 index 000000000..d6790db74 --- /dev/null +++ b/radio/src/boards/rm-h750/lcd_driver_800.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stm32_hal_ll.h" +#include "stm32_hal.h" +#include "edgetx_types.h" +#include "dma2d.h" +#include "hal.h" +#include "delays_driver.h" +#include "debug.h" +#include "lcd.h" +#include "lcd_driver_800.h" +#include "board.h" + +#include "hal/gpio.h" +#include "stm32_gpio.h" +#include "stm32_qspi.h" + +uint8_t TouchControllerType = 0; // 0: other; 1: CST836U +static const uint16_t lcd_phys_w = LCD_PHYS_W; +static const uint16_t lcd_phys_h = LCD_PHYS_H; + +static LTDC_HandleTypeDef hltdc; +static void* initialFrameBuffer = nullptr; + +static volatile uint8_t _frame_addr_reloaded = 0; + +static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, + const rect_t ©_area) +{ + (void)disp_drv; + (void)copy_area; + + // given the data cache size, this is probably + // faster than cleaning by address + SCB_CleanDCache(); + + LTDC_Layer1->CFBAR = (uint32_t)buffer; + // reload shadow registers on vertical blank + _frame_addr_reloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI); + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait + while(_frame_addr_reloaded == 0); +} + +lcdSpiInitFucPtr lcdInitFunction; +lcdSpiInitFucPtr lcdOffFunction; +lcdSpiInitFucPtr lcdOnFunction; +uint32_t lcdPixelClock; + +volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; + +static void LCD_AF_GPIOConfig(void) +{ + const gpio_t _lcd_af_gpios[] = { + GPIO_PIN(GPIOI, 12), GPIO_PIN(GPIOI, 13), GPIO_PIN(GPIOI, 14), + + GPIO_PIN(GPIOJ, 1), GPIO_PIN(GPIOJ, 2), GPIO_PIN(GPIOJ, 3), + GPIO_PIN(GPIOJ, 4), GPIO_PIN(GPIOJ, 5), GPIO_PIN(GPIOJ, 6), + GPIO_PIN(GPIOJ, 9), GPIO_PIN(GPIOJ, 10), GPIO_PIN(GPIOJ, 11), + GPIO_PIN(GPIOJ, 14), GPIO_PIN(GPIOJ, 15), + + GPIO_PIN(GPIOK, 0), GPIO_PIN(GPIOK, 1), GPIO_PIN(GPIOK, 2), + GPIO_PIN(GPIOK, 3), GPIO_PIN(GPIOK, 4), GPIO_PIN(GPIOK, 5), + GPIO_PIN(GPIOK, 6), GPIO_PIN(GPIOK, 7), + }; + + for (unsigned i = 0; i < sizeof(_lcd_af_gpios) / sizeof(_lcd_af_gpios[0]); i++) { + gpio_init_af(_lcd_af_gpios[i], GPIO_AF14, GPIO_SPEED_FREQ_LOW); + } +} + +static void lcdSpiConfig(void) +{ + LL_GPIO_InitTypeDef GPIO_InitStructure; + LL_GPIO_StructInit(&GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_RESET_GPIO_PIN; + GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Mode = LL_GPIO_MODE_OUTPUT; + GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStructure.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(LCD_RESET_GPIO, &GPIO_InitStructure); +} + +void lcdDelay() { + delay_01us(1); +} + +static void lcdReset() { + + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET(); + delay_ms(10); + + LCD_NRST_HIGH(); + delay_ms(100); +} + + + +void LCD_Init_LTDC() { + hltdc.Instance = LTDC; + + /* Configure PLLSAI prescalers for LCD */ + /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ + /* PLLSAI_VCO Output = PLLSAI_VCO Input * lcdPixelclock * 16 = XX Mhz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLL_LTDC = PLLSAI_VCO/4 = YY Mhz */ + /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = YY/4 = lcdPixelClock Mhz */ +// uint32_t clock = (lcdPixelClock*16) / 1000000; // clock*16 in MHz +// RCC_PeriphCLKInitTypeDef clkConfig; +// clkConfig.PeriphClockSelection = RCC_PERIPHCLK_LTDC; +// clkConfig.PLLI2S +// clkConfig.PLLSAI.PLLSAIN = clock; +// clkConfig.PLLSAI.PLLSAIR = 4; +// clkConfig.PLLSAIDivQ = 6; +// clkConfig.PLLSAIDivR = RCC_PLLSAIDIVR_4; +// HAL_RCCEx_PeriphCLKConfig(&clkConfig); + + /* LTDC Configuration *********************************************************/ + /* Polarity configuration */ + /* Initialize the horizontal synchronization polarity as active low */ + hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL; + /* Initialize the vertical synchronization polarity as active low */ + hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL; + /* Initialize the data enable polarity as active low */ + hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL; + /* Initialize the pixel clock polarity as input pixel clock */ + hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IIPC; + + /* Configure R,G,B component values for LCD background color */ + hltdc.Init.Backcolor.Red = 0; + hltdc.Init.Backcolor.Green = 0; + hltdc.Init.Backcolor.Blue = 0; + + /* Configure horizontal synchronization width */ + hltdc.Init.HorizontalSync = HSW-1; + /* Configure vertical synchronization height */ + hltdc.Init.VerticalSync = VSH-1; + /* Configure accumulated horizontal back porch */ + hltdc.Init.AccumulatedHBP = HSW+HBP-1; + /* Configure accumulated vertical back porch */ + hltdc.Init.AccumulatedVBP = VSH+VBP-1; + /* Configure accumulated active width */ + hltdc.Init.AccumulatedActiveW = lcd_phys_w + HBP+HSW-1; + /* Configure accumulated active height */ + hltdc.Init.AccumulatedActiveH = lcd_phys_h + VBP+VSH-1; + /* Configure total width */ + hltdc.Init.TotalWidth = lcd_phys_w + HBP + HFP + HSW -1; + /* Configure total height */ + hltdc.Init.TotalHeigh = lcd_phys_h + VBP + VFP+VSH-1; + + HAL_LTDC_Init(&hltdc); + + // Configure IRQ (line) + NVIC_SetPriority(LTDC_IRQn, LTDC_IRQ_PRIO); + NVIC_EnableIRQ(LTDC_IRQn); + + // Trigger on last line + HAL_LTDC_ProgramLineEvent(&hltdc, lcd_phys_h); + __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI); +} + +void LCD_LayerInit() { + auto& layer = hltdc.LayerCfg[0]; + + /* Windowing configuration */ + layer.WindowX0 = 0; + layer.WindowX1 = lcd_phys_w; + layer.WindowY0 = 0; + layer.WindowY1 = lcd_phys_h; + + /* Pixel Format configuration*/ + layer.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; + + /* Alpha constant (255 totally opaque) */ + layer.Alpha = 255; + + /* Default Color configuration (configure A,R,G,B component values) */ + layer.Backcolor.Blue = 0; + layer.Backcolor.Green = 0; + layer.Backcolor.Red = 0; + layer.Alpha0 = 0; + + /* Configure blending factors */ + layer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; + layer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; + + layer.ImageWidth = lcd_phys_w; + layer.ImageHeight = lcd_phys_h; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + layer.FBStartAdress = (intptr_t)initialFrameBuffer; + + /* Initialize LTDC layer 1 */ + HAL_LTDC_ConfigLayer(&hltdc, &hltdc.LayerCfg[0], 0); + + /* dithering activation */ + HAL_LTDC_EnableDither(&hltdc); +} + +extern "C" +void lcdSetInitalFrameBuffer(void* fbAddress) +{ + initialFrameBuffer = fbAddress; +} + +const char* boardLcdType = ""; + +extern "C" +void lcdInit(void) +{ +#if defined(LCD_SPI_CONFLICTS_WITH_QSPI) + stm32_qspi_nor_deinit(); +#endif + + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + /* Reset the LCD --------------------------------------------------------*/ + lcdReset(); + + /* Configure the LCD Control pins */ + LCD_AF_GPIOConfig(); + + /* Send LCD initialization commands */ + TRACE("LCD INIT (default): ST7365"); + boardLcdType = "ST7365 (Default)"; + + __HAL_RCC_LTDC_CLK_ENABLE(); + + LCD_Init_LTDC(); + LCD_LayerInit(); + + // Enable LCD display + __HAL_LTDC_ENABLE(&hltdc); + + lcdSetFlushCb(startLcdRefresh); + +#if defined(LCD_SPI_CONFLICTS_WITH_QSPI) + stm32_qspi_nor_init(); + stm32_qspi_nor_memory_mapped(); +#endif +} + +extern "C" void LTDC_IRQHandler(void) +{ + __HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_LI); + __HAL_LTDC_DISABLE_IT(&hltdc, LTDC_IT_LI); + _frame_addr_reloaded = 1; +} diff --git a/radio/src/boards/rm-h750/lcd_driver_800.h b/radio/src/boards/rm-h750/lcd_driver_800.h new file mode 100644 index 000000000..9b700f931 --- /dev/null +++ b/radio/src/boards/rm-h750/lcd_driver_800.h @@ -0,0 +1,46 @@ +/* +* Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "bsp_io.h" + +#define HBP ( 16 ) +#define VBP ( 8 ) + +#define HSW ( 4 ) +#define VSH ( 4 ) + +#define HFP ( 16 ) +#define VFP ( 8 ) + +typedef void (*lcdSpiInitFucPtr)(void); +typedef unsigned int LcdReadIDFucPtr( void ); + +extern lcdSpiInitFucPtr lcdInitFunction; +extern lcdSpiInitFucPtr lcdOffFunction; +extern lcdSpiInitFucPtr lcdOnFunction; + +#define SET_IO_INPUT( PORT, PIN ) LL_GPIO_SetPinMode( PORT, PIN, LL_GPIO_MODE_INPUT ) +#define SET_IO_OUTPUT( PORT, PIN ) LL_GPIO_SetPinMode( PORT, PIN, LL_GPIO_MODE_OUTPUT ) + +#define LCD_NRST_HIGH() LL_GPIO_SetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN) +#define LCD_NRST_LOW() LL_GPIO_ResetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN) diff --git a/radio/src/boards/rm-h750/luminosity_sensor.cpp b/radio/src/boards/rm-h750/luminosity_sensor.cpp new file mode 100644 index 000000000..75235ad99 --- /dev/null +++ b/radio/src/boards/rm-h750/luminosity_sensor.cpp @@ -0,0 +1,51 @@ +/* +* Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "edgetx.h" +#include "luminosity_sensor.h" +#include "hal/adc_driver.h" + +static uint32_t luxEMA = 0; +static bool luxInitialized = false; + +bool getPeriodicLuxSensorValue() +{ + if (adcGetMaxInputs(ADC_INPUT_LUX) < 1) return false; + + uint16_t newValue = anaIn(adcGetInputOffset(ADC_INPUT_LUX)); + + if (!luxInitialized) { + // Initialize EMA with first reading + luxEMA = (uint32_t)newValue << 8; // Scale up for precision + luxInitialized = true; + } else { + // EMA formula with alpha = 26/256 ≈ 0.102 + int32_t diff = ((int32_t)newValue << 8) - (int32_t)luxEMA; + luxEMA = luxEMA + ((diff * 26) >> 8); + } + + return true; +} + +uint16_t getLuxSensorValue() +{ + return luxEMA >> 8; // Scale back down +} \ No newline at end of file diff --git a/radio/src/boards/rm-h750/luminosity_sensor.h b/radio/src/boards/rm-h750/luminosity_sensor.h new file mode 100644 index 000000000..c9039fb67 --- /dev/null +++ b/radio/src/boards/rm-h750/luminosity_sensor.h @@ -0,0 +1,28 @@ +/* +* Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "edgetx_types.h" + +#pragma once + +bool getPeriodicLuxSensorValue(); +uint16_t getLuxSensorValue(); \ No newline at end of file diff --git a/radio/src/boards/rm-h750/sdram_driver.cpp b/radio/src/boards/rm-h750/sdram_driver.cpp index b643af154..49c21b884 100644 --- a/radio/src/boards/rm-h750/sdram_driver.cpp +++ b/radio/src/boards/rm-h750/sdram_driver.cpp @@ -96,6 +96,9 @@ extern "C" void SDRAM_GPIOConfig(void) /* GPIOG configuration */ gpio_init_af(GPIO_PIN(GPIOG, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); gpio_init_af(GPIO_PIN(GPIOG, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); +#if defined(SDRAM_32M) + gpio_init_af(GPIO_PIN(GPIOG, 2), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); +#endif gpio_init_af(GPIO_PIN(GPIOG, 4), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); gpio_init_af(GPIO_PIN(GPIOG, 5), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); gpio_init_af(GPIO_PIN(GPIOG, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); @@ -194,8 +197,13 @@ extern "C" void SDRAM_Init(void) /* FMC SDRAM control configuration */ FMC_SDRAMInitStructure.SDBank = FMC_SDRAM_BANK2; +#if defined(SDRAM_32M) + FMC_SDRAMInitStructure.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; + FMC_SDRAMInitStructure.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; +#else FMC_SDRAMInitStructure.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; FMC_SDRAMInitStructure.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; +#endif FMC_SDRAMInitStructure.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; FMC_SDRAMInitStructure.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; FMC_SDRAMInitStructure.CASLatency = FMC_SDRAM_CAS_LATENCY_3; diff --git a/radio/src/boards/rm-h750/system_clock.c b/radio/src/boards/rm-h750/system_clock.c index 2c32220da..99ab35fc4 100644 --- a/radio/src/boards/rm-h750/system_clock.c +++ b/radio/src/boards/rm-h750/system_clock.c @@ -94,7 +94,11 @@ BOOTSTRAP void SystemClock_Config(void) LL_RCC_PLL3_SetN(4); LL_RCC_PLL3_SetP(2); LL_RCC_PLL3_SetQ(2); +#if defined(RADIO_TX16SMK3) + LL_RCC_PLL3_SetR(8); // 24 MHz +#else LL_RCC_PLL3_SetR(18); +#endif LL_RCC_PLL3_Enable(); while (LL_RCC_PLL3_IsReady() != 1) { } @@ -105,7 +109,7 @@ BOOTSTRAP void SystemClock_Config(void) LL_AHB2_GRP1_PERIPH_D2SRAM3); /* Set periph clock sources */ - LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL1Q); + LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL1Q); LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL1Q); LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP); } diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index bcd368d50..b6606a56c 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -520,6 +520,10 @@ enum MixSources { MIXSRC_MIN, MIXSRC_MAX, +#if defined(LUMINOSITY_SENSOR) + MIXSRC_LIGHT, +#endif + MIXSRC_FIRST_HELI SKIP, MIXSRC_LAST_HELI SKIP = MIXSRC_FIRST_HELI + 2, @@ -588,8 +592,9 @@ enum SrcTypes { SRC_TX = 1 << 16, SRC_TIMER = 1 << 17, SRC_TELEM = 1 << 18, - SRC_NONE = 1 << 19, - SRC_INVERT = 1 << 20, + SRC_LIGHT = 1 << 19, + SRC_NONE = 1 << 30, + SRC_INVERT = 1 << 31, }; enum BacklightMode { diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 891fad317..21e0f081f 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -78,7 +78,7 @@ static inline void check_struct() #if defined(PCBXLITES) CHKSIZE(RadioData, 950); -#elif defined(RADIO_ST16) || defined(PCBPA01) || defined(RADIO_TX15) || defined(RADIO_T15PRO) +#elif defined(RADIO_ST16) || defined(PCBPA01) || defined(RADIO_TX15) || defined(RADIO_T15PRO) || defined(RADIO_TX16SMK3) CHKSIZE(RadioData, 1181); #elif defined(COLORLCD) CHKSIZE(RadioData, 1061); @@ -112,6 +112,8 @@ static inline void check_struct() CHKSIZE(ModelData, 7582); #elif defined(RADIO_T15) CHKSIZE(ModelData, 6867); +#elif defined(RADIO_TX16SMK3) + CHKSIZE(ModelData, 7642); #elif defined(RADIO_H7RS) // CHKSIZE() #elif defined(PCBHORUS) diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index f32b54088..847ca0354 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -78,6 +78,10 @@ #include "lua/custom_allocator.h" #endif +#if defined(LUMINOSITY_SENSOR) +#include "luminosity_sensor.h" +#endif + RadioData g_eeGeneral; ModelData g_model; @@ -238,6 +242,10 @@ void per10ms() if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3); #endif +#if defined(LUMINOSITY_SENSOR) + getPeriodicLuxSensorValue(); +#endif + outputTelemetryBuffer.per10ms(); heartbeat |= HEART_TIMER_10MS; @@ -2016,6 +2024,12 @@ void getMixSrcRange(const int source, int16_t & valMin, int16_t & valMax, LcdFla if (flags) *flags |= PREC1; } +#if defined(LUMINOSITY_SENSOR) + else if (asrc == MIXSRC_LIGHT) { + valMax = 100; + valMin = -valMax; + } +#endif else if (asrc == MIXSRC_TX_TIME) { valMax = 23 * 60 + 59; valMin = 0; diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index af2a8521f..0d71e40a6 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -109,6 +109,10 @@ PLAY_FUNCTION(playValue, mixsrc_t idx) PLAY_DURATION(val * 60, PLAY_TIME); } else if (idx == MIXSRC_TX_VOLTAGE) { PLAY_NUMBER(val, UNIT_VOLTS, PREC1); +#if defined(LUMINOSITY_SENSOR) + } else if (idx == MIXSRC_LIGHT) { + PLAY_NUMBER(val, UNIT_RAW, 0); +#endif } else { if (idx <= MIXSRC_LAST_CH) { val = calcRESXto100(val); diff --git a/radio/src/gui/colorlcd/controls/sourcechoice.cpp b/radio/src/gui/colorlcd/controls/sourcechoice.cpp index 6d9d8fe5e..24b11c3d5 100644 --- a/radio/src/gui/colorlcd/controls/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/controls/sourcechoice.cpp @@ -42,13 +42,7 @@ class SourceChoiceMenuToolbar : public MenuToolbar addButton(CHAR_LUA, MIXSRC_FIRST_LUA, MIXSRC_LAST_LUA, nullptr, STR_MENU_LUA); #endif -#if defined(PCBHORUS) - auto lastSource = MIXSRC_LAST_SPACEMOUSE; -#elif defined(IMU) - auto lastSource = MIXSRC_TILT_Y; -#else - auto lastSource = MIXSRC_LAST_STICK; -#endif + auto lastSource = MIXSRC_MIN - 1; addButton( CHAR_STICK, MIXSRC_FIRST_STICK, lastSource, [=](int16_t index) { @@ -62,6 +56,9 @@ class SourceChoiceMenuToolbar : public MenuToolbar addButton( CHAR_FUNCTION, MIXSRC_MIN, MIXSRC_LAST_TIMER, [=](int16_t index) { +#if defined(LUMINOSITY_SENSOR) + if (index == MIXSRC_LIGHT) return true; +#endif return (index >= MIXSRC_MIN && index <= MIXSRC_MAX) || (index >= MIXSRC_TX_VOLTAGE && index <= MIXSRC_LAST_TIMER); }, diff --git a/radio/src/gui/colorlcd/libui/list_line_button.cpp b/radio/src/gui/colorlcd/libui/list_line_button.cpp index 0d1c13137..ccb22fbd6 100644 --- a/radio/src/gui/colorlcd/libui/list_line_button.cpp +++ b/radio/src/gui/colorlcd/libui/list_line_button.cpp @@ -251,18 +251,22 @@ InputMixGroupBase::InputMixGroupBase(Window* parent, mixsrc_t idx) : etx_font(label, FONT_XS_INDEX, LV_STATE_USER_1); } -void InputMixGroupBase::adjustHeight() +void InputMixGroupBase::_adjustHeight(coord_t y) { if (getLineCount() == 0) setHeight(ListLineButton::BTN_H + PAD_SMALL * 2); - coord_t y = PAD_OUTLINE; for (auto it = lines.cbegin(); it != lines.cend(); ++it) { auto line = *it; line->updateHeight(); line->updatePos(InputMixButtonBase::LN_X, y); y += line->height() + PAD_OUTLINE; } - setHeight(y + PAD_BORDER * 2); + setHeight(y + PAD_BORDER * 2 + PAD_OUTLINE); +} + +void InputMixGroupBase::adjustHeight() +{ + _adjustHeight(0); } void InputMixGroupBase::addLine(InputMixButtonBase* line) diff --git a/radio/src/gui/colorlcd/libui/list_line_button.h b/radio/src/gui/colorlcd/libui/list_line_button.h index 96c9301af..c91d110ef 100644 --- a/radio/src/gui/colorlcd/libui/list_line_button.h +++ b/radio/src/gui/colorlcd/libui/list_line_button.h @@ -37,7 +37,7 @@ class ListLineButton : public ButtonBase virtual void refresh() = 0; - static constexpr coord_t BTN_H = EdgeTxStyles::STD_FONT_HEIGHT + PAD_BORDER * 2 + PAD_TINY * 2; + static constexpr coord_t BTN_H = EdgeTxStyles::STD_FONT_HEIGHT + PAD_BORDER * 2 + PAD_OUTLINE * 2; static constexpr coord_t GRP_W = LCD_W - PAD_SMALL * 2; protected: @@ -72,7 +72,7 @@ class InputMixButtonBase : public ListLineButton #else static LAYOUT_VAL_SCALED(LN_X, 73) #endif - static constexpr coord_t BTN_W = ListLineButton::GRP_W - LN_X - PAD_BORDER * 2 - PAD_OUTLINE; + static constexpr coord_t BTN_W = ListLineButton::GRP_W - LN_X - PAD_BORDER * 2 - PAD_OUTLINE * 2; static constexpr coord_t WGT_X = PAD_TINY; static constexpr coord_t WGT_Y = PAD_TINY; static LAYOUT_VAL_SCALED(WGT_W, 50) @@ -122,6 +122,8 @@ class InputMixGroupBase : public Window mixsrc_t idx; lv_obj_t* label; std::list lines; + + void _adjustHeight(coord_t y); }; class InputMixPageBase : public PageGroupItem diff --git a/radio/src/gui/colorlcd/lv_conf.h b/radio/src/gui/colorlcd/lv_conf.h index c60fc47c1..31f427879 100644 --- a/radio/src/gui/colorlcd/lv_conf.h +++ b/radio/src/gui/colorlcd/lv_conf.h @@ -57,10 +57,17 @@ #endif #if LV_MEM_CUSTOM == 0 /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ - #if defined(SIMU) - #define LV_MEM_SIZE (4 * 1024U * 1024U) /*[bytes]*/ + #if defined(SDRAM_32M) + #define LV_MEM 8 + #elif defined(SDRAM_16M) + #define LV_MEM 4 #else - #define LV_MEM_SIZE (2 * 1024U * 1024U) /*[bytes]*/ + #define LV_MEM 2 + #endif + #if defined(SIMU) + #define LV_MEM_SIZE (LV_MEM * 2 * 1024U * 1024U) /*[bytes]*/ + #else + #define LV_MEM_SIZE (LV_MEM * 1024U * 1024U) /*[bytes]*/ #endif /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ diff --git a/radio/src/gui/colorlcd/model/curveedit.cpp b/radio/src/gui/colorlcd/model/curveedit.cpp index 1154a2fbe..1fb98c22a 100644 --- a/radio/src/gui/colorlcd/model/curveedit.cpp +++ b/radio/src/gui/colorlcd/model/curveedit.cpp @@ -124,7 +124,11 @@ class CurveDataEdit : public Window } static LAYOUT_VAL_SCALED(ROW_HEIGHT, 82) +#if WIDE_LAYOUT + static LAYOUT_VAL_SCALED(NUM_BTN_WIDTH, 50) +#else static LAYOUT_VAL_SCALED(NUM_BTN_WIDTH, 47) +#endif static LAYOUT_VAL_SCALED(NUM_HDR_HEIGHT, 15) static LAYOUT_VAL_SCALED(PTNUM_X, 15) static LAYOUT_VAL_SCALED(PTNUM_H, 13) diff --git a/radio/src/gui/colorlcd/model/model_logical_switches.cpp b/radio/src/gui/colorlcd/model/model_logical_switches.cpp index 8f42f52c8..8c1deed6a 100644 --- a/radio/src/gui/colorlcd/model/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model/model_logical_switches.cpp @@ -36,7 +36,7 @@ #define SET_DIRTY() storageDirty(EE_MODEL) #define ETX_STATE_LS_ACTIVE LV_STATE_USER_1 -#define ETX_STATE_V1_SMALL_FONT LV_STATE_USER_1 +#define ETX_STATE_V1_SMALL_FONT LV_STATE_USER_2 static const lv_coord_t col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), LV_GRID_TEMPLATE_LAST}; diff --git a/radio/src/gui/colorlcd/model/model_mixes.cpp b/radio/src/gui/colorlcd/model/model_mixes.cpp index 0f850b141..b7f21366e 100644 --- a/radio/src/gui/colorlcd/model/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model/model_mixes.cpp @@ -214,14 +214,7 @@ class MixGroup : public InputMixGroupBase void adjustHeight() override { - coord_t y = monitorVisible ? CHNUM_Y : PAD_OUTLINE; - for (auto it = lines.cbegin(); it != lines.cend(); ++it) { - auto line = *it; - line->updateHeight(); - line->updatePos(InputMixButtonBase::LN_X, y); - y += line->height() + PAD_OUTLINE; - } - setHeight(y + PAD_BORDER * 2); + _adjustHeight(monitorVisible ? CHNUM_Y : 0); } static LAYOUT_VAL_SCALED(CHNUM_Y, 17) diff --git a/radio/src/gui/common/stdlcd/draw_functions.cpp b/radio/src/gui/common/stdlcd/draw_functions.cpp index b866ce3fb..9521c09f3 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.cpp +++ b/radio/src/gui/common/stdlcd/draw_functions.cpp @@ -450,6 +450,11 @@ void drawSourceCustomValue(coord_t x, coord_t y, mixsrc_t source, int32_t value, else if (source == MIXSRC_TX_VOLTAGE) { lcdDrawNumber(x, y, value, flags|PREC1); } +#if defined(LUMINOSITY_SENSOR) + else if (source == MIXSRC_LIGHT) { + lcdDrawNumber(x, y, value, flags); + } +#endif #if defined(INTERNAL_GPS) else if (source == MIXSRC_TX_GPS) { if (gpsData.fix) { diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 0f6bf6a90..6d58240b3 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -262,6 +262,9 @@ static struct sourceAvailableCheck sourceChecks[] = { #if defined(IMU) { MIXSRC_TILT_X, MIXSRC_TILT_Y, SRC_TILT, sourceIsAvailable }, #endif +#if defined(LUMINOSITY_SENSOR) + { MIXSRC_LIGHT, MIXSRC_LIGHT, SRC_LIGHT, sourceIsAvailable }, +#endif #if defined(PCBHORUS) { MIXSRC_FIRST_SPACEMOUSE, MIXSRC_LAST_SPACEMOUSE, SRC_SPACEMOUSE, isSourceSpacemouseAvailable }, #endif @@ -298,7 +301,7 @@ bool checkSourceAvailable(int source, uint32_t sourceTypes) } #define SRC_COMMON \ - SRC_STICK | SRC_POT | SRC_TILT | SRC_SPACEMOUSE | SRC_MINMAX | SRC_TRIM | \ + SRC_STICK | SRC_POT | SRC_TILT | SRC_LIGHT | SRC_SPACEMOUSE | SRC_MINMAX | SRC_TRIM | \ SRC_SWITCH | SRC_FUNC_SWITCH | SRC_LOGICAL_SWITCH | SRC_TRAINER | SRC_GVAR bool isSourceAvailable(int source) @@ -310,7 +313,7 @@ bool isSourceAvailable(int source) bool isSourceAvailableForBacklightOrVolume(int source) { - return checkSourceAvailable(source, SRC_SWITCH | SRC_POT | SRC_NONE); + return checkSourceAvailable(source, SRC_SWITCH | SRC_POT | SRC_LIGHT | SRC_NONE); } bool isLogicalSwitchAvailable(int index) diff --git a/radio/src/hal/adc_driver.h b/radio/src/hal/adc_driver.h index 41468445e..1659297a3 100644 --- a/radio/src/hal/adc_driver.h +++ b/radio/src/hal/adc_driver.h @@ -42,6 +42,7 @@ enum { ADC_INPUT_FLEX, ADC_INPUT_VBAT, ADC_INPUT_RTC_BAT, + ADC_INPUT_LUX, ADC_INPUT_ALL, }; diff --git a/radio/src/lua/api_colorlcd_lvgl.cpp b/radio/src/lua/api_colorlcd_lvgl.cpp index 38c79fbfb..887c4fc24 100644 --- a/radio/src/lua/api_colorlcd_lvgl.cpp +++ b/radio/src/lua/api_colorlcd_lvgl.cpp @@ -495,7 +495,7 @@ LROT_BEGIN(lvgllib, NULL, 0) LROT_NUMENTRY(SRC_ALL, 0xFFFFFFFF) LROT_NUMENTRY(SRC_INPUT, SRC_INPUT) LROT_NUMENTRY(SRC_LUA, SRC_LUA) - LROT_NUMENTRY(SRC_STICK, SRC_STICK|SRC_TILT|SRC_SPACEMOUSE) + LROT_NUMENTRY(SRC_STICK, SRC_STICK|SRC_TILT|SRC_LIGHT|SRC_SPACEMOUSE) LROT_NUMENTRY(SRC_POT, SRC_POT) LROT_NUMENTRY(SRC_OTHER, SRC_MINMAX|SRC_TX|SRC_TIMER) LROT_NUMENTRY(SRC_HELI, SRC_HELI) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 461015496..750743dd3 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -372,6 +372,10 @@ const LuaSingleField luaSingleFields[] = { {MIXSRC_TILT_Y, "tilty", "Tilt Y"}, #endif +#if defined(LUMINOSITY_SENSOR) + {MIXSRC_LIGHT, "light", "Light Sensor"}, +#endif + #if defined(PCBHORUS) {MIXSRC_SPACEMOUSE_A, "sma", "SpaceMouse A"}, {MIXSRC_SPACEMOUSE_B, "smb", "SpaceMouse B"}, diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index f60e67610..cd9125f88 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -31,6 +31,10 @@ #include "hal/switch_driver.h" #include "hal/audio_driver.h" +#if defined(LUMINOSITY_SENSOR) +#include "luminosity_sensor.h" +#endif + #define DELAY_POS_MARGIN 3 uint8_t s_mixer_first_run_done = false; @@ -403,6 +407,12 @@ getvalue_t _getValue(mixsrc_t i, bool* valid) return RESX; } +#if defined(LUMINOSITY_SENSOR) + else if (i == MIXSRC_LIGHT) { + return getLuxSensorValue() - RESX; + } +#endif + else if (i <= MIXSRC_LAST_HELI) { #if defined(HELI) return cyc_anas[i - MIXSRC_FIRST_HELI]; diff --git a/radio/src/storage/yaml/CMakeLists.txt b/radio/src/storage/yaml/CMakeLists.txt index fe222f631..6199a0238 100644 --- a/radio/src/storage/yaml/CMakeLists.txt +++ b/radio/src/storage/yaml/CMakeLists.txt @@ -40,6 +40,8 @@ elseif(PCB STREQUAL PA01) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_pa01.cpp) elseif(PCB STREQUAL TX15) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tx15.cpp) +elseif(PCB STREQUAL TX16SMK3) + set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tx16smk3.cpp) elseif(PCB STREQUAL X7) if(PCBREV STREQUAL TPRO) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tpro.cpp) diff --git a/radio/src/storage/yaml/yaml_datastructs.cpp b/radio/src/storage/yaml/yaml_datastructs.cpp index ed162e462..f2e05b952 100644 --- a/radio/src/storage/yaml/yaml_datastructs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs.cpp @@ -40,6 +40,8 @@ #include "yaml_datastructs_tx15.cpp" #elif defined(PCBT15) #include "yaml_datastructs_t15pro.cpp" +#elif defined(PCBTX16SMK3) + #include "yaml_datastructs_tx16smk3.cpp" #elif defined(PCBPL18) #if defined(RADIO_NB4P) #include "yaml_datastructs_nb4p.cpp" diff --git a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp new file mode 100644 index 000000000..9f00007e0 --- /dev/null +++ b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp @@ -0,0 +1,1042 @@ +// generated by generate_yaml.py + +// +// Enums first +// + +const struct YamlIdStr enum_BacklightMode[] = { + { e_backlight_mode_off, "backlight_mode_off" }, + { e_backlight_mode_keys, "backlight_mode_keys" }, + { e_backlight_mode_sticks, "backlight_mode_sticks" }, + { e_backlight_mode_all, "backlight_mode_all" }, + { e_backlight_mode_on, "backlight_mode_on" }, + { 0, NULL } +}; +const struct YamlIdStr enum_AntennaModes[] = { + { ANTENNA_MODE_INTERNAL, "MODE_INTERNAL" }, + { ANTENNA_MODE_ASK, "MODE_ASK" }, + { ANTENNA_MODE_PER_MODEL, "MODE_PER_MODEL" }, + { ANTENNA_MODE_EXTERNAL, "MODE_EXTERNAL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModuleType[] = { + { MODULE_TYPE_NONE, "TYPE_NONE" }, + { MODULE_TYPE_PPM, "TYPE_PPM" }, + { MODULE_TYPE_XJT_PXX1, "TYPE_XJT_PXX1" }, + { MODULE_TYPE_ISRM_PXX2, "TYPE_ISRM_PXX2" }, + { MODULE_TYPE_DSM2, "TYPE_DSM2" }, + { MODULE_TYPE_CROSSFIRE, "TYPE_CROSSFIRE" }, + { MODULE_TYPE_MULTIMODULE, "TYPE_MULTIMODULE" }, + { MODULE_TYPE_R9M_PXX1, "TYPE_R9M_PXX1" }, + { MODULE_TYPE_R9M_PXX2, "TYPE_R9M_PXX2" }, + { MODULE_TYPE_R9M_LITE_PXX1, "TYPE_R9M_LITE_PXX1" }, + { MODULE_TYPE_R9M_LITE_PXX2, "TYPE_R9M_LITE_PXX2" }, + { MODULE_TYPE_GHOST, "TYPE_GHOST" }, + { MODULE_TYPE_R9M_LITE_PRO_PXX2, "TYPE_R9M_LITE_PRO_PXX2" }, + { MODULE_TYPE_SBUS, "TYPE_SBUS" }, + { MODULE_TYPE_XJT_LITE_PXX2, "TYPE_XJT_LITE_PXX2" }, + { MODULE_TYPE_FLYSKY_AFHDS2A, "TYPE_FLYSKY_AFHDS2A" }, + { MODULE_TYPE_FLYSKY_AFHDS3, "TYPE_FLYSKY_AFHDS3" }, + { MODULE_TYPE_LEMON_DSMP, "TYPE_LEMON_DSMP" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TrainerMultiplex[] = { + { TRAINER_OFF, "OFF" }, + { TRAINER_ADD, "ADD" }, + { TRAINER_REPL, "REPL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_BeeperMode[] = { + { e_mode_quiet, "mode_quiet" }, + { e_mode_alarms, "mode_alarms" }, + { e_mode_nokeys, "mode_nokeys" }, + { e_mode_all, "mode_all" }, + { 0, NULL } +}; +const struct YamlIdStr enum_BluetoothModes[] = { + { BLUETOOTH_OFF, "OFF" }, + { BLUETOOTH_TELEMETRY, "TELEMETRY" }, + { BLUETOOTH_TRAINER, "TRAINER" }, + { 0, NULL } +}; +const struct YamlIdStr enum_Functions[] = { + { FUNC_OVERRIDE_CHANNEL, "OVERRIDE_CHANNEL" }, + { FUNC_TRAINER, "TRAINER" }, + { FUNC_INSTANT_TRIM, "INSTANT_TRIM" }, + { FUNC_RESET, "RESET" }, + { FUNC_SET_TIMER, "SET_TIMER" }, + { FUNC_ADJUST_GVAR, "ADJUST_GVAR" }, + { FUNC_VOLUME, "VOLUME" }, + { FUNC_SET_FAILSAFE, "SET_FAILSAFE" }, + { FUNC_RANGECHECK, "RANGECHECK" }, + { FUNC_BIND, "BIND" }, + { FUNC_PLAY_SOUND, "PLAY_SOUND" }, + { FUNC_PLAY_TRACK, "PLAY_TRACK" }, + { FUNC_PLAY_VALUE, "PLAY_VALUE" }, + { FUNC_PLAY_SCRIPT, "PLAY_SCRIPT" }, + { FUNC_BACKGND_MUSIC, "BACKGND_MUSIC" }, + { FUNC_BACKGND_MUSIC_PAUSE, "BACKGND_MUSIC_PAUSE" }, + { FUNC_VARIO, "VARIO" }, + { FUNC_HAPTIC, "HAPTIC" }, + { FUNC_LOGS, "LOGS" }, + { FUNC_BACKLIGHT, "BACKLIGHT" }, + { FUNC_SCREENSHOT, "SCREENSHOT" }, + { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, + { FUNC_SET_SCREEN, "SET_SCREEN" }, + { FUNC_DISABLE_AUDIO_AMP, "DISABLE_AUDIO_AMP" }, + { FUNC_RGB_LED, "RGB_LED" }, + { FUNC_PUSH_CUST_SWITCH, "PUSH_CUST_SWITCH" }, + { FUNC_TEST, "TEST" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwitchConfig[] = { + { SWITCH_NONE, "NONE" }, + { SWITCH_TOGGLE, "TOGGLE" }, + { SWITCH_2POS, "2POS" }, + { SWITCH_3POS, "3POS" }, + { SWITCH_GLOBAL, "GLOBAL" }, + { SWITCH_none, "none" }, + { SWITCH_toggle, "toggle" }, + { SWITCH_2pos, "2pos" }, + { SWITCH_3pos, "3pos" }, + { 0, NULL } +}; +const struct YamlIdStr enum_fsStartPositionType[] = { + { FS_START_OFF, "START_OFF" }, + { FS_START_ON, "START_ON" }, + { FS_START_PREVIOUS, "START_PREVIOUS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_booleanEnum[] = { + { BOOL_OFF, "OFF" }, + { BOOL_ON, "ON" }, + { 0, NULL } +}; +const struct YamlIdStr enum_QMPage[] = { + { QM_NONE, "NONE" }, + { QM_OPEN_QUICK_MENU, "OPEN_QUICK_MENU" }, + { QM_MANAGE_MODELS, "MANAGE_MODELS" }, + { QM_MODEL_SETUP, "MODEL_SETUP" }, + { QM_MODEL_FLIGHTMODES, "MODEL_FLIGHTMODES" }, + { QM_MODEL_INPUTS, "MODEL_INPUTS" }, + { QM_MODEL_MIXES, "MODEL_MIXES" }, + { QM_MODEL_OUTPUTS, "MODEL_OUTPUTS" }, + { QM_MODEL_CURVES, "MODEL_CURVES" }, + { QM_MODEL_GVARS, "MODEL_GVARS" }, + { QM_MODEL_LS, "MODEL_LS" }, + { QM_MODEL_SF, "MODEL_SF" }, + { QM_MODEL_SCRIPTS, "MODEL_SCRIPTS" }, + { QM_MODEL_TELEMETRY, "MODEL_TELEMETRY" }, + { QM_MODEL_NOTES, "MODEL_NOTES" }, + { QM_RADIO_SETUP, "RADIO_SETUP" }, + { QM_RADIO_GF, "RADIO_GF" }, + { QM_RADIO_TRAINER, "RADIO_TRAINER" }, + { QM_RADIO_HARDWARE, "RADIO_HARDWARE" }, + { QM_RADIO_VERSION, "RADIO_VERSION" }, + { QM_UI_THEMES, "UI_THEMES" }, + { QM_UI_SETUP, "UI_SETUP" }, + { QM_UI_SCREEN1, "UI_SCREEN1" }, + { QM_UI_SCREEN2, "UI_SCREEN2" }, + { QM_UI_SCREEN3, "UI_SCREEN3" }, + { QM_UI_SCREEN4, "UI_SCREEN4" }, + { QM_UI_SCREEN5, "UI_SCREEN5" }, + { QM_UI_SCREEN6, "UI_SCREEN6" }, + { QM_UI_SCREEN7, "UI_SCREEN7" }, + { QM_UI_SCREEN8, "UI_SCREEN8" }, + { QM_UI_SCREEN9, "UI_SCREEN9" }, + { QM_UI_SCREEN10, "UI_SCREEN10" }, + { QM_UI_ADD_PG, "UI_ADD_PG" }, + { QM_TOOLS_APPS, "TOOLS_APPS" }, + { QM_TOOLS_STORAGE, "TOOLS_STORAGE" }, + { QM_TOOLS_RESET, "TOOLS_RESET" }, + { QM_TOOLS_CHAN_MON, "TOOLS_CHAN_MON" }, + { QM_TOOLS_LS_MON, "TOOLS_LS_MON" }, + { QM_TOOLS_STATS, "TOOLS_STATS" }, + { QM_TOOLS_DEBUG, "TOOLS_DEBUG" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TimerModes[] = { + { TMRMODE_OFF, "OFF" }, + { TMRMODE_ON, "ON" }, + { TMRMODE_START, "START" }, + { TMRMODE_THR, "THR" }, + { TMRMODE_THR_REL, "THR_REL" }, + { TMRMODE_THR_START, "THR_START" }, + { 0, NULL } +}; +const struct YamlIdStr enum_MixerMultiplex[] = { + { MLTPX_ADD, "ADD" }, + { MLTPX_MUL, "MUL" }, + { MLTPX_REPL, "REPL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_MixSources[] = { + { MIXSRC_NONE, "NONE" }, + { MIXSRC_TILT_X, "TILT_X" }, + { MIXSRC_TILT_Y, "TILT_Y" }, + { MIXSRC_SPACEMOUSE_A, "SPACEMOUSE_A" }, + { MIXSRC_SPACEMOUSE_B, "SPACEMOUSE_B" }, + { MIXSRC_SPACEMOUSE_C, "SPACEMOUSE_C" }, + { MIXSRC_SPACEMOUSE_D, "SPACEMOUSE_D" }, + { MIXSRC_SPACEMOUSE_E, "SPACEMOUSE_E" }, + { MIXSRC_SPACEMOUSE_F, "SPACEMOUSE_F" }, + { MIXSRC_MIN, "MIN" }, + { MIXSRC_MAX, "MAX" }, + { MIXSRC_LIGHT, "LIGHT" }, + { MIXSRC_TX_VOLTAGE, "TX_VOLTAGE" }, + { MIXSRC_TX_TIME, "TX_TIME" }, + { MIXSRC_TX_GPS, "TX_GPS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_LogicalSwitchesFunctions[] = { + { LS_FUNC_NONE, "FUNC_NONE" }, + { LS_FUNC_VEQUAL, "FUNC_VEQUAL" }, + { LS_FUNC_VALMOSTEQUAL, "FUNC_VALMOSTEQUAL" }, + { LS_FUNC_VPOS, "FUNC_VPOS" }, + { LS_FUNC_VNEG, "FUNC_VNEG" }, + { LS_FUNC_APOS, "FUNC_APOS" }, + { LS_FUNC_ANEG, "FUNC_ANEG" }, + { LS_FUNC_AND, "FUNC_AND" }, + { LS_FUNC_OR, "FUNC_OR" }, + { LS_FUNC_XOR, "FUNC_XOR" }, + { LS_FUNC_EDGE, "FUNC_EDGE" }, + { LS_FUNC_EQUAL, "FUNC_EQUAL" }, + { LS_FUNC_GREATER, "FUNC_GREATER" }, + { LS_FUNC_LESS, "FUNC_LESS" }, + { LS_FUNC_DIFFEGREATER, "FUNC_DIFFEGREATER" }, + { LS_FUNC_ADIFFEGREATER, "FUNC_ADIFFEGREATER" }, + { LS_FUNC_TIMER, "FUNC_TIMER" }, + { LS_FUNC_STICKY, "FUNC_STICKY" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwashType[] = { + { SWASH_TYPE_NONE, "TYPE_NONE" }, + { SWASH_TYPE_120, "TYPE_120" }, + { SWASH_TYPE_120X, "TYPE_120X" }, + { SWASH_TYPE_140, "TYPE_140" }, + { SWASH_TYPE_90, "TYPE_90" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwitchSources[] = { + { SWSRC_NONE, "NONE" }, + { SWSRC_ON, "ON" }, + { SWSRC_ONE, "ONE" }, + { SWSRC_TELEMETRY_STREAMING, "TELEMETRY_STREAMING" }, + { SWSRC_RADIO_ACTIVITY, "RADIO_ACTIVITY" }, + { SWSRC_TRAINER_CONNECTED, "TRAINER_CONNECTED" }, + { SWSRC_OFF, "OFF" }, + { 0, NULL } +}; +const struct YamlIdStr enum_PotsWarnMode[] = { + { POTS_WARN_OFF, "WARN_OFF" }, + { POTS_WARN_MANUAL, "WARN_MANUAL" }, + { POTS_WARN_AUTO, "WARN_AUTO" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModelOverridableEnable[] = { + { OVERRIDE_GLOBAL, "GLOBAL" }, + { OVERRIDE_OFF, "OFF" }, + { OVERRIDE_ON, "ON" }, + { 0, NULL } +}; +const struct YamlIdStr enum_FailsafeModes[] = { + { FAILSAFE_NOT_SET, "NOT_SET" }, + { FAILSAFE_HOLD, "HOLD" }, + { FAILSAFE_CUSTOM, "CUSTOM" }, + { FAILSAFE_NOPULSES, "NOPULSES" }, + { FAILSAFE_RECEIVER, "RECEIVER" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorFormula[] = { + { TELEM_FORMULA_ADD, "FORMULA_ADD" }, + { TELEM_FORMULA_AVERAGE, "FORMULA_AVERAGE" }, + { TELEM_FORMULA_MIN, "FORMULA_MIN" }, + { TELEM_FORMULA_MAX, "FORMULA_MAX" }, + { TELEM_FORMULA_MULTIPLY, "FORMULA_MULTIPLY" }, + { TELEM_FORMULA_TOTALIZE, "FORMULA_TOTALIZE" }, + { TELEM_FORMULA_CELL, "FORMULA_CELL" }, + { TELEM_FORMULA_CONSUMPTION, "FORMULA_CONSUMPTION" }, + { TELEM_FORMULA_DIST, "FORMULA_DIST" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorType[] = { + { TELEM_TYPE_CUSTOM, "TYPE_CUSTOM" }, + { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; + +// +// Structs last +// + +static const struct YamlNode struct_CalibData[] = { + YAML_IDX_CUST("calib",r_calib,w_calib), + YAML_SIGNED( "mid", 16 ), + YAML_SIGNED( "spanNeg", 16 ), + YAML_SIGNED( "spanPos", 16 ), + YAML_END +}; +static const struct YamlNode struct_signed_16[] = { + YAML_IDX, + YAML_SIGNED( "val", 16 ), + YAML_END +}; +static const struct YamlNode struct_TrainerMix[] = { + YAML_IDX, + YAML_UNSIGNED( "srcChn", 6 ), + YAML_ENUM("mode", 2, enum_TrainerMultiplex, NULL), + YAML_SIGNED( "studWeight", 8 ), + YAML_END +}; +static const struct YamlNode struct_TrainerData[] = { + YAML_ARRAY("calib", 16, 4, struct_signed_16, NULL), + YAML_ARRAY("mix", 16, 4, struct_TrainerMix, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_1[] = { + YAML_STRING("name", 8), + YAML_END +}; +static const struct YamlNode struct_anonymous_2[] = { + YAML_SIGNED( "val", 16 ), + YAML_UNSIGNED( "mode", 8 ), + YAML_UNSIGNED( "param", 8 ), + YAML_SIGNED( "val2", 32 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_3[] = { + YAML_SIGNED( "val1", 32 ), + YAML_SIGNED( "val2", 32 ), + YAML_END +}; +static const struct YamlNode union_anonymous_0_elmts[] = { + YAML_STRUCT("play", 64, struct_anonymous_1, NULL), + YAML_STRUCT("all", 64, struct_anonymous_2, NULL), + YAML_STRUCT("clear", 64, struct_anonymous_3, NULL), + YAML_END +}; +static const struct YamlNode struct_CustomFunctionData[] = { + YAML_IDX, + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_ENUM("func", 6, enum_Functions, NULL), + YAML_CUSTOM("def",r_customFn,w_customFn), + YAML_PADDING( 64 ), + YAML_PADDING( 1 ), + YAML_PADDING( 7 ), + YAML_END +}; +static const struct YamlNode struct_RGBLedColor[] = { + YAML_UNSIGNED( "r", 8 ), + YAML_UNSIGNED( "g", 8 ), + YAML_UNSIGNED( "b", 8 ), + YAML_END +}; +static const struct YamlNode struct_switchDef[] = { + YAML_IDX_CUST("sw",sw_idx_read,sw_idx_write), + YAML_STRING("name", 3), + YAML_ENUM("type", 3, enum_SwitchConfig, NULL), + YAML_ENUM("start", 2, enum_fsStartPositionType, switch_is_cfs), + YAML_ENUM("onColorLuaOverride", 1, enum_booleanEnum, switch_is_cfs), + YAML_ENUM("offColorLuaOverride", 1, enum_booleanEnum, switch_is_cfs), + YAML_PADDING( 1 ), + YAML_STRUCT("onColor", 24, struct_RGBLedColor, switch_is_cfs), + YAML_STRUCT("offColor", 24, struct_RGBLedColor, switch_is_cfs), + YAML_END +}; +static const struct YamlNode struct_QuickMenuPage[] = { + YAML_IDX, + YAML_ENUM("shortcut", 8, enum_QMPage, NULL), + YAML_END +}; +static const struct YamlNode struct_RadioData[] = { + YAML_UNSIGNED( "manuallyEdited", 1 ), + YAML_SIGNED( "timezoneMinutes", 3 ), + YAML_UNSIGNED( "ppmunit", 2 ), + YAML_PADDING( 2 ), + YAML_CUSTOM("semver",nullptr,w_semver), + YAML_CUSTOM("board",nullptr,w_board), + YAML_ARRAY("calib", 48, 20, struct_CalibData, NULL), + YAML_PADDING( 16 ), + YAML_UNSIGNED( "vBatWarn", 8 ), + YAML_SIGNED( "txVoltageCalibration", 8 ), + YAML_ENUM("backlightMode", 3, enum_BacklightMode, NULL), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_UNSIGNED( "disableRtcWarning", 1 ), + YAML_UNSIGNED( "keysBacklight", 1 ), + YAML_UNSIGNED( "dontPlayHello", 1 ), + YAML_ENUM("internalModule", 8, enum_ModuleType, NULL), + YAML_STRUCT("trainer", 128, struct_TrainerData, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_PADDING( 2 ), + YAML_UNSIGNED( "fai", 1 ), + YAML_SIGNED_CUST( "beepMode", 2, r_beeperMode, w_beeperMode ), + YAML_UNSIGNED( "alarmsFlash", 1 ), + YAML_UNSIGNED( "disableMemoryWarning", 1 ), + YAML_UNSIGNED( "disableAlarmWarning", 1 ), + YAML_UNSIGNED( "stickMode", 2 ), + YAML_SIGNED( "timezone", 5 ), + YAML_UNSIGNED( "adjustRTC", 1 ), + YAML_UNSIGNED( "inactivityTimer", 8 ), + YAML_CUSTOM("telemetryBaudrate",r_telemetryBaudrate,nullptr), + YAML_UNSIGNED( "internalModuleBaudrate", 3 ), + YAML_SIGNED( "splashMode", 3 ), + YAML_SIGNED_CUST( "hapticMode", 2, r_beeperMode, w_beeperMode ), + YAML_SIGNED( "switchesDelay", 8 ), + YAML_UNSIGNED( "lightAutoOff", 8 ), + YAML_UNSIGNED( "templateSetup", 8 ), + YAML_SIGNED( "PPM_Multiplier", 8 ), + YAML_SIGNED_CUST( "hapticLength", 8, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "beepLength", 3, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "hapticStrength", 3, r_5pos, w_5pos ), + YAML_UNSIGNED( "gpsFormat", 1 ), + YAML_UNSIGNED( "audioMuteEnable", 1 ), + YAML_UNSIGNED_CUST( "speakerPitch", 8, r_spPitch, w_spPitch ), + YAML_SIGNED_CUST( "speakerVolume", 8, r_vol, w_vol ), + YAML_SIGNED_CUST( "vBatMin", 8, r_vbat_min, w_vbat_min ), + YAML_SIGNED_CUST( "vBatMax", 8, r_vbat_max, w_vbat_max ), + YAML_UNSIGNED( "backlightBright", 8 ), + YAML_UNSIGNED( "globalTimer", 32 ), + YAML_UNSIGNED( "bluetoothBaudrate", 4 ), + YAML_ENUM("bluetoothMode", 4, enum_BluetoothModes, NULL), + YAML_UNSIGNED( "countryCode", 2 ), + YAML_SIGNED( "pwrOnSpeed", 3 ), + YAML_SIGNED( "pwrOffSpeed", 3 ), + YAML_CUSTOM("jitterFilter",r_jitterFilter,nullptr), + YAML_UNSIGNED( "noJitterFilter", 1 ), + YAML_UNSIGNED( "imperial", 1 ), + YAML_UNSIGNED( "disableRssiPoweroffAlarm", 1 ), + YAML_UNSIGNED( "USBMode", 2 ), + YAML_UNSIGNED( "jackMode", 2 ), + YAML_PADDING( 1 ), + YAML_STRING("ttsLanguage", 2), + YAML_STRING("uiLanguage", 2), + YAML_SIGNED_CUST( "beepVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "wavVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "varioVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "backgroundVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "varioPitch", 8, r_vPitch, w_vPitch ), + YAML_SIGNED_CUST( "varioRange", 8, r_vPitch, w_vPitch ), + YAML_SIGNED( "varioRepeat", 8 ), + YAML_ARRAY("customFn", 88, 64, struct_CustomFunctionData, cfn_is_active), + YAML_CUSTOM("auxSerialMode",r_serialMode,nullptr), + YAML_CUSTOM("aux2SerialMode",r_serialMode,nullptr), + YAML_ARRAY("serialPort", 8, 4, struct_serialConfig, nullptr), + YAML_ARRAY("sticksConfig", 0, MAX_STICKS, struct_stickConfig, stick_name_valid), + YAML_ARRAY("slidersConfig", 0, MAX_POTS, struct_sliderConfig, nullptr), + YAML_PADDING( 8 ), + YAML_ARRAY("potsConfig", 4, 16, struct_potConfig, nullptr), + YAML_ARRAY("switchConfig", 80, 20, struct_switchDef, switchIsActive), + YAML_ARRAY("flexSwitches", 0, MAX_FLEX_SWITCHES, struct_flexSwitch, flex_sw_valid), + YAML_STRING("currModelFilename", 17), + YAML_UNSIGNED( "blOffBright", 8 ), + YAML_STRING("bluetoothName", 10), + YAML_STRING("ownerRegistrationID", 8), + YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), + YAML_UNSIGNED( "rotEncMode", 3 ), + YAML_PADDING( 2 ), + YAML_PADDING( 3 ), + YAML_SIGNED( "imuMax", 8 ), + YAML_SIGNED( "imuOffset", 8 ), + YAML_STRING("selectedTheme", 26), + YAML_SIGNED_CUST( "backlightSrc", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_SIGNED( "radioGFDisabled", 1 ), + YAML_SIGNED( "radioTrainerDisabled", 1 ), + YAML_SIGNED( "modelHeliDisabled", 1 ), + YAML_SIGNED( "modelFMDisabled", 1 ), + YAML_SIGNED( "modelCurvesDisabled", 1 ), + YAML_SIGNED( "modelGVDisabled", 1 ), + YAML_SIGNED_CUST( "volumeSrc", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_SIGNED( "modelLSDisabled", 1 ), + YAML_SIGNED( "modelSFDisabled", 1 ), + YAML_SIGNED( "modelCustomScriptsDisabled", 1 ), + YAML_SIGNED( "modelTelemetryDisabled", 1 ), + YAML_SIGNED( "disableTrainerPoweroffAlarm", 1 ), + YAML_SIGNED( "disablePwrOnOffHaptic", 1 ), + YAML_UNSIGNED( "modelQuickSelect", 1 ), + YAML_UNSIGNED( "labelSingleSelect", 1 ), + YAML_UNSIGNED( "labelMultiMode", 1 ), + YAML_UNSIGNED( "favMultiMode", 1 ), + YAML_UNSIGNED( "modelSelectLayout", 2 ), + YAML_UNSIGNED( "radioThemesDisabled", 1 ), + YAML_PADDING( 1 ), + YAML_UNSIGNED( "pwrOffIfInactive", 8 ), + YAML_ARRAY("keyShortcuts", 8, 6, struct_QuickMenuPage, NULL), + YAML_ARRAY("qmFavorites", 8, 12, struct_QuickMenuPage, NULL), + YAML_END +}; +static const struct YamlNode struct_unsigned_8[] = { + YAML_IDX, + YAML_UNSIGNED( "val", 8 ), + YAML_END +}; +static const struct YamlNode struct_ModelHeader[] = { + YAML_STRING("name", 15), + YAML_ARRAY("modelId", 8, 2, struct_unsigned_8, NULL), + YAML_STRING("bitmap", 14), + YAML_STRING("labels", 100), + YAML_END +}; +static const struct YamlNode struct_TimerData[] = { + YAML_IDX, + YAML_UNSIGNED( "start", 22 ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_SIGNED( "value", 22 ), + YAML_ENUM("mode", 3, enum_TimerModes, NULL), + YAML_UNSIGNED( "countdownBeep", 2 ), + YAML_UNSIGNED( "minuteBeep", 1 ), + YAML_UNSIGNED( "persistent", 2 ), + YAML_SIGNED( "countdownStart", 2 ), + YAML_UNSIGNED( "showElapsed", 1 ), + YAML_UNSIGNED( "extraHaptic", 1 ), + YAML_PADDING( 6 ), + YAML_STRING("name", 8), + YAML_END +}; +static const struct YamlNode struct_CurveRef[] = { + YAML_UNSIGNED( "type", 5 ), + YAML_UNSIGNED_CUST( "value", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_END +}; +static const struct YamlNode struct_MixData[] = { + YAML_UNSIGNED( "destCh", 5 ), + YAML_SIGNED_CUST( "srcRaw", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_UNSIGNED( "carryTrim", 1 ), + YAML_UNSIGNED( "mixWarn", 2 ), + YAML_ENUM("mltpx", 2, enum_MixerMultiplex, NULL), + YAML_UNSIGNED( "delayPrec", 1 ), + YAML_UNSIGNED( "speedPrec", 1 ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + YAML_PADDING( 1 ), + YAML_UNSIGNED_CUST( "weight", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_UNSIGNED_CUST( "offset", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_STRUCT("curve", 16, struct_CurveRef, NULL), + YAML_UNSIGNED( "delayUp", 8 ), + YAML_UNSIGNED( "delayDown", 8 ), + YAML_UNSIGNED( "speedUp", 8 ), + YAML_UNSIGNED( "speedDown", 8 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_LimitData[] = { + YAML_IDX, + YAML_SIGNED_CUST( "min", 11, in_read_weight, in_write_weight ), + YAML_SIGNED_CUST( "max", 11, in_read_weight, in_write_weight ), + YAML_SIGNED( "ppmCenter", 10 ), + YAML_SIGNED_CUST( "offset", 11, in_read_weight, in_write_weight ), + YAML_UNSIGNED( "symetrical", 1 ), + YAML_UNSIGNED( "revert", 1 ), + YAML_PADDING( 3 ), + YAML_SIGNED( "curve", 8 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_ExpoData[] = { + YAML_UNSIGNED( "mode", 2 ), + YAML_UNSIGNED( "scale", 14 ), + YAML_CUSTOM("carryTrim",r_carryTrim,nullptr), + YAML_SIGNED( "trimSource", 6 ), + YAML_SIGNED_CUST( "srcRaw", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_UNSIGNED_CUST( "weight", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_UNSIGNED_CUST( "offset", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_STRUCT("curve", 16, struct_CurveRef, NULL), + YAML_UNSIGNED( "chn", 5 ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + YAML_PADDING( 2 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_CurveHeader[] = { + YAML_IDX, + YAML_UNSIGNED( "type", 1 ), + YAML_UNSIGNED( "smooth", 1 ), + YAML_SIGNED( "points", 6 ), + YAML_STRING("name", 3), + YAML_END +}; +static const struct YamlNode struct_signed_8[] = { + YAML_IDX, + YAML_SIGNED( "val", 8 ), + YAML_END +}; +static const struct YamlNode struct_LogicalSwitchData[] = { + YAML_IDX, + YAML_ENUM("func", 8, enum_LogicalSwitchesFunctions, NULL), + YAML_CUSTOM("def",r_logicSw,w_logicSw), + YAML_PADDING( 10 ), + YAML_PADDING( 10 ), + YAML_SIGNED_CUST( "andsw", 10, r_swtchSrc, w_swtchSrc ), + YAML_UNSIGNED( "lsPersist", 1 ), + YAML_UNSIGNED( "lsState", 1 ), + YAML_PADDING( 16 ), + YAML_UNSIGNED( "delay", 8 ), + YAML_UNSIGNED( "duration", 8 ), + YAML_END +}; +static const struct YamlNode struct_SwashRingData[] = { + YAML_ENUM("type", 8, enum_SwashType, NULL), + YAML_UNSIGNED( "value", 8 ), + YAML_UNSIGNED_CUST( "collectiveSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED_CUST( "aileronSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED_CUST( "elevatorSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_SIGNED( "collectiveWeight", 8 ), + YAML_SIGNED( "aileronWeight", 8 ), + YAML_SIGNED( "elevatorWeight", 8 ), + YAML_END +}; +static const struct YamlNode struct_trim_t[] = { + YAML_IDX, + YAML_SIGNED( "value", 11 ), + YAML_UNSIGNED( "mode", 5 ), + YAML_END +}; +static const struct YamlNode struct_FlightModeData[] = { + YAML_IDX, + YAML_ARRAY("trim", 16, 8, struct_trim_t, NULL), + YAML_STRING("name", 10), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_PADDING( 6 ), + YAML_UNSIGNED( "fadeIn", 8 ), + YAML_UNSIGNED( "fadeOut", 8 ), + YAML_ARRAY("gvars", 16, 15, struct_signed_16, gvar_is_active), + YAML_END +}; +static const struct YamlNode struct_GVarData[] = { + YAML_IDX, + YAML_STRING("name", 3), + YAML_UNSIGNED( "min", 12 ), + YAML_UNSIGNED( "max", 12 ), + YAML_UNSIGNED( "popup", 1 ), + YAML_UNSIGNED( "prec", 1 ), + YAML_UNSIGNED( "unit", 2 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_VarioData[] = { + YAML_UNSIGNED_CUST( "source", 7, r_tele_sensor, w_tele_sensor ), + YAML_UNSIGNED( "centerSilent", 1 ), + YAML_SIGNED( "centerMax", 8 ), + YAML_SIGNED( "centerMin", 8 ), + YAML_SIGNED( "min", 8 ), + YAML_SIGNED( "max", 8 ), + YAML_END +}; +static const struct YamlNode struct_RssiAlarmData[] = { + YAML_CUSTOM("disabled",r_rssiDisabled,nullptr), + YAML_CUSTOM("warning",r_rssiWarning,nullptr), + YAML_CUSTOM("critical",r_rssiCritical,nullptr), + YAML_END +}; +static const struct YamlNode struct_RFAlarmData[] = { + YAML_SIGNED( "warning", 8 ), + YAML_SIGNED( "critical", 8 ), + YAML_END +}; +static const struct YamlNode struct_PpmModule[] = { + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_UNSIGNED( "outputType", 1 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_5[] = { + YAML_PADDING( 8 ), + YAML_UNSIGNED( "disableTelemetry", 1 ), + YAML_UNSIGNED( "disableMapping", 1 ), + YAML_UNSIGNED( "autoBindMode", 1 ), + YAML_UNSIGNED( "lowPowerMode", 1 ), + YAML_UNSIGNED( "receiverTelemetryOff", 1 ), + YAML_UNSIGNED( "receiverHigherChannels", 1 ), + YAML_PADDING( 2 ), + YAML_SIGNED( "optionValue", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_6[] = { + YAML_UNSIGNED( "power", 2 ), + YAML_PADDING( 2 ), + YAML_UNSIGNED( "receiverTelemetryOff", 1 ), + YAML_UNSIGNED( "receiverHigherChannels", 1 ), + YAML_SIGNED( "antennaMode", 2 ), + YAML_PADDING( 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_7[] = { + YAML_PADDING( 6 ), + YAML_UNSIGNED( "noninverted", 1 ), + YAML_PADDING( 1 ), + YAML_SIGNED( "refreshRate", 8 ), + YAML_END +}; +static const struct YamlNode struct_string_64[] = { + YAML_IDX, + YAML_STRING("val", 8), + YAML_END +}; +static const struct YamlNode struct_anonymous_8[] = { + YAML_UNSIGNED( "receivers", 7 ), + YAML_UNSIGNED( "racingMode", 1 ), + YAML_ARRAY("receiverName", 64, 3, struct_string_64, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_9[] = { + YAML_ARRAY("rx_id", 8, 4, struct_unsigned_8, NULL), + YAML_UNSIGNED( "mode", 3 ), + YAML_UNSIGNED( "rfPower", 1 ), + YAML_UNSIGNED( "reserved", 4 ), + YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_10[] = { + YAML_UNSIGNED( "emi", 2 ), + YAML_UNSIGNED( "telemetry", 1 ), + YAML_UNSIGNED( "phyMode", 3 ), + YAML_UNSIGNED( "reserved", 2 ), + YAML_UNSIGNED( "rfPower", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_11[] = { + YAML_UNSIGNED( "raw12bits", 1 ), + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_12[] = { + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_UNSIGNED( "crsfArmingMode", 1 ), + YAML_PADDING( 4 ), + YAML_SIGNED_CUST( "crsfArmingTrigger", 10, r_swtchSrc, w_swtchSrc ), + YAML_SIGNED( "spare3", 6 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_13[] = { + YAML_UNSIGNED( "flags", 8 ), + YAML_UNSIGNED( "enableAETR", 1 ), + YAML_END +}; +static const struct YamlNode union_anonymous_4_elmts[] = { + YAML_ARRAY("raw", 8, 25, struct_unsigned_8, NULL), + YAML_STRUCT("ppm", 16, struct_PpmModule, NULL), + YAML_STRUCT("multi", 24, struct_anonymous_5, NULL), + YAML_STRUCT("pxx", 16, struct_anonymous_6, NULL), + YAML_STRUCT("sbus", 16, struct_anonymous_7, NULL), + YAML_STRUCT("pxx2", 200, struct_anonymous_8, NULL), + YAML_STRUCT("flysky", 56, struct_anonymous_9, NULL), + YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), + YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), + YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), + YAML_STRUCT("dsmp", 16, struct_anonymous_13, NULL), + YAML_END +}; +static const struct YamlNode struct_ModuleData[] = { + YAML_IDX, + YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), + YAML_UNSIGNED( "channelsStart", 8 ), + YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), + YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), + YAML_PADDING( 4 ), + YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), + YAML_END +}; +static const struct YamlNode struct_TrainerModuleData[] = { + YAML_UNSIGNED_CUST( "mode", 8, r_trainerMode, w_trainerMode ), + YAML_UNSIGNED( "channelsStart", 8 ), + YAML_SIGNED( "channelsCount", 8 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_PADDING( 1 ), + YAML_END +}; +static const struct YamlNode union_ScriptDataInput_elmts[] = { + YAML_SIGNED( "value", 16 ), + YAML_UNSIGNED_CUST( "source", 16, r_mixSrcRaw, w_mixSrcRaw ), + YAML_END +}; +static const struct YamlNode union_ScriptDataInput[] = { + YAML_IDX, + YAML_UNION("u", 16, union_ScriptDataInput_elmts, select_script_input), + YAML_END +}; +static const struct YamlNode struct_ScriptData[] = { + YAML_IDX, + YAML_STRING("file", 6), + YAML_STRING("name", 6), + YAML_ARRAY("inputs", 16, 6, union_ScriptDataInput, NULL), + YAML_END +}; +static const struct YamlNode struct_string_32[] = { + YAML_IDX, + YAML_STRING("val", 4), + YAML_END +}; +static const struct YamlNode union_anonymous_14_elmts[] = { + YAML_UNSIGNED( "id", 16 ), + YAML_UNSIGNED( "persistentValue", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_16[] = { + YAML_UNSIGNED( "physID", 5 ), + YAML_UNSIGNED( "rxIndex", 3 ), + YAML_END +}; +static const struct YamlNode union_anonymous_15_elmts[] = { + YAML_STRUCT("frskyInstance", 8, struct_anonymous_16, NULL), + YAML_UNSIGNED( "instance", 8 ), + YAML_ENUM("formula", 8, enum_TelemetrySensorFormula, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_18[] = { + YAML_UNSIGNED( "ratio", 16 ), + YAML_SIGNED( "offset", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_19[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_UNSIGNED( "index", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_20[] = { + YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_21[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_PADDING( 24 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_22[] = { + YAML_UNSIGNED( "gps", 8 ), + YAML_UNSIGNED( "alt", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode union_anonymous_17_elmts[] = { + YAML_STRUCT("custom", 32, struct_anonymous_18, NULL), + YAML_STRUCT("cell", 32, struct_anonymous_19, NULL), + YAML_STRUCT("calc", 32, struct_anonymous_20, NULL), + YAML_STRUCT("consumption", 32, struct_anonymous_21, NULL), + YAML_STRUCT("dist", 32, struct_anonymous_22, NULL), + YAML_UNSIGNED( "param", 32 ), + YAML_END +}; +static const struct YamlNode struct_TelemetrySensor[] = { + YAML_IDX, + YAML_UNION("id1", 16, union_anonymous_14_elmts, select_id1), + YAML_UNION("id2", 8, union_anonymous_15_elmts, select_id2), + YAML_STRING("label", 4), + YAML_UNSIGNED( "subId", 8 ), + YAML_ENUM("type", 1, enum_TelemetrySensorType, NULL), + YAML_PADDING( 1 ), + YAML_UNSIGNED( "unit", 6 ), + YAML_UNSIGNED( "prec", 2 ), + YAML_UNSIGNED( "autoOffset", 1 ), + YAML_UNSIGNED( "filter", 1 ), + YAML_UNSIGNED( "logs", 1 ), + YAML_UNSIGNED( "persistent", 1 ), + YAML_UNSIGNED( "onlyPositive", 1 ), + YAML_PADDING( 1 ), + YAML_UNION("cfg", 32, union_anonymous_17_elmts, select_sensor_cfg), + YAML_END +}; +static const struct YamlNode union_WidgetOptionValue_elmts[] = { + YAML_CUSTOM("unsignedValue",r_wov_unsigned,w_wov_unsigned), + YAML_CUSTOM("signedValue",r_wov_signed,w_wov_signed), + YAML_CUSTOM("boolValue",r_wov_unsigned,w_wov_unsigned), + YAML_CUSTOM("stringValue",r_wov_string,w_wov_string), + YAML_CUSTOM("source",r_wov_source,w_wov_source), + YAML_CUSTOM("color",r_wov_color,w_wov_color), + YAML_END +}; +static const struct YamlNode struct_WidgetOptionValueTyped[] = { + YAML_IDX, + YAML_CUSTOM("type",r_wov_type,w_wov_type), + YAML_UNION("value", 0, union_WidgetOptionValue_elmts, select_wov), + YAML_END +}; +static const struct YamlNode struct_WidgetPersistentData[] = { + YAML_ARRAY("options", 0, 50, struct_WidgetOptionValueTyped, widget_option_is_active), + YAML_END +}; +static const struct YamlNode struct_ZonePersistentData[] = { + YAML_IDX, + YAML_CUSTOM("widgetName",r_widget_name,w_widget_name), + YAML_STRUCT("widgetData", 0, struct_WidgetPersistentData, isAlwaysActive), + YAML_END +}; +static const struct YamlNode union_LayoutOptionValue_elmts[] = { + YAML_CUSTOM("unsignedValue",r_lov_unsigned,w_lov_unsigned), + YAML_CUSTOM("boolValue",r_lov_unsigned,w_lov_unsigned), + YAML_CUSTOM("color",r_lov_color,w_lov_color), + YAML_END +}; +static const struct YamlNode struct_LayoutOptionValueTyped[] = { + YAML_IDX, + YAML_CUSTOM("type",r_lov_type,w_lov_type), + YAML_UNION("value", 0, union_LayoutOptionValue_elmts, select_lov), + YAML_END +}; +static const struct YamlNode struct_LayoutPersistentData[] = { + YAML_ARRAY("zones", 0, 10, struct_ZonePersistentData, widget_is_active), + YAML_ARRAY("options", 0, 10, struct_LayoutOptionValueTyped, layout_option_is_active), + YAML_END +}; +static const struct YamlNode struct_CustomScreenData[] = { + YAML_IDX, + YAML_CUSTOM("LayoutId",r_screen_id,w_screen_id), + YAML_STRUCT("layoutData", 0, struct_LayoutPersistentData, isAlwaysActive), + YAML_END +}; +static const struct YamlNode struct_TopBarPersistentData[] = { + YAML_ARRAY("zones", 0, 7, struct_ZonePersistentData, widget_is_active), + YAML_END +}; +static const struct YamlNode struct_customSwitch[] = { + YAML_IDX_CUST("sw",cfs_idx_read,cfs_idx_write), + YAML_STRING("name", 3), + YAML_ENUM("type", 3, enum_SwitchConfig, NULL), + YAML_UNSIGNED( "group", 2 ), + YAML_ENUM("start", 2, enum_fsStartPositionType, NULL), + YAML_UNSIGNED( "state", 1 ), + YAML_ENUM("onColorLuaOverride", 1, enum_booleanEnum, NULL), + YAML_ENUM("offColorLuaOverride", 1, enum_booleanEnum, NULL), + YAML_PADDING( 6 ), + YAML_STRUCT("onColor", 24, struct_RGBLedColor, isAlwaysActive), + YAML_STRUCT("offColor", 24, struct_RGBLedColor, isAlwaysActive), + YAML_END +}; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh, NULL), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; +static const struct YamlNode struct_ModelData[] = { + YAML_CUSTOM("semver",nullptr,w_semver), + YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL), + YAML_UNSIGNED( "telemetryProtocol", 3 ), + YAML_UNSIGNED( "thrTrim", 1 ), + YAML_UNSIGNED( "noGlobalFunctions", 1 ), + YAML_UNSIGNED( "displayTrims", 2 ), + YAML_UNSIGNED( "ignoreSensorIds", 1 ), + YAML_SIGNED( "trimInc", 3 ), + YAML_UNSIGNED( "disableThrottleWarning", 1 ), + YAML_UNSIGNED( "displayChecklist", 1 ), + YAML_UNSIGNED( "extendedLimits", 1 ), + YAML_UNSIGNED( "extendedTrims", 1 ), + YAML_UNSIGNED( "throttleReversed", 1 ), + YAML_UNSIGNED( "enableCustomThrottleWarning", 1 ), + YAML_UNSIGNED( "disableTelemetryWarning", 1 ), + YAML_UNSIGNED( "showInstanceIds", 1 ), + YAML_UNSIGNED( "checklistInteractive", 1 ), + YAML_PADDING( 4 ), + YAML_SIGNED( "customThrottleWarningPosition", 8 ), + YAML_UNSIGNED( "beepANACenter", 16 ), + YAML_ARRAY("mixData", 160, 64, struct_MixData, NULL), + YAML_ARRAY("limitData", 104, 32, struct_LimitData, NULL), + YAML_ARRAY("expoData", 144, 64, struct_ExpoData, NULL), + YAML_ARRAY("curves", 32, 32, struct_CurveHeader, NULL), + YAML_ARRAY("points", 8, 512, struct_signed_8, NULL), + YAML_ARRAY("logicalSw", 72, 64, struct_LogicalSwitchData, NULL), + YAML_ARRAY("customFn", 88, 64, struct_CustomFunctionData, cfn_is_active), + YAML_STRUCT("swashR", 64, struct_SwashRingData, swash_is_active), + YAML_ARRAY("flightModeData", 480, 9, struct_FlightModeData, fmd_is_active), + YAML_UNSIGNED_CUST( "thrTraceSrc", 8, r_thrSrc, w_thrSrc ), + YAML_CUSTOM("switchWarningState",r_swtchWarn,nullptr), + YAML_ARRAY("switchWarning", 2, 32, struct_swtchWarn, nullptr), + YAML_ARRAY("gvars", 56, 15, struct_GVarData, NULL), + YAML_STRUCT("varioData", 40, struct_VarioData, NULL), + YAML_UNSIGNED_CUST( "rssiSource", 8, r_tele_sensor, w_tele_sensor ), + YAML_STRUCT("rssiAlarms", 0, struct_RssiAlarmData, NULL), + YAML_STRUCT("rfAlarms", 16, struct_RFAlarmData, NULL), + YAML_UNSIGNED( "thrTrimSw", 3 ), + YAML_ENUM("potsWarnMode", 2, enum_PotsWarnMode, NULL), + YAML_ENUM("jitterFilter", 2, enum_ModelOverridableEnable, NULL), + YAML_PADDING( 1 ), + YAML_ARRAY("moduleData", 232, 2, struct_ModuleData, NULL), + YAML_ARRAY("failsafeChannels", 16, 32, struct_signed_16, NULL), + YAML_STRUCT("trainerData", 40, struct_TrainerModuleData, NULL), + YAML_ARRAY("scriptsData", 192, 9, struct_ScriptData, NULL), + YAML_ARRAY("inputNames", 32, 32, struct_string_32, NULL), + YAML_UNSIGNED( "potsWarnEnabled", 16 ), + YAML_ARRAY("potsWarnPosition", 8, 16, struct_signed_8, NULL), + YAML_ARRAY("telemetrySensors", 112, 99, struct_TelemetrySensor, NULL), + YAML_ARRAY("screenData", 0, 10, struct_CustomScreenData, screen_is_active), + YAML_STRUCT("topbarData", 0, struct_TopBarPersistentData, isAlwaysActive), + YAML_ARRAY("topbarWidgetWidth", 8, 7, struct_unsigned_8, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_STRING("modelRegistrationID", 8), + YAML_CUSTOM("functionSwitchConfig",r_functionSwitchConfig,nullptr), + YAML_CUSTOM("functionSwitchGroup",r_functionSwitchGroup,nullptr), + YAML_CUSTOM("functionSwitchStartConfig",r_functionSwitchStartConfig,nullptr), + YAML_CUSTOM("functionSwitchLogicalState",r_functionSwitchLogicalState,nullptr), + YAML_ARRAY("switchNames", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsNameConfig, nullptr), + YAML_ARRAY("functionSwitchLedONColor", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsOnColorConfig, nullptr), + YAML_ARRAY("functionSwitchLedOFFColor", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsOffColorConfig, nullptr), + YAML_ARRAY("customSwitches", 88, 6, struct_customSwitch, isAlwaysActive), + YAML_ARRAY("cfsGroupOn", 1, 8, struct_cfsGroupOn, cfsGroupIsActive), + YAML_PADDING( 8 ), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode, NULL), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), + YAML_ENUM("radioThemesDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("radioGFDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("radioTrainerDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelHeliDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelFMDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelCurvesDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelGVDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelLSDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelSFDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelCustomScriptsDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelTelemetryDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_END +}; +static const struct YamlNode struct_PartialModel[] = { + YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL), + YAML_END +}; + +#define MAX_RADIODATA_MODELDATA_PARTIALMODEL_STR_LEN 29 + +static const struct YamlNode __RadioData_root_node = YAML_ROOT( struct_RadioData ); + +const YamlNode* get_radiodata_nodes() +{ + return &__RadioData_root_node; +} +static const struct YamlNode __ModelData_root_node = YAML_ROOT( struct_ModelData ); + +const YamlNode* get_modeldata_nodes() +{ + return &__ModelData_root_node; +} +static const struct YamlNode __PartialModel_root_node = YAML_ROOT( struct_PartialModel ); + +const YamlNode* get_partialmodel_nodes() +{ + return &__PartialModel_root_node; +} + diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 3e0a2eede..cb07c912f 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -734,7 +734,13 @@ char *getSourceString(char (&destRef)[L], mixsrc_t idx, bool defaultOnly) strncpy(dest, STR_MENU_MIN, dest_len - 1); } else if (idx == MIXSRC_MAX) { strncpy(dest, STR_MENU_MAX, dest_len - 1); - } else if (idx <= MIXSRC_LAST_HELI) { + } +#if defined(LUMINOSITY_SENSOR) + else if (idx == MIXSRC_LIGHT) { + strncpy(dest, STR_SRC_LIGHT, dest_len - 1); + } +#endif + else if (idx <= MIXSRC_LAST_HELI) { idx -= MIXSRC_FIRST_HELI; getStringAtIndex(dest, STR_CYC_VSRCRAW, idx); } else if (idx <= MIXSRC_LAST_TRIM) { diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 56ba68d4f..f9658a4d2 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -29,8 +29,8 @@ set(TARGET_LINKER_DIR stm32f429_sdram) set(TARGET_EXTRAM_START 0xC0000000) if(PCBREV STREQUAL PL18U) - set(TARGET_SDRAM_SIZE 32768K) set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=32768") + set(TARGET_EXTRAM_SIZE 32M) else() set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=8192") endif() diff --git a/radio/src/targets/simu/adc_driver.cpp b/radio/src/targets/simu/adc_driver.cpp index 8d7cecf6b..852967710 100644 --- a/radio/src/targets/simu/adc_driver.cpp +++ b/radio/src/targets/simu/adc_driver.cpp @@ -32,6 +32,8 @@ void enableVBatBridge(){} void disableVBatBridge(){} bool isVBatBridgeEnabled(){ return false; } +uint16_t getLuxSensorValue() { return 1024; } + uint16_t getBatteryVoltage() { if (adcGetMaxInputs(ADC_INPUT_VBAT) < 1) return 0; diff --git a/radio/src/targets/simu/backlight_driver.cpp b/radio/src/targets/simu/backlight_driver.cpp index 3aac072d8..b63b4ae3e 100644 --- a/radio/src/targets/simu/backlight_driver.cpp +++ b/radio/src/targets/simu/backlight_driver.cpp @@ -24,6 +24,8 @@ bool boardBacklightOn = false; bool isBacklightEnabled() { return boardBacklightOn; } +bool getPeriodicLuxSensorValue() { return true; } + void backlightInit() {} #if !defined(COLORLCD) diff --git a/radio/src/targets/tx16smk3/CMakeLists.txt b/radio/src/targets/tx16smk3/CMakeLists.txt new file mode 100644 index 000000000..61080fc12 --- /dev/null +++ b/radio/src/targets/tx16smk3/CMakeLists.txt @@ -0,0 +1,212 @@ +option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) +option(PXX1 "PXX1 protocol support" ON) +option(PXX2 "PXX2 protocol support" OFF) +option(AFHDS3 "AFHDS3 TX Module" ON) +option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) +option(GHOST "Ghost TX Module" ON) +option(MODULE_SIZE_STD "Standard size TX Module" ON) +option(LUA_MIXER "Enable LUA mixer/model scripts support" ON) +option(DISK_CACHE "Enable SD card disk cache" ON) +option(INTERNAL_GPS "Support for internal GPS" ON) + +set(FIRMWARE_QSPI YES) +set(FIRMWARE_FORMAT_UF2 YES) +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") +set(CPU_TYPE STM32H7) +set(HSE_VALUE 48000000) +set(SDCARD YES) +set(STORAGE_MODELSLIST YES) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +set(BITMAPS_DIR 800x480) +set(TARGET_DIR tx16smk3) +set(RTC_BACKUP_RAM YES) +set(PPM_LIMITS_SYMETRICAL YES) +set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)") +set(HARDWARE_EXTERNAL_MODULE YES) +set(INTERNAL_MODULE_AFHDS3 NO) +set(ROTARY_ENCODER YES) +set(FUNCTION_SWITCHES_WITH_RGB YES) +set(FLEXSW "2" CACHE STRING "Max flex inputs usable as switches") +set(INTERNAL_GPS_BAUDRATE "9600" CACHE STRING "Baud rate for internal GPS") + +#option(STICKS_DEAD_ZONE "Enable sticks dead zone" YES) + +# IMU support +set(IMU ON) +add_definitions(-DIMU_ICM4207C) + +# for size report script +set(CPU_TYPE_FULL STM32H750xB) +set(TARGET_LINKER_DIR stm32h750_sdram) +set(TARGET_EXTRAM_START 0xD0000000) +set(TARGET_EXTRAM_SIZE 32M) +set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=32768") +set(FIRMWARE_STRIP_BOOTLOADER YES) + +add_definitions(-DPCBTX16SMK3 -DPCBHORUS) +add_definitions(-DBATTERY_CHARGE) +add_definitions(-DSTM32_SUPPORT_32BIT_TIMERS) +add_definitions(-DFIRMWARE_QSPI) +add_definitions(-DFIRMWARE_FORMAT_UF2) +add_definitions(-DUSE_CUSTOM_EXTI_IRQ) +add_definitions(-DUSB_CHARGER) +add_definitions(-DLUMINOSITY_SENSOR) + +set(FLAVOUR tx16smk3) +add_definitions(-DRADIO_TX16SMK3) +add_definitions(-DMANUFACTURER_RADIOMASTER) + +# defines existing internal modules +set(INTERNAL_MODULES CRSF CACHE STRING "Internal modules") +set(DEFAULT_INTERNAL_MODULE CROSSFIRE CACHE STRING "Default internal module") + +set(BITMAPS_TARGET bm800_bitmaps) +set(FONTS_TARGET x12_fonts) +set(LCD_DRIVER lcd_driver.cpp) +set(HARDWARE_TOUCH YES) +set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) +set(FIRMWARE_DEPENDENCIES datacopy) +set(SOFTWARE_KEYBOARD ON) +set(FUNCTION_SWITCHES ON) +set(USB_CHARGER YES) +set(AUX_SERIAL YES) +set(AUX2_SERIAL YES) + +add_definitions( + -DSTM32H750XBTx -DSTM32H750xx + -DSDRAM -DCCMRAM + -DCOLORLCD -DLIBOPENUI + -DHARDWARE_TOUCH -DHARDWARE_KEYS -DSOFTWARE_KEYBOARD + -DWS2812_MAX_LEDS=26 +) + +set(SDRAM ON) + +add_definitions(-DAUDIO -DVOICE -DRTCLOCK) +add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) +add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) + +if(STICKS_DEAD_ZONE) + add_definitions(-DSTICK_DEAD_ZONE) +endif() + +if(NOT UNEXPECTED_SHUTDOWN) + add_definitions(-DNO_UNEXPECTED_SHUTDOWN) +endif() + +if(DISK_CACHE) + set(SRC ${SRC} disk_cache.cpp) + add_definitions(-DDISK_CACHE) +endif() + +if(FUNCTION_SWITCHES_WITH_RGB) + set(FUNCTION_SWITCHES YES) + add_definitions(-DFUNCTION_SWITCHES) + add_definitions(-DFUNCTION_SWITCHES_RGB_LEDS) +endif() + +if(FUNCTION_SWITCHES) +add_definitions(-DFUNCTION_SWITCHES) +endif() + +if(INTERNAL_GPS) + message("-- Internal GPS enabled") + add_definitions(-DINTERNAL_GPS) + add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) + set(SRC ${SRC} gps.cpp gps_nmea.cpp gps_ubx.cpp) +endif() + +set(AFHDS3 ON) + +# VCP CLI +set(ENABLE_SERIAL_PASSTHROUGH ON CACHE BOOL "Enable serial passthrough") +set(CLI ON CACHE BOOL "Enable CLI") + +set(BOARD rm-h750) + +include_directories(boards/${BOARD}) + +set(TARGET_SRC_DIR targets/${TARGET_DIR}) +set(BOARD_SRC_DIR boards/${BOARD}) + +set(BOARD_COMMON_SRC + ${BOARD_SRC_DIR}/board.cpp + ${BOARD_SRC_DIR}/bsp_io.cpp + ${TARGET_SRC_DIR}/key_driver.cpp + ${BOARD_SRC_DIR}/lcd_driver_800.cpp + ${BOARD_SRC_DIR}/backlight_driver.cpp + ${BOARD_SRC_DIR}/haptic_driver.cpp + ${BOARD_SRC_DIR}/extflash_driver.cpp + ${BOARD_SRC_DIR}/usb_charger_driver.cpp + targets/common/arm/stm32/abnormal_reboot.cpp + targets/common/arm/stm32/delays_driver.cpp + targets/common/arm/stm32/dma2d.cpp + targets/common/arm/stm32/flash_driver.cpp + targets/common/arm/stm32/pwr_driver.cpp + targets/common/arm/stm32/rotary_encoder_driver.cpp + targets/common/arm/stm32/rtc_driver.cpp + targets/common/arm/stm32/watchdog_driver.cpp + drivers/pca95xx.cpp +) + +# Bootloader board library +add_library(board_bl OBJECT EXCLUDE_FROM_ALL + ${BOARD_COMMON_SRC} + ${BOARD_SRC_DIR}/sdram_driver.cpp + ${RADIO_SRC_DIR}/gui/colorlcd/boot_menu.cpp + targets/common/arm/stm32/stm32_qspi.cpp + targets/common/arm/stm32/stm32_pulse_driver.cpp +) +add_dependencies(board_bl minimal_board_lib) +set(BOOTLOADER_SRC ${BOOTLOADER_SRC} $) + +set(CMSIS_SRC ${BOARD_SRC_DIR}/system_clock.c) + +# Firmware board library +add_library(board OBJECT EXCLUDE_FROM_ALL + ${BOARD_COMMON_SRC} + ${BOARD_SRC_DIR}/tp_gt911.cpp + ${BOARD_SRC_DIR}/audio_driver.cpp + ${BOARD_SRC_DIR}/luminosity_sensor.cpp + drivers/icm42607C.cpp + drivers/imu_filter.cpp + drivers/tas2505.cpp + targets/common/arm/stm32/heartbeat_driver.cpp + targets/common/arm/stm32/mixer_scheduler_driver.cpp + targets/common/arm/stm32/module_timer_driver.cpp + targets/common/arm/stm32/stm32_pulse_driver.cpp + targets/common/arm/stm32/stm32_softserial_driver.cpp + targets/common/arm/stm32/stm32_qspi.cpp + targets/common/arm/stm32/stm32_i2s.cpp + targets/common/arm/stm32/stm32_switch_driver.cpp + targets/common/arm/stm32/stm32_ws2812.cpp + targets/common/arm/stm32/trainer_driver.cpp +) +add_dependencies(board board_lib) +set(FIRMWARE_SRC ${FIRMWARE_SRC} $) + +include_directories(${RADIO_SRC_DIR}/fonts/colorlcd gui/${GUI_DIR} gui/${GUI_DIR}/layouts) + +if(BOOTLOADER) + set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ../../bootloader/loadboot.cpp + ) +endif() + +set(SRC + ${SRC} + io/frsky_firmware_update.cpp + io/multi_firmware_update.cpp + ) + +if (MULTIMODULE) + add_definitions(-DMULTI_PROTOLIST) + set(SRC ${SRC} + io/multi_protolist.cpp + ) +endif() + +# Make malloc() thread-safe +add_definitions(-DTHREADSAFE_MALLOC) diff --git a/radio/src/targets/tx16smk3/bsp_io.h b/radio/src/targets/tx16smk3/bsp_io.h new file mode 100644 index 000000000..c30235f28 --- /dev/null +++ b/radio/src/targets/tx16smk3/bsp_io.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "drivers/pca95xx.h" +#include "hal/switch_driver.h" + +#define IO_EXPANDER_I2C_BUS I2C_Bus_1 +#define IO_EXPANDER1_I2C_ADDR 0x74 +#define IO_EXPANDER2_I2C_ADDR 0x75 + +// Port expander 2 (0x74) +#define BSP_TR6U PCA95XX_PIN_7 +#define BSP_TR6D PCA95XX_PIN_6 +#define BSP_TR4R PCA95XX_PIN_8 +#define BSP_TR4L PCA95XX_PIN_9 +#define BSP_TR2D PCA95XX_PIN_10 +#define BSP_TR2U PCA95XX_PIN_11 +#define BSP_TR1D PCA95XX_PIN_12 +#define BSP_TR1U PCA95XX_PIN_13 +#define BSP_TR3R PCA95XX_PIN_14 +#define BSP_TR3L PCA95XX_PIN_15 + +extern uint8_t isSwitch3Pos(uint8_t idx); + +int bsp_io_init(); +uint32_t bsp_io_read_switches(); +uint32_t bsp_io_read_fs_switches(); +uint32_t bsp_get_fs_switches(); + +uint16_t bsp_input_get(); +struct stm32_switch_t; + +//TODO compute from json +constexpr uint32_t IO_EXPANDER1_MASK = 0xFFFF; +constexpr uint32_t IO_EXPANDER2_MASK = 0xFFFF; diff --git a/radio/src/targets/tx16smk3/hal.h b/radio/src/targets/tx16smk3/hal.h new file mode 100644 index 000000000..9f0797d3e --- /dev/null +++ b/radio/src/targets/tx16smk3/hal.h @@ -0,0 +1,624 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/* + +STM32H750 + +SDMMC uses own DMA controler + +DMA1 +Stream0: LED_STRIP_TIMER_DMA_STREAM +Stream1: INTMODULE_DMA_STREAM +Stream2: FLYSKY_HALL_DMA_Stream_RX +Stream3: TELEMETRY_DMA_Stream_RX +Stream4: I2S_DMA_Stream +Stream5: INTMODULE_RX_DMA_STREAM +Stream6: AUX2_SERIAL_DMA_RX_STREAM +Stream7: TELEMETRY_DMA_Stream_TX + +DMA2 +Stream0: ADC_EXT_DMA_STREAM (do not move) +Stream1: AUX_SERIAL_DMA_TX_STREAM +Stream2: AUX_SERIAL_DMA_RX_STREAM +Stream3: EXTMODULE_TIMER_DMA_STREAM +Stream4: ADC_DMA_STREAM (do not move) +Stream5: EXTMODULE_USART_RX_DMA_STREAM +Stream6: EXTMODULE_USART_TX_DMA_STREAM +Stream7: AUX2_SERIAL_DMA_TX_STREAM + +TIM1: BACKLIGHT_TIMER +TIM2: LED_STRIP_TIMER +TIM3: HAPTIC_GPIO_TIMER +TIM4: +TIM5: EXTMODULE_TIMER +TIM7: +TIM8: TRAINER_TIMER +TIM12: MIXER_SCHEDULER_TIMER +TIM14: MS_TIMER +TIM15: PDM +TIM16: +TIM17: ROTARY_ENCODER_TIMER + +USART1: TELEMETRY_USART +USART2: EXTMODULE_USART +USART3: AUX2_SERIAL_USART +USART4: +USART5: AUX_SERIAL_USART +USART6: INTMODULE_USART + */ + +#ifndef _HAL_H_ +#define _HAL_H_ + +#define CPU_FREQ 480000000 + +#define PERI1_FREQUENCY 120000000 +#define PERI2_FREQUENCY 120000000 +#define TIMER_MULT_APB1 2 +#define TIMER_MULT_APB2 2 + +// Keys +#define KEYS_GPIO_REG_PAGEDN GPIOA +#define KEYS_GPIO_PIN_PAGEDN LL_GPIO_PIN_8 // PA.08 +#define KEYS_GPIO_REG_PAGEUP GPIOG +#define KEYS_GPIO_PIN_PAGEUP LL_GPIO_PIN_7 // PG.07 +#define KEYS_GPIO_REG_SYS GPIOB +#define KEYS_GPIO_PIN_SYS LL_GPIO_PIN_2 // PB.02 +#define KEYS_GPIO_REG_ENTER GPIOG +#define KEYS_GPIO_PIN_ENTER LL_GPIO_PIN_12 // PG.12 +#define KEYS_GPIO_REG_MDL GPIOE +#define KEYS_GPIO_PIN_MDL LL_GPIO_PIN_3 // PE.03 +#define KEYS_GPIO_REG_EXIT GPIOG +#define KEYS_GPIO_PIN_EXIT LL_GPIO_PIN_3 // PG.03 +#define KEYS_GPIO_REG_TELE GPIOI +#define KEYS_GPIO_PIN_TELE LL_GPIO_PIN_15 // PI.15 + +// Trims +#define TRIMS_GPIO_REG_LHL +#define TRIMS_GPIO_PIN_LHL + +#define TRIMS_GPIO_REG_LHR +#define TRIMS_GPIO_PIN_LHR + +#define TRIMS_GPIO_REG_LVD +#define TRIMS_GPIO_PIN_LVD + +#define TRIMS_GPIO_REG_LVU +#define TRIMS_GPIO_PIN_LVU + +#define TRIMS_GPIO_REG_RHL +#define TRIMS_GPIO_PIN_RHL + +#define TRIMS_GPIO_REG_RHR +#define TRIMS_GPIO_PIN_RHR + +#define TRIMS_GPIO_REG_RVD +#define TRIMS_GPIO_PIN_RVD + +#define TRIMS_GPIO_REG_RVU +#define TRIMS_GPIO_PIN_RVU + +#define TRIMS_GPIO_REG_RSD +#define TRIMS_GPIO_PIN_RSD + +#define TRIMS_GPIO_REG_RSU +#define TRIMS_GPIO_PIN_RSU + +#define TRIMS_GPIO_REG_LSD GPIOH +#define TRIMS_GPIO_PIN_LSD LL_GPIO_PIN_15 + +#define TRIMS_GPIO_REG_LSU GPIOH +#define TRIMS_GPIO_PIN_LSU LL_GPIO_PIN_13 + +// Extender Switches +#define SWITCHES_A_3POS +#define SWITCHES_GPIO_REG_A_H +#define SWITCHES_GPIO_PIN_A_H PCA95XX_P15 +#define SWITCHES_GPIO_REG_A_L +#define SWITCHES_GPIO_PIN_A_L PCA95XX_P14 + +#define SWITCHES_B_3POS +#define SWITCHES_GPIO_REG_B_H +#define SWITCHES_GPIO_PIN_B_H PCA95XX_P13 +#define SWITCHES_GPIO_REG_B_L +#define SWITCHES_GPIO_PIN_B_L PCA95XX_P12 + +#define SWITCHES_C_3POS +#define SWITCHES_GPIO_REG_C_H +#define SWITCHES_GPIO_PIN_C_H PCA95XX_P11 +#define SWITCHES_GPIO_REG_C_L +#define SWITCHES_GPIO_PIN_C_L PCA95XX_P10 + +#define SWITCHES_D_3POS +#define SWITCHES_GPIO_REG_D_H +#define SWITCHES_GPIO_PIN_D_H PCA95XX_P7 +#define SWITCHES_GPIO_REG_D_L +#define SWITCHES_GPIO_PIN_D_L PCA95XX_P6 + +#define SWITCHES_E_3POS +#define SWITCHES_GPIO_REG_E_H +#define SWITCHES_GPIO_PIN_E_H PCA95XX_P16 +#define SWITCHES_GPIO_REG_E_L +#define SWITCHES_GPIO_PIN_E_L PCA95XX_P17 + +#define SWITCHES_F_2POS +#define SWITCHES_GPIO_REG_F +#define SWITCHES_GPIO_PIN_F PCA95XX_P1 + +#define SWITCHES_G_3POS +#define SWITCHES_GPIO_REG_G_H +#define SWITCHES_GPIO_PIN_G_H PCA95XX_P4 +#define SWITCHES_GPIO_REG_G_L +#define SWITCHES_GPIO_PIN_G_L PCA95XX_P5 + +#define SWITCHES_H_2POS +#define SWITCHES_GPIO_REG_H +#define SWITCHES_GPIO_PIN_H PCA95XX_P0 + +#define SWITCHES_I_2POS +#define SWITCHES_GPIO_REG_I +#define SWITCHES_GPIO_PIN_I PCA95XX_P2 + +#define SWITCHES_J_2POS +#define SWITCHES_GPIO_REG_J +#define SWITCHES_GPIO_PIN_J PCA95XX_P3 + +// function switches +//SW1 +#define SWITCHES_GPIO_REG_K +#define SWITCHES_GPIO_PIN_K PCA95XX_P0 +#define SWITCHES_K_CFS_IDX 0 +#define FUNCTION_SWITCH_1 SK +//SW2 +#define SWITCHES_GPIO_REG_L +#define SWITCHES_GPIO_PIN_L PCA95XX_P1 +#define SWITCHES_L_CFS_IDX 1 +#define FUNCTION_SWITCH_2 SL +//SW3 +#define SWITCHES_GPIO_REG_M +#define SWITCHES_GPIO_PIN_M PCA95XX_P2 +#define SWITCHES_M_CFS_IDX 2 +#define FUNCTION_SWITCH_3 SM +//SW4 +#define SWITCHES_GPIO_REG_N +#define SWITCHES_GPIO_PIN_N PCA95XX_P3 +#define SWITCHES_N_CFS_IDX 3 +#define FUNCTION_SWITCH_4 SN +//SW5 +#define SWITCHES_GPIO_REG_O +#define SWITCHES_GPIO_PIN_O PCA95XX_P4 +#define SWITCHES_O_CFS_IDX 4 +#define FUNCTION_SWITCH_5 SO +//SW6 +#define SWITCHES_GPIO_REG_P +#define SWITCHES_GPIO_PIN_P PCA95XX_P5 +#define SWITCHES_P_CFS_IDX 5 +#define FUNCTION_SWITCH_6 SP + +// Expanders +#define IO_INT_GPIO GPIO_PIN(GPIOD, 3) +#define IO_RESET_GPIO GPIO_PIN(GPIOG, 10) +#if !defined(USE_EXTI3_IRQ) +#define USE_EXTI3_IRQ +#define EXTI3_IRQ_Priority 9 +#endif + +// ADC +#define ADC_GPIO_PIN_STICK_LH LL_GPIO_PIN_6 // PA.06 +#define ADC_GPIO_PIN_STICK_LV LL_GPIO_PIN_3 // PC.03 +#define ADC_GPIO_PIN_STICK_RV LL_GPIO_PIN_4 // PC.04 +#define ADC_GPIO_PIN_STICK_RH LL_GPIO_PIN_5 // PC.05 + +#define ADC_GPIO_PIN_POT1 LL_GPIO_PIN_1 // PC.01 POT1 +#define ADC_GPIO_PIN_POT2 LL_GPIO_PIN_1 // PB.01 POT2 +#define ADC_GPIO_PIN_BATT LL_GPIO_PIN_3 // PH.03 + +#define ADC_GPIO_PIN_SLIDER1 LL_GPIO_PIN_2 // PH.02 VRC/LS +#define ADC_GPIO_PIN_SLIDER2 LL_GPIO_PIN_7 // PA.07 VRD/RS + +#define ADC_GPIO_PIN_LUX LL_GPIO_PIN_0 // PB.00 Light Sensor + +#define ADC_GPIO_PIN_EXT1 LL_GPIO_PIN_5 // PA.05,ADC1_CH19 +#define ADC_GPIO_PIN_EXT2 LL_GPIO_PIN_5 // PH.05,ADC3_CH16 + +#define ADC_GPIOA_PINS (ADC_GPIO_PIN_STICK_LH | ADC_GPIO_PIN_SLIDER2|ADC_GPIO_PIN_EXT1) +#define ADC_GPIOB_PINS (ADC_GPIO_PIN_POT2 | ADC_GPIO_PIN_LUX) +#define ADC_GPIOC_PINS (ADC_GPIO_PIN_STICK_RV | ADC_GPIO_PIN_STICK_LV | ADC_GPIO_PIN_STICK_RH | ADC_GPIO_PIN_POT1) +#define ADC_GPIOH_PINS (ADC_GPIO_PIN_BATT | ADC_GPIO_PIN_SLIDER1 | ADC_GPIO_PIN_EXT2) + + +#define ADC_CHANNEL_STICK_LH LL_ADC_CHANNEL_3 // ADC12_INP3 +#define ADC_CHANNEL_STICK_LV LL_ADC_CHANNEL_13 // ADC12_INP13 +#define ADC_CHANNEL_STICK_RV LL_ADC_CHANNEL_4 // ADC12_INP4 +#define ADC_CHANNEL_STICK_RH LL_ADC_CHANNEL_8 // ADC12_INP8 + +// Each ADC cannot map more than 8 channels, otherwise it will cause problems +#define ADC_CHANNEL_POT1 LL_ADC_CHANNEL_11 // ADC12_INP11 +#define ADC_CHANNEL_POT2 LL_ADC_CHANNEL_5 // ADC12_INP5 +#define ADC_CHANNEL_BATT LL_ADC_CHANNEL_14 // ADC3_INP14 +#define ADC_CHANNEL_RTC_BAT LL_ADC_CHANNEL_VBAT // ADC3_INP17 +#define ADC_CHANNEL_SLIDER1 LL_ADC_CHANNEL_13 // ADC3_INP13 +#define ADC_CHANNEL_SLIDER2 LL_ADC_CHANNEL_7 // ADC12_INP7 + +#define ADC_CHANNEL_LUX LL_ADC_CHANNEL_9 // ADC12_INP9 -> Light Sensor + +#define ADC_CHANNEL_EXT1 LL_ADC_CHANNEL_19 // ADC1_IN19 +#define ADC_CHANNEL_EXT2 LL_ADC_CHANNEL_16 // ADC3_IN16 + +#define ADC_MAIN ADC1 +#define ADC_DMA DMA2 +#define ADC_DMA_CHANNEL LL_DMAMUX1_REQ_ADC1 +#define ADC_DMA_STREAM LL_DMA_STREAM_4 +#define ADC_DMA_STREAM_IRQ DMA2_Stream4_IRQn +#define ADC_DMA_STREAM_IRQHandler DMA2_Stream4_IRQHandler +#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_8CYCLES_5 + +#define ADC_EXT ADC3 +#define ADC_EXT_CHANNELS { ADC_CHANNEL_SLIDER1, ADC_CHANNEL_BATT, ADC_CHANNEL_RTC_BAT, ADC_CHANNEL_EXT2 } +#define ADC_EXT_DMA DMA2 +#define ADC_EXT_DMA_CHANNEL LL_DMAMUX1_REQ_ADC3 +#define ADC_EXT_DMA_STREAM LL_DMA_STREAM_0 +#define ADC_EXT_DMA_STREAM_IRQ DMA2_Stream0_IRQn +#define ADC_EXT_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler +#define ADC_EXT_SAMPTIME LL_ADC_SAMPLINGTIME_8CYCLES_5 + +#define ADC_VREF_PREC2 330 + +#define ADC_DIRECTION { \ + 0,-1,0,-1, /* gimbals */ \ + -1,-1, /* pots */ \ + 0,-1, /* sliders */ \ + 0,0, /* ext */ \ + 0, /* vbat */ \ + 0, /* rtc_bat */ \ + 0 /* lux sensor */ \ + } + +// Serial gimbal sync port +#define HALL_SYNC GPIO_PIN(GPIOH, 11) + +#define USE_EXTI9_5_IRQ // used for I2C port extender interrupt +#define EXTI9_5_IRQ_Priority 5 + +// Power +#define PWR_SWITCH_GPIO GPIO_PIN(GPIOA, 4) +#define PWR_ON_GPIO GPIO_PIN(GPIOH, 12) + +// Chargers (USB and wireless) +#define UCHARGER_GPIO GPIO_PIN(GPIOD, 11) +#define UCHARGER_EN_GPIO GPIO_PIN(GPIOB, 12) + +// TODO! Check IOLL1 to PI.01 connectivity! + +// S.Port update connector +#define HAS_SPORT_UPDATE_CONNECTOR() (false) + +// Telemetry + #define TELEMETRY_SET_INPUT 0 + #define TELEMETRY_TX_GPIO GPIO_PIN(GPIOA, 9) + #define TELEMETRY_RX_GPIO GPIO_UNDEF + #define TELEMETRY_USART USART1 + #define TELEMETRY_USART_IRQn USART1_IRQn + #define TELEMETRY_DMA DMA1 + #define TELEMETRY_DMA_Stream_TX LL_DMA_STREAM_7 + #define TELEMETRY_DMA_Channel_TX LL_DMAMUX1_REQ_USART1_TX + #define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream7_IRQn + #define TELEMETRY_DMA_TX_IRQHandler DMA1_Stream7_IRQHandler + #define TELEMETRY_DMA_Stream_RX LL_DMA_STREAM_3 + #define TELEMETRY_DMA_Channel_RX LL_DMAMUX1_REQ_USART1_RX + #define TELEMETRY_USART_IRQHandler USART1_IRQHandler + +// Software IRQ (Prio 5 -> FreeRTOS compatible) +#define TELEMETRY_USE_CUSTOM_EXTI +#define CUSTOM_EXTI_IRQ_NAME ETH_WKUP_IRQ +#define ETH_WKUP_IRQ_Priority 5 +#define CUSTOM_EXTI_IRQ_LINE 86 +#define TELEMETRY_RX_FRAME_EXTI_LINE CUSTOM_EXTI_IRQ_LINE + +// USB +#define USB_GPIO GPIOA +#define USB_GPIO_VBUS GPIO_PIN(GPIOH, 11) +#define USB_GPIO_DM GPIO_PIN(GPIOA, 11) +#define USB_GPIO_DP GPIO_PIN(GPIOA, 12) +#define USB_GPIO_AF GPIO_AF10 + +// LCD + +#define LTDC_IRQ_PRIO 4 +#define DMA_SCREEN_IRQ_PRIO 6 + +#define LCD_RESET_GPIO GPIOJ +#define LCD_RESET_GPIO_PIN LL_GPIO_PIN_12 + +// Backlight +#define BACKLIGHT_GPIO GPIO_PIN(GPIOA, 10) // TIM1_CH3 +#define BACKLIGHT_TIMER TIM1 +#define BACKLIGHT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define BACKLIGHT_GPIO_AF GPIO_AF1 +#define BACKLIGHT_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +// QSPI Flash +#define QSPI_CLK_GPIO GPIO_PIN(GPIOF, 10) +#define QSPI_CLK_GPIO_AF GPIO_AF9 +#define QSPI_CS_GPIO GPIO_PIN(GPIOG, 6) +#define QSPI_CS_GPIO_AF GPIO_AF10 +#define QSPI_MISO_GPIO GPIO_PIN(GPIOF, 9) +#define QSPI_MISO_GPIO_AF GPIO_AF10 +#define QSPI_MOSI_GPIO GPIO_PIN(GPIOF, 8) +#define QSPI_MOSI_GPIO_AF GPIO_AF10 +#define QSPI_WP_GPIO GPIO_PIN(GPIOF, 7) +#define QSPI_WP_GPIO_AF GPIO_AF9 +#define QSPI_HOLD_GPIO GPIO_PIN(GPIOF, 6) +#define QSPI_HOLD_GPIO_AF GPIO_AF9 +#define QSPI_FLASH_SIZE 0x1000000 + + +// SD +#define SD_SDIO SDMMC1 +#define SD_SDIO_CLK_DIV(fq) (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) / (2 * fq)) +#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz + +#define SD2_PRESENT_GPIO GPIO_PIN(GPIOH, 8) // PH.08 +#define SD2_SDIO SDMMC2 +#define SD2_SDIO_PIN_D0 GPIO_PIN(GPIOB, 14) +#define SD2_SDIO_AF_D0 GPIO_AF9 +#define SD2_SDIO_PIN_D1 GPIO_PIN(GPIOB, 15) +#define SD2_SDIO_AF_D1 GPIO_AF9 +#define SD2_SDIO_PIN_D2 GPIO_PIN(GPIOG, 11) +#define SD2_SDIO_AF_D2 GPIO_AF10 +#define SD2_SDIO_PIN_D3 GPIO_PIN(GPIOB, 4) +#define SD2_SDIO_AF_D3 GPIO_AF9 +#define SD2_SDIO_PIN_CLK GPIO_PIN(GPIOD, 6) +#define SD2_SDIO_AF_CLK GPIO_AF11 +#define SD2_SDIO_PIN_CMD GPIO_PIN(GPIOD, 7) +#define SD2_SDIO_AF_CMD GPIO_AF11 +#define SD2_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz + +#define STORAGE_USE_SDIO + + +// AUDIO +#define AUDIO_I2C I2C_Bus_2 +#define AUDIO_SPI SPI2 +#define AUDIO_RESET_PIN GPIO_PIN(GPIOH, 10) +#define AUDIO_HP_DETECT_PIN GPIO_PIN(GPIOH, 14) +#define I2S_DMA DMA1 +#define I2S_DMA_Stream LL_DMA_STREAM_4 +#define I2S_DMA_Stream_Request LL_DMAMUX1_REQ_SPI2_TX +#define I2S_DMA_Stream_IRQn DMA1_Stream4_IRQn +#define I2S_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + +// I2C Bus +#define I2C_B1 I2C4 +#define I2C_B1_SDA_GPIO GPIO_PIN(GPIOD, 13) // PD.13 +#define I2C_B1_SCL_GPIO GPIO_PIN(GPIOD, 12) // PD.12 +#define I2C_B1_GPIO_AF LL_GPIO_AF_4 +#define I2C_B1_CLK_RATE 400000 + +#define I2C_B2 I2C1 +#define I2C_B2_SDA_GPIO GPIO_PIN(GPIOB, 9) // PB.09 +#define I2C_B2_SCL_GPIO GPIO_PIN(GPIOB, 8) // PB.08 +#define I2C_B2_GPIO_AF LL_GPIO_AF_4 +#define I2C_B2_CLK_RATE 400000 + +// Haptic: TIM3_CH2 +#define HAPTIC_PWM +#define HAPTIC_GPIO GPIO_PIN(GPIOC, 7) +#define HAPTIC_GPIO_TIMER TIM3 +#define HAPTIC_GPIO_AF GPIO_AF2 +#define HAPTIC_TIMER_OUTPUT_ENABLE TIM_CCER_CC2E | TIM_CCER_CC2NE; +#define HAPTIC_TIMER_MODE TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE +#define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR2 + +// LED Strip +#define LED_STRIP_LENGTH 26 // 6POS + 2 rings of 10 +#define BLING_LED_STRIP_START 6 +#define BLING_LED_STRIP_LENGTH 20 +#define CFS_LED_STRIP_START 0 +#define CFS_LED_STRIP_LENGTH 6 +#define CFS_LEDS_PER_SWITCH 1 +#define LED_STRIP_GPIO GPIO_PIN(GPIOA, 0) // PA.00 / TIM2_CH1 +#define LED_STRIP_GPIO_AF LL_GPIO_AF_1 // TIM1/2/16/17 +#define LED_STRIP_TIMER TIM2 +#define LED_STRIP_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define LED_STRIP_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 +#define LED_STRIP_TIMER_DMA DMA1 +#define LED_STRIP_TIMER_DMA_CHANNEL LL_DMAMUX1_REQ_TIM2_UP +#define LED_STRIP_TIMER_DMA_STREAM LL_DMA_STREAM_0 +#define LED_STRIP_TIMER_DMA_IRQn DMA1_Stream0_IRQn +#define LED_STRIP_TIMER_DMA_IRQHandler DMA1_Stream0_IRQHandler +#define LED_STRIP_REFRESH_PERIOD 50 //ms + +#define STATUS_LEDS +#define GPIO_LED_GPIO_ON gpio_set +#define GPIO_LED_GPIO_OFF gpio_clear +#define LED_RED_GPIO GPIO_PIN(GPIOI, 8) // PI.08 +#define LED_GREEN_GPIO GPIO_PIN(GPIOI, 11) // PI.11 +#define LED_BLUE_GPIO GPIO_PIN(GPIOI, 10) // PI.10 + +// Internal Module +#define INTMODULE_PWR_GPIO GPIO_PIN(GPIOB, 13) // PB.13 +#define INTMODULE_BOOTCMD_GPIO GPIO_PIN(GPIOH, 9) // PH.09 +#define INTMODULE_BOOTCMD_DEFAULT 0 +#define INTMODULE_TX_GPIO GPIO_PIN(GPIOG, 14) +#define INTMODULE_RX_GPIO GPIO_PIN(GPIOG, 9) +#define INTMODULE_USART USART6 +#define INTMODULE_GPIO_AF LL_GPIO_AF_7 +#define INTMODULE_USART_IRQn USART6_IRQn +#define INTMODULE_USART_IRQHandler USART6_IRQHandler +#define INTMODULE_DMA DMA1 +#define INTMODULE_DMA_STREAM LL_DMA_STREAM_1 +#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn +#define INTMODULE_DMA_FLAG_TC DMA_FLAG_TCIF1 +#define INTMODULE_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_TX +#define INTMODULE_RX_DMA DMA1 +#define INTMODULE_RX_DMA_STREAM LL_DMA_STREAM_5 +#define INTMODULE_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_RX +#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream5_IRQn +#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream5_IRQHandler + +// External Module +#define EXTMODULE +#define EXTMODULE_PULSES +#define EXTMODULE_PWR_GPIO GPIO_PIN(GPIOD, 4) // PD.04 +#define EXTMODULE_TX_GPIO GPIO_PIN(GPIOA, 2) // TIM2_CH3, TIM5_CH3,TIM15_CH1, +#define EXTMODULE_RX_GPIO GPIO_PIN(GPIOA, 3) +#define EXTMODULE_TX_GPIO_AF LL_GPIO_AF_3 // TIM5_CH3 +#define EXTMODULE_TIMER TIM5 +#define EXTMODULE_TIMER_32BITS +#define EXTMODULE_TIMER_Channel LL_TIM_CHANNEL_CH3 +#define EXTMODULE_TIMER_IRQn TIM5_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM5_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF LL_GPIO_AF_2 + + +//USART +#define EXTMODULE_USART USART2 +#define EXTMODULE_USART_TX_DMA DMA2 +#define EXTMODULE_USART_TX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_TX +#define EXTMODULE_USART_TX_DMA_STREAM LL_DMA_STREAM_6 +#define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_RX +#define EXTMODULE_USART_RX_DMA_STREAM LL_DMA_STREAM_5 +#define EXTMODULE_USART_IRQHandler USART2_IRQHandler +#define EXTMODULE_USART_IRQn USART2_IRQn + +//TIMER +#define EXTMODULE_TIMER_DMA_CHANNEL LL_DMAMUX1_REQ_TIM5_UP +#define EXTMODULE_TIMER_DMA DMA2 +#define EXTMODULE_TIMER_DMA_STREAM LL_DMA_STREAM_6 +#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream6_IRQn +#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream6_IRQHandler + +// Trainer Port +#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA) + +#define TRAINER_IN_GPIO GPIO_PIN(GPIOI, 6) +#define TRAINER_IN_TIMER_Channel LL_TIM_CHANNEL_CH2 + +#define TRAINER_OUT_GPIO GPIO_PIN(GPIOI, 5) +#define TRAINER_OUT_TIMER_Channel LL_TIM_CHANNEL_CH1 + +#define TRAINER_TIMER TIM8 +#define TRAINER_TIMER_IRQn TIM8_CC_IRQn +#define TRAINER_TIMER_IRQHandler TIM8_CC_IRQHandler +#define TRAINER_GPIO_AF LL_GPIO_AF_3 +#define TRAINER_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) + +#define TRAINER_DETECT_GPIO GPIO_PIN(GPIOH, 4) + +// AUX ports +#define AUX_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 6) // PB.06 +#define AUX_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 5) // PB.05 +#define AUX_SERIAL_USART UART5 +#define AUX_SERIAL_USART_IRQHandler UART5_IRQHandler +#define AUX_SERIAL_USART_IRQn UART5_IRQn +#define AUX_SERIAL_DMA_TX DMA2 +#define AUX_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_1 +#define AUX_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_UART5_TX +#define AUX_SERIAL_DMA_RX DMA2 +#define AUX_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_2 +#define AUX_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_UART5_RX +#define AUX_SERIAL_PWR_GPIO GPIO_PIN(GPIOB, 7) // PB.07 + +#define AUX2_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 10) // PB.10 +#define AUX2_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 11) // PB.11 +#define AUX2_SERIAL_USART USART3 +#define AUX2_SERIAL_USART_IRQHandler USART3_IRQHandler +#define AUX2_SERIAL_USART_IRQn USART3_IRQn +#define AUX2_SERIAL_DMA_TX DMA2 +#define AUX2_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_7 +#define AUX2_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_USART3_TX +#define AUX2_SERIAL_DMA_RX DMA1 +#define AUX2_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_6 +#define AUX2_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_USART3_RX +#define AUX2_SERIAL_PWR_GPIO GPIO_PIN(GPIOC, 13) // PC.13 + +// Touch +#define TOUCH_I2C_BUS I2C_Bus_1 +#define TOUCH_INT_GPIO GPIO_PIN(GPIOE, 2) // PE.02 +#define TOUCH_RST_GPIO GPIO_PIN(GPIOJ, 13) // PJ.13 + +// TOUCH_INT_EXTI IRQ +#if !defined(USE_EXTI2_IRQ) +#define USE_EXTI2_IRQ +#define EXTI2_IRQ_Priority 9 +#endif + +#define IMU_I2C_BUS I2C_Bus_2 +#define IMU_I2C_ADDRESS 0x6A +#define IMU_INT_GPIO GPIO_PIN(GPIOG, 13) // PG.13 +#if !defined(USE_EXTI15_10_IRQ) + #define USE_EXTI15_10_IRQ + #define EXTI15_10_IRQ_Priority 6 +#endif + +//ROTARY emulation for trims as buttons +#define ROTARY_ENCODER_NAVIGATION +// Rotary Encoder +#define ROTARY_ENCODER_INVERTED +#define ROTARY_ENCODER_GPIO_A GPIOI +#define ROTARY_ENCODER_GPIO_PIN_A LL_GPIO_PIN_7 +#define ROTARY_ENCODER_GPIO_B GPIOJ +#define ROTARY_ENCODER_GPIO_PIN_B LL_GPIO_PIN_8 +#define ROTARY_ENCODER_POSITION() (((ROTARY_ENCODER_GPIO_A->IDR >> 7) & 0x01)|((ROTARY_ENCODER_GPIO_B->IDR >> 7) & 0x02)) +#define ROTARY_ENCODER_EXTI_LINE1 LL_EXTI_LINE_7 +#define ROTARY_ENCODER_EXTI_LINE2 LL_EXTI_LINE_8 +#if !defined(USE_EXTI7_IRQ) + #define USE_EXTI7_IRQ + #define EXTI7_IRQ_Priority 5 +#endif +#if !defined(USE_EXTI8_IRQ) + #define USE_EXTI8_IRQ + #define EXTI8_IRQ_Priority 5 +#endif +#define ROTARY_ENCODER_EXTI_PORT_A LL_SYSCFG_EXTI_PORTI +#define ROTARY_ENCODER_EXTI_PORT_B LL_SYSCFG_EXTI_PORTJ +#define ROTARY_ENCODER_EXTI_SYS_LINE1 LL_SYSCFG_EXTI_LINE7 +#define ROTARY_ENCODER_EXTI_SYS_LINE2 LL_SYSCFG_EXTI_LINE8 +#define ROTARY_ENCODER_TIMER TIM17 +#define ROTARY_ENCODER_TIMER_IRQn TIM17_IRQn +#define ROTARY_ENCODER_TIMER_IRQHandler TIM17_IRQHandler + +// Millisecond timer +#define MS_TIMER TIM14 +#define MS_TIMER_IRQn TIM8_TRG_COM_TIM14_IRQn +#define MS_TIMER_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler + +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER TIM12 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler + +#define LCD_W 800 +#define LCD_H 480 + +#define LCD_PHYS_W LCD_W +#define LCD_PHYS_H LCD_H + +#define LCD_DEPTH 16 + +#define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH + +#endif // _HAL_H_ diff --git a/radio/src/targets/tx16smk3/key_driver.cpp b/radio/src/targets/tx16smk3/key_driver.cpp new file mode 100644 index 000000000..2684f5465 --- /dev/null +++ b/radio/src/targets/tx16smk3/key_driver.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/key_driver.h" + +#include "bsp_io.h" +#include "stm32_hal_ll.h" +#include "stm32_gpio_driver.h" +#include "stm32_i2c_driver.h" + +#include "hal.h" +#include "delays_driver.h" +#include "keys.h" + +#include "stm32_keys.inc" +#include "debug.h" + +void keysInit() { + _init_keys(); + _init_trims(); +} + +uint32_t readKeys() { return _read_keys(); } + +/* The output bit-order has to be: + 0 LHL TR3L (Left equals down) + 1 LHR TR3R + 2 LVD TR1D + 3 LVU TR1U + 4 RVD TR2D + 5 RVU TR2U + 6 RHL TR4L + 7 RHR TR4R +*/ + +enum PhysicalTrims { + TR3L = 0, + TR3R, + TR1D = 2, + TR1U, + TR2D = 4, + TR2U, + TR4L = 6, + TR4R, + TR5U = 8, + TR5D, + TR6U = 10, + TR6D +}; + +uint32_t readTrims() +{ + uint32_t result = 0; + uint32_t keys = bsp_get_fs_switches(); + +#define _TRIM(t) \ + if ((keys & BSP_##t) == 0) result |= 1 << t; + + _TRIM(TR1U); + _TRIM(TR1D); + _TRIM(TR3L); + _TRIM(TR3R); + _TRIM(TR2U); + _TRIM(TR2D); + _TRIM(TR4L); + _TRIM(TR4R); + if (!LL_GPIO_IsInputPinSet(GPIOH, LL_GPIO_PIN_15)) result |= (1 << TR5U); + if (!LL_GPIO_IsInputPinSet(GPIOH, LL_GPIO_PIN_13)) result |= (1 << TR5D); + _TRIM(TR6U); + _TRIM(TR6D); + +#undef _TRIM + + return result; +} diff --git a/radio/src/targets/tx16smk3/usb_descriptor.h b/radio/src/targets/tx16smk3/usb_descriptor.h new file mode 100644 index 000000000..58b63795b --- /dev/null +++ b/radio/src/targets/tx16smk3/usb_descriptor.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _USB_DESCRIPTOR_H_ +#define _USB_DESCRIPTOR_H_ + +#define USB_NAME "TX16SMK3" +#define USB_MANUFACTURER 'R', 'M', ' ', ' ', ' ', ' ', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'T', 'X', '1', '6', 'S', 'M', 'K', '3' /* 8 Bytes */ + +#endif // _USB_DESCRIPTOR_H_ diff --git a/radio/src/tests/images/color/bitmap_800x480.png b/radio/src/tests/images/color/bitmap_800x480.png new file mode 100644 index 0000000000000000000000000000000000000000..16245115ee994d012f8da48c283c247c432a44e4 GIT binary patch literal 30848 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 D-h8Lj literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/lines_800x480.png b/radio/src/tests/images/color/lines_800x480.png new file mode 100644 index 0000000000000000000000000000000000000000..756df429c42c7f8683a0a14c1dc9d1a127f92a07 GIT binary patch literal 12883 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 literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/masks_800x480.png b/radio/src/tests/images/color/masks_800x480.png new file mode 100644 index 0000000000000000000000000000000000000000..e2264a779b95e1ee76482f362038ecef398a9997 GIT binary patch literal 31962 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 Q4S_$?!lFX!gQfZZ3*MQp3IG5A literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/primitives_EN_800x480.png b/radio/src/tests/images/color/primitives_EN_800x480.png new file mode 100644 index 0000000000000000000000000000000000000000..570dcabbf407dfc99013607752b9f65f86bc1e71 GIT binary patch literal 19763 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 EFPBK>`2YX_ literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/transparency_EN_800x480.png b/radio/src/tests/images/color/transparency_EN_800x480.png new file mode 100644 index 0000000000000000000000000000000000000000..3f3f8b48fb8ebca382a7b001d1ca1ca2c158b892 GIT binary patch literal 18798 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 #define STR_SRC_BATT currentLangStrings->STR_SRC_BATT #define STR_SRC_GPS currentLangStrings->STR_SRC_GPS +#define STR_SRC_LIGHT currentLangStrings->STR_SRC_LIGHT #define STR_SRC_TIME currentLangStrings->STR_SRC_TIME #define STR_SRC_TIMER currentLangStrings->STR_SRC_TIMER #define STR_STACK_AUDIO currentLangStrings->STR_STACK_AUDIO diff --git a/radio/src/translations/string_list.h b/radio/src/translations/string_list.h index dbe30cf43..ac04fc90a 100644 --- a/radio/src/translations/string_list.h +++ b/radio/src/translations/string_list.h @@ -945,6 +945,7 @@ STR(SPLASHSCREEN) STR(SPORT_UPDATE_POWER_MODE) STR(SRC_BATT) STR(SRC_GPS) +STR(SRC_LIGHT) STR(SRC_TIME) STR(SRC_TIMER) STR(STACK_AUDIO) diff --git a/radio/util/hw_defs/hal_adc.py b/radio/util/hw_defs/hal_adc.py index 52aaef290..51b86500d 100644 --- a/radio/util/hw_defs/hal_adc.py +++ b/radio/util/hw_defs/hal_adc.py @@ -100,6 +100,11 @@ class ADCInputParser: 'name': 'RTC_BAT', 'suffix': 'RTC_BAT', }, + { + 'type': 'LUX', + 'name': 'LUX', + 'suffix': 'LUX', + }, { 'range': AZ_seq(), 'type': ADCInput.TYPE_SWITCH, @@ -237,7 +242,7 @@ class ADCInputParser: if 'PWM_STICKS' in self.hw_defs: ch = self.hw_defs.get(f'STICK_PWM_CHANNEL_{adc_input.name}') adc_input.pwm_channel = idx if ch is None else ch - if adc_input.type != 'VBAT' and adc_input.type != 'RTC_BAT': + if adc_input.type != 'VBAT' and adc_input.type != 'RTC_BAT' and adc_input.type != 'LUX': d = self.dirs[idx] if d < 0: adc_input.inverted = True diff --git a/radio/util/hw_defs/hal_adc_inputs.jinja b/radio/util/hw_defs/hal_adc_inputs.jinja index 26d158292..8181ecce5 100644 --- a/radio/util/hw_defs/hal_adc_inputs.jinja +++ b/radio/util/hw_defs/hal_adc_inputs.jinja @@ -48,12 +48,22 @@ static const etx_hal_adc_input_t _rtc_bat_inputs[] = { {% endfor %} }; -{% set n_inputs = rtc_bat_offset + n_rtc_bat %} +{% set lux_offset = rtc_bat_offset + n_rtc_bat %} +{% set lux_inputs = adc_inputs.inputs | selectattr('type', '==', 'LUX') | list %} +{% set n_lux = lux_inputs | count %} +static const etx_hal_adc_input_t _lux_inputs[] = { + {% for input in lux_inputs %} + { "{{ input.name }}", nullptr, nullptr }, + {% endfor %} +}; + +{% set n_inputs = lux_offset + n_lux %} static const etx_hal_adc_inputs_t _hal_inputs[] = { { {{ n_mains }}, 0, _main_inputs }, { {{ n_flex }}, {{ flex_offset }}, _flex_inputs }, { {{ n_vbat }}, {{ vbat_offset }}, _vbat_inputs }, { {{ n_rtc_bat }}, {{ rtc_bat_offset }}, _rtc_bat_inputs }, + { {{ n_lux }}, {{ lux_offset }}, _lux_inputs }, { {{ n_inputs }}, 0, nullptr }, }; diff --git a/radio/util/hw_defs/hal_keys.py b/radio/util/hw_defs/hal_keys.py index 8a00b0a31..6c255a28b 100644 --- a/radio/util/hw_defs/hal_keys.py +++ b/radio/util/hw_defs/hal_keys.py @@ -58,7 +58,7 @@ KEYS = [ KEY_LABELS = [ { - "targets": {"boxer", "f16", "mt12", "gx12", "pocket", "tx12", "tx12mk2", "tx16s", "v16", "zorro"}, + "targets": {"boxer", "f16", "mt12", "gx12", "pocket", "tx12", "tx12mk2", "tx16s", "tx16smk3","v16", "zorro"}, "keys": { "EXIT": { "label": "RTN" } } diff --git a/radio/util/hw_defs/legacy_names.py b/radio/util/hw_defs/legacy_names.py index d0101f382..86f9d6c29 100644 --- a/radio/util/hw_defs/legacy_names.py +++ b/radio/util/hw_defs/legacy_names.py @@ -89,6 +89,73 @@ LEGACY_NAMES = [ } } }, + { + "targets": {"tx16smk3"}, + "inputs": { + "LH": { + "yaml": "Rud", + "lua": "rud", + "description": "Rudder" + }, + "LV": { + "yaml": "Ele", + "lua": "ele", + "description": "Elevator" + }, + "RV": { + "yaml": "Thr", + "lua": "thr", + "description": "Throttle" + }, + "RH": { + "yaml": "Ail", + "lua": "ail", + "description": "Aileron" + }, + "P1": { + "yaml": "S1", + "lua": "s1", + "label": "S1", + "short_label": "1", + "description": "Potentiometer S1" + }, + "P2": { + "yaml": "S2", + "lua": "s2", + "label": "S2", + "short_label": "2", + "description": "Potentiometer S2" + }, + "SL1": { + "yaml": "LS", + "lua": "ls", + "label": "LS", + "short_label": "L", + "description": "Left slider" + }, + "SL2": { + "yaml": "RS", + "lua": "rs", + "label": "RS", + "short_label": "R", + "description": "Right slider" + }, + "EXT1": { + "yaml": "EXT1", + "lua": "ext1", + "label": "EXT1", + "short_label": "E1", + "description": "Ext 1" + }, + "EXT2": { + "yaml": "EXT2", + "lua": "ext2", + "label": "EXT2", + "short_label": "E2", + "description": "Ext 2" + } + } + }, { "targets": {"t15", "t15pro", "tx15"}, "inputs": { diff --git a/radio/util/hw_defs/pot_config.py b/radio/util/hw_defs/pot_config.py index 8f5dcbfcd..bde8cf904 100644 --- a/radio/util/hw_defs/pot_config.py +++ b/radio/util/hw_defs/pot_config.py @@ -157,6 +157,14 @@ POT_CONFIG = { "SL1": {"default": "SLIDER"}, "SL2": {"default": "SLIDER"} }, + "tx16smk3": { + "P1": {"default": "POT_CENTER"}, + "P2": {"default": "POT_CENTER"}, + "SL1": {"default": "SLIDER"}, + "SL2": {"default": "SLIDER"}, + "EXT1": {"default": "NONE"}, + "EXT2": {"default": "NONE"}, + }, "f16": { "P1": {"default": "POT_CENTER"}, "P2": {"default": "MULTIPOS"}, diff --git a/radio/util/hw_defs/switch_config.py b/radio/util/hw_defs/switch_config.py index e72eb24c2..8a0fbab05 100644 --- a/radio/util/hw_defs/switch_config.py +++ b/radio/util/hw_defs/switch_config.py @@ -420,6 +420,26 @@ SWITCH_CONFIG = { "SG": {"default": "3POS"}, "SH": {"default": "TOGGLE"} }, + "tx16smk3": { + "SA": {"default": "3POS"}, + "SB": {"default": "3POS"}, + "SC": {"default": "3POS"}, + "SD": {"default": "3POS"}, + "SE": {"default": "3POS"}, + "SF": {"default": "2POS"}, + "SG": {"default": "3POS"}, + "SH": {"default": "2POS"}, + #optional + "SI": {"default": "NONE"}, + "SJ": {"default": "NONE"}, + # custom switches + "SK": {"default": "2POS"}, + "SL": {"default": "2POS"}, + "SM": {"default": "2POS"}, + "SN": {"default": "2POS"}, + "SO": {"default": "2POS"}, + "SP": {"default": "2POS"}, + }, "f16": { "SA": {"default": "3POS"}, "SB": {"default": "3POS"}, diff --git a/tools/build-common.sh b/tools/build-common.sh index 69f07e57b..ac9fb1524 100644 --- a/tools/build-common.sh +++ b/tools/build-common.sh @@ -125,6 +125,9 @@ get_target_build_options() { tx16s) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=TX16S" ;; + tx16smk3) + BUILD_OPTIONS+="-DPCB=TX16SMK3" + ;; f16) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=F16" ;; diff --git a/tools/build-companion.sh b/tools/build-companion.sh index 230249a5e..434452510 100755 --- a/tools/build-companion.sh +++ b/tools/build-companion.sh @@ -219,7 +219,7 @@ declare -a simulator_plugins=( t8 t12 t12max tx12 tx12mk2 t15 t15pro t16 t18 t20 t20v2 xlite xlites x10 x10express x12s - zorro tx16s tx15 + zorro tx16s tx16smk3 tx15 commando8 boxer pocket mt12 gx12 tlite tpro tprov2 tpros bumblebee lr3pro t14 nv14 el18 pl18 pl18ev pl18u st16 pa01 diff --git a/tools/generate-hw-defs.sh b/tools/generate-hw-defs.sh index 811c97454..8c931daaf 100755 --- a/tools/generate-hw-defs.sh +++ b/tools/generate-hw-defs.sh @@ -9,7 +9,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" : "${SRCDIR:=$(dirname "$(pwd)/$0")/..}" -: ${FLAVOR:="nv14;el18;pl18;pl18ev;nb4p;st16;t12;t12max;t15;t15pro;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v14;v12;tx15"} +: ${FLAVOR:="nv14;el18;pl18;pl18ev;nb4p;st16;t12;t12max;t15;t15pro;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;tx16smk3;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v14;v12;tx15"} : ${COMMON_OPTIONS:="-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_RULE_MESSAGES=OFF -Wno-dev -DCMAKE_MESSAGE_LOG_LEVEL=WARNING"} # wipe build directory clean diff --git a/tools/generate-yaml.sh b/tools/generate-yaml.sh index bc37f1af9..402f2ca1b 100755 --- a/tools/generate-yaml.sh +++ b/tools/generate-yaml.sh @@ -11,7 +11,7 @@ if [[ -n ${GCC_ARM} ]] ; then export PATH=${GCC_ARM}:$PATH fi -: ${FLAVOR:="t15;tx16s;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;pa01;tx15;t15pro"} +: ${FLAVOR:="t15;tx16s;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;pa01;tx15;t15pro;tx16smk3"} : ${SRCDIR:=$(dirname "$(pwd)/$0")/..} : ${COMMON_OPTIONS:="-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_RULE_MESSAGES=OFF -Wno-dev -DDISABLE_COMPANION=YES -DCMAKE_MESSAGE_LOG_LEVEL=WARNING"}