From cc17046e7a362b8646eb41bcd87af4f1c159ab90 Mon Sep 17 00:00:00 2001 From: richardclli Date: Wed, 6 Dec 2023 07:00:44 +0800 Subject: [PATCH 1/6] feat: Flysky PL18 Support (#4105) Co-authored-by: raphaelcoeffic Co-authored-by: rotorman Co-authored-by: Xy201207 Co-authored-by: Peter Feerick --- .github/workflows/actions.yml | 6 +- .github/workflows/nightly.yml | 1 + companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 4 + companion/src/firmwares/boards.cpp | 49 +- companion/src/firmwares/boards.h | 13 +- companion/src/firmwares/generalsettings.cpp | 11 +- .../src/firmwares/opentx/opentxeeprom.cpp | 20 +- .../src/firmwares/opentx/opentxinterface.cpp | 15 +- companion/src/generaledit/generalsetup.cpp | 2 +- .../src/images/simulator/PL18/bottom.png | Bin 0 -> 178 bytes companion/src/images/simulator/PL18/left.png | Bin 0 -> 1273 bytes companion/src/images/simulator/PL18/right.png | Bin 0 -> 1273 bytes companion/src/images/simulator/PL18/top.png | Bin 0 -> 178 bytes companion/src/modeledit/setup.cpp | 2 +- companion/src/modelprinter.cpp | 2 +- companion/src/simulation/CMakeLists.txt | 1 + companion/src/simulation/simulateduiwidget.h | 13 + .../src/simulation/simulateduiwidgetPL18.cpp | 76 + .../src/simulation/simulateduiwidgetPL18.ui | 206 ++ companion/src/simulation/simulatorwidget.cpp | 3 + fw.json | 2 + radio/src/CMakeLists.txt | 4 +- radio/src/bitmaps/480x272/CMakeLists.txt | 2 + .../boards/generic_stm32/analog_inputs.cpp | 3 + radio/src/boards/generic_stm32/inputs.cpp | 10 +- .../src/boards/generic_stm32/module_ports.cpp | 4 +- radio/src/cli.cpp | 6 +- radio/src/dataconstants.h | 19 +- radio/src/datastructs.h | 2 +- radio/src/datastructs_private.h | 2 +- radio/src/debug.h | 4 +- radio/src/gui/colorlcd/radio_calibration.cpp | 2 +- radio/src/gui/colorlcd/radio_diaganas.cpp | 2 +- radio/src/gui/colorlcd/radio_diagkeys.cpp | 9 + .../colorlcd/radio_ghost_module_config.cpp | 4 +- .../gui/colorlcd/radio_ghost_module_config.h | 2 +- radio/src/gui/colorlcd/radio_version.cpp | 11 +- radio/src/gui/colorlcd/special_functions.cpp | 9 +- radio/src/hal/key_driver.cpp | 2 + radio/src/hal/key_driver.h | 6 +- radio/src/keys.cpp | 6 +- radio/src/myeeprom.h | 8 +- radio/src/opentx.h | 4 - radio/src/opentx_types.h | 1 + radio/src/pulses/multi.cpp | 7 +- radio/src/sdcard.cpp | 6 +- radio/src/simu.cpp | 2 +- radio/src/storage/yaml/CMakeLists.txt | 2 + radio/src/storage/yaml/yaml_datastructs.cpp | 2 + .../storage/yaml/yaml_datastructs_funcs.cpp | 2 +- .../storage/yaml/yaml_datastructs_pl18.cpp | 915 ++++++ radio/src/targets/common/arm/CMakeLists.txt | 24 +- .../arm/stm32/bootloader/CMakeLists.txt | 16 +- .../common/arm/stm32/bootloader/boot.cpp | 42 +- .../common/arm/stm32/bootloader/boot.h | 4 + .../common/arm/stm32/diskio_spi_flash.cpp | 10 +- .../targets/common/arm/stm32/pwr_driver.cpp | 2 + .../targets/common/arm/stm32/spi_flash.cpp | 17 +- .../targets/common/arm/stm32/stm32_adc.cpp | 18 +- .../src/targets/common/arm/stm32/stm32_adc.h | 1 + radio/src/targets/common/arm/stm32/usb_bsp.c | 4 +- radio/src/targets/common/arm/stm32/usb_conf.h | 4 + .../targets/common/arm/stm32/usb_driver.cpp | 2 + radio/src/targets/horus/CMakeLists.txt | 1 - radio/src/targets/nv14/CMakeLists.txt | 1 - radio/src/targets/nv14/board.cpp | 4 +- radio/src/targets/nv14/board.h | 14 +- radio/src/targets/pl18/CMakeLists.txt | 167 + radio/src/targets/pl18/backlight_driver.cpp | 95 + radio/src/targets/pl18/battery_driver.cpp | 436 +++ radio/src/targets/pl18/battery_driver.h | 60 + radio/src/targets/pl18/board.cpp | 261 ++ radio/src/targets/pl18/board.h | 250 ++ .../src/targets/pl18/bootloader/boot_menu.cpp | 261 ++ radio/src/targets/pl18/extmodule_helper.cpp | 49 + radio/src/targets/pl18/hal.h | 719 +++++ radio/src/targets/pl18/haptic_driver.cpp | 63 + radio/src/targets/pl18/key_driver.cpp | 220 ++ radio/src/targets/pl18/lcd_driver.cpp | 2876 +++++++++++++++++ radio/src/targets/pl18/lcd_driver.h | 92 + radio/src/targets/pl18/led_driver.cpp | 60 + radio/src/targets/pl18/libopenui_config.h | 29 + radio/src/targets/pl18/sdram_driver.c | 257 ++ radio/src/targets/pl18/touch_driver.cpp | 470 +++ radio/src/targets/pl18/touch_driver.h | 31 + radio/src/targets/pl18/tp_cst340.cpp | 560 ++++ radio/src/targets/pl18/tp_cst340.h | 289 ++ radio/src/targets/simu/opentxsimulator.cpp | 2 + radio/src/targets/simu/simpgmspace.cpp | 2 +- radio/src/targets/simu/simufatfs.cpp | 1 + radio/src/targets/taranis/CMakeLists.txt | 4 - radio/src/targets/taranis/board.h | 8 + radio/src/tasks.h | 7 + .../src/tests/images/color/bitmap_480x320.png | Bin 0 -> 23258 bytes .../tests/images/color/clipping_480x320.png | Bin 0 -> 5121 bytes .../src/tests/images/color/lines_480x320.png | Bin 0 -> 5734 bytes .../src/tests/images/color/masks_480x320.png | Bin 0 -> 16592 bytes .../images/color/primitives_EN_480x320.png | Bin 0 -> 10452 bytes .../images/color/transparency_EN_480x320.png | Bin 0 -> 10019 bytes .../src/tests/images/color/vline_480x320.png | Bin 0 -> 5186 bytes radio/src/translations/cn.h | 12 +- radio/src/translations/cz.h | 14 +- radio/src/translations/da.h | 10 + radio/src/translations/de.h | 14 +- radio/src/translations/en.h | 14 +- radio/src/translations/es.h | 14 +- radio/src/translations/fi.h | 10 + radio/src/translations/fr.h | 14 +- radio/src/translations/he.h | 10 + radio/src/translations/it.h | 14 +- radio/src/translations/jp.h | 10 + radio/src/translations/nl.h | 14 +- radio/src/translations/pl.h | 14 +- radio/src/translations/pt.h | 14 +- radio/src/translations/ru.h | 10 + radio/src/translations/se.h | 10 + radio/src/translations/tw.h | 10 + radio/util/hw_defs/hal_keys.jinja | 9 +- radio/util/hw_defs/legacy_names.py | 136 + radio/util/hw_defs/pot_config.py | 18 + radio/util/hw_defs/stm32_adc_inputs.jinja | 1 + radio/util/hw_defs/switch_config.py | 22 + tools/build-companion.sh | 8 +- tools/build-flysky.py | 13 +- tools/build-gh.sh | 6 + tools/commit-tests.sh | 6 + tools/generate-yaml.sh | 5 +- 128 files changed, 9179 insertions(+), 172 deletions(-) create mode 100644 companion/src/images/simulator/PL18/bottom.png create mode 100644 companion/src/images/simulator/PL18/left.png create mode 100644 companion/src/images/simulator/PL18/right.png create mode 100644 companion/src/images/simulator/PL18/top.png create mode 100644 companion/src/simulation/simulateduiwidgetPL18.cpp create mode 100644 companion/src/simulation/simulateduiwidgetPL18.ui create mode 100644 radio/src/storage/yaml/yaml_datastructs_pl18.cpp create mode 100644 radio/src/targets/pl18/CMakeLists.txt create mode 100644 radio/src/targets/pl18/backlight_driver.cpp create mode 100644 radio/src/targets/pl18/battery_driver.cpp create mode 100644 radio/src/targets/pl18/battery_driver.h create mode 100644 radio/src/targets/pl18/board.cpp create mode 100644 radio/src/targets/pl18/board.h create mode 100644 radio/src/targets/pl18/bootloader/boot_menu.cpp create mode 100644 radio/src/targets/pl18/extmodule_helper.cpp create mode 100644 radio/src/targets/pl18/hal.h create mode 100644 radio/src/targets/pl18/haptic_driver.cpp create mode 100644 radio/src/targets/pl18/key_driver.cpp create mode 100644 radio/src/targets/pl18/lcd_driver.cpp create mode 100644 radio/src/targets/pl18/lcd_driver.h create mode 100644 radio/src/targets/pl18/led_driver.cpp create mode 100644 radio/src/targets/pl18/libopenui_config.h create mode 100644 radio/src/targets/pl18/sdram_driver.c create mode 100644 radio/src/targets/pl18/touch_driver.cpp create mode 100644 radio/src/targets/pl18/touch_driver.h create mode 100644 radio/src/targets/pl18/tp_cst340.cpp create mode 100644 radio/src/targets/pl18/tp_cst340.h create mode 100644 radio/src/tests/images/color/bitmap_480x320.png create mode 100644 radio/src/tests/images/color/clipping_480x320.png create mode 100644 radio/src/tests/images/color/lines_480x320.png create mode 100644 radio/src/tests/images/color/masks_480x320.png create mode 100644 radio/src/tests/images/color/primitives_EN_480x320.png create mode 100644 radio/src/tests/images/color/transparency_EN_480x320.png create mode 100644 radio/src/tests/images/color/vline_480x320.png diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 4fcb62a870f..91807761949 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -46,6 +46,8 @@ jobs: - tx16s - nv14 - el18 + - pl18 + - pl18ev - t12 - t16 - t18 @@ -90,9 +92,9 @@ jobs: matrix: target: - nv14;el18 + - pl18;pl18ev - t12 - - t16 - - t18 + - t16;t18 - t8;zorro;pocket;mt12;commando8 - tlite;tpro;tprov2;lr3pro - t20 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 009f2147320..4406634ecd4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -16,6 +16,7 @@ jobs: matrix: target: - nv14;el18 + - pl18;pl18ev - t12 - t16 - t18 diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index c623a69a268..7cb6f9242a8 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -318,6 +318,8 @@ elseif(PCB STREQUAL X10 AND PCBREV STREQUAL T18) set(FLAVOUR t18) elseif(PCB STREQUAL NV14 AND PCBREV STREQUAL EL18) set(FLAVOUR el18) +elseif(PCB STREQUAL PL18) + set(FLAVOUR pl18) else() string(TOLOWER ${PCB} FLAVOUR) endif() diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index 792e2f494b9..c28331e9a68 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -293,6 +293,10 @@ images/simulator/NV14/right.png images/simulator/NV14/top.png images/simulator/NV14/bottom.png + images/simulator/PL18/left.png + images/simulator/PL18/right.png + images/simulator/PL18/top.png + images/simulator/PL18/bottom.png images/wizard/ailerons.png images/wizard/airbrakes.png images/wizard/elevons.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 08e058ac863..f7f2be15615 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -110,7 +110,9 @@ uint32_t Boards::getFourCC(Type board) case BOARD_FLYSKY_NV14: return 0x3A78746F; case BOARD_FLYSKY_EL18: - return 0x3A78746F; // TODO: check this + return 0x3A78746F; + case BOARD_FLYSKY_PL18: + return 0x4878746F; default: return 0; } @@ -159,6 +161,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_RADIOMASTER_TX16S: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: + case BOARD_FLYSKY_PL18: return 0; default: return 0; @@ -206,6 +209,7 @@ int Boards::getFlashSize(Type board) case BOARD_RADIOMASTER_TX16S: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: + case BOARD_FLYSKY_PL18: return FSIZE_HORUS; case BOARD_UNKNOWN: return FSIZE_MAX; @@ -419,6 +423,20 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) if (index < DIM(switches)) return switches[index]; } + else if (IS_FLYSKY_PL18(board)) { + const Board::SwitchInfo switches[] = { + {SWITCH_2POS, "SA"}, + {SWITCH_3POS, "SB"}, + {SWITCH_2POS, "SC"}, + {SWITCH_3POS, "SD"}, + {SWITCH_3POS, "SE"}, + {SWITCH_2POS, "SF"}, + {SWITCH_3POS, "SG"}, + {SWITCH_3POS, "SH"} + }; + if (index < DIM(switches)) + return switches[index]; + } else if (IS_FAMILY_HORUS_OR_T16(board)) { const Board::SwitchInfo switches[] = { {SWITCH_3POS, "SA"}, @@ -497,6 +515,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 7; else if (IS_HORUS_X12S(board)) return 3; + else if (IS_FLYSKY_PL18(board)) + return 3; else return 3; @@ -509,7 +529,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case Sliders: if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(board) || IS_JUMPER_T20(board)) return 4; - else if (IS_TARANIS_X9D(board) || IS_HORUS_X10(board) || IS_FAMILY_T16(board)) + else if (IS_TARANIS_X9D(board) || IS_HORUS_X10(board) || IS_FAMILY_T16(board) || IS_FLYSKY_PL18(board)) return 2; else return 0; @@ -531,7 +551,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) getCapability(board, Board::MouseAnalogs) + getCapability(board, Board::GyroAnalogs); case MultiposPots: - if (IS_HORUS_OR_TARANIS(board) && !(IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board))) + if (IS_HORUS_OR_TARANIS(board) && !(IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PL18(board))) return getCapability(board, Board::Pots); else return 0; @@ -558,6 +578,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 6; else if (board == BOARD_FLYSKY_NV14 || board == BOARD_FLYSKY_EL18) return 8; + else if (board == BOARD_FLYSKY_PL18) + return 8; else if (board == BOARD_RADIOMASTER_TX12_MK2 || board == BOARD_RADIOMASTER_BOXER || board == BOARD_JUMPER_TPRO) return 6; else if (board == BOARD_RADIOMASTER_POCKET) @@ -600,7 +622,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return getCapability(board, Board::Switches); case SwitchPositions: - if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) + if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PL18(board)) return getCapability(board, Board::Switches) * 3; else return 9; @@ -610,7 +632,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case NumTrims: - if (IS_FAMILY_HORUS_OR_T16(board) && !(IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board))) + if (IS_FLYSKY_PL18(board)) + return 8; + else if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) return 6; else if (IS_IFLIGHT_COMMANDO8(board)) return 0; @@ -626,7 +650,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return IS_STM32(board) ? true : false; case HasColorLcd: - return IS_FAMILY_HORUS_OR_T16(board); + return IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board); case HasSDCard: return IS_STM32(board); @@ -647,7 +671,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return false; case SportMaxBaudRate: - if (IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_TARANIS_X7_ACCESS(board) || + if (IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PL18(board) ||IS_TARANIS_X7_ACCESS(board) || (IS_TARANIS(board) && !IS_TARANIS_XLITE(board) && !IS_TARANIS_X7(board) && !IS_TARANIS_X9LITE(board))) return 400000; // 400K and higher else @@ -833,6 +857,14 @@ StringTagMappingTable Boards::getAnalogNamesLookupTable(Board::Type board, const {tr("TltY").toStdString(), "TILT_Y", 14}, }); } + } else if (IS_FLYSKY_PL18(board)) { + tbl.insert(tbl.end(), { + {tr("VRA").toStdString(), "POT1"}, + {tr("VRB").toStdString(), "POT2"}, + {tr("VRC").toStdString(), "POT3"}, + {tr("LS").toStdString(), "LS"}, + {tr("RS").toStdString(), "RS"}, + }); } else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) { if (version < adcVersion) { tbl.insert(tbl.end(), { @@ -964,6 +996,8 @@ QString Boards::getBoardName(Board::Type board) return "FlySky NV14"; case BOARD_FLYSKY_EL18: return "FlySky EL18"; + case BOARD_FLYSKY_PL18: + return "FlySky PL18"; case BOARD_BETAFPV_LR3PRO: return "BETAFPV LR3PRO"; case BOARD_IFLIGHT_COMMANDO8: @@ -1198,6 +1232,7 @@ int Boards::getDefaultInternalModules(Board::Type board) case BOARD_JUMPER_TLITE_F4: case BOARD_JUMPER_TPRO: case BOARD_JUMPER_TPROV2: + case BOARD_FLYSKY_PL18: return (int)MODULE_TYPE_MULTIMODULE; case BOARD_BETAFPV_LR3PRO: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 75c6473c918..b853436bdfe 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -69,6 +69,7 @@ namespace Board { BOARD_JUMPER_TLITE, BOARD_JUMPER_TLITE_F4, BOARD_FLYSKY_NV14, + BOARD_FLYSKY_PL18, BOARD_RADIOMASTER_ZORRO, BOARD_JUMPER_TPRO, BOARD_BETAFPV_LR3PRO, @@ -398,6 +399,11 @@ inline bool IS_FLYSKY_EL18(Board::Type board) return (board == Board::BOARD_FLYSKY_EL18); } +inline bool IS_FLYSKY_PL18(Board::Type board) +{ + return (board == Board::BOARD_FLYSKY_PL18); +} + inline bool IS_TARANIS_XLITE(Board::Type board) { return board == Board::BOARD_TARANIS_XLITE || board == Board::BOARD_TARANIS_XLITES; @@ -475,7 +481,9 @@ inline bool IS_FAMILY_HORUS(Board::Type board) inline bool IS_FAMILY_HORUS_OR_T16(Board::Type board) { - return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board)/*generally*/ || IS_FLYSKY_EL18(board)/*generally*/; + return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board) || + IS_FLYSKY_NV14(board)/*generally*/ || IS_FLYSKY_EL18(board)/*generally*/ + || IS_FLYSKY_PL18(board); } inline bool IS_HORUS_OR_TARANIS(Board::Type board) @@ -485,7 +493,8 @@ inline bool IS_HORUS_OR_TARANIS(Board::Type board) inline bool IS_STM32(Board::Type board) { - return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board); + return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board) || + IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PL18(board); } inline bool IS_ARM(Board::Type board) diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index b4cc9e80b55..9e7688c99ca 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -154,6 +154,8 @@ void GeneralSettings::init() strcpy(bluetoothName, "t16"); else if (IS_FLYSKY_NV14(board)) strcpy(bluetoothName, "nv14"); + else if (IS_FLYSKY_PL18(board)) + strcpy(bluetoothName, "pl18"); else if (IS_FAMILY_HORUS_OR_T16(board)) strcpy(bluetoothName, "horus"); else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board)) @@ -269,7 +271,7 @@ void GeneralSettings::init() internalModule = g.profile[g.sessionId()].defaultInternalModule(); - if (IS_FLYSKY_NV14(board)) + if (IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) stickDeadZone = 2; } @@ -285,7 +287,7 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board) return; // TODO: move to Boards, like with switches - if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) { + if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) { potConfig[0] = Board::POT_WITH_DETENT; potConfig[1] = Board::POT_MULTIPOS_SWITCH; potConfig[2] = Board::POT_WITH_DETENT; @@ -294,6 +296,11 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board) potConfig[0] = Board::POT_WITHOUT_DETENT; potConfig[1] = Board::POT_WITHOUT_DETENT; } + else if (IS_FLYSKY_PL18(board)) { + potConfig[0] = Board::POT_WITHOUT_DETENT; + potConfig[1] = Board::POT_WITHOUT_DETENT; + potConfig[2] = Board::POT_WITHOUT_DETENT; + } else if (IS_TARANIS_XLITE(board)) { potConfig[0] = Board::POT_WITHOUT_DETENT; potConfig[1] = Board::POT_WITHOUT_DETENT; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index d5339d9982b..b7c44f0988e 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -116,7 +116,7 @@ inline int MAX_POTS_STORAGE(Board::Type board, int version) { if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board)) return 3; - if (version <= 220 && IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) + if (version <= 220 && IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) return 5; if (IS_FAMILY_T12(board)) return 2; @@ -147,7 +147,7 @@ inline int MAX_XPOTS(Board::Type board, int version) inline int MAX_SLIDERS_STORAGE(Board::Type board, int version) { - if (version >= 219 && (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))) + if (version >= 219 && (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board))) return 4; return Boards::getCapability(board, Board::Sliders); } @@ -183,7 +183,7 @@ inline int SWITCHES_CONFIG_SIZE(Board::Type board, int version) inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version) { - if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) + if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) return 2; else return 0; @@ -211,10 +211,10 @@ inline int MAX_GYRO_ANALOGS(Board::Type board, int version) #define MAX_CURVES(board, version) ((version >= 219 || HAS_LARGE_LCD(board)) ? 32 : 16) #define MAX_GVARS(board, version) 9 #define MAX_SCRIPTS(board) (IS_FAMILY_HORUS_OR_T16(board) ? 9 : 7) -#define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board) || IS_FLYSKY_NV14(board)) ? 60 : 40)) +#define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) ? 60 : 40)) #define NUM_PPM_INPUTS(board, version) 16 #define ROTENC_COUNT(board, version) ((IS_STM32(board) && version >= 218) ? 0 : 1) -#define MAX_AUX_TRIMS(board) ((IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) ? 2 : 0) +#define MAX_AUX_TRIMS(board) ((IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) ? 2 : 0) #define MAX_SOURCE_TYPE_SPECIAL(board, version) SOURCE_TYPE_SPECIAL_COUNT inline int switchIndex(int i, Board::Type board, unsigned int version) @@ -2910,7 +2910,7 @@ void OpenTxModelData::beforeExport() // TODO remove when enum not radio specific requires eeprom change and conversion // Note: this must mirror reverse afterImport - if (!IS_FLYSKY_NV14(board)) + if (!IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) modelData.trainerMode -= 1; if (modelData.trainerMode > TRAINER_MODE_SLAVE_JACK) { @@ -2957,7 +2957,7 @@ void OpenTxModelData::afterImport() // TODO remove when enum not radio specific requires eeprom change and conversion // Note: this must mirror reverse beforeExport - if (!IS_FLYSKY_NV14(board)) + if (!IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) modelData.trainerMode += 1; if (modelData.trainerMode > TRAINER_MODE_SLAVE_JACK) { @@ -3003,7 +3003,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type internalField.Append(new UnsignedField<16>(this, chkSum)); - if (!IS_FAMILY_HORUS_OR_T16(board) || (IS_FLYSKY_NV14(board))) { + if (!IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { internalField.Append(new UnsignedField<8>(this, generalData.currModelIndex)); internalField.Append(new UnsignedField<8>(this, generalData.contrast)); } @@ -3067,11 +3067,11 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type internalField.Append(new SignedField<8>(this, generalData.PPM_Multiplier)); internalField.Append(new SignedField<8>(this, generalData.hapticLength)); - if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) { + if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { internalField.Append(new UnsignedField<8>(this, generalData.reNavigation)); } - if ((!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) { + if ((!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { internalField.Append(new UnsignedField<8>(this, generalData.stickReverse)); } diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 34d441279c0..192fb4c32b7 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -124,6 +124,8 @@ const char * OpenTxEepromInterface::getName() return "EdgeTX for FlySky NV14"; case BOARD_FLYSKY_EL18: return "EdgeTX for FlySky EL18"; + case BOARD_FLYSKY_PL18: + return "EdgeTX for FlySky PL18"; case BOARD_BETAFPV_LR3PRO: return "EdgeTx for BETAFPV LR3PRO"; case BOARD_IFLIGHT_COMMANDO8: @@ -664,6 +666,8 @@ int OpenTxFirmware::getCapability(::Capability capability) case LcdWidth: if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) return 320; + else if (IS_FLYSKY_PL18(board)) + return 480; else if (IS_FAMILY_HORUS_OR_T16(board)) return 480; else if (IS_TARANIS_SMALL(board)) @@ -675,6 +679,8 @@ int OpenTxFirmware::getCapability(::Capability capability) case LcdHeight: if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) return 480; + else if (IS_FLYSKY_PL18(board)) + return 320; else if (IS_FAMILY_HORUS_OR_T16(board)) return 272; else @@ -777,7 +783,7 @@ int OpenTxFirmware::getCapability(::Capability capability) IS_JUMPER_TPRO(board) || IS_RADIOMASTER_TX12_MK2(board) || IS_RADIOMASTER_BOXER(board) || IS_RADIOMASTER_POCKET(board); case HasBluetooth: return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || - IS_TARANIS_X9DP_2019(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) ? true : false; + IS_TARANIS_X9DP_2019(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PL18(board)) ? true : false; case HasADCJitterFilter: return IS_HORUS_OR_TARANIS(board); case HasTelemetryBaudrate: @@ -1244,6 +1250,13 @@ void registerOpenTxFirmwares() addOpenTxRfOptions(firmware, FLEX + AFHDS2A + AFHDS3); registerOpenTxFirmware(firmware); + /* FlySky PL18 board */ + firmware = new OpenTxFirmware(FIRMWAREID("pl18"), Firmware::tr("FlySky PL18"), BOARD_FLYSKY_PL18); + addOpenTxFrskyOptions(firmware); + firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); + registerOpenTxFirmware(firmware); + /* FrSky Horus X10 board */ firmware = new OpenTxFirmware(FIRMWAREID("x10"), Firmware::tr("FrSky Horus X10 / X10S"), BOARD_X10); addOpenTxFrskyOptions(firmware); diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 75d3b2dabc7..265aad75ac1 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -175,7 +175,7 @@ ui(new Ui::GeneralSetup) ui->usbModeCB->hide(); } - if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board)) { + if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { ui->hatsModeCB->setModel(new FilteredItemModel(GeneralSettings::hatsModeItemModel())); ui->hatsModeCB->setField(generalSettings.hatsMode, this); } diff --git a/companion/src/images/simulator/PL18/bottom.png b/companion/src/images/simulator/PL18/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc10a49d42fe209dea8c83e3b8a9eeae29e6b43 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yoxj5K>M(rM(rtrimsDisplay->setField(model.trimsDisplay, this); - if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board)) { + if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { ui->cboHatsMode->setModel(panelFilteredModels->getItemModel(FIM_HATSMODE)); ui->cboHatsMode->setField(model.hatsMode, this); } diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 5501340990f..e524454aaa3 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -841,7 +841,7 @@ QString ModelPrinter::printSettingsTrim() str << printLabelValue(tr("Display"), printTrimsDisplayMode()); str << printLabelValue(tr("Extended"), printBoolean(model.extendedTrims, BOOLEAN_YESNO)); Board::Type board = firmware->getBoard(); - if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board)) { + if (IS_FLYSKY_EL18(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) { str << printLabelValue(tr("Hats Mode"), printHatsMode()); } return str.join(" "); diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 54a994dcf4b..f700a917d5d 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -40,6 +40,7 @@ set(${PROJECT_NAME}_SRCS simulateduiwidgetJumperTPRO.cpp simulateduiwidgetLR3PRO.cpp simulateduiwidgetNV14.cpp + simulateduiwidgetPL18.cpp simulateduiwidgetT8.cpp simulateduiwidgetTX12.cpp simulateduiwidgetTX16S.cpp diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index a3856aa86a1..11f99a33743 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -125,6 +125,7 @@ namespace Ui { class SimulatedUIWidgetT8; class SimulatedUIWidgetNV14; class SimulatedUIWidgetEL18; + class SimulatedUIWidgetPL18; } class SimulatedUIWidget9X: public SimulatedUIWidget @@ -407,4 +408,16 @@ class SimulatedUIWidgetEL18: public SimulatedUIWidget Ui::SimulatedUIWidgetEL18 * ui; }; +class SimulatedUIWidgetPL18: public SimulatedUIWidget +{ + Q_OBJECT + + public: + explicit SimulatedUIWidgetPL18(SimulatorInterface * simulator, QWidget * parent = nullptr); + virtual ~SimulatedUIWidgetPL18(); + + private: + Ui::SimulatedUIWidgetPL18 * ui; +}; + #endif // SIMULATEDUIWIDGET_H diff --git a/companion/src/simulation/simulateduiwidgetPL18.cpp b/companion/src/simulation/simulateduiwidgetPL18.cpp new file mode 100644 index 00000000000..5e3554245cc --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetPL18.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h + +#include "simulateduiwidget.h" +#include "ui_simulateduiwidgetPL18.h" + +SimulatedUIWidgetPL18::SimulatedUIWidgetPL18(SimulatorInterface *simulator, QWidget * parent): + SimulatedUIWidget(simulator, parent), + ui(new Ui::SimulatedUIWidgetPL18) +{ + RadioUiAction * act; + + ui->setupUi(this); + + // add actions in order of appearance on the help menu + + // Note: the PL18 has no physical buttons though at some point the trim joystick is repurposed + // allow for colorlcd key events and see what works + // the mouse click areas do not map to visual buttons on the background images + + act = new RadioUiAction(3, QList() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL); + addRadioWidget(ui->rightbuttons->addArea(QRect(10, 1, 80, 35), "PL18/left.png", act)); + + m_mouseMidClickAction = new RadioUiAction(2, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ROT_DN); + addRadioWidget(ui->rightbuttons->addArea(QRect(10, 40, 80, 35), "PL18/left.png", m_mouseMidClickAction)); + + act = new RadioUiAction(6, QList() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 80, 80, 35), "PL18/left.png", act)); + + act = new RadioUiAction(5, QList() << Qt::Key_Right, SIMU_STR_HLP_KEY_RGT, SIMU_STR_HLP_ACT_TELE); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 120, 80, 35), "PL18/left.png", act)); + + act = new RadioUiAction(1, QList() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 160, 80, 35), "PL18/left.png", act)); + + act = new RadioUiAction(0, QList() << Qt::Key_PageUp, SIMU_STR_HLP_KEY_PGUP, SIMU_STR_HLP_ACT_PGUP); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 200, 80, 35), "PL18/left.png", act)); + + act = new RadioUiAction(4, QList() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, + SIMU_STR_HLP_KEY_DN % "
" % SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_RTN); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 240, 80, 35), "PL18/left.png", act)); + + m_scrollUpAction = new RadioUiAction(-1, QList() << Qt::Key_Minus, SIMU_STR_HLP_KEY_MIN % "|" % SIMU_STR_HLP_MOUSE_UP, SIMU_STR_HLP_ACT_ROT_LFT); + m_scrollDnAction = new RadioUiAction(-1, QList() << Qt::Key_Plus << Qt::Key_Equal, SIMU_STR_HLP_KEY_PLS % "|" % SIMU_STR_HLP_MOUSE_DN, SIMU_STR_HLP_ACT_ROT_RGT); + connectScrollActions(); + + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 280, 30, 30), "PL18/left.png", m_screenshotAction)); + + m_backlightColors << QColor(47, 123, 227); + + setLcd(ui->lcd); +} + +SimulatedUIWidgetPL18::~SimulatedUIWidgetPL18() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetPL18.ui b/companion/src/simulation/simulateduiwidgetPL18.ui new file mode 100644 index 00000000000..c1bbcaedaf4 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetPL18.ui @@ -0,0 +1,206 @@ + + + SimulatedUIWidgetPL18 + + + + 0 + 0 + 520 + 500 + + + + + 0 + 0 + + + + + 520 + 500 + + + + + 520 + 500 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 100 + 500 + + + + + 100 + 500 + + + + background:url(:/images/simulator/PL18/right.png) + + + + + + + + 0 + 0 + + + + + 480 + 320 + + + + + 480 + 320 + + + + + 5 + + + + + + + + + 0 + 0 + + + + + 100 + 500 + + + + + 100 + 500 + + + + true + + + background:url(:/images/simulator/PL18/left.png); + + + + + + + + 0 + 0 + + + + + 320 + 10 + + + + + 320 + 10 + + + + + 5 + + + + background:url(:/images/simulator/PL18/top.png) + + + + + + + + 0 + 0 + + + + + 320 + 10 + + + + + 320 + 10 + + + + + 5 + false + + + + background:url(:/images/simulator/PL18/bottom.png) + + + + + + + + LcdWidget + QWidget +
lcdwidget.h
+ 1 +
+ + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+
+ + +
diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index dc0d34acc26..36f30b2586c 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -133,6 +133,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_FLYSKY_EL18: radioUiWidget = new SimulatedUIWidgetEL18(simulator, this); break; + case Board::BOARD_FLYSKY_PL18: + radioUiWidget = new SimulatedUIWidgetPL18(simulator, this); + break; default: radioUiWidget = new SimulatedUIWidget9X(simulator, this); break; diff --git a/fw.json b/fw.json index 728a2188278..b92436535d2 100644 --- a/fw.json +++ b/fw.json @@ -3,6 +3,8 @@ ["BETAFPV LiteRadio 3 Pro", "lr3pro-"], ["Flysky EL18", "el18-"], ["Flysky NV14", "nv14-"], + ["Flysky PL18", "pl18-"], + ["Flysky PL18EV", "pl18ev-"], ["FrSky Horus X10", "x10-"], ["FrSky Horus X10 Access", "x10-access-"], ["FrSky Horus X12s", "x12s-"], diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 577889d7d4a..e72fe13f0fd 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 NV14) +set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S NV14 PL18) set(RADIO_LANGUAGES CN CZ DA DE EN ES FI FR HE IT JP PT RU SK SE PL HU NL TW) set(TTS_LANGUAGES CN CZ DA DE EN ES FR HE IT JP PT RU SK SE PL HU NL) @@ -85,6 +85,8 @@ if(PCB STREQUAL X12S OR PCB STREQUAL X10) include(targets/horus/CMakeLists.txt) elseif(PCB STREQUAL NV14) include(targets/nv14/CMakeLists.txt) +elseif(PCB STREQUAL PL18) + include(targets/pl18/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) include(targets/taranis/CMakeLists.txt) else() diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index a1458053abd..6c0f81f6b0a 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -4,6 +4,8 @@ set(MASK_ARGS ${BITMAP_SIZE_ARGS}) if(PCB STREQUAL NV14) set(BITMAP_TARGET_PREFIX nv14) +elseif(PCB STREQUAL PL18) + set(BITMAP_TARGET_PREFIX pl18) elseif(PCB STREQUAL X12S) set(BITMAP_TARGET_PREFIX x12s) else() diff --git a/radio/src/boards/generic_stm32/analog_inputs.cpp b/radio/src/boards/generic_stm32/analog_inputs.cpp index 19036a17973..bbcbc7edaab 100644 --- a/radio/src/boards/generic_stm32/analog_inputs.cpp +++ b/radio/src/boards/generic_stm32/analog_inputs.cpp @@ -47,6 +47,9 @@ constexpr uint8_t n_ADC_spi = DIM(_ADC_spi); constexpr uint8_t n_GPIO = DIM(_ADC_GPIOs); constexpr uint8_t n_inputs = DIM(_ADC_inputs); +static_assert(n_inputs <= MAX_ADC_INPUTS, "Too many ADC inputs"); +static_assert(n_inputs <= MAX_ANALOG_INPUTS, "Too many analog inputs"); + static bool adc_init() { bool success = stm32_hal_adc_init(_ADC_adc, n_ADC, _ADC_inputs, _ADC_GPIOs, n_GPIO); diff --git a/radio/src/boards/generic_stm32/inputs.cpp b/radio/src/boards/generic_stm32/inputs.cpp index 385400e03f0..c1c3341c049 100644 --- a/radio/src/boards/generic_stm32/inputs.cpp +++ b/radio/src/boards/generic_stm32/inputs.cpp @@ -26,23 +26,27 @@ #include "stm32_keys.inc" -void keysInit() +#define __weak __attribute__((weak)) + +__weak void keysInit() { _init_keys(); _init_trims(); } -uint32_t readKeys() +__weak uint32_t readKeys() { return _read_keys(); } -uint32_t readTrims() +__weak uint32_t readTrims() { uint32_t trims = _read_trims(); + #if defined(PCBXLITE) if (_read_keys() & (1 << KEY_SHIFT)) return ((trims & 0x03) << 6) | ((trims & 0x0c) << 2); #endif + return trims; } diff --git a/radio/src/boards/generic_stm32/module_ports.cpp b/radio/src/boards/generic_stm32/module_ports.cpp index e4d37be48d7..e7e07fd3d74 100644 --- a/radio/src/boards/generic_stm32/module_ports.cpp +++ b/radio/src/boards/generic_stm32/module_ports.cpp @@ -28,6 +28,7 @@ #include "board.h" #include "dataconstants.h" +#if defined (HARDWARE_INTERNAL_MODULE) #if defined(INTMODULE_USART) #define INTMODULE_USART_IRQ_PRIORITY 5 @@ -110,7 +111,8 @@ extern "C" void INTMODULE_TIMER_IRQHandler() DEFINE_STM32_SOFTSERIAL_PORT(InternalModule, intmoduleTimer); -#endif +#endif // INTMODULE_USART +#endif // HARDWARE_INTERNAL_MODULE #include "module_timer_driver.h" diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 8a06aaa9269..f21ca87806d 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1040,6 +1040,7 @@ int cliSet(const char **argv) } #if defined(ENABLE_SERIAL_PASSTHROUGH) +#if defined(HARDWARE_INTERNAL_MODULE) static etx_module_state_t *spInternalModuleState = nullptr; static void spInternalModuleTx(uint8_t* buf, uint32_t len) @@ -1060,6 +1061,7 @@ static const etx_serial_init spIntmoduleSerialInitParams = { .polarity = ETX_Pol_Normal, }; +#endif // HARDWARE_INTERNAL_MODULE // TODO: use proper method instead extern bool cdcConnected; extern uint32_t usbSerialBaudRate(void*); @@ -1576,7 +1578,7 @@ int cliCrypt(const char ** argv) } #endif -#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) +#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) && !defined(PCBPL18) // from tp_gt911.cpp extern uint8_t tp_gt911_cfgVer; @@ -1648,7 +1650,7 @@ const CliCommand cliCommands[] = { #if defined(ACCESS_DENIED) && defined(DEBUG_CRYPT) { "crypt", cliCrypt, "" }, #endif -#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) +#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) && !defined(PCBPL18) { "reset_gt911", cliResetGT911, ""}, #endif { nullptr, nullptr, nullptr } /* sentinel */ diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 2459207fdb2..c69b8071d12 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -39,7 +39,7 @@ #define LABELS_LENGTH 100 // Maximum length of the label string #define LABEL_LENGTH 16 -#if defined(PCBHORUS) || defined(PCBNV14) +#if defined(PCBHORUS) || defined(PCBNV14) || defined(PCBPL18) #define MAX_MODELS 60 #define MAX_OUTPUT_CHANNELS 32 // number of real output channels CH1-CH32 #define MAX_FLIGHT_MODES 9 @@ -98,7 +98,7 @@ enum CurveType { #define MIN_POINTS_PER_CURVE 3 #define MAX_POINTS_PER_CURVE 17 -#if defined(PCBHORUS) || defined(PCBNV14) +#if defined(PCBHORUS) || defined(PCBNV14) || defined(PCBPL18) #define LEN_MODEL_NAME 15 #define LEN_TIMER_NAME 8 #define LEN_FLIGHT_MODE_NAME 10 @@ -408,10 +408,10 @@ enum PotsWarnMode { #define MAX_FLEX_SWITCHES 0 #endif -#if defined(RADIO_T20) -#define MAX_TRIMS 8 +#if NUM_TRIMS > 6 +#define MAX_TRIMS 8 #else -#define MAX_TRIMS 6 +#define MAX_TRIMS 6 #endif #define MAX_XPOTS_POSITIONS (MAX_POTS * XPOTS_MULTIPOS_COUNT) @@ -428,6 +428,13 @@ enum SwitchSources { SWSRC_FIRST_TRIM SKIP, SWSRC_LAST_TRIM SKIP = SWSRC_FIRST_TRIM + 2 * MAX_TRIMS - 1, +#if NUM_TRIMS > 6 + SWSRC_TrimT7Down, + SWSRC_TrimT7Up, + SWSRC_TrimT8Down, + SWSRC_TrimT8Up, +#endif + SWSRC_FIRST_LOGICAL_SWITCH SKIP, SWSRC_LAST_LOGICAL_SWITCH SKIP = SWSRC_FIRST_LOGICAL_SWITCH + MAX_LOGICAL_SWITCHES - 1, @@ -605,7 +612,7 @@ enum Functions { #if defined(DEBUG) FUNC_MAX SKIP #else - FUNC_MAX SKIP = FUNC_TEST - 1 + FUNC_MAX SKIP = FUNC_TEST #endif }; diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 392aefe8fba..fba828f073a 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -87,7 +87,7 @@ static inline void check_struct() CHKSIZE(CurveHeader, 4); CHKSIZE(CustomScreenData, 852); CHKTYPE(TopBarPersistentData, 444); -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) // TODO #else // Common for all variants diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index b149e4e12c0..3c8ec69cde9 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -602,7 +602,7 @@ PACK(struct CustomScreenData { #define TOPBAR_DATA #endif -#if defined(PCBHORUS) || defined(PCBTARANIS) || defined(PCBNV14) +#if defined(PCBHORUS) || defined(PCBTARANIS) || defined(PCBNV14) || defined(PCBPL18) #define SCRIPT_DATA \ NOBACKUP(ScriptData scriptsData[MAX_SCRIPTS]); #else diff --git a/radio/src/debug.h b/radio/src/debug.h index aa31b77cf1d..a5f84ddbec1 100644 --- a/radio/src/debug.h +++ b/radio/src/debug.h @@ -46,8 +46,8 @@ EXTERN_C(extern volatile uint32_t g_tmr10ms); #define debugPrintf(...) #endif -#define TRACE_TIME_FORMAT "%0.2fs: " -#define TRACE_TIME_VALUE ((float)g_tmr10ms / 100.0) +#define TRACE_TIME_FORMAT "%dms: " +#define TRACE_TIME_VALUE (g_tmr10ms * 10) #define TRACE_NOCRLF(...) debugPrintf(__VA_ARGS__) #define TRACE(f_, ...) debugPrintf((TRACE_TIME_FORMAT f_ CRLF), TRACE_TIME_VALUE, ##__VA_ARGS__) diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 9c54cbea837..8ddba702d29 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -93,7 +93,7 @@ void RadioCalibrationPage::buildBody(FormWindow * window) deco->setSlidersVisible(true); deco->setFlightModeVisible(false); -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) new TextButton(window, {LCD_W - 120, LCD_H - 140, 90, 40}, "Next", [=]() -> uint8_t { nextStep(); diff --git a/radio/src/gui/colorlcd/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio_diaganas.cpp index 903c32c8161..18ef7709ba9 100644 --- a/radio/src/gui/colorlcd/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio_diaganas.cpp @@ -177,7 +177,7 @@ class AnaCalibratedViewWindow: public AnaViewWindow { }, COLOR_THEME_PRIMARY1); lv_obj_set_grid_cell(lbl->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 5, LV_GRID_ALIGN_CENTER, 0, 1); -#if !defined(SIMU) && !defined(PCBNV14) +#if !defined(SIMU) && !defined(PCBNV14) && !defined(PCBPL18) line = newLine(grid); auto lbl2 = new StaticText(line, rect_t{}, std::string("Touch GT911 FW ver: ") + std::to_string(touchGT911fwver), COLOR_THEME_PRIMARY1); lv_obj_set_grid_cell(lbl2->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 5, LV_GRID_ALIGN_CENTER, 0, 1); diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index a12c102e85c..d8dee02bb92 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -25,7 +25,11 @@ #include "hal/rotary_encoder.h" +#if defined(PCBPL18) +static const uint8_t _trimMap[MAX_TRIMS * 2] = {8, 9, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 0, 1, 6, 7}; +#else static const uint8_t _trimMap[MAX_TRIMS * 2] = {6, 7, 4, 5, 2, 3, 0, 1, 8, 9, 10, 11}; +#endif static EnumKeys get_ith_key(uint8_t i) { @@ -122,8 +126,13 @@ class RadioKeyDiagsWindow : public Window for (uint8_t i = 0; i < keysGetMaxTrims() * 2; i++) { coord_t y = 1 + FH + FH * (i / 2); if (i & 1) { +#if defined(PCBPL18) + dc->drawText(TRIM_COLUMN, y, "TR", COLOR_THEME_PRIMARY1); + dc->drawNumber(TRIM_COLUMN + 20, y, i / 2 + 1, COLOR_THEME_PRIMARY1); +#else dc->drawText(TRIM_COLUMN, y, "T", COLOR_THEME_PRIMARY1); dc->drawNumber(TRIM_COLUMN + 10, y, i / 2 + 1, COLOR_THEME_PRIMARY1); +#endif } displayTrimState(dc, i & 1 ? TRIM_PLUS_COLUMN : TRIM_MINUS_COLUMN, y, _trimMap[i]); } diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index f5976861f1c..0120f1775ff 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -110,12 +110,14 @@ static void ghostmoduleconfig_cb(lv_event_t* e) } } +#if defined(HARDWARE_KEYS) && !defined(PCBPL18) void RadioGhostModuleConfig::onCancel() { reusableBuffer.ghostMenu.buttonAction = GHST_BTN_JOYLEFT; reusableBuffer.ghostMenu.menuAction = GHST_MENU_CTRL_NONE; moduleState[EXTERNAL_MODULE].counter = GHST_MENU_CONTROL; } +#endif RadioGhostModuleConfig::RadioGhostModuleConfig(uint8_t moduleIdx) : Page(ICON_RADIO_TOOLS), @@ -144,7 +146,7 @@ void RadioGhostModuleConfig::buildBody(FormWindow * window) new GhostModuleConfigWindow(window, {0, 0, LCD_W, LCD_H - MENU_HEADER_HEIGHT - 5}); } -#if defined(HARDWARE_KEYS) +#if defined(HARDWARE_KEYS) && !defined(PCBPL18) void RadioGhostModuleConfig::onEvent(event_t event) { switch (event) { diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.h b/radio/src/gui/colorlcd/radio_ghost_module_config.h index d80a6991559..3d758ff37ea 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.h +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.h @@ -28,7 +28,7 @@ class RadioGhostModuleConfig: public Page public: explicit RadioGhostModuleConfig(uint8_t moduleIdx); -#if defined(HARDWARE_KEYS) +#if defined(HARDWARE_KEYS) && !defined(PCBPL18) void onEvent(event_t event) override; void checkEvents() override; void onCancel() override; diff --git a/radio/src/gui/colorlcd/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp index ce97935bd25..7d38e02b685 100644 --- a/radio/src/gui/colorlcd/radio_version.cpp +++ b/radio/src/gui/colorlcd/radio_version.cpp @@ -77,13 +77,14 @@ class VersionDialog : public Dialog memclear(&reusableBuffer.hardwareAndSettings.modules, sizeof(reusableBuffer.hardwareAndSettings.modules)); reusableBuffer.hardwareAndSettings.updateTime = get_tmr10ms(); - +#if defined(HARDWARE_INTERNAL_MODULE) // Query modules if (isModulePXX2(INTERNAL_MODULE) && modulePortPowered(INTERNAL_MODULE)) { moduleState[INTERNAL_MODULE].readModuleInformation( &reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1); } +#endif if (isModulePXX2(EXTERNAL_MODULE) && modulePortPowered(EXTERNAL_MODULE)) { moduleState[EXTERNAL_MODULE].readModuleInformation( @@ -165,11 +166,13 @@ class VersionDialog : public Dialog void update() { +#if defined(HARDWARE_INTERNAL_MODULE) updateModule(INTERNAL_MODULE, int_name, int_module_status_w, int_status, int_rx_name_w, int_rx_name, int_rx_status_w, int_rx_status); +#endif updateModule(EXTERNAL_MODULE, ext_name, ext_module_status_w, ext_status, @@ -309,11 +312,13 @@ class VersionDialog : public Dialog { if (get_tmr10ms() >= reusableBuffer.hardwareAndSettings.updateTime) { // Query modules +#if defined(HARDWARE_INTERNAL_MODULE) if (isModulePXX2(INTERNAL_MODULE) && modulePortPowered(INTERNAL_MODULE)) { moduleState[INTERNAL_MODULE].readModuleInformation( &reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1); } +#endif if (isModulePXX2(EXTERNAL_MODULE) && modulePortPowered(EXTERNAL_MODULE)) { moduleState[EXTERNAL_MODULE].readModuleInformation( &reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE], @@ -332,7 +337,7 @@ RadioVersionPage::RadioVersionPage(): { } -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) extern const char* boardLcdType; #endif @@ -355,7 +360,7 @@ void RadioVersionPage::build(FormWindow * window) version += options[i]; } -#if defined(PCBNV14) && !defined(SIMU) +#if (defined(PCBNV14) || defined(PCBPL18)) && !defined(SIMU) version += nl; version += "LCD: "; version += boardLcdType; diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 20b2056ce45..1d35d1b0a43 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -180,20 +180,21 @@ class SpecialFunctionEditPage : public Page case FUNC_PLAY_TRACK: case FUNC_BACKGND_MUSIC: case FUNC_PLAY_SCRIPT: + case FUNC_RGB_LED: new StaticText(line, rect_t{}, STR_VALUE, 0, COLOR_THEME_PRIMARY1); new FileChoice( line, rect_t{}, - func == FUNC_PLAY_SCRIPT - ? SCRIPTS_FUNCS_PATH + func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED + ? (func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH : SCRIPTS_RGB_PATH) : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + std::string(currentLanguagePack->id, 2), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, + (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), [=]() { return std::string(cfn->play.name, ZLEN(cfn->play.name)); }, [=](std::string newValue) { strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); SET_DIRTY(); - if (func == FUNC_PLAY_SCRIPT) + if (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) LUA_LOAD_MODEL_SCRIPTS(); }, true); // strip extension diff --git a/radio/src/hal/key_driver.cpp b/radio/src/hal/key_driver.cpp index 6529bc04d70..dc500167f8e 100644 --- a/radio/src/hal/key_driver.cpp +++ b/radio/src/hal/key_driver.cpp @@ -22,6 +22,8 @@ #include "key_driver.h" #include "definitions.h" +#include "dataconstants.h" + #include "hal_keys.inc" uint32_t keysGetSupported() diff --git a/radio/src/hal/key_driver.h b/radio/src/hal/key_driver.h index f0d20baa5f5..1701071a621 100644 --- a/radio/src/hal/key_driver.h +++ b/radio/src/hal/key_driver.h @@ -50,11 +50,7 @@ enum EnumKeys { MAX_KEYS }; -#if defined(RADIO_T20) -#define MAX_TRIMS 8 -#else -#define MAX_TRIMS 6 -#endif + // returns a bit field with each key set as (1 << KEY_xxx) uint32_t readKeys(); diff --git a/radio/src/keys.cpp b/radio/src/keys.cpp index e9cd936dfa0..54e3f3a238c 100644 --- a/radio/src/keys.cpp +++ b/radio/src/keys.cpp @@ -30,11 +30,7 @@ #include "timers_driver.h" #include "hal/watchdog_driver.h" #include "hal/rotary_encoder.h" - -// required by watchdog macro.. -#if !defined(SIMU) -#include "stm32_cmsis.h" -#endif +#include "dataconstants.h" // long key press minimum duration (x10ms), // must be less than KEY_REPEAT_DELAY diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index a1ca3c55395..69eafc19f2a 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -169,7 +169,13 @@ enum CurveRefType { #define TRIM_ELE (-2) #define TRIM_THR (-3) #define TRIM_AIL (-4) -#if defined(PCBHORUS) +#if defined(PCBPL18) + #define TRIM_T5 (-5) + #define TRIM_T6 (-6) + #define TRIM_T7 (-7) + #define TRIM_T8 (-8) + #define TRIM_LAST TRIM_T8 +#elif defined(PCBHORUS) #define TRIM_T5 (-5) #define TRIM_T6 (-6) #define TRIM_LAST TRIM_T6 diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 101d417d255..ffb7cc60477 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -203,10 +203,6 @@ extern uint8_t heartbeat; #include "keys.h" #include "pwr.h" -// #if defined(PCBFRSKY) || defined(PCBNV14) -// extern uint8_t potsPos[NUM_XPOTS]; -// #endif - bool trimDown(uint8_t idx); #if defined(KEYS_GPIO_REG_BIND) diff --git a/radio/src/opentx_types.h b/radio/src/opentx_types.h index 4f556d12c04..07bcd68b809 100644 --- a/radio/src/opentx_types.h +++ b/radio/src/opentx_types.h @@ -83,6 +83,7 @@ typedef uint32_t LcdFlags; typedef uint16_t event_t; typedef uint32_t tmr10ms_t; +typedef int32_t rotenc_t; typedef int32_t getvalue_t; typedef uint32_t mixsrc_t; typedef int32_t swsrc_t; diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 27ddaf56cd2..e2fbd4bd6c7 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -116,12 +116,9 @@ static void sendFailsafeChannels(uint8_t*& p_buf, uint8_t module) static void setupPulsesMulti(uint8_t*& p_buf, uint8_t module) { static int counter[2] = {0,0}; //TODO - static uint8_t invert[2] = {0x00, //internal -#if defined(PCBTARANIS) || defined(PCBHORUS) || defined(PCBNV14) + static uint8_t invert[2] = { + 0x00, //internal 0x08 //external -#else - 0x00 //external -#endif }; uint8_t type=MULTI_NORMAL; diff --git a/radio/src/sdcard.cpp b/radio/src/sdcard.cpp index 9555ebb60ca..7b45c20d9f7 100644 --- a/radio/src/sdcard.cpp +++ b/radio/src/sdcard.cpp @@ -545,7 +545,7 @@ void sdMount() void sdDone() { TRACE("sdDone"); - + if (sdMounted()) { audioQueue.stopSD(); @@ -557,8 +557,10 @@ void sdDone() f_close(&g_bluetoothFile); #endif - f_mount(nullptr, "", 0); // unmount SD + f_mount(nullptr, "", 0); // unmount SD } + + storageDeInit(); } uint32_t sdMounted() diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 9523affbbbc..e06ab5936ed 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -348,7 +348,7 @@ long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v) void OpenTxSim::updateKeysAndSwitches(bool start) { static int keys[] = { -#if defined(PCBNV14) +#if defined(PCBFLYSKY) // no keys #elif defined(PCBHORUS) KEY_Page_Up, KEY_PAGEUP, diff --git a/radio/src/storage/yaml/CMakeLists.txt b/radio/src/storage/yaml/CMakeLists.txt index b320ff016a9..6a7e537bbe6 100644 --- a/radio/src/storage/yaml/CMakeLists.txt +++ b/radio/src/storage/yaml/CMakeLists.txt @@ -20,6 +20,8 @@ elseif(PCB STREQUAL X10) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_x10.cpp) elseif(PCB STREQUAL NV14) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_nv14.cpp) +elseif(PCB STREQUAL PL18) + set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_pl18.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 fe77e7c5220..3b1a2254cf1 100644 --- a/radio/src/storage/yaml/yaml_datastructs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs.cpp @@ -34,6 +34,8 @@ #include "yaml_datastructs_x10.cpp" #elif defined(PCBNV14) #include "yaml_datastructs_nv14.cpp" +#elif defined(PCBPL18) + #include "yaml_datastructs_pl18.cpp" #elif defined(PCBX7) #if defined(RADIO_TPRO) || defined(RADIO_TPROV2) #include "yaml_datastructs_tpro.cpp" diff --git a/radio/src/storage/yaml/yaml_datastructs_funcs.cpp b/radio/src/storage/yaml/yaml_datastructs_funcs.cpp index 85cec0b2301..0745654ef8f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_funcs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_funcs.cpp @@ -38,7 +38,7 @@ // ======== // // If any of these static_assert() fails, you need to check that -// the functions bellow are still applicable. +// the functions below are still applicable. // // Please note that the sizes used here are those from the v220 format // (see storage/conversions/yaml/datastructs_220.h) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp new file mode 100644 index 00000000000..283256262bd --- /dev/null +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -0,0 +1,915 @@ +// generated by generate_yaml.py + +// +// Enums first +// + +const struct YamlIdStr enum_HatsMode[] = { + { HATSMODE_TRIMS_ONLY, "TRIMS_ONLY" }, + { HATSMODE_KEYS_ONLY, "KEYS_ONLY" }, + { HATSMODE_SWITCHABLE, "SWITCHABLE" }, + { HATSMODE_GLOBAL, "GLOBAL" }, + { 0, NULL } +}; +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_TEST, "TEST" }, + { 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_MIN, "MIN" }, + { MIXSRC_MAX, "MAX" }, + { MIXSRC_TrimRud, "TrimRud" }, + { MIXSRC_TrimEle, "TrimEle" }, + { MIXSRC_TrimThr, "TrimThr" }, + { MIXSRC_TrimAil, "TrimAil" }, + { MIXSRC_TrimT5, "TrimT5" }, + { MIXSRC_TrimT6, "TrimT6" }, + { MIXSRC_TrimT7, "TrimT7" }, + { MIXSRC_TrimT8, "TrimT8" }, + { 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_TrimT7Down, "TrimT7Down" }, + { SWSRC_TrimT7Up, "TrimT7Up" }, + { SWSRC_TrimT8Down, "TrimT8Down" }, + { SWSRC_TrimT8Up, "TrimT8Up" }, + { 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_ZoneOptionValueEnum[] = { + { ZOV_Unsigned, "Unsigned" }, + { ZOV_Signed, "Signed" }, + { ZOV_Bool, "Bool" }, + { ZOV_String, "String" }, + { ZOV_Source, "Source" }, + { ZOV_Color, "Color" }, + { 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), + 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", 6), + YAML_END +}; +static const struct YamlNode struct_anonymous_2[] = { + YAML_SIGNED( "val", 16 ), + YAML_UNSIGNED( "mode", 8 ), + YAML_UNSIGNED( "param", 8 ), + YAML_SIGNED( "spare", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_3[] = { + YAML_SIGNED( "val1", 32 ), + YAML_SIGNED( "val2", 16 ), + YAML_END +}; +static const struct YamlNode union_anonymous_0_elmts[] = { + YAML_STRUCT("play", 48, struct_anonymous_1, NULL), + YAML_STRUCT("all", 48, struct_anonymous_2, NULL), + YAML_STRUCT("clear", 48, 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), + YAML_CUSTOM("def",r_customFn,w_customFn), + YAML_PADDING( 48 ), + YAML_PADDING( 1 ), + YAML_PADDING( 7 ), + YAML_END +}; +static const struct YamlNode struct_RadioData[] = { + YAML_UNSIGNED( "manuallyEdited", 1 ), + YAML_SIGNED( "timezoneMinutes", 3 ), + YAML_ENUM("hatsMode", 2, enum_HatsMode), + YAML_UNSIGNED( "ppmunit", 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_SIGNED( "currModel", 8 ), + YAML_UNSIGNED( "contrast", 8 ), + YAML_UNSIGNED( "vBatWarn", 8 ), + YAML_SIGNED( "txVoltageCalibration", 8 ), + YAML_ENUM("backlightMode", 3, enum_BacklightMode), + YAML_ENUM("antennaMode", 2, enum_AntennaModes), + YAML_UNSIGNED( "disableRtcWarning", 1 ), + YAML_UNSIGNED( "keysBacklight", 1 ), + YAML_UNSIGNED( "dontPlayHello", 1 ), + YAML_ENUM("internalModule", 8, enum_ModuleType), + 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_PADDING( 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), + 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_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", 72, 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_ARRAY("potsConfig", 4, 16, struct_potConfig, nullptr), + YAML_ARRAY("switchConfig", 2, 32, struct_switchConfig, nullptr), + YAML_ARRAY("flexSwitches", 0, MAX_FLEX_SWITCHES, struct_flexSwitch, flex_sw_valid), + YAML_STRING("currModelFilename", 17), + YAML_UNSIGNED( "modelQuickSelect", 1 ), + YAML_UNSIGNED( "blOffBright", 7 ), + YAML_STRING("bluetoothName", 10), + YAML_STRING("ownerRegistrationID", 8), + YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), + YAML_UNSIGNED( "rotEncMode", 2 ), + YAML_SIGNED( "uartSampleMode", 2 ), + YAML_PADDING( 3 ), + YAML_UNSIGNED( "audioMuteEnable", 1 ), + YAML_STRING("selectedTheme", 26), + YAML_UNSIGNED( "radioThemesDisabled", 1 ), + YAML_UNSIGNED( "radioGFDisabled", 1 ), + YAML_UNSIGNED( "radioTrainerDisabled", 1 ), + YAML_UNSIGNED( "modelHeliDisabled", 1 ), + YAML_UNSIGNED( "modelFMDisabled", 1 ), + YAML_UNSIGNED( "modelCurvesDisabled", 1 ), + YAML_UNSIGNED( "modelGVDisabled", 1 ), + YAML_UNSIGNED( "modelLSDisabled", 1 ), + YAML_UNSIGNED( "modelSFDisabled", 1 ), + YAML_UNSIGNED( "modelCustomScriptsDisabled", 1 ), + YAML_UNSIGNED( "modelTelemetryDisabled", 1 ), + 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), + 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", 8 ), + YAML_SIGNED_CUST( "value", 8, in_read_weight, in_write_weight ), + YAML_END +}; +static const struct YamlNode struct_MixData[] = { + YAML_SIGNED_CUST( "weight", 11, in_read_weight, in_write_weight ), + YAML_UNSIGNED( "destCh", 5 ), + YAML_UNSIGNED_CUST( "srcRaw", 10, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED( "carryTrim", 1 ), + YAML_UNSIGNED( "mixWarn", 2 ), + YAML_ENUM("mltpx", 2, enum_MixerMultiplex), + YAML_PADDING( 1 ), + YAML_SIGNED_CUST( "offset", 13, in_read_weight, in_write_weight ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + 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_UNSIGNED_CUST( "srcRaw", 10, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED( "chn", 5 ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + YAML_SIGNED_CUST( "weight", 8, in_read_weight, in_write_weight ), + YAML_STRING("name", 6), + YAML_SIGNED_CUST( "offset", 8, in_read_weight, in_write_weight ), + YAML_STRUCT("curve", 16, struct_CurveRef, NULL), + 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), + YAML_CUSTOM("def",r_logicSw,w_logicSw), + YAML_PADDING( 10 ), + YAML_PADDING( 10 ), + YAML_SIGNED_CUST( "andsw", 10, r_swtchSrc, w_swtchSrc ), + YAML_PADDING( 1 ), + YAML_PADDING( 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), + 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, 9, 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_END +}; +static const struct YamlNode struct_anonymous_13[] = { + YAML_UNSIGNED( "flags", 8 ), + 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", 8, struct_anonymous_12, NULL), + YAML_STRUCT("dsmp", 8, 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), + 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), + 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), + 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_ZoneOptionValue_elmts[] = { + YAML_UNSIGNED( "unsignedValue", 32 ), + YAML_SIGNED( "signedValue", 32 ), + YAML_UNSIGNED( "boolValue", 32 ), + YAML_STRING("stringValue", 8), + YAML_CUSTOM("source",r_zov_source,w_zov_source), + YAML_CUSTOM("color",r_zov_color,w_zov_color), + YAML_END +}; +static const struct YamlNode struct_ZoneOptionValueTyped[] = { + YAML_IDX, + YAML_ENUM("type", 32, enum_ZoneOptionValueEnum), + YAML_UNION("value", 64, union_ZoneOptionValue_elmts, select_zov), + YAML_END +}; +static const struct YamlNode struct_WidgetPersistentData[] = { + YAML_ARRAY("options", 96, 5, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_ZonePersistentData[] = { + YAML_IDX, + YAML_STRING("widgetName", 12), + YAML_STRUCT("widgetData", 480, struct_WidgetPersistentData, NULL), + YAML_END +}; +static const struct YamlNode struct_LayoutPersistentData[] = { + YAML_ARRAY("zones", 576, 10, struct_ZonePersistentData, NULL), + YAML_ARRAY("options", 96, 10, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_CustomScreenData[] = { + YAML_IDX, + YAML_STRING("LayoutId", 12), + YAML_STRUCT("layoutData", 6720, struct_LayoutPersistentData, NULL), + YAML_END +}; +static const struct YamlNode struct_TopBarPersistentData[] = { + YAML_ARRAY("zones", 576, 6, struct_ZonePersistentData, NULL), + YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + 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_ENUM("hatsMode", 2, enum_HatsMode), + YAML_PADDING( 2 ), + 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", 136, 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", 72, 64, struct_CustomFunctionData, cfn_is_active), + YAML_STRUCT("swashR", 64, struct_SwashRingData, swash_is_active), + YAML_ARRAY("flightModeData", 384, 9, struct_FlightModeData, fmd_is_active), + YAML_UNSIGNED_CUST( "thrTraceSrc", 8, r_thrSrc, w_thrSrc ), + YAML_CUSTOM("switchWarningState",r_swtchWarn,w_swtchWarn), + YAML_PADDING( 64 ), + YAML_ARRAY("gvars", 56, 9, 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), + YAML_ENUM("jitterFilter", 2, enum_ModelOverridableEnable), + 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, 60, struct_TelemetrySensor, NULL), + YAML_ARRAY("screenData", 6816, 10, struct_CustomScreenData, NULL), + YAML_STRUCT("topbarData", 3552, struct_TopBarPersistentData, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), + YAML_ENUM("radioThemesDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("radioGFDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("radioTrainerDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelHeliDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelFMDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelCurvesDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelGVDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelLSDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelSFDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelCustomScriptsDisabled", 2, enum_ModelOverridableEnable), + YAML_ENUM("modelTelemetryDisabled", 2, enum_ModelOverridableEnable), + 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/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index a24ee4e8242..e7e353b02e2 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -22,17 +22,19 @@ option(DEBUG_USB_INTERRUPTS "Count individual USB interrupts" OFF) option(DEBUG_TIMERS "Time critical parts of the code" OFF) option(DEBUG_BLUETOOTH "Debug Bluetooth" OFF) -# option to select the default internal module -#set(DEFAULT_INTERNAL_MODULE NONE CACHE STRING "Default internal module") -set_property(CACHE DEFAULT_INTERNAL_MODULE PROPERTY STRINGS ${INTERNAL_MODULES}) - -# define variables for internal modules -foreach(module ${INTERNAL_MODULES}) - set(INTERNAL_MODULE_${module} ON CACHE BOOL "Support for ${module} internal module") - if (INTERNAL_MODULE_${module}) - message("-- Adding support for ${module} as internal module") - endif() -endforeach() +if(INTERNAL_MODULES) + # option to select the default internal module + #set(DEFAULT_INTERNAL_MODULE NONE CACHE STRING "Default internal module") + set_property(CACHE DEFAULT_INTERNAL_MODULE PROPERTY STRINGS ${INTERNAL_MODULES}) + + # define variables for internal modules + foreach(module ${INTERNAL_MODULES}) + set(INTERNAL_MODULE_${module} ON CACHE BOOL "Support for ${module} internal module") + if (INTERNAL_MODULE_${module}) + message("-- Adding support for ${module} as internal module") + endif() + endforeach() +endif() if(INTERNAL_MODULE_PXX1) add_definitions(-DHARDWARE_INTERNAL_MODULE) diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 117e1c6431c..85e66458cf2 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -69,8 +69,7 @@ if(DEBUG_SEGGER_RTT) ) endif() - -if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14) +if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14 OR PCB STREQUAL PL18) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../gui/colorlcd/fonts.cpp @@ -79,7 +78,6 @@ if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14) ../../../../../thirdparty/libopenui/src/bitmapbuffer.cpp ../../../../../thirdparty/libopenui/thirdparty/lz4/lz4.c ../../../../../targets/common/arm/stm32/dma2d.cpp - ../../../../../targets/common/arm/stm32/diskio_sdio.cpp ../../../../../targets/common/arm/stm32/rtc_driver.cpp ../../../../../targets/common/arm/stm32/diskio_spi_flash.cpp ../../../../../targets/common/arm/stm32/spi_flash.cpp @@ -90,12 +88,22 @@ if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14) ../../../../../targets/${TARGET_DIR}/haptic_driver.cpp ) + if(NOT PCB STREQUAL PL18) + set(BOOTLOADER_SRC ${BOOTLOADER_SRC} + ../../../../../targets/common/arm/stm32/diskio_sdio.cpp + ) + else() + set(BOOTLOADER_SRC ${BOOTLOADER_SRC} + ../../../../../targets/pl18/key_driver.cpp + ) + endif() + # Add LVGL sources foreach(LVGL_FILE ${LVGL_SRC_FILES_MINIMAL}) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../${LVGL_FILE}) endforeach() - if(NOT PCB STREQUAL NV14) + if(NOT (PCB STREQUAL NV14 OR PCB STREQUAL PL18)) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../targets/${TARGET_DIR}/led_driver.cpp diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index a83ab59afd2..32a1bdce27f 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -56,12 +56,17 @@ #define APP_START_ADDRESS (uint32_t)(FIRMWARE_ADDRESS + BOOTLOADER_SIZE) -#if defined(EEPROM) +#if defined(EEPROM) || defined(SPI_FLASH) #define MAIN_MENU_LEN 3 #else #define MAIN_MENU_LEN 2 #endif +#if defined(SPI_FLASH) + #include "spi_flash.h" + #define SEL_CLEAR_FLASH_STORAGE_MENU_LEN 2 +#endif + typedef void (*voidFunction)(void); #define jumpTo(addr) do { \ @@ -369,6 +374,10 @@ int bootloaderMain() memoryType = MEM_EEPROM; state = ST_DIR_CHECK; break; +#elif defined(SPI_FLASH) + case 1: + state = ST_CLEAR_FLASH_CHECK; + break; #endif default: if(vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN-1)) @@ -511,6 +520,35 @@ int bootloaderMain() else if (memoryType == MEM_EEPROM && eepromWritten >= EEPROM_SIZE) { state = ST_FLASH_DONE; // Backstop } +#endif +#if defined(SPI_FLASH) + } else if (state == ST_CLEAR_FLASH_CHECK) { + bootloaderDrawScreen(state, vpos); + if (event == EVT_KEY_REPT(KEY_DOWN) || event == EVT_KEY_FIRST(KEY_DOWN)) { + if (vpos < SEL_CLEAR_FLASH_STORAGE_MENU_LEN - 1) { vpos++; } + continue; + } + if (event == EVT_KEY_REPT(KEY_UP) || event == EVT_KEY_FIRST(KEY_UP)) { + if (vpos > 0) { vpos--; } + continue; + } + if (event == EVT_KEY_LONG(KEY_ENTER) && vpos == 0) + { + state = ST_CLEAR_FLASH; + } else if (event == EVT_KEY_BREAK(KEY_EXIT) || + (event == EVT_KEY_BREAK(KEY_ENTER) && vpos == 1) ) { + vpos = 0; + state = ST_START; + continue; + } + } else if (state == ST_CLEAR_FLASH) { + bootloaderDrawScreen(state, 0); + lcdRefresh(); + if(event != EVT_KEY_BREAK(KEY_ENTER)) + continue; + flashSpiEraseAll(); + vpos = 0; + state = ST_START; #endif } else if (state == ST_RADIO_MENU) { if(bootloaderRadioMenu(radioMenuItem, event)) @@ -565,6 +603,6 @@ int bootloaderMain() return 0; } -#if !defined(SIMU) && (defined(PCBHORUS) || defined(PCBNV14)) +#if !defined(SIMU) && (defined(PCBHORUS) || defined(PCBFLYSKY)) void *__dso_handle = nullptr; #endif diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.h b/radio/src/targets/common/arm/stm32/bootloader/boot.h index fd93b3ef567..5f747239fef 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.h +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.h @@ -54,6 +54,10 @@ enum BootloaderState { ST_FLASH_DONE, ST_RESTORE_MENU, ST_USB, +#if defined(SPI_FLASH) + ST_CLEAR_FLASH_CHECK, + ST_CLEAR_FLASH, +#endif ST_RADIO_MENU, ST_REBOOT, }; diff --git a/radio/src/targets/common/arm/stm32/diskio_spi_flash.cpp b/radio/src/targets/common/arm/stm32/diskio_spi_flash.cpp index bdd7db86785..3f8dc014b6f 100644 --- a/radio/src/targets/common/arm/stm32/diskio_spi_flash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_spi_flash.cpp @@ -30,6 +30,7 @@ #include "drivers/frftl.h" static FrFTL _frftl; +static bool frftlInitDone = false; static bool flashRead(uint32_t addr, uint8_t* buf, uint32_t len) { @@ -89,6 +90,7 @@ static DSTATUS spi_flash_initialize(BYTE lun) if (!ftlInit(&_frftl, &_frftl_cb, flashSizeMB)) { return STA_NOINIT; } + frftlInitDone = true; #endif return 0; @@ -102,7 +104,7 @@ static DSTATUS spi_flash_status (BYTE lun) static DRESULT spi_flash_read(BYTE lun, BYTE * buff, DWORD sector, UINT count) { #if defined(USE_FLASH_FTL) - while(count) { + while(frftlInitDone && count) { if(!ftlRead(&_frftl, sector, (uint8_t*)buff)) { return RES_ERROR; @@ -122,7 +124,7 @@ static DRESULT spi_flash_read(BYTE lun, BYTE * buff, DWORD sector, UINT count) static DRESULT spi_flash_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count) { #if defined(USE_FLASH_FTL) - if (!ftlWrite(&_frftl, sector, count, (uint8_t*)buff)) { + if (frftlInitDone && !ftlWrite(&_frftl, sector, count, (uint8_t*)buff)) { return RES_ERROR; } #else @@ -166,7 +168,7 @@ static DRESULT spi_flash_ioctl(BYTE lun, BYTE ctrl, void *buff) case CTRL_SYNC: #if defined(USE_FLASH_FTL) - if (!ftlSync(&_frftl)) { + if (frftlInitDone && !ftlSync(&_frftl)) { res = RES_ERROR; } #else @@ -176,7 +178,7 @@ static DRESULT spi_flash_ioctl(BYTE lun, BYTE ctrl, void *buff) case CTRL_TRIM: #if defined(USE_FLASH_FTL) - if (!ftlTrim(&_frftl, *(DWORD*)buff, 1 + *((DWORD*)buff + 1) - *(DWORD*)buff)) { + if (frftlInitDone && !ftlTrim(&_frftl, *(DWORD*)buff, 1 + *((DWORD*)buff + 1) - *(DWORD*)buff)) { res = RES_ERROR; } #endif diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index 46a50b36e72..fb5b7e49e96 100644 --- a/radio/src/targets/common/arm/stm32/pwr_driver.cpp +++ b/radio/src/targets/common/arm/stm32/pwr_driver.cpp @@ -38,9 +38,11 @@ void pwrInit() #endif // Internal module power +#if defined(HARDWARE_INTERNAL_MODULE) INTERNAL_MODULE_OFF(); GPIO_InitStructure.GPIO_Pin = INTMODULE_PWR_GPIO_PIN; GPIO_Init(INTMODULE_PWR_GPIO, &GPIO_InitStructure); +#endif // External module power EXTERNAL_MODULE_PWR_OFF(); diff --git a/radio/src/targets/common/arm/stm32/spi_flash.cpp b/radio/src/targets/common/arm/stm32/spi_flash.cpp index 100ce9559ec..c9e25567837 100644 --- a/radio/src/targets/common/arm/stm32/spi_flash.cpp +++ b/radio/src/targets/common/arm/stm32/spi_flash.cpp @@ -223,10 +223,19 @@ static bool flash_read_sfdp(SpiFlashDescriptor* desc) return true; } +void flashSpiSync() +{ + uint8_t status; + do { + flash_do_cmd(FLASH_CMD_STATUS, 0, &status, 1); + } while (status & 0x01); +} + bool flashSpiInit(void) { stm32_spi_init(&_flash_spi); delay_ms(1); + flashSpiSync(); if (!flash_read_id(&_flashDescriptor)) { return false; @@ -239,14 +248,6 @@ bool flashSpiInit(void) return true; } -void flashSpiSync() -{ - uint8_t status; - do { - flash_do_cmd(FLASH_CMD_STATUS, 0, &status, 1); - } while (status & 0x01); -} - uint32_t flashSpiGetSize() { return (1UL << _flashDescriptor.log2Size); diff --git a/radio/src/targets/common/arm/stm32/stm32_adc.cpp b/radio/src/targets/common/arm/stm32/stm32_adc.cpp index 4244a5d96a9..48c98278ee9 100644 --- a/radio/src/targets/common/arm/stm32/stm32_adc.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_adc.cpp @@ -26,9 +26,10 @@ #include "opentx.h" -#define ADC_COMMON ((ADC_Common_TypeDef *) ADC_BASE) -#define MAX_ADC_INPUTS 32 -#define OVERSAMPLING 4 +#define ADC_COMMON ((ADC_Common_TypeDef *)ADC_BASE) +#define OVERSAMPLING 4 + +#define SAMPLING_TIMEOUT_US 500 // Please note that we use the same prio for DMA TC and ADC IRQs // to avoid issues with preemption between these 2 @@ -43,7 +44,7 @@ static volatile uint32_t _adc_inhibit_mask; static uint16_t _adc_dma_buffer[MAX_ADC_INPUTS] __DMA; // ADCs started -static uint8_t _adc_started_mask; +static volatile uint8_t _adc_started_mask; static volatile uint8_t _adc_completed; static const stm32_adc_t* _adc_ADCs; @@ -532,8 +533,14 @@ void stm32_hal_adc_wait_completion(const stm32_adc_t* ADCs, uint8_t n_ADC, (void)inputs; (void)n_inputs; + auto timeout = timersGetUsTick(); while(!_adc_completed) { // busy wait + if ((uint32_t)(timersGetUsTick() - timeout) >= SAMPLING_TIMEOUT_US) { + TRACE("ADC timeout"); + _adc_started_mask = 0; + return; + } } } @@ -572,8 +579,7 @@ static void _adc_chain_conversions(const stm32_adc_t* adc) void stm32_hal_adc_dma_isr(const stm32_adc_t* adc) { // Disable IRQ - auto dma_stream = _dma_get_stream(adc->DMAx, adc->DMA_Stream); - CLEAR_BIT(dma_stream->CR, DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE); + adc_dma_clear_flags(adc->DMAx, adc->DMA_Stream); uint16_t* dma_buffer = _adc_dma_buffer + adc->offset; copy_adc_values(dma_buffer, adc, _adc_inputs); diff --git a/radio/src/targets/common/arm/stm32/stm32_adc.h b/radio/src/targets/common/arm/stm32/stm32_adc.h index 9d73a709988..97ee324e1dc 100644 --- a/radio/src/targets/common/arm/stm32/stm32_adc.h +++ b/radio/src/targets/common/arm/stm32/stm32_adc.h @@ -24,6 +24,7 @@ #include "stm32_hal_ll.h" #include "hal/adc_driver.h" +#define MAX_ADC_INPUTS 32 struct stm32_adc_input_t { GPIO_TypeDef* GPIOx; diff --git a/radio/src/targets/common/arm/stm32/usb_bsp.c b/radio/src/targets/common/arm/stm32/usb_bsp.c index 1317a11a87c..26a964749e9 100644 --- a/radio/src/targets/common/arm/stm32/usb_bsp.c +++ b/radio/src/targets/common/arm/stm32/usb_bsp.c @@ -49,7 +49,8 @@ void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev) GPIO_PinAFConfig(USB_GPIO, USB_GPIO_PinSource_DM, USB_GPIO_AF); GPIO_PinAFConfig(USB_GPIO, USB_GPIO_PinSource_DP, USB_GPIO_AF); - + +#if defined(USB_GPIO_PIN_VBUS) /* Configure VBUS Pin */ GPIO_InitStructure.GPIO_Pin = USB_GPIO_PIN_VBUS; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; @@ -57,6 +58,7 @@ void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev) GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(USB_GPIO, &GPIO_InitStructure); +#endif RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE) ; diff --git a/radio/src/targets/common/arm/stm32/usb_conf.h b/radio/src/targets/common/arm/stm32/usb_conf.h index 22f3b2656db..337195ed4c9 100644 --- a/radio/src/targets/common/arm/stm32/usb_conf.h +++ b/radio/src/targets/common/arm/stm32/usb_conf.h @@ -29,6 +29,8 @@ #include "stm32f2xx.h" #endif +#include "hal.h" + /* USB Core and PHY interface configuration. Tip: To avoid modifying these defines each time you need to change the USB configuration, you can declare the needed define in your toolchain @@ -97,7 +99,9 @@ #endif /****************** USB OTG MISC CONFIGURATION ********************************/ +#if defined(USB_GPIO_PIN_VBUS) #define VBUS_SENSING_ENABLED +#endif /****************** USB OTG MODE CONFIGURATION ********************************/ //#define USE_HOST_MODE diff --git a/radio/src/targets/common/arm/stm32/usb_driver.cpp b/radio/src/targets/common/arm/stm32/usb_driver.cpp index 57545f2e90b..67f43716dae 100644 --- a/radio/src/targets/common/arm/stm32/usb_driver.cpp +++ b/radio/src/targets/common/arm/stm32/usb_driver.cpp @@ -57,6 +57,7 @@ void setSelectedUsbMode(int mode) selectedUsbMode = usbMode(mode); } +#if defined(USB_GPIO_PIN_VBUS) int usbPlugged() { static uint8_t debouncedState = 0; @@ -71,6 +72,7 @@ int usbPlugged() return debouncedState; } +#endif USB_OTG_CORE_HANDLE USB_OTG_dev; diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 8d409d6cf71..62b2a30e399 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -158,7 +158,6 @@ add_definitions(-DPCBHORUS -DSTM32F429_439xx -DSTM32F429xx -DSDRAM -DCCMRAM -DCO add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) -add_definitions(-DHARDWARE_TRAINER_JACK) add_definitions(-DSTM32_SUPPORT_32BIT_TIMERS) set(SDRAM ON) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index cc502c9307b..3913b075ed1 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -19,7 +19,6 @@ set(BITMAPS_DIR 480x272) set(HARDWARE_EXTERNAL_MODULE YES) set(TARGET_DIR nv14) -add_definitions(-DHARDWARE_TRAINER_JACK) set(RTC_BACKUP_RAM YES) set(PPM_LIMITS_SYMETRICAL YES) diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index becfd623c3c..2c1ba8680dd 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -38,7 +38,6 @@ #include "flysky_gimbal_driver.h" #include "timers_driver.h" -#include "lcd_driver.h" #include "lcd_driver.h" #include "battery_driver.h" #include "touch_driver.h" @@ -73,7 +72,8 @@ void delay_self(int count) for (; count > 0; count--); } } -#define RCC_AHB1PeriphMinimum (PWR_RCC_AHB1Periph |\ + +#define RCC_AHB1PeriphMinimum (PWR_RCC_AHB1Periph | \ LCD_RCC_AHB1Periph |\ BACKLIGHT_RCC_AHB1Periph |\ SDRAM_RCC_AHB1Periph \ diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index af203d5de4b..19f50e6a643 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -115,25 +115,17 @@ extern HardwareOptions hardwareOptions; #endif // defined(SIMU) #define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF -#define IS_UART_MODULE(port) (port == INTERNAL_MODULE) -#define IS_PXX2_INTERNAL_ENABLED() (false) #if !defined(NUM_FUNCTIONS_SWITCHES) #define NUM_FUNCTIONS_SWITCHES 0 #endif -#define NUM_TRIMS_KEYS (NUM_TRIMS * 2) - -#define DEFAULT_STICK_DEADZONE 2 - -// 2 pots without detent -#define DEFAULT_POTS_CONFIG \ - (POT_WITHOUT_DETENT << 0) + \ - (POT_WITHOUT_DETENT << 2) +#define DEFAULT_STICK_DEADZONE 2 #define BATTERY_WARN 36 // 3.6V #define BATTERY_MIN 35 // 3.5V #define BATTERY_MAX 42 // 4.2V +#define BATTERY_DIVIDER 2942 #if defined(__cplusplus) && !defined(SIMU) extern "C" { @@ -275,6 +267,4 @@ bool touchPanelEventOccured(); struct TouchState touchPanelRead(); struct TouchState getInternalTouchState(); -#define BATTERY_DIVIDER 2942 - #endif // _BOARD_H_ diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt new file mode 100644 index 00000000000..10eb2ed1469 --- /dev/null +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -0,0 +1,167 @@ +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) + +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") +set(CPU_TYPE STM32F4) +set(HSE_VALUE 12000000) +set(SDCARD YES) +set(STORAGE_MODELSLIST YES) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +set(BITMAPS_DIR 480x272) +set(TARGET_DIR pl18) +set(LINKER_SCRIPT targets/pl18/stm32f4_flash.ld) +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(WIRELESS_CHARGER YES) + +#option(STICKS_DEAD_ZONE "Enable sticks dead zone" YES) +#option(AFHDS2 "Support for AFHDS2" OFF) + +# for size report script +set(CPU_TYPE_FULL STM32F429xI) +set(TARGET_LINKER_DIR stm32f429_sdram) +set(TARGET_LINKER_PARAMS "-Wl,--defsym=__SDRAM_START__=0xC0000000") +set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=8192") + +#set(RF_BAUD_RATE 921600 230400 115200 57600 38400 19200 9600 4800 2400 1200) +#set(PCB_RF_BAUD 921600 CACHE STRING "INTERNAL_MODULE_BAUDRATE: ${RF_BAUD_RATE}") +#set_property(CACHE PCB_RF_BAUD PROPERTY STRINGS ${RF_BAUD_RATE}) + +add_definitions(-DPCBPL18 -DPCBFLYSKY) +add_definitions(-DBATTERY_CHARGE) +add_definitions(-DSOFTWARE_VOLUME) +add_definitions(-DSPI_FLASH) +add_definitions(-DSTM32_SUPPORT_32BIT_TIMERS) + +if(PCBREV STREQUAL PL18EV) + set(FLAVOUR pl18ev) + add_definitions(-DRADIO_PL18EV) +else() + set(FLAVOUR pl18) + add_definitions(-DRADIO_PL18) + + # Defines internal modules for PL18 via UART7 + set(INTERNAL_MODULES MULTI CACHE STRING "Internal modules") + set(DEFAULT_INTERNAL_MODULE MULTIMODULE CACHE STRING "Default internal module") +endif() + +set(BITMAPS_TARGET pl18_bitmaps) +set(FONTS_TARGET x12_fonts) +set(LCD_DRIVER lcd_driver.cpp) +set(TOUCH_DRIVER touch_driver.cpp) +set(HARDWARE_TOUCH YES) +set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) +set(FIRMWARE_DEPENDENCIES datacopy) + +set(HARDWARE_TOUCH ON) +set(SOFTWARE_KEYBOARD ON) +set(FLYSKY_GIMBAL ON) + +add_definitions( + -DSTM32F429_439xx -DSTM32F429xx + -DSDRAM -DCCMRAM -DCOLORLCD -DLIBOPENUI + -DHARDWARE_TOUCH -DHARDWARE_KEYS + -DSOFTWARE_KEYBOARD -DUSE_HATS_AS_KEYS) + +set(SDRAM ON) + +add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) +add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) +add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) +add_definitions(-DCROSSFIRE_NATIVE) +add_definitions(-DHARDWARE_EXTERNAL_MODULE) + +if(WIRELESS_CHARGER) + add_definitions(-DWIRELESS_CHARGER) +endif() + +if(STICKS_DEAD_ZONE) + add_definitions(-DSTICK_DEAD_ZONE) +endif() + +if(NOT UNEXPECTED_SHUTDOWN) + add_definitions(-DNO_UNEXPECTED_SHUTDOWN) +endif() + +set(AFHDS3 ON) + +# VCP CLI +set(ENABLE_SERIAL_PASSTHROUGH ON CACHE BOOL "Enable serial passthrough") +set(CLI ON CACHE BOOL "Enable CLI") + +include_directories(${RADIO_SRC_DIR}/fonts/colorlcd gui/${GUI_DIR} gui/${GUI_DIR}/layouts) + +file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIR}/gui/colorlcd ${RADIO_SRC_DIR}/gui/colorlcd/themes/*.cpp) +file(GLOB LAYOUTS_SRC RELATIVE ${RADIO_SRC_DIR}/gui/colorlcd ${RADIO_SRC_DIR}/gui/colorlcd/layouts/*.cpp) +file(GLOB WIDGETS_SRC RELATIVE ${RADIO_SRC_DIR}/gui/colorlcd ${RADIO_SRC_DIR}/gui/colorlcd/widgets/*.cpp) + +set(SRC + ${SRC} + io/frsky_firmware_update.cpp + ) + +set(GVAR_SCREEN model_gvars.cpp) + +if(BOOTLOADER) + set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ../common/arm/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() + +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ${LCD_DRIVER} + ${TOUCH_DRIVER} + board.cpp + key_driver.cpp + battery_driver.cpp + backlight_driver.cpp + led_driver.cpp + sdram_driver.c + ) + +set(FIRMWARE_SRC + ${FIRMWARE_SRC} + targets/common/arm/stm32/audio_dac_driver.cpp + targets/common/arm/stm32/dma2d.cpp + targets/common/arm/stm32/spi_flash.cpp + targets/common/arm/stm32/diskio_spi_flash.cpp + targets/common/arm/stm32/stm32_ws2812.cpp + boards/generic_stm32/rgb_leds.cpp + drivers/frftl.cpp + ) + +# Make malloc() thread-safe +add_definitions(-DTHREADSAFE_MALLOC) + +set(STM32LIB_SRC + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_sdio.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_fmc.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_ltdc.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_tim.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_dma2d.c + ) + diff --git a/radio/src/targets/pl18/backlight_driver.cpp b/radio/src/targets/pl18/backlight_driver.cpp new file mode 100644 index 00000000000..3ddd078c2c6 --- /dev/null +++ b/radio/src/targets/pl18/backlight_driver.cpp @@ -0,0 +1,95 @@ +/* + * 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 "opentx_types.h" +#include "board.h" + +#include "globals.h" +#include "lcd_driver.h" + +void backlightLowInit( void ) +{ + RCC_AHB1PeriphClockCmd(BACKLIGHT_RCC_AHB1Periph, ENABLE); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_WriteBit( BACKLIGHT_GPIO, BACKLIGHT_GPIO_PIN, Bit_RESET ); +} + +void backlightInit() +{ + // PIN init + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource, BACKLIGHT_GPIO_AF); + + // TODO review this when the timer will be chosen + BACKLIGHT_TIMER->ARR = 100; + BACKLIGHT_TIMER->PSC = BACKLIGHT_TIMER_FREQ / 1000000 - 1; // 10kHz (same as FrOS) + BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE; // PWM mode 1 + BACKLIGHT_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1NE; + BACKLIGHT_TIMER->CCR1 = 100; // 100% on init + BACKLIGHT_TIMER->EGR = TIM_EGR_UG; + BACKLIGHT_TIMER->CR1 |= TIM_CR1_CEN; // Counter enable + BACKLIGHT_TIMER->BDTR |= TIM_BDTR_MOE; +} + +uint8_t lastDutyCycle = 0; + +void backlightEnable(uint8_t dutyCycle) +{ + BACKLIGHT_TIMER->CCR1 = dutyCycle; + if(!dutyCycle) { + //experimental to turn off LCD when no backlight + if(lcdOffFunction) lcdOffFunction(); + } + else if(!lastDutyCycle) { + if(lcdOnFunction) lcdOnFunction(); + else lcdInit(); + } + lastDutyCycle = dutyCycle; +} + +void lcdOff() { + backlightEnable(0); +} + +void lcdOn(){ + if(lcdOnFunction) lcdOnFunction(); + else lcdInit(); + backlightEnable(BACKLIGHT_LEVEL_MAX); +} + +bool boardBacklightOn; + +bool isBacklightEnabled() +{ + return boardBacklightOn; +} diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp new file mode 100644 index 00000000000..b12a1ea7ff2 --- /dev/null +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -0,0 +1,436 @@ +/* + * 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 "opentx.h" +#include "battery_driver.h" + +#define __BATTERY_DRIVER_C__ + +#define BATTERY_W 140 +#define BATTERY_H 200 +#define BATTERY_TOP ((LCD_H - BATTERY_H)/2) +#define BATTERY_CONNECTOR_W 32 +#define BATTERY_CONNECTOR_H 10 +#define BATTERY_BORDER 4 +#define BATTERY_W_INNER (BATTERY_W - 2*BATTERY_BORDER) +#define BATTERY_H_INNER (BATTERY_H - 2*BATTERY_BORDER) +#define BATTERY_TOP_INNER (BATTERY_TOP + BATTERY_BORDER) + +#define UCHARGER_SAMPLING_CNT 10 +#define UCHARGER_CHARGING_SAMPLING_CNT 10 +#define WCHARGER_SAMPLING_CNT 30 +#define WCHARGER_CHARGING_SAMPLING_CNT 10 +#define WCHARGER_LOW_CURRENT_DELAY_CNT 6000 +#define WCHARGER_HIGH_CURRENT_DELAY_CNT 24000 + +typedef struct +{ + bool hasCharger : 1; + bool isChargeEnd : 1; + bool isChargerDetectionReady : 1; + bool isChargingDetectionReady : 1; + bool isHighCurrent : 1; + uint8_t chargerSamplingCount; + uint8_t chargingSamplingCount; + uint8_t chargeEndSamplingCount; +} STRUCT_BATTERY_CHARGER; + +STRUCT_BATTERY_CHARGER uCharger; // USB charger +#if defined(WIRELESS_CHARGER) +STRUCT_BATTERY_CHARGER wCharger; // Wireless charger +uint16_t wirelessLowCurrentDelay = 0; +uint16_t wirelessHighCurrentDelay = 0; +#endif + +void chargerDetection(STRUCT_BATTERY_CHARGER* charger, uint8_t chargerPinActive, uint8_t samplingCountThreshold) +{ + if ((charger->hasCharger && chargerPinActive) || (!charger->hasCharger && !chargerPinActive)) + { + charger->chargerSamplingCount = 0; + } + else + { + charger->chargerSamplingCount++; + if (charger->chargerSamplingCount >= samplingCountThreshold) + { + charger->chargerSamplingCount = 0; + charger->hasCharger = !charger->hasCharger; + charger->isChargerDetectionReady = true; + } + } +} + +void resetChargeEndDetection(STRUCT_BATTERY_CHARGER* charger) +{ + charger->isChargeEnd = false; + charger->isChargingDetectionReady = false; + charger->chargingSamplingCount = 0; + charger->isHighCurrent = false; +} + +void chargeEndDetection(STRUCT_BATTERY_CHARGER* charger, uint8_t chargeEndPinActive, uint8_t samplingCountThreshold) +{ + if (charger->isChargeEnd) + { + if (chargeEndPinActive) + { + charger->chargingSamplingCount = 0; + if (charger->isChargingDetectionReady) + { + charger->chargeEndSamplingCount = 0; + } + else + { + charger->chargeEndSamplingCount++; + if (charger->chargeEndSamplingCount >= samplingCountThreshold) + { + charger->chargeEndSamplingCount = 0; + charger->isChargingDetectionReady = true; + } + } + } + else + { + charger->chargeEndSamplingCount = 0; + charger->chargingSamplingCount++; + if (charger->chargingSamplingCount >= samplingCountThreshold) + { + charger->chargingSamplingCount = 0; + charger->isChargeEnd = false; + charger->isChargingDetectionReady = true; + } + } + } + else + { + if (!chargeEndPinActive) + { + charger->chargeEndSamplingCount = 0; + if (charger->isChargingDetectionReady) + { + charger->chargingSamplingCount = 0; + } + else + { + charger->chargingSamplingCount++; + if (charger->chargingSamplingCount >= samplingCountThreshold) + { + charger->chargingSamplingCount = 0; + charger->isChargingDetectionReady = true; + } + } + } + else + { + charger->chargingSamplingCount = 0; + charger->chargeEndSamplingCount++; + if (charger->chargeEndSamplingCount >= samplingCountThreshold) + { + charger->chargeEndSamplingCount = 0; + charger->isChargeEnd = true; + charger->isChargingDetectionReady = true; + } + } + } +} + +uint16_t get_battery_charge_state() +{ + uint16_t state = CHARGE_UNKNOWN; + + chargerDetection(&uCharger, IS_UCHARGER_ACTIVE(), UCHARGER_SAMPLING_CNT); + if (uCharger.isChargerDetectionReady) + { + if (uCharger.hasCharger) // USB charger can be detected properly no matter it is enabled or not + { + ENABLE_UCHARGER(); + chargeEndDetection(&uCharger, IS_UCHARGER_CHARGE_END_ACTIVE(), UCHARGER_CHARGING_SAMPLING_CNT); + if (uCharger.isChargingDetectionReady) + { + if (uCharger.isChargeEnd) + { + state = CHARGE_FINISHED; + } + else + { + state = CHARGE_STARTED; + } + } + } + else + { + resetChargeEndDetection(&uCharger); + + // Disable USB charger if it is not present, so that wireless charger can be detected properly + DISABLE_UCHARGER(); + } + } + +#if defined(WIRELESS_CHARGER) + chargerDetection(&wCharger, IS_WCHARGER_ACTIVE(), WCHARGER_SAMPLING_CNT); + if (wCharger.isChargerDetectionReady) + { + if (wCharger.hasCharger) // Wireless charger can only be detected when USB charger is disabled + { + chargeEndDetection(&wCharger, IS_WCHARGER_CHARGE_END_ACTIVE(), WCHARGER_CHARGING_SAMPLING_CNT); + if (wCharger.isChargingDetectionReady) + { + if (wCharger.isChargeEnd) + { + state = CHARGE_FINISHED; + } + else + { + state = CHARGE_STARTED; + } + } + + // Charge current control + wirelessLowCurrentDelay = 0; + if (wirelessHighCurrentDelay >= WCHARGER_HIGH_CURRENT_DELAY_CNT) + { + wCharger.isHighCurrent = true; + WCHARGER_CURRENT_HIGH(); + } + else + { + wirelessHighCurrentDelay++; + } + } + else + { + resetChargeEndDetection(&wCharger); + + // Charge current control + wirelessHighCurrentDelay = 0; + if (wirelessLowCurrentDelay >= WCHARGER_LOW_CURRENT_DELAY_CNT) + { + wCharger.isHighCurrent = false; + WCHARGER_CURRENT_LOW(); + } + else + { + wirelessLowCurrentDelay++; + } + } + } + +#endif + + return state; +} + +bool isChargerActive() +{ +#if defined(WIRELESS_CHARGER) + while (!(uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady)) + { + get_battery_charge_state(); + delay_ms(10); + } + return uCharger.hasCharger || wCharger.hasCharger; +#else + while (!uCharger.isChargerDetectionReady) + { + get_battery_charge_state(); + delay_ms(10); + } + return uCharger.hasCharger; +#endif +} + +void battery_charge_init() +{ + GPIO_InitTypeDef GPIO_InitStructure; + + // Input pins + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + + // USB charger status pins + GPIO_InitStructure.GPIO_Pin = UCHARGER_GPIO_PIN; + GPIO_Init(UCHARGER_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = UCHARGER_CHARGE_END_GPIO_PIN; + GPIO_Init(UCHARGER_CHARGE_END_GPIO, &GPIO_InitStructure); + +#if defined(WIRELESS_CHARGER) + // Wireless charger status pins + GPIO_InitStructure.GPIO_Pin = WCHARGER_GPIO_PIN; + GPIO_Init(WCHARGER_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = WCHARGER_CHARGE_END_GPIO_PIN; + GPIO_Init(WCHARGER_CHARGE_END_GPIO, &GPIO_InitStructure); +#endif + + // Output pins + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + + // USB charger control pins + GPIO_InitStructure.GPIO_Pin = UCHARGER_EN_GPIO_PIN; + GPIO_Init(UCHARGER_EN_GPIO, &GPIO_InitStructure); + + // USB charger state init + ENABLE_UCHARGER(); + uCharger.hasCharger = !IS_UCHARGER_ACTIVE(); // Init for sampling count works + uCharger.isChargerDetectionReady = false; + resetChargeEndDetection(&uCharger); + +#if defined(WIRELESS_CHARGER) + // Wireless charger control pins + GPIO_InitStructure.GPIO_Pin = WCHARGER_EN_GPIO_PIN; + GPIO_Init(WCHARGER_EN_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = WCHARGER_I_CONTROL_GPIO_PIN; + GPIO_Init(WCHARGER_I_CONTROL_GPIO, &GPIO_InitStructure); + + // Wireless charger state init + ENABLE_WCHARGER(); + WCHARGER_CURRENT_LOW(); + wCharger.hasCharger = !IS_WCHARGER_ACTIVE(); // Init for sampling count works + wCharger.isChargerDetectionReady = false; + resetChargeEndDetection(&wCharger); + +#endif +} + +void drawChargingInfo(uint16_t chargeState) { + static int progress = 0; + const char* text = chargeState == CHARGE_STARTED ? STR_BATTERYCHARGING : STR_BATTERYFULL; + int h = 0; + LcdFlags color = 0; + if (CHARGE_STARTED == chargeState) + { + if (progress >= 100) + { + progress = 0; + } + else + { + progress += 25; + } + text = STR_BATTERYCHARGING; + h = ((BATTERY_H_INNER * progress) / 100); + color = COLOR_THEME_EDIT; + } + else if (CHARGE_FINISHED == chargeState) + { + text = STR_BATTERYFULL; + h = BATTERY_H_INNER; + color = COLOR_THEME_EDIT; + } + else + { + text = STR_BATTERYNONE; + h = BATTERY_H_INNER; + color = COLOR_THEME_PRIMARY1; + } + + BACKLIGHT_ENABLE(); + lcd->drawSizedText(LCD_W / 2, LCD_H - 50, text, strlen(text), CENTERED | COLOR_THEME_PRIMARY2); + + lcd->drawFilledRect((LCD_W - BATTERY_W) / 2, BATTERY_TOP, BATTERY_W, BATTERY_H, SOLID, COLOR_THEME_PRIMARY2); + lcd->drawFilledRect((LCD_W - BATTERY_W_INNER) / 2, BATTERY_TOP_INNER, BATTERY_W_INNER, BATTERY_H_INNER, SOLID, COLOR_THEME_PRIMARY1); + + lcd->drawFilledRect((LCD_W - BATTERY_W_INNER) / 2, BATTERY_TOP_INNER + BATTERY_H_INNER - h, BATTERY_W_INNER, h, SOLID, color); + lcd->drawFilledRect((LCD_W - BATTERY_CONNECTOR_W) / 2, BATTERY_TOP - BATTERY_CONNECTOR_H, BATTERY_CONNECTOR_W, BATTERY_CONNECTOR_H, SOLID, COLOR_THEME_PRIMARY2); +} +#define CHARGE_INFO_DURATION 500 + +//this method should be called by timer interrupt or by GPIO interrupt +void handle_battery_charge(uint32_t last_press_time) +{ +#if !defined(SIMU) + static uint32_t updateTime = 0; + static uint16_t lastState = CHARGE_UNKNOWN; + static uint32_t info_until = 0; + static bool lcdInited = false; + + uint32_t now = get_tmr10ms(); + uint16_t chargeState = get_battery_charge_state(); + if (chargeState != CHARGE_UNKNOWN) { + + if (lastState != chargeState) { + //avoid positive check when none and unknown + if (lastState + chargeState > 1) { + //charge state changed - last state known + info_until = now + (CHARGE_INFO_DURATION); + } + } + //power buttons pressed + else if (now - last_press_time < POWER_ON_DELAY) { + info_until = now + CHARGE_INFO_DURATION; + } + lastState = chargeState; + } + + + if(now > info_until) { + info_until = 0; + lcd->clear(); + BACKLIGHT_DISABLE(); + if(lcdInited) { + lcdOff(); + } + return; + } + + + if (updateTime == 0 || ((get_tmr10ms() - updateTime) >= 50)) + { + if (!lcdInited) { + backlightInit(); + lcdInit(); + lcdInitDisplayDriver(); + lcdInited = true; + } + else { + lcdOn(); + } + updateTime = get_tmr10ms(); + lcdInitDirectDrawing(); + lcd->clear(); + drawChargingInfo(chargeState); + + // DEBUG INFO +#if 0 + char buffer[1024]; + + sprintf(buffer, "%d,%d,%d,%d", uCharger.isChargerDetectionReady, uCharger.hasCharger, IS_UCHARGER_ACTIVE(), uCharger.chargerSamplingCount); + lcd->drawSizedText(100, 10, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); + + sprintf(buffer, "%d,%d,%d,%d,%d,", uCharger.isChargingDetectionReady, uCharger.isChargeEnd, IS_UCHARGER_CHARGE_END_ACTIVE(), uCharger.chargingSamplingCount, uCharger.chargeEndSamplingCount); + lcd->drawSizedText(100, 40, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); + + sprintf(buffer, "%d,%d,%d,%d,%d", wCharger.isChargerDetectionReady, wCharger.hasCharger, IS_WCHARGER_ACTIVE(), wCharger.chargerSamplingCount, wCharger.isHighCurrent); + lcd->drawSizedText(100, 70, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); + + sprintf(buffer, "%d,%d,%d,%d,%d,", wCharger.isChargingDetectionReady, wCharger.isChargeEnd, IS_WCHARGER_CHARGE_END_ACTIVE(), wCharger.chargingSamplingCount, wCharger.chargeEndSamplingCount); + lcd->drawSizedText(100, 100, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); + + sprintf(buffer, "%d", isChargerActive()); + lcd->drawSizedText(100, 130, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); +#endif + + lcdRefresh(); + } +#endif +} + diff --git a/radio/src/targets/pl18/battery_driver.h b/radio/src/targets/pl18/battery_driver.h new file mode 100644 index 00000000000..6fdd651d51f --- /dev/null +++ b/radio/src/targets/pl18/battery_driver.h @@ -0,0 +1,60 @@ +/* + * 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 __BATTERY_DRIVER_H__ + #define __BATTERY_DRIVER_H__ +/*************************************************************************************************** + +***************************************************************************************************/ + +#include "board.h" +#include "hal.h" + +enum ChargeState +{ + CHARGE_UNKNOWN, + CHARGE_NONE, + CHARGE_STARTED, + CHARGE_FINISHED +}; + +#define IS_UCHARGER_ACTIVE() GPIO_ReadInputDataBit(UCHARGER_GPIO, UCHARGER_GPIO_PIN) +#define IS_UCHARGER_CHARGE_END_ACTIVE() GPIO_ReadInputDataBit(UCHARGER_CHARGE_END_GPIO, UCHARGER_CHARGE_END_GPIO_PIN) +#define ENABLE_UCHARGER() GPIO_SetBits(UCHARGER_EN_GPIO, UCHARGER_EN_GPIO_PIN) +#define DISABLE_UCHARGER() GPIO_ResetBits(UCHARGER_EN_GPIO, UCHARGER_EN_GPIO_PIN) + +#define IS_WCHARGER_ACTIVE() GPIO_ReadInputDataBit(WCHARGER_GPIO, WCHARGER_GPIO_PIN) +#define IS_WCHARGER_CHARGE_END_ACTIVE() GPIO_ReadInputDataBit(WCHARGER_CHARGE_END_GPIO, WCHARGER_CHARGE_END_GPIO_PIN) +#define ENABLE_WCHARGER() GPIO_SetBits(WCHARGER_EN_GPIO, WCHARGER_EN_GPIO_PIN) +#define DISABLE_WCHARGER() GPIO_ResetBits(WCHARGER_EN_GPIO, WCHARGER_EN_GPIO_PIN) +#define WCHARGER_CURRENT_LOW() GPIO_ResetBits(WCHARGER_I_CONTROL_GPIO, WCHARGER_I_CONTROL_GPIO_PIN) +#define WCHARGER_CURRENT_HIGH() GPIO_SetBits(WCHARGER_I_CONTROL_GPIO, WCHARGER_I_CONTROL_GPIO_PIN) + +extern void battery_charge_init(); +extern void handle_battery_charge(uint32_t last_press_time); +extern uint16_t get_battery_charge_state(); +extern uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps +extern bool isChargerActive(); + +#endif diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp new file mode 100644 index 00000000000..7c4cd43166b --- /dev/null +++ b/radio/src/targets/pl18/board.cpp @@ -0,0 +1,261 @@ +/* + * 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_adc.h" + +#include "stm32_ws2812.h" +#include "boards/generic_stm32/rgb_leds.h" + +#include "board.h" +#include "boards/generic_stm32/module_ports.h" + +#include "hal/adc_driver.h" +#include "hal/trainer_driver.h" +#include "hal/switch_driver.h" +#include "hal/abnormal_reboot.h" +#include "hal/watchdog_driver.h" + +#include "globals.h" +#include "sdcard.h" +#include "touch.h" +#include "debug.h" + +#include "flysky_gimbal_driver.h" +#include "timers_driver.h" + +#include "battery_driver.h" +#include "touch_driver.h" + +#include "bitmapbuffer.h" +#include "colors.h" + +#include + +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif +#include "usb_dcd_int.h" +#include "usb_bsp.h" +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +// common ADC driver +extern const etx_hal_adc_driver_t _adc_driver; + +#if defined(SEMIHOSTING) +extern "C" void initialise_monitor_handles(); +#endif + +#if defined(SPI_FLASH) +extern "C" void flushFTL(); +#endif + +void delay_self(int count) +{ + for (int i = 50000; i > 0; i--) + { + for (; count > 0; count--); + } +} +#define RCC_AHB1PeriphMinimum (PWR_RCC_AHB1Periph |\ + LCD_RCC_AHB1Periph |\ + BACKLIGHT_RCC_AHB1Periph |\ + SDRAM_RCC_AHB1Periph \ + ) +#define RCC_AHB1PeriphOther (AUDIO_RCC_AHB1Periph |\ + TELEMETRY_RCC_AHB1Periph |\ + TRAINER_RCC_AHB1Periph |\ + HAPTIC_RCC_AHB1Periph |\ + EXTMODULE_RCC_AHB1Periph \ + ) +#define RCC_AHB3PeriphMinimum (SDRAM_RCC_AHB3Periph) +#define RCC_APB1PeriphMinimum (BACKLIGHT_RCC_APB1Periph) +#define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ + AUDIO_RCC_APB1Periph \ + ) +#define RCC_APB2PeriphMinimum (LCD_RCC_APB2Periph) +#define RCC_APB2PeriphOther (HAPTIC_RCC_APB2Periph) + +void boardInit() +{ +#if defined(SEMIHOSTING) + initialise_monitor_handles(); +#endif + +#if !defined(SIMU) + RCC_AHB1PeriphClockCmd(RCC_AHB1PeriphMinimum | RCC_AHB1PeriphOther, ENABLE); + RCC_AHB3PeriphClockCmd(RCC_AHB3PeriphMinimum, ENABLE); + RCC_APB1PeriphClockCmd(RCC_APB1PeriphMinimum | RCC_APB1PeriphOther, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2PeriphMinimum | RCC_APB2PeriphOther, ENABLE); + + // enable interrupts + __enable_irq(); +#endif + +#if defined(DEBUG) + serialSetMode(SP_AUX1, UART_MODE_DEBUG); // indicate AUX1 is used + serialInit(SP_AUX1, UART_MODE_DEBUG); // early AUX1 init +#endif + + TRACE("\nPL18 board started :)"); + delay_ms(10); + TRACE("RCC->CSR = %08x", RCC->CSR); + + pwrInit(); + boardInitModulePorts(); + + init_trainer(); + battery_charge_init(); + flysky_gimbal_init(); + timersInit(); + touchPanelInit(); + usbInit(); + + extern const stm32_pulse_timer_t _led_timer; + + ws2812_init(&_led_timer, LED_STRIP_LENGTH); + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + ws2812_set_color(i, 0, 0, 0); + } + ws2812_update(&_led_timer); + + uint32_t press_start = 0; + uint32_t press_end = 0; + + if (UNEXPECTED_SHUTDOWN()) { + pwrOn(); + } else if (isChargerActive()) { + while (true) { + pwrOn(); + uint32_t now = get_tmr10ms(); + if (pwrPressed()) { + press_end = now; + if (press_start == 0) press_start = now; + if ((now - press_start) > POWER_ON_DELAY) { + break; + } + } else if (!isChargerActive()) { + boardOff(); + } else { + uint32_t press_end_touch = press_end; + if (touchPanelEventOccured()) { + touchPanelRead(); + press_end_touch = get_tmr10ms(); + } + press_start = 0; + handle_battery_charge(press_end_touch); + delay_ms(10); + press_end = 0; + } + } + } + + keysInit(); + switchInit(); + audioInit(); + adcInit(&_adc_driver); + hapticInit(); + + + #if defined(RTCLOCK) + rtcInit(); // RTC must be initialized before rambackupRestore() is called +#endif + + lcdSetInitalFrameBuffer(lcdFront->getData()); + +#if defined(DEBUG) + DBGMCU_APB1PeriphConfig( + DBGMCU_IWDG_STOP | DBGMCU_TIM1_STOP | DBGMCU_TIM2_STOP | + DBGMCU_TIM3_STOP | DBGMCU_TIM4_STOP | DBGMCU_TIM5_STOP | + DBGMCU_TIM6_STOP | DBGMCU_TIM7_STOP | DBGMCU_TIM8_STOP | + DBGMCU_TIM9_STOP | DBGMCU_TIM10_STOP | DBGMCU_TIM11_STOP | + DBGMCU_TIM12_STOP | DBGMCU_TIM13_STOP | DBGMCU_TIM14_STOP, + ENABLE); +#endif +} + +extern void rtcDisableBackupReg(); + +void boardOff() +{ + lcdOff(); + + while (pwrPressed()) { + WDG_RESET(); + } + + SysTick->CTRL = 0; // turn off systick + + // Shutdown the Haptic + hapticDone(); + + rtcDisableBackupReg(); + +#if !defined(BOOT) + if (isChargerActive()) + { + delay_ms(100); // Add a delay to wait for lcdOff +// RTC->BKP0R = SOFTRESET_REQUEST; + NVIC_SystemReset(); + } + else +#endif + { +// RTC->BKP0R = SHUTDOWN_REQUEST; + pwrOff(); + } + + // We reach here only in forced power situations, such as hw-debugging with external power + // Enter STM32 stop mode / deep-sleep + // Code snippet from ST Nucleo PWR_EnterStopMode example +#define PDMode 0x00000000U +#if defined(PWR_CR_MRUDS) && defined(PWR_CR_LPUDS) && defined(PWR_CR_FPDS) + MODIFY_REG(PWR->CR, (PWR_CR_PDDS | PWR_CR_LPDS | PWR_CR_FPDS | PWR_CR_LPUDS | PWR_CR_MRUDS), PDMode); +#elif defined(PWR_CR_MRLVDS) && defined(PWR_CR_LPLVDS) && defined(PWR_CR_FPDS) + MODIFY_REG(PWR->CR, (PWR_CR_PDDS | PWR_CR_LPDS | PWR_CR_FPDS | PWR_CR_LPLVDS | PWR_CR_MRLVDS), PDMode); +#else + MODIFY_REG(PWR->CR, (PWR_CR_PDDS| PWR_CR_LPDS), PDMode); +#endif /* PWR_CR_MRUDS && PWR_CR_LPUDS && PWR_CR_FPDS */ + +/* Set SLEEPDEEP bit of Cortex System Control Register */ + SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk)); + + // To avoid HardFault at return address, end in an endless loop + while (1) { + + } +} + +int usbPlugged() +{ + static uint8_t debouncedState = 0; + static uint8_t lastState = 0; + + uint8_t state = GPIO_ReadInputDataBit(UCHARGER_GPIO, UCHARGER_GPIO_PIN); + + if (state == lastState) + debouncedState = state; + else + lastState = state; + + return debouncedState; +} diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h new file mode 100644 index 00000000000..631f0b82d2d --- /dev/null +++ b/radio/src/targets/pl18/board.h @@ -0,0 +1,250 @@ +/* + * 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 _BOARD_H_ +#define _BOARD_H_ + +#include "definitions.h" +#include "opentx_constants.h" + +#include "board_common.h" +#include "hal.h" +#include "hal/serial_port.h" +#include "hal/watchdog_driver.h" + +#define FLASHSIZE 0x200000 +#define BOOTLOADER_SIZE 0x20000 +#define FIRMWARE_ADDRESS 0x08000000 + +#define MB *1024*1024 +#define LUA_MEM_EXTRA_MAX (2 MB) // max allowed memory usage for Lua bitmaps (in bytes) +#define LUA_MEM_MAX (6 MB) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited + +extern uint16_t sessionTimer; + +#define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) + +// Board driver +void boardInit(); +void boardOff(); + +// CPU Unique ID +#define LEN_CPU_UID (3*8+2) +void getCPUUniqueID(char * s); + +// Flash Write driver +#define FLASH_PAGESIZE 256 +void unlockFlash(); +void lockFlash(); +void flashWrite(uint32_t * address, const uint32_t * buffer); +uint32_t isFirmwareStart(const uint8_t * buffer); +uint32_t isBootloaderStart(const uint8_t * buffer); + +// SDRAM driver +void SDRAM_Init(); + +// Pulses driver +#if !defined(SIMU) + +#define INTERNAL_MODULE_ON() GPIO_SetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +#define INTERNAL_MODULE_OFF() GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +#define EXTERNAL_MODULE_ON() GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) +#define EXTERNAL_MODULE_OFF() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) +#define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF +#define BLUETOOTH_MODULE_ON() GPIO_ResetBits(BT_EN_GPIO, BT_EN_GPIO_PIN) +#define BLUETOOTH_MODULE_OFF() GPIO_SetBits(BT_EN_GPIO, BT_EN_GPIO_PIN) +#define IS_INTERNAL_MODULE_ON() (false) +#define IS_EXTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) == Bit_SET) + +#else + +#define INTERNAL_MODULE_OFF() +#define INTERNAL_MODULE_ON() +#define EXTERNAL_MODULE_ON() +#define EXTERNAL_MODULE_OFF() +#define BLUETOOTH_MODULE_ON() +#define BLUETOOTH_MODULE_OFF() +#define IS_INTERNAL_MODULE_ON() (false) +#define IS_EXTERNAL_MODULE_ON() (false) + +#endif // defined(SIMU) + +#if !defined(NUM_FUNCTIONS_SWITCHES) +#define NUM_FUNCTIONS_SWITCHES 0 +#endif + +#define NUM_TRIMS 8 +#define DEFAULT_STICK_DEADZONE 2 + +#define BATTERY_WARN 37 // 3.7V +#define BATTERY_MIN 35 // 3.4V +#define BATTERY_MAX 43 // 4.3V +#define BATTERY_DIVIDER 962 + +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif + +// Power driver +#define SOFT_PWR_CTRL +#define POWER_ON_DELAY 10 // 1s +void pwrInit(); +void extModuleInit(); +uint32_t pwrCheck(); +uint32_t lowPowerCheck(); + +void pwrOn(); +void pwrSoftReboot(); +void pwrOff(); +void pwrResetHandler(); +bool pwrPressed(); +bool pwrOffPressed(); +#if defined(PWR_EXTRA_SWITCH_GPIO) + bool pwrForcePressed(); +#else + #define pwrForcePressed() false +#endif +uint32_t pwrPressedDuration();; + +const etx_serial_port_t* auxSerialGetPort(int port_nr); +#define AUX_SERIAL_POWER_ON() +#define AUX_SERIAL_POWER_OFF() + +// LED driver +void ledInit(); +void ledOff(); +void ledRed(); +void ledBlue(); +void ledGreen(); + +// LCD driver +void lcdSetInitalFrameBuffer(void* fbAddress); +void lcdInit(); +void lcdCopy(void * dest, void * src); + +void lcdOff(); +void lcdOn(); + +#define lcdRefreshWait(...) + +// Backlight driver +#define BACKLIGHT_LEVEL_MAX 100 +#define BACKLIGHT_FORCED_ON BACKLIGHT_LEVEL_MAX + 1 +#define BACKLIGHT_LEVEL_MIN 1 + +extern bool boardBacklightOn; +void backlightLowInit( void ); +void backlightInit(); +void backlightEnable(uint8_t dutyCycle); +void backlightFullOn(); +bool isBacklightEnabled(); + +#define BACKLIGHT_ENABLE() \ + { \ + boardBacklightOn = true; \ + backlightEnable(BACKLIGHT_LEVEL_MAX - currentBacklightBright); \ + } + +#define BACKLIGHT_DISABLE() \ + { \ + boardBacklightOn = false; \ + backlightEnable(((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && \ + (g_eeGeneral.backlightMode != e_backlight_mode_off)) \ + ? 0 \ + : g_eeGeneral.blOffBright); \ + } + +#if !defined(SIMU) +void usbJoystickUpdate(); +#endif +#if defined(RADIO_PL18EV) +#define USB_NAME "FlySky PL18EV" +#define USB_MANUFACTURER 'F', 'l', 'y', 'S', 'k', 'y', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'P', 'L', '1', '8', 'E', 'V', ' ', ' ' /* 8 Bytes */ +#else +#define USB_NAME "FlySky PL18" +#define USB_MANUFACTURER 'F', 'l', 'y', 'S', 'k', 'y', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'P', 'L', '1', '8', ' ', ' ', ' ', ' ' /* 8 Bytes */ +#endif + +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +// Audio driver +void audioInit(); +void audioConsumeCurrentBuffer(); +void audioSpiWriteBuffer(const uint8_t * buffer, uint32_t size); +void audioSpiSetSpeed(uint8_t speed); +uint8_t audioHardReset(); +uint8_t audioSoftReset(); +void audioSendRiffHeader(); +void audioOn(); +void audioOff(); +bool isAudioReady(); +bool audioChipReset(); + +#define SPI_SPEED_2 0 +#define SPI_SPEED_4 1 +#define SPI_SPEED_8 2 +#define SPI_SPEED_16 3 +#define SPI_SPEED_32 4 +#define SPI_SPEED_64 5 +#define SPI_SPEED_128 6 +#define SPI_SPEED_256 7 + +#define audioDisableIrq() // interrupts must stay enabled on Horus +#define audioEnableIrq() // interrupts must stay enabled on Horus +#if defined(PCBNV14) +#define setSampleRate(freq) +#else +void setSampleRate(uint32_t frequency); +#endif +void setScaledVolume(uint8_t volume); +void setVolume(uint8_t volume); +int32_t getVolume(); +#define VOLUME_LEVEL_MAX 23 +#define VOLUME_LEVEL_DEF 12 + +// Telemetry driver +#define INTMODULE_FIFO_SIZE 512 +#define TELEMETRY_FIFO_SIZE 512 + +// Haptic driver +void hapticInit(); +void hapticDone(); +void hapticOff(); +void hapticOn(uint32_t pwmPercent); + +// Second serial port driver +//#define AUX_SERIAL +#define DEBUG_BAUDRATE 115200 +#define LUA_DEFAULT_BAUDRATE 115200 + +extern uint8_t currentTrainerMode; +void checkTrainerSettings(); + +// Touch panel driver +bool touchPanelEventOccured(); +struct TouchState touchPanelRead(); +struct TouchState getInternalTouchState(); + +#endif // _BOARD_H_ diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp new file mode 100644 index 00000000000..3acc373092c --- /dev/null +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -0,0 +1,261 @@ +/* + * 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 "board.h" +#include "fw_version.h" +#include "lcd.h" + +#include "translations.h" + +#include "../../common/arm/stm32/bootloader/boot.h" +#include "../../common/arm/stm32/bootloader/bin_files.h" + +#include + +#define SELECTED_COLOR (INVERS | COLOR_THEME_SECONDARY1) +#define DEFAULT_PADDING 28 +#define DOUBLE_PADDING 56 +#define MESSAGE_TOP (LCD_H - (2*DOUBLE_PADDING)) + +const uint8_t __bmp_plug_usb[] { +#include "bmp_plug_usb.lbm" +}; +LZ4Bitmap BMP_PLUG_USB(BMP_ARGB4444, __bmp_plug_usb); + +const uint8_t __bmp_usb_plugged[] { +#include "bmp_usb_plugged.lbm" +}; +LZ4Bitmap BMP_USB_PLUGGED(BMP_ARGB4444, __bmp_usb_plugged); + +#define BL_GREEN COLOR2FLAGS(RGB(73, 219, 62)) +#define BL_RED COLOR2FLAGS(RGB(229, 32, 30)) +#define BL_BACKGROUND COLOR2FLAGS(BLACK) +#define BL_FOREGROUND COLOR2FLAGS(WHITE) +#define BL_SELECTED COLOR2FLAGS(RGB(11, 65, 244)) // deep blue + +extern BitmapBuffer * lcd; + +void bootloaderInitScreen() +{ + lcdInitDisplayDriver(); + backlightInit(); + backlightEnable(100); + setHatsAsKeys(true); +} + +static void bootloaderDrawTitle(const char* text) +{ + lcd->drawText(LCD_W/2, DEFAULT_PADDING, text, CENTERED | BL_FOREGROUND); + lcd->drawSolidFilledRect(DEFAULT_PADDING, DOUBLE_PADDING, LCD_W - DOUBLE_PADDING, 2, BL_FOREGROUND); +} + +static void bootloaderDrawFooter() +{ + lcd->drawSolidFilledRect(DEFAULT_PADDING, LCD_H - (DEFAULT_PADDING + 10), LCD_W - DOUBLE_PADDING, 2, BL_FOREGROUND); +} + +static void bootloaderDrawBackground() +{ + lcd->clear(BL_BACKGROUND); +} + +void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) +{ + lcdInitDirectDrawing(); + bootloaderDrawBackground(); + + int center = LCD_W/2; + if (st == ST_START) { + + bootloaderDrawTitle(BOOTLOADER_TITLE); + + lcd->drawText(102, 75, LV_SYMBOL_CHARGE, BL_FOREGROUND); + coord_t pos = lcd->drawText(124, 75, TR_BL_WRITE_FW, BL_FOREGROUND); + pos += 8; + +#if defined(SPI_FLASH) + lcd->drawText(102, 110, LV_SYMBOL_SD_CARD, BL_FOREGROUND); + pos = lcd->drawText(124, 110, TR_BL_ERASE_FLASH, BL_FOREGROUND); + pos += 8; + + lcd->drawText(100, 145, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(124, 145, TR_BL_EXIT, BL_FOREGROUND); +#else + lcd->drawText(100, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(124, 110, TR_BL_EXIT, BL_FOREGROUND); +#endif + + pos -= 92; + lcd->drawSolidRect(92, 72 + (opt * 35), pos, 26, 2, BL_SELECTED); + + lcd->drawBitmap(60, 214, (const BitmapBuffer*)&BMP_PLUG_USB); + lcd->drawText(195, 223, TR_BL_USB_PLUGIN, BL_FOREGROUND); + lcd->drawText(195, 248, TR_BL_USB_MASS_STORE, BL_FOREGROUND); + + bootloaderDrawFooter(); + lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(), CENTERED | BL_FOREGROUND); + } +#if defined(SPI_FLASH) + else if (st == ST_CLEAR_FLASH_CHECK) { + + bootloaderDrawTitle(TR_BL_ERASE_INT_FLASH); + + lcd->drawText(102, 75, LV_SYMBOL_SD_CARD, BL_FOREGROUND); + coord_t pos = lcd->drawText(124, 75, TR_BL_ERASE_FLASH, BL_FOREGROUND); + pos += 8; + + lcd->drawText(100, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(124, 110, TR_BL_EXIT, BL_FOREGROUND); + + pos -= 92; + lcd->drawSolidRect(92, 72 + (opt * 35), pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_SD_CARD TR_BL_ERASE_KEY, BL_FOREGROUND); + lcd->drawText(305, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE TR_BL_EXIT_KEY, BL_FOREGROUND); + } + else if (st == ST_CLEAR_FLASH) { + bootloaderDrawTitle(TR_BL_ERASE_INT_FLASH); + + lcd->drawText(center, 75, TR_BL_ERASE_FLASH_MSG, CENTERED | BL_FOREGROUND); + bootloaderDrawFooter(); + } +#endif + + else if (st == ST_USB) { + lcd->drawBitmap(center - 26, 98, (const BitmapBuffer*)&BMP_USB_PLUGGED); + lcd->drawText(center, 168, TR_BL_USB_CONNECTED, CENTERED | BL_FOREGROUND); + } else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || + st == ST_FLASH_CHECK || st == ST_FLASHING || + st == ST_FLASH_DONE) { + + bootloaderDrawTitle(LV_SYMBOL_SD_CARD " /FIRMWARE"); + + if (st == ST_FLASHING || st == ST_FLASH_DONE) { + LcdFlags color = BL_RED; // red + + if (st == ST_FLASH_DONE) { + color = BL_GREEN /* green */; + opt = 100; // Completed > 100% + } + + lcd->drawRect(DEFAULT_PADDING, 120, LCD_W - DOUBLE_PADDING, 31, 2, + SOLID, BL_SELECTED); + lcd->drawSolidFilledRect(DEFAULT_PADDING + 4, 124, + ((LCD_W - DOUBLE_PADDING - 8) * opt) / 100, 23, + color); + } else if (st == ST_DIR_CHECK) { + if (opt == FR_NO_PATH) { + lcd->drawText(20, MESSAGE_TOP, + LV_SYMBOL_CLOSE TR_BL_DIR_MISSING, BL_FOREGROUND); + } else { + lcd->drawText(20, MESSAGE_TOP, LV_SYMBOL_CLOSE TR_BL_DIR_EMPTY, + BL_FOREGROUND); + } + } else if (st == ST_FLASH_CHECK) { + bootloaderDrawFilename(str, 0, true); + + if (opt == FC_ERROR) { + lcd->drawText(20, MESSAGE_TOP, + LV_SYMBOL_CLOSE " " TR_BL_INVALID_FIRMWARE, + BL_FOREGROUND); + } else if (opt == FC_OK) { + VersionTag tag; + memset(&tag, 0, sizeof(tag)); + extractFirmwareVersion(&tag); + + lcd->drawText(LCD_W / 4 + DEFAULT_PADDING, + MESSAGE_TOP - DEFAULT_PADDING, + TR_BL_FORK, RIGHT | BL_FOREGROUND); + lcd->drawSizedText(LCD_W / 4 + 6 + DEFAULT_PADDING, + MESSAGE_TOP - DEFAULT_PADDING, tag.fork, 6, + BL_FOREGROUND); + + lcd->drawText(LCD_W / 4 + DEFAULT_PADDING, MESSAGE_TOP, + TR_BL_VERSION, RIGHT | BL_FOREGROUND); + lcd->drawText(LCD_W / 4 + 6 + DEFAULT_PADDING, MESSAGE_TOP, + tag.version, BL_FOREGROUND); + + lcd->drawText(LCD_W / 4 + DEFAULT_PADDING, + MESSAGE_TOP + DEFAULT_PADDING, + TR_BL_RADIO, RIGHT | BL_FOREGROUND); + lcd->drawText(LCD_W / 4 + 6 + DEFAULT_PADDING, + MESSAGE_TOP + DEFAULT_PADDING, tag.flavour, + BL_FOREGROUND); + + lcd->drawText(DOUBLE_PADDING, MESSAGE_TOP, LV_SYMBOL_OK, BL_GREEN); + } + } + + bootloaderDrawFooter(); + + if (st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { + + if (st == ST_FILE_LIST) { + lcd->drawText(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_CHARGE TR_BL_SELECT_KEY, BL_FOREGROUND); + } else if (st == ST_FLASH_CHECK && opt == FC_OK) { + lcd->drawText(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_CHARGE TR_BL_FLASH_KEY, BL_FOREGROUND); + } else if (st == ST_FLASHING) { + lcd->drawText(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_CHARGE TR_BL_WRITING_FW, BL_FOREGROUND); + } else if (st == ST_FLASH_DONE) { + lcd->drawText(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_CHARGE TR_BL_WRITING_COMPL, BL_FOREGROUND); + } + } + + if (st != ST_FLASHING) { + lcd->drawText(305, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE TR_BL_EXIT_KEY, BL_FOREGROUND); + } + } +} + +void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) +{ + lcd->drawText(DEFAULT_PADDING, 75 + (line * 25), LV_SYMBOL_FILE, BL_FOREGROUND); + lcd->drawText(DEFAULT_PADDING + 30, 75 + (line * 25), str, BL_FOREGROUND); + + if (selected) { + lcd->drawSolidRect(DEFAULT_PADDING + 25, 72 + (line * 25), + LCD_W - (DEFAULT_PADDING + 25) - 28, 26, 2, BL_SELECTED); + } +} +uint32_t bootloaderGetMenuItemCount(int baseCount) +{ + return baseCount; +} + +bool bootloaderRadioMenu(uint32_t menuItem, event_t event) +{ + return true; +} + +void blExit(void) +{ + lcdClear(); + lcdRefresh(); + lcdRefreshWait(); +} diff --git a/radio/src/targets/pl18/extmodule_helper.cpp b/radio/src/targets/pl18/extmodule_helper.cpp new file mode 100644 index 00000000000..dbb02e90be2 --- /dev/null +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -0,0 +1,49 @@ +/* + * 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 "board.h" + +void EXTERNAL_MODULE_ON() +{ + GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); +} + +void EXTERNAL_MODULE_OFF() +{ + GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); +} + +void extModuleInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = EXTMODULE_RX_INVERT_GPIO_PIN; + GPIO_Init(EXTMODULE_RX_INVERT_GPIO, &GPIO_InitStructure); + + EXTMODULE_TX_INVERTED(); + EXTMODULE_RX_INVERTED(); +} diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h new file mode 100644 index 00000000000..ce3f32c9623 --- /dev/null +++ b/radio/src/targets/pl18/hal.h @@ -0,0 +1,719 @@ +/* + * 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 _HAL_H_ +#define _HAL_H_ + +#define CPU_FREQ 168000000 + +// HSI is at 168Mhz (over-drive is not enabled!) +#define PERI1_FREQUENCY 42000000 +#define PERI2_FREQUENCY 84000000 +#define TIMER_MULT_APB1 2 +#define TIMER_MULT_APB2 2 + +/* Timers Allocation: + * TIM1 = Haptic + * TIM4 = Trainer + * TIM6 = Audio + * TIM7 = 2 MHz counter + * + * + * TIM14 = 5 ms counter + */ + +/* DMA Allocation: + DMA/Stream/Channel + 1/5/7 DAC/Audio + 2/4/0 ADC1 + 2/0/2 ADC3 + 2/3/4 SDIO +*/ + +// Keys +// PL18/PL18EV only has virtual keys via trim buttons +// #define KEYS_GPIO_PIN_PGUP /* for activating PGUP in keys diagnose screen */ + +// 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_LSD +#define TRIMS_GPIO_PIN_LSD + +#define TRIMS_GPIO_REG_LSU +#define TRIMS_GPIO_PIN_LSU + +#define TRIMS_GPIO_REG_RSD +#define TRIMS_GPIO_PIN_RSD + +#define TRIMS_GPIO_REG_RSU +#define TRIMS_GPIO_PIN_RSU + +#define TRIMS_GPIO_REG_T7L +#define TRIMS_GPIO_PIN_T7L + +#define TRIMS_GPIO_REG_T7R +#define TRIMS_GPIO_PIN_T7R + +#define TRIMS_GPIO_REG_T8D +#define TRIMS_GPIO_PIN_T8D + +#define TRIMS_GPIO_REG_T8U +#define TRIMS_GPIO_PIN_T8U + +#define TRIMS_GPIO_REG_TR1U GPIOH->IDR +#define TRIMS_GPIO_PIN_TR1U LL_GPIO_PIN_8 // PH.08 +#define TRIMS_GPIO_REG_TR1D GPIOH->IDR +#define TRIMS_GPIO_PIN_TR1D LL_GPIO_PIN_9 // PH.09 +#define TRIMS_GPIO_REG_TR2U GPIOH->IDR +#define TRIMS_GPIO_PIN_TR2U LL_GPIO_PIN_10 // PH.10 +#define TRIMS_GPIO_REG_TR2D GPIOH->IDR +#define TRIMS_GPIO_PIN_TR2D LL_GPIO_PIN_11 // PH.11 + +// active 4x4 column/row based key-matrix to support up to 16 buttons with only 8 GPIOs +#define TRIMS_GPIO_OUT1 GPIOG +#define TRIMS_GPIO_OUT1_PIN LL_GPIO_PIN_2 // PG.02 +#define TRIMS_GPIO_OUT2 GPIOG +#define TRIMS_GPIO_OUT2_PIN LL_GPIO_PIN_10 // PG.10 +#define TRIMS_GPIO_OUT3 GPIOG +#define TRIMS_GPIO_OUT3_PIN LL_GPIO_PIN_11 // PG.11 +// OUT4 routed on MCU PCB, but not attached to any physical buttons, free to use for extensions +#define TRIMS_GPIO_OUT4 GPIOH +#define TRIMS_GPIO_OUT4_PIN LL_GPIO_PIN_7 // PH.07 + +#define TRIMS_GPIO_REG_IN1 GPIOB->IDR +#define TRIMS_GPIO_PIN_IN1 LL_GPIO_PIN_15 // PB.15 +#define TRIMS_GPIO_REG_IN2 GPIOC->IDR +#define TRIMS_GPIO_PIN_IN2 LL_GPIO_PIN_13 // PC.13 +#define TRIMS_GPIO_REG_IN3 GPIOD->IDR +#define TRIMS_GPIO_PIN_IN3 LL_GPIO_PIN_7 // PD.07 +#define TRIMS_GPIO_REG_IN4 GPIOJ->IDR +#define TRIMS_GPIO_PIN_IN4 LL_GPIO_PIN_12 // PJ.12 + +// Index of all trims + +#define KEYS_GPIOB_PINS (LL_GPIO_PIN_15) + +// PC8 allocated to SDIO D0, is not required to sample SWA ! +#define KEYS_GPIOC_PINS (LL_GPIO_PIN_13) + +#define KEYS_GPIOD_PINS (LL_GPIO_PIN_7) + +#define KEYS_GPIOH_PINS \ + (LL_GPIO_PIN_8 | LL_GPIO_PIN_9 | LL_GPIO_PIN_10 | LL_GPIO_PIN_11) + +#define KEYS_GPIOJ_PINS (LL_GPIO_PIN_12) + +#define KEYS_OUT_GPIOG_PINS (LL_GPIO_PIN_2 | LL_GPIO_PIN_10 | LL_GPIO_PIN_11) + +#define KEYS_OUT_GPIOH_PINS (LL_GPIO_PIN_7) + + +// Monitor pin +// #define MONITOR_RCC_AHB1Periph (RCC_AHB1Periph_GPIOJ) +// #define VBUS_MONITOR_GPIO (GPIOJ) +// #define VBUS_MONITOR_PIN (LL_GPIO_PIN_14) + +// Switches: +// Switches A and C on PL18/PL18EV are 2-position switches, +// so there is no NEED to configure two pins for Switches A and C. +// +// Especially, as on current dev. state, using PC8 for SDIO D0. +// (happy coincidence ;) +// +// #define SWITCHES_GPIO_REG_A_H GPIOC +// #define SWITCHES_GPIO_PIN_A_H LL_GPIO_PIN_8 // PC.08 +// #define SWITCHES_GPIO_REG_A_L GPIOC +// #define SWITCHES_GPIO_PIN_A_L LL_GPIO_PIN_9 // PC.09 + +#define SWITCHES_GPIO_REG_A GPIOC +#define SWITCHES_GPIO_PIN_A LL_GPIO_PIN_9 // PC.09 + +// High rail of Switch C is not required and thus PC10 is free to use for +// customizations. +// +// #define SWITCHES_GPIO_REG_C_H GPIOC +// #define SWITCHES_GPIO_PIN_C_H LL_GPIO_PIN_10 // PC.10 +// #define SWITCHES_GPIO_REG_C_L GPIOC +// #define SWITCHES_GPIO_PIN_C_L LL_GPIO_PIN_11 // PC.11 + +#define SWITCHES_GPIO_REG_C GPIOC +#define SWITCHES_GPIO_PIN_C LL_GPIO_PIN_11 // PC.11 + +// ADC + +#define ADC_GPIO_PIN_STICK_LH +#define ADC_GPIO_PIN_STICK_LV +#define ADC_GPIO_PIN_STICK_RV +#define ADC_GPIO_PIN_STICK_RH + +#define ADC_GPIO_PIN_POT1 LL_GPIO_PIN_6 // PA.06 VRA +#define ADC_GPIO_PIN_POT2 LL_GPIO_PIN_4 // PC.04 VRB +#define ADC_GPIO_PIN_POT3 LL_GPIO_PIN_8 // PF.08 VRC +#define ADC_GPIO_PIN_SLIDER1 LL_GPIO_PIN_9 // PF.09 VRD/LS +#define ADC_GPIO_PIN_SLIDER2 LL_GPIO_PIN_7 // PA.07 VRE/RS + +#if defined(RADIO_PL18EV) +#define ADC_GPIO_PIN_EXT1 LL_GPIO_PIN_5 // PA.05 +#define ADC_GPIO_PIN_EXT2 LL_GPIO_PIN_2 // PA.02 +#define ADC_GPIO_PIN_EXT3 LL_GPIO_PIN_6 // PF.06 +#define ADC_GPIO_PIN_EXT4 LL_GPIO_PIN_3 // PA.03 +#endif + +#define ADC_GPIO_PIN_SWB LL_GPIO_PIN_1 // PC.01 +#define ADC_GPIO_PIN_SWD LL_GPIO_PIN_0 // PC.00 +#define ADC_GPIO_PIN_SWE LL_GPIO_PIN_2 // PC.02 +#define ADC_GPIO_PIN_SWF LL_GPIO_PIN_0 // PB.00 +#define ADC_GPIO_PIN_SWG LL_GPIO_PIN_1 // PB.01 +#define ADC_GPIO_PIN_SWH LL_GPIO_PIN_10 // PF.10 + +#define ADC_GPIO_PIN_BATT LL_GPIO_PIN_5 // PC.05 + +#define ADC_GPIOA_PINS (ADC_GPIO_PIN_POT1 | ADC_GPIO_PIN_SLIDER2 | \ + ADC_GPIO_PIN_EXT1 | ADC_GPIO_PIN_EXT2 | ADC_GPIO_PIN_EXT4) +#define ADC_GPIOB_PINS (ADC_GPIO_PIN_SWF | ADC_GPIO_PIN_SWG) +#define ADC_GPIOC_PINS (ADC_GPIO_PIN_POT2 | ADC_GPIO_PIN_BATT | \ + ADC_GPIO_PIN_SWB | ADC_GPIO_PIN_SWD | ADC_GPIO_PIN_SWE) +#define ADC_GPIOF_PINS (ADC_GPIO_PIN_POT3 | ADC_GPIO_PIN_SLIDER1 | \ + ADC_GPIO_PIN_EXT3 | ADC_GPIO_PIN_SWH) + +#define ADC_CHANNEL_STICK_LH +#define ADC_CHANNEL_STICK_LV +#define ADC_CHANNEL_STICK_RV +#define ADC_CHANNEL_STICK_RH + +// Each ADC cannot map more than 8 channels, otherwise it will cause problems + +#define ADC_CHANNEL_POT1 LL_ADC_CHANNEL_6 // ADC12_IN6 -> ADC1_IN6 +#define ADC_CHANNEL_POT2 LL_ADC_CHANNEL_14 // ADC12_IN14 -> ADC1_IN14 +#define ADC_CHANNEL_POT3 LL_ADC_CHANNEL_6 // ADC3_IN6 -> ADC3_IN6 +#define ADC_CHANNEL_SLIDER1 LL_ADC_CHANNEL_7 // ADC3_IN7 -> ADC3_IN7 +#define ADC_CHANNEL_SLIDER2 LL_ADC_CHANNEL_7 // ADC12_IN7 -> ADC1_IN7 + +#if defined(RADIO_PL18EV) +// Left, right stick end pot on PL18EV +#define ADC_CHANNEL_EXT1 LL_ADC_CHANNEL_5 // ADC12_IN5 -> ADC1_IN5 +#define ADC_CHANNEL_EXT2 LL_ADC_CHANNEL_2 // ADC123_IN2 -> ADC1_IN2 + +// Left, right stick end buttons on PL18EV +#define ADC_CHANNEL_EXT3 LL_ADC_CHANNEL_4 // ADC3_IN4 -> ADC3_IN4 +#define ADC_CHANNEL_EXT4 LL_ADC_CHANNEL_3 // ADC123_IN3 -> ADC3_IN3 +#endif + +// Analog switches +#define ADC_CHANNEL_SWB LL_ADC_CHANNEL_11 // ADC123_IN11 -> ADC3_IN11 +#define ADC_CHANNEL_SWD LL_ADC_CHANNEL_10 // ADC123_IN10 -> ADC3_IN10 +#define ADC_CHANNEL_SWE LL_ADC_CHANNEL_12 // ADC123_IN12 -> ADC3_IN12 +#define ADC_CHANNEL_SWF LL_ADC_CHANNEL_8 // ADC12_IN8 -> ADC1_IN8 +#define ADC_CHANNEL_SWG LL_ADC_CHANNEL_9 // ADC12_IN9 -> ADC1_IN9 +#define ADC_CHANNEL_SWH LL_ADC_CHANNEL_8 // ADC3_IN8 -> ADC3_IN8 + +#define ADC_CHANNEL_BATT LL_ADC_CHANNEL_15 // ADC12_IN15 -> ADC1_IN15 + +#if !defined(RADIO_PL18EV) +// Disabled for PL18EV because 2 ADC 16 channels are fully mapped already +#define ADC_CHANNEL_RTC_BAT LL_ADC_CHANNEL_VBAT // ADC1_IN18 +#endif + +#define ADC_MAIN ADC1 +#define ADC_EXT ADC3 + +#define ADC_EXT_CHANNELS \ + { ADC_CHANNEL_POT3, ADC_CHANNEL_SLIDER1, ADC_CHANNEL_EXT3, ADC_CHANNEL_EXT4, \ + ADC_CHANNEL_SWB, ADC_CHANNEL_SWD, ADC_CHANNEL_SWE, ADC_CHANNEL_SWH \ + } + +#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_28CYCLES +#define ADC_DMA DMA2 +#define ADC_DMA_CHANNEL LL_DMA_CHANNEL_0 +#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_EXT_DMA DMA2 +#define ADC_EXT_DMA_CHANNEL LL_DMA_CHANNEL_2 +#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_28CYCLES + +#define ADC_VREF_PREC2 660 + +#if defined(RADIO_PL18EV) +#define ADC_DIRECTION { \ + 0,0,0,0, /* gimbals */ \ + 0,0,0, /* pots */ \ + -1,-1, /* sliders */ \ + 0,0,0,0, /* ext1-4 */ \ + 0, /* vbat */ \ + -1, /* SWB */ \ + -1, /* SWD */ \ + 0, /* SWE */ \ + 0, /* SWF */ \ + 0, /* SWG */ \ + 0 /* SWH */ \ + } +#else +#define ADC_DIRECTION { \ + 0,0,0,0, /* gimbals */ \ + 0,0,0, /* pots */ \ + -1,-1, /* sliders */ \ + 0, /* vbat */ \ + 0, /* rtc_bat */ \ + -1, /* SWB */ \ + -1, /* SWD */ \ + 0, /* SWE */ \ + 0, /* SWF */ \ + 0, /* SWG */ \ + 0 /* SWH */ \ + } +#endif + +// Power +#define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define PWR_ON_GPIO GPIOI +#define PWR_SWITCH_GPIO GPIOI +#define PWR_SWITCH_GPIO_PIN GPIO_Pin_11 // PI.11 +#define PWR_ON_GPIO_PIN GPIO_Pin_14 // PI.14 + +// Chargers (USB and wireless) +#define CHARGER_RCC_AHB1Periph ( RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOI ) + +#define UCHARGER_GPIO GPIOB +#define UCHARGER_GPIO_PIN GPIO_Pin_14 // PB.14 input + +#define UCHARGER_CHARGE_END_GPIO GPIOB +#define UCHARGER_CHARGE_END_GPIO_PIN GPIO_Pin_13 // PB.13 input + +#define UCHARGER_EN_GPIO GPIOG +#define UCHARGER_EN_GPIO_PIN GPIO_Pin_3 // PG.03 output + +#if defined (WIRELESS_CHARGER) + + #define WCHARGER_GPIO GPIOI + #define WCHARGER_GPIO_PIN GPIO_Pin_9 // PI.09 input + + #define WCHARGER_CHARGE_END_GPIO GPIOI + #define WCHARGER_CHARGE_END_GPIO_PIN GPIO_Pin_10 // PI.10 input + + #define WCHARGER_EN_GPIO GPIOH + #define WCHARGER_EN_GPIO_PIN GPIO_Pin_4 // PH.04 output + + #define WCHARGER_I_CONTROL_GPIO GPIOH + #define WCHARGER_I_CONTROL_GPIO_PIN GPIO_Pin_13 // PH.13 output + +#endif + +// TODO! Check IOLL1 to PI.01 connectivity! + +// S.Port update connector +#define SPORT_MAX_BAUDRATE 400000 +#define SPORT_UPDATE_RCC_AHB1Periph 0 +#define HAS_SPORT_UPDATE_CONNECTOR() (false) + +// Serial Port (DEBUG) +// We will temporarily used the PPM and the HEARTBEAT PINS +#define AUX_SERIAL_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE) +#define AUX_SERIAL_RCC_APB1Periph 0 +#define AUX_SERIAL_RCC_APB2Periph RCC_APB2Periph_USART6 +#define AUX_SERIAL_GPIO GPIOC +#define AUX_SERIAL_GPIO_PIN_TX GPIO_Pin_6 // PC.06 +#define AUX_SERIAL_GPIO_PIN_RX GPIO_Pin_7 // PC.07 +#define AUX_SERIAL_GPIO_PinSource_TX GPIO_PinSource6 +#define AUX_SERIAL_GPIO_PinSource_RX GPIO_PinSource7 +#define AUX_SERIAL_GPIO_AF GPIO_AF_USART6 +#define AUX_SERIAL_USART USART6 +#define AUX_SERIAL_USART_IRQHandler USART6_IRQHandler +#define AUX_SERIAL_USART_IRQn USART6_IRQn +#define AUX_SERIAL_TX_INVERT_GPIO GPIOE +#define AUX_SERIAL_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 +#define AUX_SERIAL_RX_INVERT_GPIO GPIOI +#define AUX_SERIAL_RX_INVERT_GPIO_PIN GPIO_Pin_15 // PI.15 + +//used in BOOTLOADER +#define SERIAL_RCC_AHB1Periph 0 +#define SERIAL_RCC_APB1Periph 0 +#define AUX2_SERIAL_RCC_AHB1Periph 0 +#define AUX2_SERIAL_RCC_APB1Periph 0 +#define AUX2_SERIAL_RCC_APB2Periph 0 +#define KEYS_BACKLIGHT_RCC_AHB1Periph 0 + +// Telemetry +#define TELEMETRY_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_DMA1) +#define TELEMETRY_RCC_APB1Periph RCC_APB1Periph_USART2 +#define TELEMETRY_REV_GPIO GPIOJ +#define TELEMETRY_RX_REV_GPIO_PIN GPIO_Pin_8 // PJ.08 +#define TELEMETRY_TX_REV_GPIO_PIN GPIO_Pin_7 // PJ.07 +#define TELEMETRY_DIR_GPIO GPIOJ +#define TELEMETRY_DIR_GPIO_PIN GPIO_Pin_13 // PJ.13 +#define TELEMETRY_SET_INPUT 1 +#define TELEMETRY_GPIO GPIOD +#define TELEMETRY_TX_GPIO_PIN GPIO_Pin_5 // PD.05 +#define TELEMETRY_RX_GPIO_PIN GPIO_Pin_6 // PD.06 +#define TELEMETRY_GPIO_PinSource_TX GPIO_PinSource5 +#define TELEMETRY_GPIO_PinSource_RX GPIO_PinSource6 +#define TELEMETRY_GPIO_AF GPIO_AF_USART2 +#define TELEMETRY_USART USART2 +#define TELEMETRY_DMA DMA1 +#define TELEMETRY_DMA_Stream_TX LL_DMA_STREAM_6 +#define TELEMETRY_DMA_Channel_TX DMA_Channel_4 +#define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream6_IRQn +#define TELEMETRY_DMA_TX_IRQHandler DMA1_Stream6_IRQHandler +#define TELEMETRY_DMA_TX_FLAG_TC DMA_IT_TCIF6 +// #define TELEMETRY_DMA_Stream_RX LL_DMA_STREAM_5 +// #define TELEMETRY_DMA_Channel_RX LL_DMA_CHANNEL_4 +#define TELEMETRY_USART_IRQHandler USART2_IRQHandler +#define TELEMETRY_USART_IRQn USART2_IRQn + +#define TELEMETRY_DIR_OUTPUT() TELEMETRY_DIR_GPIO->BSRRH = TELEMETRY_DIR_GPIO_PIN +#define TELEMETRY_DIR_INPUT() TELEMETRY_DIR_GPIO->BSRRL = TELEMETRY_DIR_GPIO_PIN +#define TELEMETRY_TX_POL_NORM() TELEMETRY_REV_GPIO->BSRRH = TELEMETRY_TX_REV_GPIO_PIN +#define TELEMETRY_TX_POL_INV() TELEMETRY_REV_GPIO->BSRRL = TELEMETRY_TX_REV_GPIO_PIN +#define TELEMETRY_RX_POL_NORM() TELEMETRY_REV_GPIO->BSRRH = TELEMETRY_RX_REV_GPIO_PIN +#define TELEMETRY_RX_POL_INV() TELEMETRY_REV_GPIO->BSRRL = TELEMETRY_RX_REV_GPIO_PIN + +// Software IRQ (Prio 5 -> FreeRTOS compatible) +#define TELEMETRY_RX_FRAME_EXTI_LINE LL_EXTI_LINE_4 +#define USE_EXTI4_IRQ +#define EXTI4_IRQ_Priority 5 + +// USB +#define USB_RCC_AHB1Periph_GPIO RCC_AHB1Periph_GPIOA +#define USB_GPIO GPIOA +// #define USB_GPIO_PIN_VBUS GPIO_Pin_9 // PA.09 +#define USB_GPIO_PIN_ID GPIO_Pin_10 // PA.10 +#define USB_GPIO_PIN_DM GPIO_Pin_11 // PA.11 +#define USB_GPIO_PIN_DP GPIO_Pin_12 // PA.12 +#define USB_GPIO_PinSource_DM GPIO_PinSource11 +#define USB_GPIO_PinSource_DP GPIO_PinSource12 +#define USB_GPIO_AF GPIO_AF_OTG1_FS + +// LCD +#define LCD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_GPIOK | RCC_AHB1Periph_DMA2D) +#define LCD_RCC_APB1Periph 0 +#define LCD_RCC_APB2Periph RCC_APB2Periph_LTDC +#define LCD_NRST_GPIO GPIOG +#define LCD_NRST_GPIO_PIN LL_GPIO_PIN_9 // PG.09 +#define LCD_SPI_GPIO GPIOE +#define LCD_SPI_CS_GPIO_PIN LL_GPIO_PIN_4 // PE.04 +#define LCD_SPI_SCK_GPIO_PIN LL_GPIO_PIN_2 // PE.02 +#define LCD_SPI_MISO_GPIO_PIN LL_GPIO_PIN_5 // PE.05 +#define LCD_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_6 // PE.06 +#define LTDC_IRQ_PRIO 4 +#define DMA_SCREEN_IRQ_PRIO 6 + +// Backlight +// TODO TIM3, TIM8, TIM14, review the channel in backlight_driver.cpp according to the chosen timer +#define BACKLIGHT_RCC_AHB1Periph RCC_AHB1Periph_GPIOA +#define BACKLIGHT_RCC_APB1Periph RCC_APB1Periph_TIM2 +#define BACKLIGHT_RCC_APB2Periph 0 +#define BACKLIGHT_GPIO GPIOA +#define BACKLIGHT_GPIO_PIN GPIO_Pin_15 +#define BACKLIGHT_GPIO_PinSource GPIO_PinSource15 +#define BACKLIGHT_TIMER TIM2 +#define BACKLIGHT_GPIO_AF GPIO_AF_TIM2 +#define BACKLIGHT_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +//used in BOOTLOADER +#define SERIAL_RCC_AHB1Periph 0 +#define SERIAL_RCC_APB1Periph 0 +#define ROTARY_ENCODER_RCC_APB1Periph 0 + +// SPI NOR Flash +#define FLASH_SPI SPI6 +#define FLASH_SPI_CS_GPIO GPIOG +#define FLASH_SPI_CS_GPIO_PIN LL_GPIO_PIN_6 // PG.06 +#define FLASH_SPI_GPIO GPIOG +#define FLASH_SPI_SCK_GPIO_PIN LL_GPIO_PIN_13 // PG.13 +#define FLASH_SPI_MISO_GPIO_PIN LL_GPIO_PIN_12 // PG.12 +#define FLASH_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_14 // PG.14 +// #define FLASH_SPI_DMA DMA2 +// #define FLASH_SPI_DMA_CHANNEL LL_DMA_CHANNEL_1 +// #define FLASH_SPI_DMA_TX_STREAM LL_DMA_STREAM_5 +// #define FLASH_SPI_DMA_TX_IRQn DMA2_Stream5_IRQn +// #define FLASH_SPI_DMA_TX_IRQHandler DMA2_Stream5_IRQHandler +// #define FLASH_SPI_DMA_RX_STREAM LL_DMA_STREAM_6 +// #define FLASH_SPI_DMA_RX_IRQn DMA2_Stream6_IRQn +// #define FLASH_SPI_DMA_RX_IRQHandler DMA2_Stream6_IRQHandler +#define STORAGE_USE_SPI_FLASH + +// SDRAM +#define SDRAM_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH) +#define SDRAM_RCC_AHB3Periph RCC_AHB3Periph_FMC + +// Audio +#define AUDIO_RCC_APB1Periph (RCC_APB1Periph_TIM6 | RCC_APB1Periph_DAC) +#define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) +#define AUDIO_OUTPUT_GPIO GPIOA +#define AUDIO_OUTPUT_GPIO_PIN GPIO_Pin_4 // PA.04 +#define AUDIO_GPIO_PinSource GPIO_PinSource4 +#define AUDIO_DMA_Stream DMA1_Stream5 +#define AUDIO_DMA_Stream_IRQn DMA1_Stream5_IRQn +#define AUDIO_TIM_IRQn TIM6_DAC_IRQn +#define AUDIO_TIM_IRQHandler TIM6_DAC_IRQHandler +#define AUDIO_DMA_Stream_IRQHandler DMA1_Stream5_IRQHandler +#define AUDIO_TIMER TIM6 +#define AUDIO_DMA DMA1 + +// I2C Bus +#define I2C_B1 I2C1 +#define I2C_B1_GPIO GPIOB +#define I2C_B1_SDA_GPIO_PIN LL_GPIO_PIN_7 // PB.07 +#define I2C_B1_SCL_GPIO_PIN LL_GPIO_PIN_8 // PB.08 +#define I2C_B1_GPIO_AF LL_GPIO_AF_4 + +// Touch +#define TOUCH_I2C_BUS I2C_Bus_1 +#define TOUCH_I2C_CLK_RATE 400000 +#define TOUCH_INT_GPIO GPIOB +#define TOUCH_INT_GPIO_PIN LL_GPIO_PIN_9 // PB.09 +#define TOUCH_RST_GPIO GPIOB +#define TOUCH_RST_GPIO_PIN LL_GPIO_PIN_12 // PB.12 +#define TOUCH_INT_EXTI_Line LL_EXTI_LINE_9 +#define TOUCH_INT_EXTI_Port LL_SYSCFG_EXTI_PORTB +#define TOUCH_INT_EXTI_SysCfgLine LL_SYSCFG_EXTI_LINE9 + +// TOUCH_INT_EXTI IRQ +#if !defined(USE_EXTI9_5_IRQ) + #define USE_EXTI9_5_IRQ + #define EXTI9_5_IRQ_Priority 9 +#endif + +// Haptic: TIM1_CH1 +#define HAPTIC_PWM +#define HAPTIC_RCC_AHB1Periph RCC_AHB1Periph_GPIOA +#define HAPTIC_RCC_APB2Periph RCC_APB2ENR_TIM1EN +#define HAPTIC_GPIO GPIOA +#define HAPTIC_GPIO_PIN GPIO_Pin_8 +#define HAPTIC_GPIO_TIMER TIM1 +#define HAPTIC_GPIO_AF GPIO_AF_TIM1 +#define HAPTIC_GPIO_PinSource GPIO_PinSource8 +#define HAPTIC_TIMER_OUTPUT_ENABLE TIM_CCER_CC1E | TIM_CCER_CC1NE; +#define HAPTIC_TIMER_MODE TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE +#define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR1 + +// Flysky Hall Stick +#define FLYSKY_HALL_SERIAL_USART UART4 +#define FLYSKY_HALL_SERIAL_GPIO GPIOA +#define FLYSKY_HALL_DMA_Channel LL_DMA_CHANNEL_4 +#define FLYSKY_HALL_SERIAL_TX_GPIO_PIN LL_GPIO_PIN_0 // PA.00 +#define FLYSKY_HALL_SERIAL_RX_GPIO_PIN LL_GPIO_PIN_1 // PA.01 +#define FLYSKY_HALL_SERIAL_GPIO_AF LL_GPIO_AF_8 + +#define FLYSKY_HALL_RCC_AHB1Periph RCC_AHB1Periph_DMA1 +#define FLYSKY_HALL_RCC_APB1Periph RCC_APB1Periph_UART4 + +#define FLYSKY_HALL_SERIAL_USART_IRQHandler UART4_IRQHandler +#define FLYSKY_HALL_SERIAL_USART_IRQn UART4_IRQn +#define FLYSKY_HALL_SERIAL_DMA DMA1 +#define FLYSKY_HALL_DMA_Stream_RX LL_DMA_STREAM_2 +#define FLYSKY_HALL_DMA_Stream_TX LL_DMA_STREAM_4 + +// LED Strip +#define LED_STRIP_LENGTH 4 +#define LED_STRIP_GPIO GPIOH +#define LED_STRIP_GPIO_PIN_DATA LL_GPIO_PIN_12 // PH.12 / TIM5_CH3 +#define LED_STRIP_GPIO_PIN_AF LL_GPIO_AF_2 // TIM3/4/5 +#define LED_STRIP_TIMER TIM5 +#define LED_STRIP_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define LED_STRIP_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define LED_STRIP_TIMER_DMA DMA1 +#define LED_STRIP_TIMER_DMA_CHANNEL LL_DMA_CHANNEL_6 +#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 + + +// Internal Module +#if defined(RADIO_PL18) +#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_DMA1) +#define INTMODULE_PWR_GPIO GPIOI +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_0 // PI.00 +#define INTMODULE_GPIO GPIOF +#define INTMODULE_TX_GPIO_PIN LL_GPIO_PIN_7 // PF.07 +#define INTMODULE_RX_GPIO_PIN LL_GPIO_PIN_6 // PF.06 +#define INTMODULE_USART UART7 +#define INTMODULE_GPIO_AF LL_GPIO_AF_8 +#define INTMODULE_USART_IRQn UART7_IRQn +#define INTMODULE_USART_IRQHandler UART7_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_DMA_CHANNEL_5 +#define INTMODULE_RX_DMA DMA1 +#define INTMODULE_RX_DMA_STREAM LL_DMA_STREAM_3 +#define INTMODULE_RX_DMA_CHANNEL LL_DMA_CHANNEL_5 +// #define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn +// #define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream_IRQHandler + +#define INTMODULE_TIMER TIM3 +#define INTMODULE_TIMER_IRQn TIM3_IRQn +#define INTMODULE_TIMER_IRQHandler TIM3_IRQHandler +#define INTMODULE_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#endif + +// External Module +#define EXTMODULE +#define EXTMODULE_PULSES +#define EXTMODULE_PWR_GPIO GPIOD +#define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_11 // PD.11 +#define EXTMODULE_RCC_AHB1Periph \ + (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ + RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) +#define EXTMODULE_TX_GPIO GPIOC +#define EXTMODULE_TX_GPIO_PIN LL_GPIO_PIN_6 // PC.06 +#define EXTMODULE_TX_GPIO_AF LL_GPIO_AF_3 // TIM8_CH1 +#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_RX_GPIO GPIOC +#define EXTMODULE_RX_GPIO_PIN LL_GPIO_PIN_7 // PC.07 +#define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_TIMER TIM8 +#define EXTMODULE_TIMER_Channel LL_TIM_CHANNEL_CH1 +#define EXTMODULE_TIMER_IRQn TIM8_UP_TIM13_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF LL_GPIO_AF_3 +//USART +#define EXTMODULE_USART USART6 +#define EXTMODULE_USART_GPIO GPIOC +#define EXTMODULE_USART_GPIO_AF GPIO_AF_USART6 +#define EXTMODULE_USART_GPIO_AF_LL LL_GPIO_AF_8 +#define EXTMODULE_USART_TX_DMA DMA2 +#define EXTMODULE_USART_TX_DMA_CHANNEL LL_DMA_CHANNEL_5 +#define EXTMODULE_USART_TX_DMA_STREAM DMA2_Stream7 +#define EXTMODULE_USART_TX_DMA_STREAM_LL LL_DMA_STREAM_7 + +#define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMA_CHANNEL_5 +#define EXTMODULE_USART_RX_DMA_STREAM DMA2_Stream2 +#define EXTMODULE_USART_RX_DMA_STREAM_LL LL_DMA_STREAM_2 + +#define EXTMODULE_USART_IRQHandler USART6_IRQHandler +#define EXTMODULE_USART_IRQn USART6_IRQn + +//TIMER +#define EXTMODULE_TIMER_DMA_CHANNEL LL_DMA_CHANNEL_7 +#define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_TIMER_DMA DMA2 +#define EXTMODULE_TIMER_DMA_STREAM_LL LL_DMA_STREAM_1 +#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream1_IRQHandler + +#define EXTMODULE_TX_INVERT_GPIO GPIOE +#define EXTMODULE_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 +#define EXTMODULE_RX_INVERT_GPIO GPIOI +#define EXTMODULE_RX_INVERT_GPIO_PIN GPIO_Pin_15 // PI.15 + +#define EXTMODULE_TX_NORMAL() EXTMODULE_TX_INVERT_GPIO->BSRRH = EXTMODULE_TX_INVERT_GPIO_PIN +#define EXTMODULE_TX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_TX_INVERT_GPIO_PIN +#define EXTMODULE_RX_NORMAL() EXTMODULE_RX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN +#define EXTMODULE_RX_INVERTED() EXTMODULE_RX_INVERT_GPIO->BSRRL = EXTMODULE_RX_INVERT_GPIO_PIN + +// Trainer Port +#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD) +#define TRAINER_GPIO GPIOD + +#define TRAINER_IN_GPIO_PIN LL_GPIO_PIN_12 // PD.12 +#define TRAINER_IN_TIMER_Channel LL_TIM_CHANNEL_CH1 + +#define TRAINER_OUT_GPIO_PIN LL_GPIO_PIN_13 // PD.13 +#define TRAINER_OUT_TIMER_Channel LL_TIM_CHANNEL_CH2 + +#define TRAINER_TIMER TIM4 +#define TRAINER_TIMER_IRQn TIM4_IRQn +#define TRAINER_TIMER_IRQHandler TIM4_IRQHandler +#define TRAINER_GPIO_AF LL_GPIO_AF_2 +#define TRAINER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +//ROTARY emulation for trims as buttons +#define ROTARY_ENCODER_NAVIGATION + +//BLUETOOTH +#define BLUETOOTH_ON_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define BT_EN_GPIO GPIOI +#define BT_EN_GPIO_PIN GPIO_Pin_8 // PI.8 + +#define BT_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOH) +#define BT_RCC_APB1Periph (RCC_APB1Periph_USART3) +#define BT_RCC_APB2Periph 0 + +#define BT_USART USART3 +#define BT_GPIO_AF GPIO_AF_USART3 +#define BT_USART_IRQn USART3_IRQn +#define BT_GPIO_TXRX GPIOB +#define BT_TX_GPIO_PIN GPIO_Pin_10 // PB.10 +#define BT_RX_GPIO_PIN GPIO_Pin_11 // PB.11 +#define BT_TX_GPIO_PinSource GPIO_PinSource10 +#define BT_RX_GPIO_PinSource GPIO_PinSource11 +#define BT_USART_IRQHandler USART3_IRQHandler + +#define BT_CONNECTED_GPIO GPIOJ +#define BT_CONNECTED_GPIO_PIN GPIO_Pin_1 // PJ.01 + +#define BT_CMD_MODE_GPIO GPIOH +#define BT_CMD_MODE_GPIO_PIN GPIO_Pin_6 // PH.6 + +// 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 480 +#define LCD_H 320 + +#define LCD_PHYS_W 320 +#define LCD_PHYS_H 480 + +#define LCD_DEPTH 16 +#define LCD_CONTRAST_DEFAULT 20 + +#endif // _HAL_H_ diff --git a/radio/src/targets/pl18/haptic_driver.cpp b/radio/src/targets/pl18/haptic_driver.cpp new file mode 100644 index 00000000000..d8fc8f809da --- /dev/null +++ b/radio/src/targets/pl18/haptic_driver.cpp @@ -0,0 +1,63 @@ +/* + * 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 "board.h" + +void hapticOff(void) +{ + HAPTIC_TIMER_COMPARE_VALUE = 0; +} + +void hapticOn(uint32_t pwmPercent) +{ + if (pwmPercent > 100) { + pwmPercent = 100; + } + HAPTIC_TIMER_COMPARE_VALUE = pwmPercent; +} + +void hapticInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = HAPTIC_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(HAPTIC_GPIO, &GPIO_InitStructure); + + GPIO_PinAFConfig(HAPTIC_GPIO, HAPTIC_GPIO_PinSource, HAPTIC_GPIO_AF); + + HAPTIC_GPIO_TIMER->ARR = 100; + HAPTIC_GPIO_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 10000 - 1; + HAPTIC_GPIO_TIMER->CCMR1 = HAPTIC_TIMER_MODE; // PWM + HAPTIC_GPIO_TIMER->CCER = HAPTIC_TIMER_OUTPUT_ENABLE; + HAPTIC_GPIO_TIMER->CCR1 = 0; + HAPTIC_GPIO_TIMER->EGR = TIM_EGR_UG; + HAPTIC_GPIO_TIMER->CR1 = TIM_CR1_CEN; // counter enable + HAPTIC_GPIO_TIMER->BDTR |= TIM_BDTR_MOE; +} + +void hapticDone(void) +{ + hapticOff(); + RCC_AHB1PeriphClockCmd(HAPTIC_RCC_AHB1Periph, DISABLE); +} diff --git a/radio/src/targets/pl18/key_driver.cpp b/radio/src/targets/pl18/key_driver.cpp new file mode 100644 index 00000000000..cd09c374d71 --- /dev/null +++ b/radio/src/targets/pl18/key_driver.cpp @@ -0,0 +1,220 @@ +/* + * 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 "stm32_hal_ll.h" +#include "stm32_gpio_driver.h" + +#include "hal.h" +#include "delays_driver.h" +#include "keys.h" + +/* The output bit-order has to be: + 0 LHL TR7L (Left equals down) + 1 LHR TR7R + 2 LVD TR5D + 3 LVU TR5U + 4 RVD TR6D + 5 RVU TR6U + 6 RHL TR8L + 7 RHR TR8R + 8 LSD TR1D + 9 LSU TR1U + 10 RSD TR2D + 11 RSU TR2U + 12 EX1D TR3D + 13 EX1U TR3U + 14 EX2D TR4D + 15 EX2U TR4U +*/ + +enum PhysicalTrims +{ + TR7L = 0, + TR7R, + TR5D = 2, + TR5U, + TR6D = 4, + TR6U, + TR8L = 6, + TR8R, + TR1D = 8, + TR1U, + TR2D = 10, + TR2U, + TR3D = 12, + TR3U, + TR4D = 14, + TR4U, +}; + +void keysInit() +{ + stm32_gpio_enable_clock(GPIOB); + stm32_gpio_enable_clock(GPIOC); + stm32_gpio_enable_clock(GPIOD); + stm32_gpio_enable_clock(GPIOF); + stm32_gpio_enable_clock(GPIOH); + stm32_gpio_enable_clock(GPIOJ); + + LL_GPIO_InitTypeDef pinInit; + LL_GPIO_StructInit(&pinInit); + pinInit.Mode = LL_GPIO_MODE_INPUT; + pinInit.Pull = LL_GPIO_PULL_DOWN; + + pinInit.Pin = KEYS_GPIOB_PINS; + LL_GPIO_Init(GPIOB, &pinInit); + + pinInit.Pin = KEYS_GPIOC_PINS; + LL_GPIO_Init(GPIOC, &pinInit); + + pinInit.Pin = KEYS_GPIOD_PINS; + LL_GPIO_Init(GPIOD, &pinInit); + + pinInit.Pin = KEYS_GPIOH_PINS; + LL_GPIO_Init(GPIOH, &pinInit); + + pinInit.Pin = KEYS_GPIOJ_PINS; + LL_GPIO_Init(GPIOJ, &pinInit); + + // Matrix outputs + pinInit.Mode = LL_GPIO_MODE_OUTPUT; + pinInit.Pull = LL_GPIO_PULL_NO; + + pinInit.Pin = KEYS_OUT_GPIOG_PINS; + LL_GPIO_Init(GPIOG, &pinInit); + + pinInit.Pin = KEYS_OUT_GPIOH_PINS; + LL_GPIO_Init(GPIOH, &pinInit); +} + +static uint32_t _readKeyMatrix() +{ + // This function avoids concurrent matrix agitation + + uint32_t result = 0; + /* Bit 0 - TR3 down + * Bit 1 - TR3 up + * Bit 2 - TR4 down + * Bit 3 - TR4 up + * Bit 4 - TR5 down + * Bit 5 - TR5 up + * Bit 6 - TR6 down + * Bit 7 - TR6 up + * Bit 8 - TR7 left + * Bit 9 - TR7 right + * Bit 10 - TR8 left + * Bit 11 - TR8 right + */ + + volatile static struct + { + uint32_t oldResult = 0; + uint8_t ui8ReadInProgress = 0; + } syncelem; + + if (syncelem.ui8ReadInProgress != 0) return syncelem.oldResult; + + // ui8ReadInProgress was 0, increment it + syncelem.ui8ReadInProgress++; + // Double check before continuing, as non-atomic, non-blocking so far + // If ui8ReadInProgress is above 1, then there was concurrent task calling it, exit + if (syncelem.ui8ReadInProgress > 1) return syncelem.oldResult; + + // If we land here, we have exclusive access to Matrix + LL_GPIO_ResetOutputPin(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_PIN); + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT4, TRIMS_GPIO_OUT4_PIN); + delay_us(10); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) + result |= 1 << TR7L; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << TR7R; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << TR5D; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << TR5U; + + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_PIN); + LL_GPIO_ResetOutputPin(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + delay_us(10); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) + result |= 1 << TR3D; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << TR3U; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << TR4U; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << TR4D; + + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + LL_GPIO_ResetOutputPin(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + delay_us(10); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) + result |= 1 << TR6U; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << TR6D; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << TR8L; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << TR8R; + + LL_GPIO_SetOutputPin(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + + syncelem.oldResult = result; + syncelem.ui8ReadInProgress = 0; + + return result; +} + +uint32_t readKeys() +{ + uint32_t result = 0; + + if (getHatsAsKeys()) { + uint32_t mkeys = _readKeyMatrix(); + if (mkeys & (1 << TR4D)) result |= 1 << KEY_ENTER; + if (mkeys & (1 << TR4U)) result |= 1 << KEY_EXIT; + } + + return result; +} + +uint32_t readTrims() +{ + uint32_t result = 0; + + result |= _readKeyMatrix(); + + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) + result |= 1 << (TR1U); + if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) + result |= 1 << (TR1D); + + if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) + result |= 1 << (TR2U); + if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) + result |= 1 << (TR2D); + + return result; +} diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp new file mode 100644 index 00000000000..7067e9d4cb7 --- /dev/null +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -0,0 +1,2876 @@ +/* + * 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 "opentx_types.h" +#include "dma2d.h" +#include "hal.h" +#include "delays_driver.h" +#include "debug.h" +#include "lcd.h" +#include "lcd_driver.h" + +uint8_t TouchControllerType = 0; //0:cst340; 1 ft6236 +static volatile uint16_t lcd_phys_w = LCD_PHYS_W; +static volatile 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; + + LTDC_Layer1->CFBAR &= ~(LTDC_LxCFBAR_CFBADD); + 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_Delay(void) { + volatile unsigned int i; + + for (i = 0; i < 20; i++) { + ; + } +} + +enum ENUM_IO_SPEED +{ + IO_SPEED_LOW, + IO_SPEED_MID, + IO_SPEED_QUICK, + IO_SPEED_HIGH +}; + +enum ENUM_IO_MODE +{ + IO_MODE_INPUT, + IO_MODE_OUTPUT, + IO_MODE_ALTERNATE, + IO_MODE_ANALOG +}; + +static void LCD_AF_GPIOConfig(void) { + /* + ----------------------------------------------------------------------------- + LCD_CLK <-> PG.07 | LCD_HSYNC <-> PI.12 | LCD_R3 <-> PJ.02 | LCD_G5 <-> PK.00 + | LCD VSYNC <-> PI.13 | LCD_R4 <-> PJ.03 | LCD_G6 <-> PK.01 + | | LCD_R5 <-> PJ.04 | LCD_G7 <-> PK.02 + | | LCD_R6 <-> PJ.05 | LCD_B4 <-> PK.03 + | | LCD_R7 <-> PJ.06 | LCD_B5 <-> PK.04 + | | LCD_G2 <-> PJ.09 | LCD_B6 <-> PK.05 + | | LCD_G3 <-> PJ.10 | LCD_B7 <-> PK.06 + | | LCD_G4 <-> PJ.11 | LCD_DE <-> PK.07 + | | LCD_B3 <-> PJ.15 | + */ + + LL_GPIO_InitTypeDef GPIO_InitStructure; + LL_GPIO_StructInit(&GPIO_InitStructure); + + // GPIOG configuration + GPIO_InitStructure.Pin = LL_GPIO_PIN_7; + GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStructure.Pull = LL_GPIO_PULL_NO; + GPIO_InitStructure.Alternate = LL_GPIO_AF_14; // AF LTDC + LL_GPIO_Init(GPIOG, &GPIO_InitStructure); + + // GPIOI configuration + GPIO_InitStructure.Pin = LL_GPIO_PIN_12 | LL_GPIO_PIN_13; + LL_GPIO_Init(GPIOI, &GPIO_InitStructure); + + // GPIOJ configuration + GPIO_InitStructure.Pin = LL_GPIO_PIN_2 | LL_GPIO_PIN_3 | LL_GPIO_PIN_4 | LL_GPIO_PIN_5 | LL_GPIO_PIN_6 | LL_GPIO_PIN_9 | LL_GPIO_PIN_10 | LL_GPIO_PIN_11 | LL_GPIO_PIN_15; + LL_GPIO_Init(GPIOJ, &GPIO_InitStructure); + + // GPIOK configuration + GPIO_InitStructure.Pin = LL_GPIO_PIN_0 | LL_GPIO_PIN_1 | LL_GPIO_PIN_2 | LL_GPIO_PIN_3 | LL_GPIO_PIN_4 | LL_GPIO_PIN_5 | LL_GPIO_PIN_6 | LL_GPIO_PIN_7; + LL_GPIO_Init(GPIOK, &GPIO_InitStructure); +} + +static void lcdSpiConfig(void) { + LL_GPIO_InitTypeDef GPIO_InitStructure; + LL_GPIO_StructInit(&GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_SPI_SCK_GPIO_PIN | LCD_SPI_MOSI_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_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_SPI_CS_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_UP; + LL_GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_NRST_GPIO_PIN; + LL_GPIO_Init(LCD_NRST_GPIO, &GPIO_InitStructure); + + /* Set the chip select pin always low */ + LCD_CS_LOW(); +} + +void lcdDelay() { + delay_01us(1); +} + +static void lcdReset() { + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET(); + delay_ms(100); + + LCD_NRST_HIGH(); + delay_ms(100); +} + +unsigned char LCD_ReadByteOnFallingEdge(void) { + unsigned int i; + unsigned char ReceiveData = 0; + + LCD_MOSI_HIGH(); + LCD_MOSI_AS_INPUT(); + + for (i = 0; i < 8; i++) { + LCD_DELAY(); + LCD_SCK_HIGH(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + + LCD_SCK_LOW(); + LCD_DELAY(); + LCD_DELAY(); + if (LCD_READ_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + + LCD_MOSI_AS_OUTPUT(); + + return (ReceiveData); +} + +static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { + + LCD_SCK_LOW(); + lcdDelay(); + + if (data_enable) { + LCD_MOSI_HIGH(); + } else { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + lcdDelay(); + + for (int i = 0; i < 8; i++) { + LCD_SCK_LOW(); + lcdDelay(); + + if (byte & 0x80) { + LCD_MOSI_HIGH(); + } else { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + byte <<= 1; + + lcdDelay(); + } + + LCD_SCK_LOW(); +} + +unsigned char LCD_ReadByte(void) { + unsigned int i; + unsigned char ReceiveData = 0; + + LCD_MOSI_HIGH(); + LCD_MOSI_AS_INPUT(); + for (i = 0; i < 8; i++) { + LCD_SCK_LOW(); + lcdDelay(); + ReceiveData <<= 1; + LCD_SCK_HIGH(); + lcdDelay(); + if (LCD_READ_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + LCD_SCK_LOW(); + LCD_MOSI_AS_OUTPUT(); + return (ReceiveData); +} + +unsigned char LCD_ReadRegister(unsigned char Register) { + unsigned char ReadData = 0; + + lcdWriteByte(0, Register); + lcdDelay(); + lcdDelay(); + ReadData = LCD_ReadByte(); + return (ReadData); +} + +void lcdWriteCommand(uint8_t command) { + lcdWriteByte(0, command); +} + +void lcdWriteData(uint8_t data) { + lcdWriteByte(1, data); +} + +void LCD_HX8357D_Init(void) { +#if 0 + lcdWriteCommand(0x11); + delay_ms(200); + + lcdWriteCommand(0xB9); + lcdWriteData(0xFF); + lcdWriteData(0x83); + lcdWriteData(0x57); + + lcdWriteCommand(0xB1); + lcdWriteData(0x00); + lcdWriteData(0x14); + lcdWriteData(0x1C); + lcdWriteData(0x1C); + lcdWriteData(0xC7); + lcdWriteData(0x21); + + lcdWriteCommand(0xB3); + lcdWriteData(0x83); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x06); + + lcdWriteCommand(0xB4); + lcdWriteData(0x11); + lcdWriteData(0x40); + lcdWriteData(0x00); + lcdWriteData(0x2A); + lcdWriteData(0x2A); + lcdWriteData(0x20); + lcdWriteData(0x4E); + + lcdWriteCommand(0xB5); + lcdWriteData(0x03); + lcdWriteData(0x03); + + lcdWriteCommand(0xB6); + lcdWriteData(0x38); + + lcdWriteCommand(0xC0); + lcdWriteData(0x24); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0xc8); + lcdWriteData(0x08); + + lcdWriteCommand(0xC2); + lcdWriteData(0x00); + lcdWriteData(0x08); + lcdWriteData(0x04); + + lcdWriteCommand(0xCC); + lcdWriteData(0x00); + +//GAMMA 2.5" + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x01); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0x36); + lcdWriteData(0x28); + lcdWriteCommand( 0x2A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0xDF ); + lcdWriteCommand( 0x2B ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0x3F ); + + lcdWriteCommand(0x29); + delay_ms(10); +#elif 0 + delay_ms(50); + lcdWriteCommand(0xB9); //EXTC + lcdWriteData(0xFF); //EXTC + lcdWriteData(0x83); //EXTC + lcdWriteData(0x57); //EXTC + delay_ms(5); + + lcdWriteCommand(0x3A); + lcdWriteData(0x65); //262k + + lcdWriteCommand(0xB3); //COLOR FORMAT + lcdWriteData(0x83); //SDO_EN,BYPASS,EPF[1:0],0,0,RM,DM //43 + + lcdWriteCommand(0xB6); // + lcdWriteData(0x5a); //VCOMDC + + lcdWriteCommand(0x35); // TE ON + lcdWriteData(0x01); + + lcdWriteCommand(0xB0); + lcdWriteData(0x68); //70Hz + + lcdWriteCommand(0xCC); // Set Panel + lcdWriteData(0x00); // + + lcdWriteCommand(0xB1); // + lcdWriteData(0x00); // + lcdWriteData(0x11); //BT + lcdWriteData(0x1C); //VSPR + lcdWriteData(0x1C); //VSNR + lcdWriteData(0x83); //AP + lcdWriteData(0x48); //FS 0xAA + + lcdWriteCommand(0xB4); // + lcdWriteData(0x02); //NW + lcdWriteData(0x40); //RTN + lcdWriteData(0x00); //DIV + lcdWriteData(0x2A); //DUM + lcdWriteData(0x2A); //DUM + lcdWriteData(0x0D); //GDON + lcdWriteData(0x78); //GDOFF 0x4F + lcdWriteCommand(0xC0); //STBA + lcdWriteData(0x50); //OPON + lcdWriteData(0x50); //OPON + lcdWriteData(0x01); // + lcdWriteData(0x3C); // + lcdWriteData(0x1E); // + lcdWriteData(0x08); //GEN + + /* + lcdWriteCommand(0xE0); // + lcdWriteData(0x02); //1 + lcdWriteData(0x06); //2 + lcdWriteData(0x09); //3 + lcdWriteData(0x1C); //4 + lcdWriteData(0x27); //5 + lcdWriteData(0x3C); //6 + lcdWriteData(0x48); //7 + lcdWriteData(0x50); //8 + lcdWriteData(0x49); //9 + lcdWriteData(0x42); //10 + lcdWriteData(0x3E); //11 + lcdWriteData(0x35); //12 + lcdWriteData(0x31); //13 + lcdWriteData(0x2A); //14 + lcdWriteData(0x28); //15 + lcdWriteData(0x03); //16 + lcdWriteData(0x02); //17 v1 + lcdWriteData(0x06); //18 + lcdWriteData(0x09); //19 + lcdWriteData(0x1C); //20 + lcdWriteData(0x27); //21 + lcdWriteData(0x3C); //22 + lcdWriteData(0x48); //23 + lcdWriteData(0x50); //24 + lcdWriteData(0x49); //25 + lcdWriteData(0x42); //26 + lcdWriteData(0x3E); //27 + lcdWriteData(0x35); //28 + lcdWriteData(0x31); //29 + lcdWriteData(0x2A); //30 + lcdWriteData(0x28); //31 + lcdWriteData(0x03); //32 + lcdWriteData(0x44); //33 + lcdWriteData(0x01); //34 + */ + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteCommand(0x36); + lcdWriteData(0x38); + + lcdWriteCommand(0x11); // SLPOUT + delay_ms(200); + + lcdWriteCommand(0x29); // Display On + delay_ms(25); + lcdWriteCommand(0x2C); +#else + lcdWriteCommand(0x11); + delay_ms(200); + + lcdWriteCommand(0xB9); + lcdWriteData(0xFF); + lcdWriteData(0x83); + lcdWriteData(0x57); + delay_ms(5); + +// lcdWriteCommand(0x36); +// lcdWriteData(0x10); + + lcdWriteCommand(0xB1); + lcdWriteData(0x00); + lcdWriteData(0x14); + lcdWriteData(0x1C); + lcdWriteData(0x1C); + lcdWriteData(0xC7); + lcdWriteData(0x21); + lcdWriteCommand(0xB3); + lcdWriteData(0x83); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x06); + lcdWriteCommand(0xB4); + lcdWriteData(0x11); + lcdWriteData(0x40); + lcdWriteData(0x00); + lcdWriteData(0x2A); + lcdWriteData(0x2A); + lcdWriteData(0x20); + lcdWriteData(0x4E); + lcdWriteCommand(0xB5); + lcdWriteData(0x03); + lcdWriteData(0x03); + + lcdWriteCommand(0xB6); + lcdWriteData(0x38); + + lcdWriteCommand(0xC0); + lcdWriteData(0x24); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0xc8); + lcdWriteData(0x08); + lcdWriteCommand(0xC2); + lcdWriteData(0x00); + lcdWriteData(0x08); + lcdWriteData(0x04); + //GAMMA 2.5" + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x01); + // lcdWriteCommand(0x2A); + // lcdWriteData(0); + // lcdWriteData(0); + // lcdWriteData(480 >> 8); + // lcdWriteData(480); + // lcdWriteCommand(0x2B); + // lcdWriteData(0); + // lcdWriteData(0); + // lcdWriteData(320 >> 8); + // lcdWriteData(320); + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0xCC); + lcdWriteData(0x01); + + lcdWriteCommand( 0x2A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0xDF ); + lcdWriteCommand( 0x2B ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0x3F ); + + lcdWriteCommand(0x36); + lcdWriteData(0x20); + + lcdWriteCommand(0xB9); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + delay_ms(5); + lcdWriteCommand(0x29); +#endif +} + +void LCD_HX8357D_On(void) { + lcdWriteCommand(0x28); + lcdWriteCommand(0x29); +} + +void LCD_HX8357D_Off(void) { + lcdWriteCommand(0x28); +} + +unsigned int LCD_HX8357D_ReadID(void) { + lcdReset(); + int ID = 0; + + lcdWriteCommand( 0xB9 ); + lcdWriteData( 0xff ); + lcdWriteData( 0x83 ); + lcdWriteData( 0x57 ); + + lcdWriteCommand( 0xFE ); + lcdWriteData( 0xd0 ); + ID = LCD_ReadRegister( 0xff ); + + lcdWriteCommand( 0xB9 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + + return (ID); +} + +void LCD_ILI9481_Init(void) { + lcdWriteCommand(0x11); + delay_ms(120); + + lcdWriteCommand(0xE4); + lcdWriteData(0x0A); + + lcdWriteCommand(0xF0); + lcdWriteData(0x01); + + lcdWriteCommand(0xF3); + lcdWriteData(0x02); + lcdWriteData(0x1A); + + lcdWriteCommand(0xD0); + lcdWriteData(0x07); + lcdWriteData(0x42); + lcdWriteData(0x1B); + + lcdWriteCommand(0xD1); + lcdWriteData(0x00); + lcdWriteData(0x00); //04 + lcdWriteData(0x1A); + + lcdWriteCommand(0xD2); + lcdWriteData(0x01); + lcdWriteData(0x00); //11 + + lcdWriteCommand(0xC0); + lcdWriteData(0x10); + lcdWriteData(0x3B); // + lcdWriteData(0x00); // + lcdWriteData(0x02); + lcdWriteData(0x11); + + lcdWriteCommand(0xC5); + lcdWriteData(0x03); + + lcdWriteCommand(0xC8); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x47); + lcdWriteData(0x60); + lcdWriteData(0x04); + lcdWriteData(0x16); + lcdWriteData(0x03); + lcdWriteData(0x67); + lcdWriteData(0x67); + lcdWriteData(0x06); + lcdWriteData(0x0F); + lcdWriteData(0x00); + + lcdWriteCommand(0x36); + lcdWriteData(0x08); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); //0x55=65k color, 0x66=262k color. + + lcdWriteCommand(0x2A); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x3F); + + lcdWriteCommand(0x2B); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0xE0); + + lcdWriteCommand(0xB4); + lcdWriteData(0x11); + + lcdWriteCommand(0xc6); + lcdWriteData(0x82); + + delay_ms(120); + + lcdWriteCommand(0x21); + lcdWriteCommand(0x29); + lcdWriteCommand(0x2C); + +} + +void LCD_ILI9481_On(void) { + lcdWriteCommand(0x29); +} + +void LCD_ILI9481_Off(void) { + lcdWriteCommand(0x28); +} + +unsigned int LCD_ILI9481_ReadID(void) { +#if 1 + /* Have a issue here */ + return 0; +#else + int ID = 0; + int Data; + + + lcdWriteByte(0, 0xBF); + + Data = LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + ID = LCD_ReadByteOnFallingEdge(); + ID <<= 8; + ID |= LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + + LCD_DELAY(); + LCD_DELAY(); + LCD_DELAY(); + + lcdWriteCommand(0xC6); + lcdWriteData(0x82); + //lcdWriteData( 0x9b ); + return (ID); +#endif +} + +void LCD_ILI9486_On(void) { + lcdWriteCommand(0x29); +} + +void LCD_ILI9486_Init(void) { + lcdWriteCommand(0XFB); + lcdWriteData(0x00); + + lcdWriteCommand(0xf2); + lcdWriteData(0x18); + lcdWriteData(0xa3); + lcdWriteData(0x12); + lcdWriteData(0x02); + lcdWriteData(0xb2); + lcdWriteData(0x12); + lcdWriteData(0xff); + lcdWriteData(0x13); + lcdWriteData(0x00); + lcdWriteCommand(0xf1); + lcdWriteData(0x36); + lcdWriteData(0x04); + lcdWriteData(0x00); + lcdWriteData(0x3c); + lcdWriteData(0x0f); + lcdWriteData(0x8f); + lcdWriteCommand(0xf8); + lcdWriteData(0x21); + lcdWriteData(0x04); + lcdWriteCommand(0xf9); + lcdWriteData(0x00); + lcdWriteData(0x08); + lcdWriteCommand(0x36); + lcdWriteData(0x18); + lcdWriteCommand(0x3a); + lcdWriteData(0x65); + lcdWriteCommand(0xc0); + lcdWriteData(0x0f); + lcdWriteData(0x0f); + lcdWriteCommand(0xc1); + lcdWriteData(0x41); + + lcdWriteCommand(0xc5); + lcdWriteData(0x00); + lcdWriteData(0x27); + lcdWriteData(0x80); + lcdWriteCommand(0xb6); + lcdWriteData(0xb2); + lcdWriteData(0x42); + lcdWriteData(0x3b); + lcdWriteCommand(0xb1); + lcdWriteData(0xb0); + lcdWriteData(0x11); + lcdWriteCommand(0xb4); + lcdWriteData(0x02); + lcdWriteCommand(0xb7); + lcdWriteData(0xC6); + + lcdWriteCommand(0xe0); + lcdWriteData(0x0f); + lcdWriteData(0x1C); + lcdWriteData(0x18); + lcdWriteData(0x0B); + lcdWriteData(0x0D); + lcdWriteData(0x06); + lcdWriteData(0x48); + lcdWriteData(0x87); + lcdWriteData(0x3A); + lcdWriteData(0x09); + lcdWriteData(0x15); + lcdWriteData(0x08); + lcdWriteData(0x0D); + lcdWriteData(0x04); + lcdWriteData(0x00); + + lcdWriteCommand(0xe1); + lcdWriteData(0x0f); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x0A); + lcdWriteData(0x0B); + lcdWriteData(0x03); + lcdWriteData(0x4B); + lcdWriteData(0x31); + lcdWriteData(0x39); + lcdWriteData(0x03); + lcdWriteData(0x0F); + lcdWriteData(0x03); + lcdWriteData(0x22); + lcdWriteData(0x1D); + lcdWriteData(0x00); + + lcdWriteCommand(0x21); + lcdWriteCommand(0x11); + delay_ms(120); + lcdWriteCommand(0x28); + + LCD_ILI9486_On(); +} + +void LCD_ILI9486_Off(void) { + lcdWriteCommand(0x28); +} + +unsigned int LCD_ILI9486_ReadID(void) { + int ID = 0; + + lcdWriteCommand(0XF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + lcdWriteCommand(0XB0); + lcdWriteData(0X80); + + lcdWriteCommand(0XFB); + lcdWriteData(0x10 | 0x00); + ID = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0XFB); + lcdWriteData(0x10 | 0x01); + ID = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0XFB); + lcdWriteData(0x10 | 0x02); + ID = LCD_ReadRegister(0xd3); + ID <<= 8; + lcdWriteCommand(0XFB); + lcdWriteData(0x10 | 0x03); + ID |= LCD_ReadRegister(0xd3); + + lcdWriteCommand(0XFB); + lcdWriteData(0x00); + + return (ID); +} + +void LCD_ILI9488_On(void) { + // Display ON + lcdWriteCommand(0x29); +} + +void LCD_ILI9488_Init(void) { + + // lcdWriteCommand(0xFB); + // lcdWriteData(0x00); + + // Adjust Control 3: + // -> DSI write DCS command, use stream packet RGB 666 + lcdWriteCommand(0xF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + // Power Control 1: + // -> VREG1OUT = 4.6250 + // -> VREG2OUT = -4.1250 + lcdWriteCommand(0xC0); + lcdWriteData(0x11); + lcdWriteData(0x09); + + // Power Control 2: + // -> VGH = VCI x 6, VGL = VCI x 4 + lcdWriteCommand(0xC1); + lcdWriteData(0x41); + + // VCOM Control + lcdWriteCommand(0xC5); + lcdWriteData(0x00); // NV memory not programmed + lcdWriteData(0x0A); // VCM_REG + lcdWriteData(0x80); // VCM_REG_EN + + // Frame Rate Control + lcdWriteCommand(0xB1); + lcdWriteData(0xB0); + lcdWriteData(0x11); + + // Display Inversion Control + lcdWriteCommand(0xB4); + lcdWriteData(0x01); + + lcdWriteCommand(0xB5); + lcdWriteData(VFP); + lcdWriteData(VBP); + lcdWriteData(HFP); + lcdWriteData(HBP); + + // Display Function Control + lcdWriteCommand(0xB6); + // !DM | RM | RCM + lcdWriteData(0x20 | 0x40); + lcdWriteData(0x02); + lcdWriteData(0x3B); // (59 + 1) x 8 = 480 lines + + // Entry Set Mode + lcdWriteCommand(0xB7); + lcdWriteData(0xc6); + + lcdWriteCommand(0xBE); + lcdWriteData(0x00); + // lcdWriteData(0x04); // ???? + + lcdWriteCommand(0xE9); + lcdWriteData(0x00); + + // Column Address Set + // -> SC=0, EC=479 + lcdWriteCommand( 0x2A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0xDF ); + + // Page Address Set + // -> SP=0, EP=319 + lcdWriteCommand( 0x2B ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0x3F ); + + // Memory Access Control + lcdWriteCommand(0x36); + lcdWriteData(0x28); // 0x20 -> swap col/rows + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x07); + lcdWriteData(0x10); + lcdWriteData(0x09); + lcdWriteData(0x17); + lcdWriteData(0x0B); + lcdWriteData(0x41); + lcdWriteData(0x89); + lcdWriteData(0x4B); + lcdWriteData(0x0A); + lcdWriteData(0x0C); + lcdWriteData(0x0E); + lcdWriteData(0x18); + lcdWriteData(0x1B); + lcdWriteData(0x0F); + + lcdWriteCommand(0xE1); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x1A); + lcdWriteData(0x04); + lcdWriteData(0x0E); + lcdWriteData(0x06); + lcdWriteData(0x2F); + lcdWriteData(0x45); + lcdWriteData(0x43); + lcdWriteData(0x02); + lcdWriteData(0x0A); + lcdWriteData(0x09); + lcdWriteData(0x32); + lcdWriteData(0x36); + lcdWriteData(0x0F); + + // Sleep OUT + lcdWriteCommand(0x11); + delay_ms(120); + + // Display OFF + lcdWriteCommand(0x28); + + LCD_ILI9488_On(); +} + +void LCD_ILI9488_Off(void) { + lcdWriteCommand(0x28); +} + +void LCD_ILI9488_ReadDevice(void) { + int Index = 0; + int Parameter = 0x80; + + lcdWriteCommand(0xF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + lcdWriteCommand(0xB0); + lcdWriteData(0x80); + + lcdWriteCommand(0xFB); + lcdWriteData(Parameter | 0x00); + LCD_ReadBuffer[Index++] = LCD_ReadRegister(0xd3); + + //lcdWriteCommand(0X2E); + lcdWriteCommand(0xFB); + lcdWriteData(Parameter | 0x01); //Parameter2=0x88 + LCD_ReadBuffer[Index++] = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0XFB); + lcdWriteData(Parameter | 0x02); //Parameter2=0x88 + LCD_ReadBuffer[Index++] = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0XFB); + lcdWriteData(Parameter | 0x03); //Parameter2=0x88 + LCD_ReadBuffer[Index++] = LCD_ReadRegister(0xd3); +} + +unsigned int LCD_ILI9488_ReadID(void) { + int ID = 0; + + // Adjust Control 3: + // -> DSI write DCS command, use stream packet RGB 666 + lcdWriteCommand(0xF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + // Interface Mode Control: + // SDA_EN = 1, DIN/SDA pin is used for 3/4 wire serial + // interface and SDO pin is not used. + lcdWriteCommand(0xB0); + lcdWriteData(0x80); + + // first byte is dummy one + lcdWriteCommand(0xFB); + lcdWriteData(0x80 | 0x00); + ID = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0xFB); + lcdWriteData(0x80 | 0x01); + ID = LCD_ReadRegister(0xd3); + + lcdWriteCommand(0xFB); + lcdWriteData(0x80 | 0x02); + ID = LCD_ReadRegister(0xd3); + ID <<= 8; + + lcdWriteCommand(0xFB); + lcdWriteData(0x80 | 0x03); + ID |= LCD_ReadRegister(0xd3); + + lcdWriteCommand(0xFB); + lcdWriteData(0x00); + + return (ID); +} + +void LCD_ST7796S_On(void) { + lcdWriteCommand(0x29); +} + +void LCD_ST7796S_Init(void) { + delay_ms(120); + + lcdWriteCommand( 0x11 ); + delay_ms(120); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0xC3 ); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0x96 ); + + lcdWriteCommand( 0x36 ); + lcdWriteData( 0x28 ); + + lcdWriteCommand( 0x2A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0xDF ); + lcdWriteCommand( 0x2B ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0x3F ); + + lcdWriteCommand( 0x3A ); + lcdWriteData( 0x66 ); + + //SET RGB STRAT + lcdWriteCommand (0xB0 ); //SET HS VS DE CLK 上升还是下降有效 + lcdWriteData( 0x80 ); + + lcdWriteCommand( 0xB4 ); + lcdWriteData( 0x01 ); + + lcdWriteCommand( 0xB6 ); + // lcdWriteData( 0x20 ); + // lcdWriteData( 0x02 ); + // lcdWriteData( 0x3B ); + lcdWriteData( 0x20 ); + lcdWriteData( 0x02 ); + lcdWriteData( 0x3B ); + //SET RGB END + + lcdWriteCommand( 0xB7); + lcdWriteData( 0xC6); + + lcdWriteCommand( 0xB9 ); + lcdWriteData( 0x02 ); + lcdWriteData( 0xE0 ); + + lcdWriteCommand( 0xC0 ); + lcdWriteData( 0x80 ); + lcdWriteData( 0x65 ); + + lcdWriteCommand( 0xC1 ); + lcdWriteData( 0x0D ); + + lcdWriteCommand( 0xC2 ); + lcdWriteData( 0xA7 ); + + lcdWriteCommand( 0xC5 ); + lcdWriteData( 0x14 ); + + lcdWriteCommand( 0xE8 ); + lcdWriteData( 0x40 ); + lcdWriteData( 0x8A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x29 ); + lcdWriteData( 0x19 ); + lcdWriteData( 0xA5 ); + lcdWriteData( 0x33 ); + + lcdWriteCommand( 0xE0 ); + lcdWriteData( 0xD0 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x05 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x21 ); + lcdWriteData( 0x25 ); + lcdWriteData( 0x43 ); + lcdWriteData( 0x3F ); + lcdWriteData( 0x37 ); + lcdWriteData( 0x13 ); + lcdWriteData( 0x13 ); + lcdWriteData( 0x29 ); + lcdWriteData( 0x32 ); + + lcdWriteCommand( 0xE1 ); + lcdWriteData( 0xD0 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x06 ); + lcdWriteData( 0x09 ); + lcdWriteData( 0x06 ); + lcdWriteData( 0x03 ); + lcdWriteData( 0x25 ); + lcdWriteData( 0x32 ); + lcdWriteData( 0x3E ); + lcdWriteData( 0x18 ); + lcdWriteData( 0x15 ); + lcdWriteData( 0x15 ); + lcdWriteData( 0x2B ); + lcdWriteData( 0x30 ); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0x3C ); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0x69 ); + + delay_ms(120); + + if( !TouchControllerType ) { + lcdWriteCommand( 0x21 ); + } + + LCD_ST7796S_On(); +} + +void LCD_ST7796S_Off(void) { + lcdWriteCommand(0x28); +} + +unsigned int LCD_ST7796S_ReadID(void) { + lcdReset(); + unsigned int ID = 0; + + lcdWriteCommand( 0XF0 ); + lcdWriteData( 0XC3 ); + lcdWriteCommand( 0XF0 ); + lcdWriteData( 0X96 ); + + lcdWriteCommand( 0XB0 ); + lcdWriteData( 0X80 ); + + lcdWriteCommand( 0XD3 ); + + LCD_MOSI_AS_INPUT(); + LCD_SCK_LOW(); + lcdDelay(); + lcdDelay(); + LCD_SCK_HIGH(); + lcdDelay(); + lcdDelay(); + + LCD_ReadByte(); + ID += (uint16_t)(LCD_ReadByte())<<8; + ID += LCD_ReadByte(); + + return (ID); + } + + +unsigned int LCD_NT35310_ReadID( void ) +{ + unsigned int ID = 0x3531; + + return( ID ); + +} + +void LCD_NT35310_Init( void ) +{ +#if 1 + lcdWriteCommand(0xED); + lcdWriteData(0x01); + lcdWriteData(0xFE); + + lcdWriteCommand(0xEE); + lcdWriteData(0xDE); + lcdWriteData(0x21); + + lcdWriteCommand(0x11); + delay_ms(120); + lcdWriteCommand(0xB3); + lcdWriteData(0x21); + + + + lcdWriteCommand(0xC0); + lcdWriteData(0x33); + lcdWriteData(0x33); + lcdWriteData(0x10); + lcdWriteData(0x10); + + + lcdWriteCommand(0xC4); + lcdWriteData(0x56); //3a + + lcdWriteCommand(0xBF); + lcdWriteData(0xAA); + + lcdWriteCommand(0xB0); + lcdWriteData(0x0D); + lcdWriteData(0x00); + lcdWriteData(0x0D); + lcdWriteData(0x00); + lcdWriteData(0x11); + lcdWriteData(0x00); + lcdWriteData(0x19); + lcdWriteData(0x00); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x2D); + lcdWriteData(0x00); + lcdWriteData(0x3D); + lcdWriteData(0x00); + lcdWriteData(0x5D); + lcdWriteData(0x00); + lcdWriteData(0x5D); + lcdWriteData(0x00); + + lcdWriteCommand(0xB1); + lcdWriteData(0x80); + lcdWriteData(0x00); + lcdWriteData(0x8B); + lcdWriteData(0x00); + lcdWriteData(0x96); + lcdWriteData(0x00); + + lcdWriteCommand(0xB2); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x03); + lcdWriteData(0x00); + + lcdWriteCommand(0xB3); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xB4); + lcdWriteData(0x8B); + lcdWriteData(0x00); + lcdWriteData(0x96); + lcdWriteData(0x00); + lcdWriteData(0xA1); + lcdWriteData(0x00); + + lcdWriteCommand(0xB5); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x03); + lcdWriteData(0x00); + lcdWriteData(0x04); + lcdWriteData(0x00); + lcdWriteCommand(0xB6); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xB7); + lcdWriteData(0x3E); + lcdWriteData(0x00); + lcdWriteData(0x5E); + lcdWriteData(0x00); + lcdWriteData(0x9E); + lcdWriteData(0x00); + lcdWriteData(0x74); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0xAC); + lcdWriteData(0x00); + lcdWriteData(0xDC); + lcdWriteData(0x00); + lcdWriteData(0x70); + lcdWriteData(0x00); + lcdWriteData(0xB9); + lcdWriteData(0x00); + lcdWriteData(0xEC); + lcdWriteData(0x00); + lcdWriteData(0xDC); + lcdWriteData(0x00); + + lcdWriteCommand(0xB8); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xBA); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC1); + lcdWriteData(0x20); + lcdWriteData(0x00); + lcdWriteData(0x54); + lcdWriteData(0x00); + lcdWriteData(0xFF); + lcdWriteData(0x00); + + lcdWriteCommand(0xC2); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x04); + lcdWriteData(0x00); + + lcdWriteCommand(0xC3); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x3A); + lcdWriteData(0x00); + lcdWriteData(0x39); + lcdWriteData(0x00); + lcdWriteData(0x37); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x2F); + lcdWriteData(0x00); + lcdWriteData(0x2C); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x00); + lcdWriteData(0x26); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x2F); + lcdWriteData(0x00); + lcdWriteData(0x2C); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x00); + lcdWriteData(0x26); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + + lcdWriteCommand(0xC4); + lcdWriteData(0x62); + lcdWriteData(0x00); + lcdWriteData(0x05); + lcdWriteData(0x00); + lcdWriteData(0x84); + lcdWriteData(0x00); + lcdWriteData(0xF0); + lcdWriteData(0x00); + lcdWriteData(0x18); + lcdWriteData(0x00); + lcdWriteData(0xA4); + lcdWriteData(0x00); + lcdWriteData(0x18); + lcdWriteData(0x00); + lcdWriteData(0x50); + lcdWriteData(0x00); + lcdWriteData(0x0C); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x00); + lcdWriteData(0x95); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + lcdWriteData(0xE6); + lcdWriteData(0x00); + + lcdWriteCommand(0xC5); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + lcdWriteData(0x76); + lcdWriteData(0x00); + lcdWriteData(0x88); + lcdWriteData(0x00); + + lcdWriteCommand(0xC6); + lcdWriteData(0x20); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x00); + + lcdWriteCommand(0xC7); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC8); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC9); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xE0); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE1); + lcdWriteData(0x01); + lcdWriteData(0x00); + lcdWriteData(0x05); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE2); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE3); + lcdWriteData(0x01); + lcdWriteData(0x00); + lcdWriteData(0x05); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE4); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE5); + lcdWriteData(0x01); + lcdWriteData(0x00); + lcdWriteData(0x05); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x25); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x6F); + lcdWriteData(0x00); + lcdWriteData(0x7F); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0x98); + lcdWriteData(0x00); + lcdWriteData(0xA6); + lcdWriteData(0x00); + lcdWriteData(0xAE); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBB); + lcdWriteData(0x00); + lcdWriteData(0xC0); + lcdWriteData(0x00); + lcdWriteData(0xC9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE6); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE7); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE8); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE9); + lcdWriteData(0xAA); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0x00); + lcdWriteData(0xAA); + + lcdWriteCommand(0xCF); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xF0); + lcdWriteData(0x00); + lcdWriteData(0x50); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xF1); + lcdWriteData(0x01); + + lcdWriteCommand(0xF9); + lcdWriteData(0x06); + lcdWriteData(0x10); + lcdWriteData(0x29); + lcdWriteData(0x00); + + lcdWriteCommand(0xDF); + lcdWriteData(0x10); + delay_ms(20); + lcdWriteCommand(0x36); +// if( IsHorizontal ) +// lcdWriteData(0x00);//需修改 +// else + lcdWriteData(0x14); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0x21); + + lcdWriteCommand(0x35); + lcdWriteData(0x00); + + lcdWriteCommand(0x29); +#else + lcdWriteCommand(0xED); + lcdWriteData(0x01); + lcdWriteData(0xFE); + + lcdWriteCommand(0xEE); + lcdWriteData(0xDE); + lcdWriteData(0x21); + + lcdWriteCommand(0x11); + SYSTEM_DelayMS(120); + lcdWriteCommand(0xB3); + lcdWriteData(0x21); + + + lcdWriteCommand(0xc0); + lcdWriteData(0x56); + lcdWriteData(0x56); + lcdWriteData(0x24); + lcdWriteData(0x24); + + lcdWriteCommand(0xC4); + lcdWriteData(0x30); //3a + + lcdWriteCommand(0xBF); + lcdWriteData(0xAA); + + lcdWriteCommand(0xB0); + lcdWriteData(0x0D); + lcdWriteData(0x00); + lcdWriteData(0x0D); + lcdWriteData(0x00); + lcdWriteData(0x11); + lcdWriteData(0x00); + lcdWriteData(0x19); + lcdWriteData(0x00); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x2D); + lcdWriteData(0x00); + lcdWriteData(0x3D); + lcdWriteData(0x00); + lcdWriteData(0x5D); + lcdWriteData(0x00); + lcdWriteData(0x5D); + lcdWriteData(0x00); + + lcdWriteCommand(0xB1); + lcdWriteData(0x80); + lcdWriteData(0x00); + lcdWriteData(0x8B); + lcdWriteData(0x00); + lcdWriteData(0x96); + lcdWriteData(0x00); + + lcdWriteCommand(0xB2); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x03); + lcdWriteData(0x00); + + lcdWriteCommand(0xB3); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xB4); + lcdWriteData(0x8B); + lcdWriteData(0x00); + lcdWriteData(0x96); + lcdWriteData(0x00); + lcdWriteData(0xA1); + lcdWriteData(0x00); + + lcdWriteCommand(0xB5); + lcdWriteData(0x02); + lcdWriteData(0x00); + lcdWriteData(0x03); + lcdWriteData(0x00); + lcdWriteData(0x04); + lcdWriteData(0x00); + lcdWriteCommand(0xB6); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xB7); + lcdWriteData(0x3E); + lcdWriteData(0x00); + lcdWriteData(0x5E); + lcdWriteData(0x00); + lcdWriteData(0x9E); + lcdWriteData(0x00); + lcdWriteData(0x74); + lcdWriteData(0x00); + lcdWriteData(0x8C); + lcdWriteData(0x00); + lcdWriteData(0xAC); + lcdWriteData(0x00); + lcdWriteData(0xDC); + lcdWriteData(0x00); + lcdWriteData(0x70); + lcdWriteData(0x00); + lcdWriteData(0xB9); + lcdWriteData(0x00); + lcdWriteData(0xEC); + lcdWriteData(0x00); + lcdWriteData(0xDC); + lcdWriteData(0x00); + + lcdWriteCommand(0xB8); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xBA); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC1); + lcdWriteData(0x20); + lcdWriteData(0x00); + lcdWriteData(0x54); + lcdWriteData(0x00); + lcdWriteData(0xFF); + lcdWriteData(0x00); + + lcdWriteCommand(0xC2); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x04); + lcdWriteData(0x00); + + lcdWriteCommand(0xC3); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x3A); + lcdWriteData(0x00); + lcdWriteData(0x39); + lcdWriteData(0x00); + lcdWriteData(0x37); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x2F); + lcdWriteData(0x00); + lcdWriteData(0x2C); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x00); + lcdWriteData(0x26); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x36); + lcdWriteData(0x00); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x2F); + lcdWriteData(0x00); + lcdWriteData(0x2C); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x00); + lcdWriteData(0x26); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + + lcdWriteCommand(0xC4); + lcdWriteData(0x62); + lcdWriteData(0x00); + lcdWriteData(0x05); + lcdWriteData(0x00); + lcdWriteData(0x84); + lcdWriteData(0x00); + lcdWriteData(0xF0); + lcdWriteData(0x00); + lcdWriteData(0x18); + lcdWriteData(0x00); + lcdWriteData(0xA4); + lcdWriteData(0x00); + lcdWriteData(0x18); + lcdWriteData(0x00); + lcdWriteData(0x50); + lcdWriteData(0x00); + lcdWriteData(0x0C); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x00); + lcdWriteData(0x95); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + lcdWriteData(0xE6); + lcdWriteData(0x00); + + lcdWriteCommand(0xC5); + lcdWriteData(0x32); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + lcdWriteData(0x76); + lcdWriteData(0x00); + lcdWriteData(0x88); + lcdWriteData(0x00); + + lcdWriteCommand(0xC6); + lcdWriteData(0x20); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x00); + + lcdWriteCommand(0xC7); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC8); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xC9); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xE0); + lcdWriteData(0x01); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xCF); + lcdWriteData(0x00); + lcdWriteData(0xD3); + lcdWriteData(0x00); + lcdWriteData(0xDA); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE1); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xD0); + lcdWriteData(0x00); + lcdWriteData(0xD4); + lcdWriteData(0x00); + lcdWriteData(0xD9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE2); + lcdWriteData(0x10); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xCF); + lcdWriteData(0x00); + lcdWriteData(0xD3); + lcdWriteData(0x00); + lcdWriteData(0xDA); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE3); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xD0); + lcdWriteData(0x00); + lcdWriteData(0xD4); + lcdWriteData(0x00); + lcdWriteData(0xD9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE4); + lcdWriteData(0x01); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xCF); + lcdWriteData(0x00); + lcdWriteData(0xD3); + lcdWriteData(0x00); + lcdWriteData(0xDA); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE5); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x1F); + lcdWriteData(0x00); + lcdWriteData(0x3C); + lcdWriteData(0x00); + lcdWriteData(0x59); + lcdWriteData(0x00); + lcdWriteData(0x67); + lcdWriteData(0x00); + lcdWriteData(0x72); + lcdWriteData(0x00); + lcdWriteData(0x82); + lcdWriteData(0x00); + lcdWriteData(0x93); + lcdWriteData(0x00); + lcdWriteData(0xA0); + lcdWriteData(0x00); + lcdWriteData(0xAB); + lcdWriteData(0x00); + lcdWriteData(0xB4); + lcdWriteData(0x00); + lcdWriteData(0xBF); + lcdWriteData(0x00); + lcdWriteData(0xC6); + lcdWriteData(0x00); + lcdWriteData(0xCA); + lcdWriteData(0x00); + lcdWriteData(0xD0); + lcdWriteData(0x00); + lcdWriteData(0xD4); + lcdWriteData(0x00); + lcdWriteData(0xD9); + lcdWriteData(0x00); + lcdWriteData(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xE6); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE7); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE8); + lcdWriteData(0x55); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x56); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x57); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x77); + lcdWriteData(0x00); + lcdWriteData(0x66); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x44); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x33); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x23); + lcdWriteData(0x00); + lcdWriteData(0x65); + lcdWriteData(0x00); + + lcdWriteCommand(0xE9); + lcdWriteData(0xAA); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0x00); + lcdWriteData(0xAA); + + lcdWriteCommand(0xCF); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xF0); + lcdWriteData(0x00); + lcdWriteData(0x50); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x00); + + lcdWriteCommand(0xF1); + lcdWriteData(0x01); + + lcdWriteCommand(0xee); + lcdWriteData(0xde); + lcdWriteData(0x21); + + lcdWriteCommand(0xF3); + lcdWriteData(0x00); + + lcdWriteCommand(0xF9); + lcdWriteData(0x06); + lcdWriteData(0x10); + lcdWriteData(0x29); + lcdWriteData(0x00); + + lcdWriteCommand(0xDF); + lcdWriteData(0x10); + SYSTEM_DelayMS(20); + lcdWriteCommand(0x36); + if( IsHorizontal ) + lcdWriteData(0x14);//需修改 + else + lcdWriteData(0x14); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0x21); + + lcdWriteCommand(0x35); + lcdWriteData(0x00); + + lcdWriteCommand(0x28); +#endif +} + +void LCD_NT35310_On( void ) +{ + lcdWriteCommand( 0x29 ); +} + +void LCD_NT35310_Off( void ) +{ + lcdWriteCommand( 0x28 ); +} + +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.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; + /* Configure vertical synchronization height */ + hltdc.Init.VerticalSync = VSH; + /* Configure accumulated horizontal back porch */ + hltdc.Init.AccumulatedHBP = HBP; + /* Configure accumulated vertical back porch */ + hltdc.Init.AccumulatedVBP = VBP; + /* Configure accumulated active width */ + hltdc.Init.AccumulatedActiveW = lcd_phys_w + HBP; + /* Configure accumulated active height */ + hltdc.Init.AccumulatedActiveH = lcd_phys_h + VBP; + /* Configure total width */ + hltdc.Init.TotalWidth = lcd_phys_w + HBP + HFP; + /* Configure total height */ + hltdc.Init.TotalHeigh = lcd_phys_h + VBP + VFP; + + 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) +{ + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + /* Reset the LCD --------------------------------------------------------*/ + lcdReset(); + + /* Configure the LCD Control pins */ + LCD_AF_GPIOConfig(); + + /* Send LCD initialization commands */ + if (LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { + TRACE("LCD INIT: ILI9481"); + boardLcdType = "ILI9481"; + lcdInitFunction = LCD_ILI9481_Init; + lcdOffFunction = LCD_ILI9481_Off; + lcdOnFunction = LCD_ILI9481_On; + lcdPixelClock = 12000000; + } else if (LCD_ILI9486_ReadID() == LCD_ILI9486_ID) { + TRACE("LCD INIT: ILI9486"); + boardLcdType = "ILI9486"; + lcdInitFunction = LCD_ILI9486_Init; + lcdOffFunction = LCD_ILI9486_Off; + lcdOnFunction = LCD_ILI9486_On; + lcdPixelClock = 12000000; + } else if (LCD_ILI9488_ReadID() == LCD_ILI9488_ID) { + TRACE("LCD INIT: ILI9488"); + boardLcdType = "ILI9488"; + lcdInitFunction = LCD_ILI9488_Init; + lcdOffFunction = LCD_ILI9488_Off; + lcdOnFunction = LCD_ILI9488_On; + lcdPixelClock = 12000000; + lcd_phys_w = LCD_PHYS_H; + lcd_phys_h = LCD_PHYS_W; + } else if (LCD_HX8357D_ReadID() == LCD_HX8357D_ID) { + TRACE("LCD INIT: HX8357D"); + boardLcdType = "HX8357D"; + lcdInitFunction = LCD_HX8357D_Init; + lcdOffFunction = LCD_HX8357D_Off; + lcdOnFunction = LCD_HX8357D_On; + lcdPixelClock = 12000000; + } else if (LCD_ST7796S_ReadID() == LCD_ST7796S_ID ) { + TRACE("LCD INIT: ST7796S"); + boardLcdType = "ST7796S"; + lcdInitFunction = LCD_ST7796S_Init; + lcdOffFunction = LCD_ST7796S_Off; + lcdOnFunction = LCD_ST7796S_On; + lcdPixelClock = 14500000; + } + else{ + TRACE("LCD INIT (default): ST7796S"); + boardLcdType = "ST7796S (Default)"; + lcdInitFunction = LCD_ST7796S_Init; + lcdOffFunction = LCD_ST7796S_Off; + lcdOnFunction = LCD_ST7796S_On; + lcdPixelClock = 12000000; + } + + lcdInitFunction(); + + LCD_Init_LTDC(); + LCD_LayerInit(); + + // Enable LCD display + __HAL_LTDC_ENABLE(&hltdc); + + lcdSetFlushCb(startLcdRefresh); +} + +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/targets/pl18/lcd_driver.h b/radio/src/targets/pl18/lcd_driver.h new file mode 100644 index 00000000000..d1c4c756c64 --- /dev/null +++ b/radio/src/targets/pl18/lcd_driver.h @@ -0,0 +1,92 @@ +/* + * 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 __LCD_DRIVER_H__ +#define __LCD_DRIVER_H__ + +#define HBP ( 24 ) // TODO use names from FlySky +#define VBP ( 10 ) + +#define HSW ( 4 ) +#define VSH ( 2 ) + +#define HFP ( 140 - HBP ) +#define VFP ( 22 - VBP ) + +#define LCD_ST7796S_ID ( 0x7796 ) +#define LCD_ILI9481_ID ( 0x9481 ) +#define LCD_ILI9486_ID ( 0x9486 ) +#define LCD_ILI9488_ID ( 0x9488 ) +#define LCD_HX8357D_ID ( 0x99 ) + +#define LCD_DELAY() LCD_Delay() + +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_NRST_GPIO, LCD_NRST_GPIO_PIN) +#define LCD_NRST_LOW() LL_GPIO_ResetOutputPin(LCD_NRST_GPIO, LCD_NRST_GPIO_PIN) + +#define LCD_CS_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN) +#define LCD_CS_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN) + +#define LCD_SCK_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN) +#define LCD_SCK_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN) + +#define LCD_MOSI_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN) +#define LCD_MOSI_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN) + +#define LCD_MOSI_AS_INPUT() SET_IO_INPUT( LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN ) +#define LCD_MOSI_AS_OUTPUT() SET_IO_OUTPUT( LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN ) + +#define LCD_READ_DATA_PIN() LL_GPIO_IsInputPinSet(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN) + + +#if 1 +#define HORIZONTAL_SYNC_WIDTH ( 4 ) +#define HORIZONTAL_BACK_PORCH ( 24 ) +#define HORIZONTAL_FRONT_PORCH ( 140 - HORIZONTAL_BACK_PORCH ) +#define VERTICAL_SYNC_HEIGHT ( 2 ) +#define VERTICAL_BACK_PORCH ( 10 ) +#define VERTICAL_FRONT_PORCH ( 22 - VERTICAL_BACK_PORCH ) +#else +#define HORIZONTAL_SYNC_WIDTH ( 4 ) +#define HORIZONTAL_BACK_PORCH ( 20 ) +#define HORIZONTAL_FRONT_PORCH ( 60 - HORIZONTAL_BACK_PORCH ) +#define VERTICAL_SYNC_HEIGHT ( 2 ) +#define VERTICAL_BACK_PORCH ( 6 ) +#define VERTICAL_FRONT_PORCH ( 14 - VERTICAL_BACK_PORCH ) +#endif + + + +#endif + + + + diff --git a/radio/src/targets/pl18/led_driver.cpp b/radio/src/targets/pl18/led_driver.cpp new file mode 100644 index 00000000000..8b909202fd0 --- /dev/null +++ b/radio/src/targets/pl18/led_driver.cpp @@ -0,0 +1,60 @@ +/* + * 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 "board.h" +#include "boards/generic_stm32/rgb_leds.h" + +void ledInit() +{ + // Do nothing +} + +void ledOff() +{ + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 0, 0); + } + rgbLedColorApply(); +} + +void ledRed() +{ + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 50, 0, 0); + } + rgbLedColorApply(); +} + +void ledGreen() +{ + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 50, 0); + } + rgbLedColorApply(); +} + +void ledBlue() +{ + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 0, 50); + } + rgbLedColorApply(); +} diff --git a/radio/src/targets/pl18/libopenui_config.h b/radio/src/targets/pl18/libopenui_config.h new file mode 100644 index 00000000000..5a5abf70a3f --- /dev/null +++ b/radio/src/targets/pl18/libopenui_config.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. + */ + +#pragma once + +constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 132; +constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH; +constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 8) + 8; + +// Disable rotary encoder, as the PL18 does not have one +#define ROTARY_ENCODER_SPEED() 0 diff --git a/radio/src/targets/pl18/sdram_driver.c b/radio/src/targets/pl18/sdram_driver.c new file mode 100644 index 00000000000..2cc88ae84a5 --- /dev/null +++ b/radio/src/targets/pl18/sdram_driver.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "board.h" +#include "stm32f4xx_fmc.h" + +#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000) +#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001) +#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002) +#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004) +#define SDRAM_MODEREG_BURST_FULL_PAGE ((uint16_t)0x0007) +#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000) +#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008) +#define SDRAM_MODEREG_CAS_LATENCY_1 ((uint16_t)0x0010) +#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020) +#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030) +#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000) +#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000) +#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200) + +void SDRAM_GPIOConfig(void) +{ + /* + ------------------------------------------------------------------------------------------------------------------------------------------------ + PC3 <-> FMC_SDCKE0 | PD0 <-> FMC_D2 | PE0 <-> FMC_NBL0 | PF0 <-> FMC_A0 | PG0 <-> FMC_A10 | PH3 <-> FMC_SDNE0 | PI0 <-> FMC_D24 + | PD1 <-> FMC_D3 | PE1 <-> FMC_NBL1 | PF1 <-> FMC_A1 | PG1 <-> FMC_A11 | PH5 <-> FMC_SDNWE | PI1 <-> FMC_D25 + | PD8 <-> FMC_D13 | PE7 <-> FMC_D4 | PF2 <-> FMC_A2 | PG4 <-> FMC_BA0 | PH8 <-> FMC_D16 | PI2 <-> FMC_D26 + | PD9 <-> FMC_D14 | PE8 <-> FMC_D5 | PF3 <-> FMC_A3 | PG5 <-> FMC_BA1 | PH9 <-> FMC_D17 | PI3 <-> FMC_D27 + | PD10 <-> FMC_D15 | PE9 <-> FMC_D6 | PF4 <-> FMC_A4 | PG8 <-> FMC_SDCLK | PH10 <-> FMC_D18 | PI4 <-> FMC_NBL2 + | PD14 <-> FMC_D0 | PE10 <-> FMC_D7 | PF5 <-> FMC_A5 | PG15 <-> FMC_NCAS | PH11 <-> FMC_D19 | PI5 <-> FMC_NBL3 + | PD15 <-> FMC_D1 | PE11 <-> FMC_D8 | PF11 <-> FMC_NRAS | | PH12 <-> FMC_D20 | PI6 <-> FMC_D28 + | | PE12 <-> FMC_D9 | PF12 <-> FMC_A6 | | PH13 <-> FMC_D21 | PI7 <-> FMC_D29 + | | PE13 <-> FMC_D10 | PF13 <-> FMC_A7 | | PH14 <-> FMC_D22 | PI9 <-> FMC_D30 + | | PE14 <-> FMC_D11 | PF14 <-> FMC_A8 | | PH15 <-> FMC_D23 | PI10 <-> FMC_D31 + | | PE15 <-> FMC_D12 | PF15 <-> FMC_A9 | | | + */ + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + + /* GPIOC configuration */ + GPIO_PinAFConfig(GPIOC, GPIO_PinSource3 , GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + /* GPIOD configuration */ + GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOD, &GPIO_InitStructure); + + /* GPIOE configuration */ + GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOE, &GPIO_InitStructure); + + /* GPIOF configuration */ + GPIO_PinAFConfig(GPIOF, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource2, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource3, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource4, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource5, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource11, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource12, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource13, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOF, &GPIO_InitStructure); + + /* GPIOG configuration */ + GPIO_PinAFConfig(GPIOG, GPIO_PinSource0 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource1 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource4 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource5 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource8 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource15 , GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_15; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + /* GPIOH configuration */ + GPIO_PinAFConfig(GPIOH, GPIO_PinSource3, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOH, GPIO_PinSource5, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5; + GPIO_Init(GPIOH, &GPIO_InitStructure); +} + +void SDRAM_InitSequence(void) +{ + FMC_SDRAMCommandTypeDef FMC_SDRAMCommandStructure; + uint32_t tmpr = 0; + + /* Step 3 --------------------------------------------------------------------*/ + /* Configure a clock configuration enable command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 4 --------------------------------------------------------------------*/ + /* Insert 100 ms delay */ + delay_ms(100); + + /* Step 5 --------------------------------------------------------------------*/ + /* Configure a PALL (precharge all) command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 6 --------------------------------------------------------------------*/ + /* Configure a Auto-Refresh command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 4; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the first command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the second command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 7 --------------------------------------------------------------------*/ + /* Program the external memory mode register */ + tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 | + SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | + SDRAM_MODEREG_CAS_LATENCY_3 | + SDRAM_MODEREG_OPERATING_MODE_STANDARD | + SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; + + /* Configure a load Mode register command*/ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 8 --------------------------------------------------------------------*/ + /* Set the refresh rate counter */ + /* (15.62 us x Freq) - 20 */ + /* Set the device refresh counter */ + FMC_SetRefreshCount(683);//904 + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } +} + + +void SDRAM_Init(void) +{ + //delay funcion needed + delaysInit(); + // Clocks must be enabled here, because the sdramInit is called before main + RCC_AHB1PeriphClockCmd(SDRAM_RCC_AHB1Periph, ENABLE); + RCC_AHB3PeriphClockCmd(SDRAM_RCC_AHB3Periph, ENABLE); + + /* GPIO configuration for FMC SDRAM bank */ + SDRAM_GPIOConfig(); + + /* FMC Configuration ---------------------------------------------------------*/ + FMC_SDRAMInitTypeDef FMC_SDRAMInitStructure; + FMC_SDRAMTimingInitTypeDef FMC_SDRAMTimingInitStructure; + + /* FMC SDRAM Bank configuration */ + /* Timing configuration for 90 Mhz of SD clock frequency (168Mhz/2) */ + /* TMRD: 2 Clock cycles */ + FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2; + /* TXSR: min=70ns (7x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7; + /* TRAS: min=42ns (4x11.11ns) max=120k (ns) */ + FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4; + /* TRC: min=70 (7x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 7; + /* TWR: min=1+ 7ns (1+1x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2; + /* TRP: 20ns => 2x11.11ns */ + FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2; + /* TRCD: 20ns => 2x11.11ns */ + FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2; + + /* FMC SDRAM control configuration */ + FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank1_SDRAM; + /* Row addressing: [7:0] */ + FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b; + /* Column addressing: [11:0] */ + FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b; + FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = FMC_SDMemory_Width_16b; + FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4; + FMC_SDRAMInitStructure.FMC_CASLatency = FMC_CAS_Latency_3; + FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable; + FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_2; + FMC_SDRAMInitStructure.FMC_ReadBurst = FMC_Read_Burst_Enable; + FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1; + FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure; + + /* FMC SDRAM bank initialization */ + FMC_SDRAMInit(&FMC_SDRAMInitStructure); + + /* FMC SDRAM device initialization sequence */ + SDRAM_InitSequence(); +} diff --git a/radio/src/targets/pl18/touch_driver.cpp b/radio/src/targets/pl18/touch_driver.cpp new file mode 100644 index 00000000000..c672aa925bb --- /dev/null +++ b/radio/src/targets/pl18/touch_driver.cpp @@ -0,0 +1,470 @@ +/* + * 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 "stm32_i2c_driver.h" +#include "stm32_gpio_driver.h" +#include "stm32_exti_driver.h" + +#include "hal.h" +#include "timers_driver.h" +#include "delays_driver.h" +#include "touch_driver.h" + +#include "debug.h" + +#define TAP_TIME 25 +#define I2C_TIMEOUT_MAX 5 // 5 ms + +// FT6236 definitions +#define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) +#define TOUCH_FT6236_REG_TH_GROUP 0x80 +#define TOUCH_FT6236_REG_PERIODACTIVE 0x88 +#define TOUCH_FT6236_REG_LIB_VER_H 0xa1 +#define TOUCH_FT6236_REG_LIB_VER_L 0xa2 +#define TOUCH_FT6236_REG_CIPHER 0xa3 +#define TOUCH_FT6236_REG_FIRMID 0xa6 +#define TOUCH_FT6236_REG_FOCALTECH_ID 0xa8 +#define TOUCH_FT6236_REG_RELEASE_CODE_ID 0xaf +#define TOUCH_FT6206_REG_TD_STAT 0x02 +#define TOUCH_FT6206_REG_P1_XH 0x03 +#define TOUCH_FT6206_EVT_SHIFT 6 +#define TOUCH_FT6206_EVT_MASK (3 << TOUCH_FT6206_EVT_SHIFT) +#define TOUCH_FT6206_EVT_CONTACT 0x02 +#define TOUCH_FT6206_MASK_TD_STAT 0x0f + +// CST340 definitions +#define TOUCH_CST340_I2C_ADDRESS 0x1a +#define TOUCH_CST340_REG_FINGER1 0x00 +#define TOUCH_CST340_EVT_CONTACT 0x06 + +// CHSC5448 definitions +#define TOUCH_CHSC5448_I2C_ADDRESS 0x2e +#define TOUCH_CHSC5448_REG_ADDR 0x2c000020 +#define TOUCH_CHSC5448_EVT_CONTACT 0x08 +#define TOUCH_CHSC5448_MAX_POINTS 5 + +typedef enum {TC_NONE, TC_FT6236, TC_CST340, TC_CHSC5448} TouchController; + +#if defined(DEBUG) +const char TOUCH_CONTROLLER_STR[][10] = {"", "FT6236", "CST340", "CHSC5448"}; +#endif + +TouchController touchController = TC_NONE; + +struct TouchControllerDescriptor +{ + bool (*hasTouchEvent)(); + bool (*touchRead)(uint16_t * X, uint16_t * Y); + void (*printDebugInfo)(); + bool needTranspose; +}; + +union rpt_point_t +{ + struct + { + unsigned char x_l8; + unsigned char y_l8; + unsigned char z; + unsigned char x_h4:4; + unsigned char y_h4:4; + unsigned char id:4; + unsigned char event:4; + }rp; + unsigned char data[5]; +}; + +extern uint8_t TouchControllerType; + +static const TouchControllerDescriptor *tcd = nullptr; +static TouchState internalTouchState = {}; +volatile static bool touchEventOccured; +static tmr10ms_t downTime = 0; +static tmr10ms_t tapTime = 0; +static short tapCount = 0; + +static void _touch_exti_isr(void) +{ + touchEventOccured = true; +} + +static void _touch_exti_stop(void) +{ + stm32_exti_disable(TOUCH_INT_EXTI_Line); +} + +static void _touch_exti_config(void) +{ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + LL_SYSCFG_SetEXTISource(TOUCH_INT_EXTI_Port, TOUCH_INT_EXTI_SysCfgLine); + + stm32_exti_enable(TOUCH_INT_EXTI_Line, LL_EXTI_TRIGGER_FALLING, _touch_exti_isr); +} + +static void _touch_gpio_config(void) +{ + LL_GPIO_InitTypeDef gpioInit; + LL_GPIO_StructInit(&gpioInit); + + stm32_gpio_enable_clock(TOUCH_RST_GPIO); + stm32_gpio_enable_clock(TOUCH_INT_GPIO); + + gpioInit.Mode = LL_GPIO_MODE_OUTPUT; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + gpioInit.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + gpioInit.Pull = LL_GPIO_PULL_NO; + + gpioInit.Pin = TOUCH_RST_GPIO_PIN; + LL_GPIO_Init(TOUCH_RST_GPIO, &gpioInit); + LL_GPIO_SetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + + gpioInit.Pin = TOUCH_INT_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_INPUT; + gpioInit.Pull = LL_GPIO_PULL_UP; + gpioInit.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; + LL_GPIO_Init(TOUCH_INT_GPIO, &gpioInit); + LL_GPIO_SetOutputPin(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN); +} + +static void _touch_reset() +{ + LL_GPIO_ResetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + delay_ms(10); + LL_GPIO_SetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + delay_ms(300); +} + +static void _i2c_init(void) +{ + TRACE("Touch I2C Init"); + + if (stm32_i2c_init(TOUCH_I2C_BUS, TOUCH_I2C_CLK_RATE) < 0) { + TRACE("Touch I2C Init ERROR: stm32_i2c_init failed"); + return; + } +} + +static void _i2c_reInit(void) +{ + stm32_i2c_deinit(TOUCH_I2C_BUS); + _i2c_init(); +} + +static int _i2c_read(uint8_t addr, uint32_t reg, uint8_t regSize, uint8_t* data, uint16_t len, uint32_t timeout) +{ + if (regSize > 2) { + if(stm32_i2c_master_tx(TOUCH_I2C_BUS, addr, (uint8_t*) ®, regSize, 3) < 0) + return false; + delay_us(5); + if(stm32_i2c_master_rx(TOUCH_I2C_BUS, addr, data, len, I2C_TIMEOUT_MAX) < 0) + return false; + return true; + } else { + return stm32_i2c_read(TOUCH_I2C_BUS, addr, reg, regSize, data, len, timeout); + } +} + +static uint8_t _i2c_readRetry(uint8_t addr, uint32_t reg, uint8_t regSize) +{ + uint8_t result; + uint8_t tryCount = 3; + while (_i2c_read(addr, reg, regSize, &result, 1, I2C_TIMEOUT_MAX) < 0) { + if (--tryCount == 0) break; + _i2c_reInit(); + } + return result; +} + +static uint16_t _i2c_readMultipleRetry(uint8_t addr, uint32_t reg, uint8_t regSize, uint8_t * buffer, uint16_t length) +{ + uint8_t tryCount = 3; + while (_i2c_read(addr, reg, regSize, buffer, length, I2C_TIMEOUT_MAX) < 0) { + if (--tryCount == 0) break; + _i2c_reInit(); + } + return length; +} + +static bool ft6236TouchRead(uint16_t * X, uint16_t * Y) +{ + // Read register FT6206_TD_STAT_REG to check number of touches detection + uint8_t nbTouch = _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6206_REG_TD_STAT, 1); + nbTouch &= TOUCH_FT6206_MASK_TD_STAT; + bool hasTouch = nbTouch > 0; + + if (hasTouch) { + uint8_t dataxy[4]; + // Read X and Y positions and event + _i2c_readMultipleRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6206_REG_P1_XH, 1, dataxy, sizeof(dataxy)); + + // Send back ready X position to caller + *X = ((dataxy[0] & 0x0f) << 8) | dataxy[1]; + // Send back ready Y position to caller + *Y = ((dataxy[2] & 0x0f) << 8) | dataxy[3]; + + uint8_t event = (dataxy[0] & TOUCH_FT6206_EVT_MASK) >> TOUCH_FT6206_EVT_SHIFT; + return event == TOUCH_FT6206_EVT_CONTACT; + } + return false; +} + +static bool ft6236HasTouchEvent() +{ + return touchEventOccured; +} + +static void ft6236PrintDebugInfo() +{ +#if defined(DEBUG) + TRACE("ft6x36: thrhld = %d", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_TH_GROUP, 1) * 4); + TRACE("ft6x36: rep rate=", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_PERIODACTIVE, 1) * 10); + TRACE("ft6x36: fw lib 0x%02X %02X", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_LIB_VER_H, 1), + _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_LIB_VER_L, 1)); + TRACE("ft6x36: fw v 0x%02X", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_FIRMID, 1)); + TRACE("ft6x36: CHIP ID 0x%02X", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_CIPHER, 1)); + TRACE("ft6x36: CTPM ID 0x%02X", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_FOCALTECH_ID, 1)); + TRACE("ft6x36: rel code 0x%02X", _i2c_readRetry(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_RELEASE_CODE_ID, 1)); +#endif + +} + +static bool cst340TouchRead(uint16_t * X, uint16_t * Y) +{ + uint8_t data[4]; + + // Read X and Y positions + _i2c_readMultipleRetry(TOUCH_CST340_I2C_ADDRESS, TOUCH_CST340_REG_FINGER1, 1, data, sizeof(data)); + + // Send back X position to caller + if (X) *X = ((data[1]<<4) + ((data[3]>>4)&0x0f)); + // Send back Y position to caller + if (Y) *Y = ((data[2]<<4) + ((data[3])&0x0f)); + + return data[0] == TOUCH_CST340_EVT_CONTACT; +} + +static bool cst340HasTouchEvent() +{ + static bool lastHasTouch = false; + bool ret = touchEventOccured; + if (ret) { + uint8_t hasTouch = cst340TouchRead(nullptr, nullptr); + if (!hasTouch && !lastHasTouch) { + TRACE("Interrupt occurs without touch event!!"); + touchEventOccured = false; + ret = false; + } + lastHasTouch = hasTouch; + } + + return ret; +} + +static void cst340PrintDebugInfo() +{ + // TODO, when necessary +} + +static bool chsc5448TouchRead(uint16_t * X, uint16_t * Y) +{ + static uint8_t readbuffer[84]; + const uint8_t reportSize = ((TOUCH_CHSC5448_MAX_POINTS * 5 + 2) + 3) & 0xfc; + + int ptCnt = 0; + union rpt_point_t* ppt; + + _i2c_readMultipleRetry(TOUCH_CHSC5448_I2C_ADDRESS, TOUCH_CHSC5448_REG_ADDR, 4, readbuffer, reportSize); + ptCnt = readbuffer[1] & 0x0f; + ppt = (union rpt_point_t*)&readbuffer[2]; + *X = ((ppt->rp.x_h4 & 0x0f) << 8) | ppt->rp.x_l8; + *Y = ((ppt->rp.y_h4 & 0x0f) << 8) | ppt->rp.y_l8; + uint8_t event = ppt->rp.event; + + return ptCnt > 0 && event == TOUCH_CHSC5448_EVT_CONTACT; +} + +static bool chsc5448HasTouchEvent() +{ + return touchEventOccured; +} + +static void chsc5448PrintDebugInfo() +{ + // TODO, when necessary +} + +static const TouchControllerDescriptor FT6236 = +{ + .hasTouchEvent = ft6236HasTouchEvent, + .touchRead = ft6236TouchRead, + .printDebugInfo = ft6236PrintDebugInfo, + .needTranspose = true, +}; + +static const TouchControllerDescriptor CST340 = +{ + .hasTouchEvent = cst340HasTouchEvent, + .touchRead = cst340TouchRead, + .printDebugInfo = cst340PrintDebugInfo, + .needTranspose = true, +}; + +static const TouchControllerDescriptor CHSC5448 = +{ + .hasTouchEvent = chsc5448HasTouchEvent, + .touchRead = chsc5448TouchRead, + .printDebugInfo = chsc5448PrintDebugInfo, + .needTranspose = false, +}; + +void _detect_touch_controller() +{ + if (stm32_i2c_is_dev_ready(TOUCH_I2C_BUS, TOUCH_CST340_I2C_ADDRESS, 3, I2C_TIMEOUT_MAX) == 0) { + touchController = TC_CST340; + tcd = &CST340; + TouchControllerType = 0; + } else if (stm32_i2c_is_dev_ready(TOUCH_I2C_BUS, TOUCH_CHSC5448_I2C_ADDRESS, 3, I2C_TIMEOUT_MAX) == 0) { + touchController = TC_CHSC5448; + tcd = &CHSC5448; + TouchControllerType = 0; + } else { + touchController = TC_FT6236; + tcd = &FT6236; + TouchControllerType = 1; + } +} + +void touchPanelDeInit() +{ + _touch_exti_stop(); +} + +bool touchPanelInit() +{ + _touch_gpio_config(); + _i2c_init(); + _touch_reset(); + _detect_touch_controller(); + _touch_exti_config(); + + tcd->printDebugInfo(); + + return touchController != TC_NONE; +} + +bool touchPanelEventOccured() +{ + return tcd->hasTouchEvent(); +} + +struct TouchState touchPanelRead() +{ + if (!touchEventOccured) return internalTouchState; + + touchEventOccured = false; + + tmr10ms_t now = get_tmr10ms(); + internalTouchState.tapCount = 0; + unsigned short touchX; + unsigned short touchY; + bool hasTouchContact = tcd->touchRead(&touchX, &touchY); + + if (tcd->needTranspose) { + // Touch sensor is rotated by 90 deg + unsigned short tmp = touchY; + touchY = 319 - touchX; + touchX = tmp; + } + + if (hasTouchContact) { + int dx = touchX - internalTouchState.x; + int dy = touchY - internalTouchState.y; + internalTouchState.x = touchX; + internalTouchState.y = touchY; + + if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { + internalTouchState.startX = internalTouchState.x; + internalTouchState.startY = internalTouchState.y; + internalTouchState.event = TE_DOWN; + } + else if (internalTouchState.event == TE_DOWN) { + if (dx >= SLIDE_RANGE || dx <= -SLIDE_RANGE || dy >= SLIDE_RANGE || dy <= -SLIDE_RANGE) { + internalTouchState.event = TE_SLIDE; + internalTouchState.deltaX = (short) dx; + internalTouchState.deltaY = (short) dy; + } + else { + internalTouchState.event = TE_DOWN; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + } + } + else if (internalTouchState.event == TE_SLIDE) { + internalTouchState.event = TE_SLIDE; //no change + internalTouchState.deltaX = (short) dx; + internalTouchState.deltaY = (short) dy; + } + + if (internalTouchState.event == TE_DOWN && downTime == 0) { + downTime = now; + } + } + else { + if (internalTouchState.event == TE_DOWN) { + internalTouchState.event = TE_UP; + if (now - downTime <= TAP_TIME) { + if (now - tapTime > TAP_TIME) { + tapCount = 1; + } else { + tapCount++; + } + internalTouchState.tapCount = tapCount; + tapTime = now; + } else { + internalTouchState.tapCount = 0; // not a tap + } + downTime = 0; + } else { + tapCount = 0; + internalTouchState.tapCount = 0; + internalTouchState.event = TE_SLIDE_END; + } + } + + TouchState ret = internalTouchState; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + if (internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) + internalTouchState.event = TE_NONE; + +#if defined(DEBUG) + TRACE("%s: event=%d,X=%d,Y=%d", TOUCH_CONTROLLER_STR[touchController], ret.event, ret.x, ret.y); +#endif + + return ret; +} + +struct TouchState getInternalTouchState() +{ + return internalTouchState; +} diff --git a/radio/src/targets/pl18/touch_driver.h b/radio/src/targets/pl18/touch_driver.h new file mode 100644 index 00000000000..6b0fb01a8f6 --- /dev/null +++ b/radio/src/targets/pl18/touch_driver.h @@ -0,0 +1,31 @@ +/* + * 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 "touch.h" + +extern void touchPanelDeInit(); +extern bool touchPanelInit(); + +struct TouchState touchPanelRead(); +bool touchPanelEventOccured(); +struct TouchState getInternalTouchState(); diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp new file mode 100644 index 00000000000..c8e633ff3cf --- /dev/null +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -0,0 +1,560 @@ +/* + * 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 "stm32_i2c_driver.h" +#include "stm32_gpio_driver.h" +#include "stm32_exti_driver.h" + +#include "hal.h" +#include "timers_driver.h" +#include "delays_driver.h" +#include "tp_cst340.h" + +#include "debug.h" + +#define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) +#define TOUCH_CST340_I2C_ADDRESS 0x1A + + +extern uint8_t TouchControllerType; + +volatile static bool touchEventOccured; +enum TouchControllers {TC_NONE, TC_FT6236, TC_CST340}; +TouchControllers touchController = TC_NONE; + +static tc_handle_TypeDef tc_handle = {0, 0}; + +tmr10ms_t downTime = 0; +tmr10ms_t tapTime = 0; +short tapCount = 0; +#define TAP_TIME 25 + +struct TouchControllerDescriptor +{ + void (*read)(uint16_t * X, uint16_t * Y, uint32_t * event); + uint8_t (*detectTouch)(); + void (*printDebugInfo)(); + uint32_t contactEvent; +}; + +static const TouchControllerDescriptor *tc = nullptr; + +static TouchState internalTouchState = {}; + +static void _cst340_exti_isr(void) +{ + touchEventOccured = true; +} + +static void TOUCH_AF_ExtiStop(void) +{ + stm32_exti_disable(TOUCH_INT_EXTI_Line); +} + +static void TOUCH_AF_ExtiConfig(void) +{ + __HAL_RCC_SYSCFG_CLK_ENABLE(); + LL_SYSCFG_SetEXTISource(TOUCH_INT_EXTI_Port, TOUCH_INT_EXTI_SysCfgLine); + + stm32_exti_enable(TOUCH_INT_EXTI_Line, + LL_EXTI_TRIGGER_FALLING, + _cst340_exti_isr); +} + +static void TOUCH_AF_GPIOConfig(void) +{ + LL_GPIO_InitTypeDef gpioInit; + LL_GPIO_StructInit(&gpioInit); + + stm32_gpio_enable_clock(TOUCH_RST_GPIO); + stm32_gpio_enable_clock(TOUCH_INT_GPIO); + + gpioInit.Mode = LL_GPIO_MODE_OUTPUT; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + gpioInit.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + gpioInit.Pull = LL_GPIO_PULL_NO; + + gpioInit.Pin = TOUCH_RST_GPIO_PIN; + LL_GPIO_Init(TOUCH_RST_GPIO, &gpioInit); + LL_GPIO_SetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + + gpioInit.Pin = TOUCH_INT_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_INPUT; + gpioInit.Pull = LL_GPIO_PULL_UP; + gpioInit.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; + LL_GPIO_Init(TOUCH_INT_GPIO, &gpioInit); + LL_GPIO_SetOutputPin(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN); +} + +void I2C_Init_Radio(void) +{ + TRACE("CST340 I2C Init"); + + if (stm32_i2c_init(TOUCH_I2C_BUS, TOUCH_I2C_CLK_RATE) < 0) { + TRACE("CST340 ERROR: stm32_i2c_init failed"); + return; + } +} + +// void Touch_DeInit() +// { +// I2C_DeInit(I2C_B1); +// __HAL_RCC_I2C1_FORCE_RESET(); +// delay_ms(150); +// __HAL_RCC_I2C1_RELEASE_RESET(); +// } + +#define I2C_TIMEOUT_MAX 5 // 5 ms + +bool touch_i2c_read(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) +{ +// if(touchController == TC_CST836U) +// { +// if(stm32_i2c_master_tx(TOUCH_I2C_BUS, addr, ®, 1, 3) < 0) +// return false; +// delay_us(5); +// if(stm32_i2c_master_rx(TOUCH_I2C_BUS, addr, data, len, I2C_TIMEOUT_MAX) < 0) +// return false; +// } else { + if (stm32_i2c_read(TOUCH_I2C_BUS, addr, reg, 1, data, len, I2C_TIMEOUT_MAX) < 0) + return false; +// } + + return true; +} + +#if 0 +static bool touch_i2c_write(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) +{ + if (stm32_i2c_write(TOUCH_I2C_BUS, addr, reg, 1, data, len, I2C_TIMEOUT_MAX) < 0) + return false; + + return true; +} + +static void TS_IO_Write(uint8_t addr, uint8_t reg, uint8_t data) +{ + uint8_t tryCount = 3; + while (!touch_i2c_write(addr, reg, &data, 1)) { + if (--tryCount == 0) break; + I2C_Init(); + } +} +#endif + +static uint8_t TS_IO_Read(uint8_t addr, uint8_t reg) +{ + uint8_t result; + uint8_t tryCount = 3; + while (!touch_i2c_read(addr, reg, &result, 1)) { + if (--tryCount == 0) break; + I2C_Init_Radio(); +// I2C_Init(); + } + return result; +} + +static uint16_t TS_IO_ReadMultiple(uint8_t addr, uint8_t reg, uint8_t * buffer, uint16_t length) +{ + uint8_t tryCount = 3; + while (!touch_i2c_read(addr, reg, buffer, length)) { + if (--tryCount == 0) break; + I2C_Init_Radio(); +// I2C_Init(); + } + return 1; +} + +static void touch_ft6236_debug_info(void) +{ +#if defined(DEBUG) + TRACE("ft6x36: thrhld = %d", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_TH_GROUP) * 4); + TRACE("ft6x36: rep rate=", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_PERIODACTIVE) * 10); + TRACE("ft6x36: fw lib 0x%02X %02X", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_LIB_VER_H), TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_LIB_VER_L)); + TRACE("ft6x36: fw v 0x%02X", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_FIRMID)); + TRACE("ft6x36: CHIP ID 0x%02X", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_CIPHER)); + TRACE("ft6x36: CTPM ID 0x%02X", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_FOCALTECH_ID)); + TRACE("ft6x36: rel code 0x%02X", TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, TOUCH_FT6236_REG_RELEASE_CODE_ID)); +#endif +} + +/** + * @brief Return if there is touches detected or not. + * Try to detect new touches and forget the old ones (reset internal global + * variables). + * @param DeviceAddr: Device address on communication Bus. + * @retval : Number of active touches detected (can be 0, 1 or 2). + */ +static uint8_t ft6x06_TS_DetectTouch() +{ + volatile uint8_t nbTouch = 0; + + /* Read register FT6206_TD_STAT_REG to check number of touches detection */ + nbTouch = TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, FT6206_TD_STAT_REG); + nbTouch &= FT6206_TD_STAT_MASK; + if (nbTouch > FT6206_MAX_DETECTABLE_TOUCH) { + /* If invalid number of touch detected, set it to zero */ + nbTouch = 0; + } + /* Update ft6x06 driver internal global : current number of active touches */ + tc_handle.currActiveTouchNb = nbTouch; + + /* Reset current active touch index on which to work on */ + tc_handle.currActiveTouchIdx = 0; + return (nbTouch); +} + +#if 0 +/** + * @brief Get the touch detailed informations on touch number 'touchIdx' (0..1) + * This touch detailed information contains : + * - weight that was applied to this touch + * - sub-area of the touch in the touch panel + * - event of linked to the touch (press down, lift up, ...) + * @param DeviceAddr: Device address on communication Bus (I2C slave address of FT6x06). + * @param touchIdx : Passed index of the touch (0..1) on which we want to get the + * detailed information. + * @param pWeight : Pointer to to get the weight information of 'touchIdx'. + * @param pArea : Pointer to to get the sub-area information of 'touchIdx'. + * @param pEvent : Pointer to to get the event information of 'touchIdx'. + + * @retval None. + */ +static void ft6x06_TS_GetTouchInfo(uint16_t DeviceAddr, + uint32_t touchIdx, + uint32_t * pWeight, + uint32_t * pArea, + uint32_t * pEvent) +{ + uint8_t regAddress = 0; + uint8_t dataxy[3]; + + if (touchIdx < ft6x06_handle.currActiveTouchNb) { + switch (touchIdx) { + case 0 : + regAddress = FT6206_P1_WEIGHT_REG; + break; + + case 1 : + regAddress = FT6206_P2_WEIGHT_REG; + break; + + default : + break; + + } /* end switch(touchIdx) */ + + /* Read weight, area and Event Id of touch index */ + TS_IO_ReadMultiple(DeviceAddr, regAddress, dataxy, sizeof(dataxy)); + + /* Return weight of touch index */ + *pWeight = (dataxy[0] & FT6206_TOUCH_WEIGHT_MASK) >> FT6206_TOUCH_WEIGHT_SHIFT; + /* Return area of touch index */ + *pArea = (dataxy[1] & FT6206_TOUCH_AREA_MASK) >> FT6206_TOUCH_AREA_SHIFT; + /* Return Event Id of touch index */ + *pEvent = (dataxy[2] & FT6206_TOUCH_EVT_FLAG_MASK) >> FT6206_TOUCH_EVT_FLAG_SHIFT; + + } /* of if(touchIdx < ft6x06_handle.currActiveTouchNb) */ +} +#endif + +/** + * @brief Get the touch screen X and Y positions values + * Manage multi touch thanks to touch Index global + * variable 'tc_handle.currActiveTouchIdx'. + * @param DeviceAddr: Device address on communication Bus. + * @param X: Pointer to X position value + * @param Y: Pointer to Y position value + * @retval None. + */ +static void ft6x06_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) +{ + uint8_t regAddress = 0; + uint8_t dataxy[4]; + + if (tc_handle.currActiveTouchIdx < tc_handle.currActiveTouchNb) { + switch (tc_handle.currActiveTouchIdx) { + case 0 : + regAddress = FT6206_P1_XH_REG; + break; + case 1 : + regAddress = FT6206_P2_XH_REG; + break; + + default : + break; + } + + /* Read X and Y positions */ + TS_IO_ReadMultiple(TOUCH_FT6236_I2C_ADDRESS, regAddress, dataxy, sizeof(dataxy)); + /* Send back ready X position to caller */ + *X = ((dataxy[0] & FT6206_MSB_MASK) << 8) | (dataxy[1] & FT6206_LSB_MASK); + /* Send back ready Y position to caller */ + *Y = ((dataxy[2] & FT6206_MSB_MASK) << 8) | (dataxy[3] & FT6206_LSB_MASK); + + *event = (dataxy[0] & FT6206_TOUCH_EVT_FLAG_MASK) >> FT6206_TOUCH_EVT_FLAG_SHIFT; + /* + uint32_t weight; + uint32_t area; + ft6x06_TS_GetTouchInfo(DeviceAddr, ft6x06_handle.currActiveTouchIdx, &weight, &area, event); + */ + tc_handle.currActiveTouchIdx++; + } +} + +static void touch_cst340_debug_info(void) +{ +#if 0 // Disabled because cannot compile, will fix when necessary +#if defined(DEBUG) + uint8_t tmp[4]; + if (!TS_IO_Write(CST340_MODE_DEBUG_INFO, tmp, 0)) + TRACE("CST340 chip NOT FOUND"); + + // Check the value, expected ChipID + uint32_t chipId = tmp[0] << 8) + tmp[1]; + + if (!I2C_CST340_ReadRegister(CST340_FWVER_REG, tmp, 4)) + TRACE("Error reading CST340 firmware version!"); + uint32_t fwVersion = tmp[0] << 24 | tmp[1]<<16 | tmp[2]<<8 | tmp[0]; + + // Enter normal mode + if (!I2C_CST340_WriteRegister(CST340_MODE_NORMAL, tmp, 0)) + TRACE("ERROR chaning CST340 mode back to normal!"); + + TRACE("cst340: fw ver 0x%08X", fwVersion); + TRACE("cst836u: chip id 0x%04X", chipId); +#endif +#endif +} + +/** + * @brief Get the touch screen X and Y positions values + * @param DeviceAddr: Device address on communication Bus. + * @param X: Pointer to X position value + * @param Y: Pointer to Y position value + * @retval None. + */ +static void cst340_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) +{ + uint8_t dataxy[4]; + + /* Read X and Y positions */ + TS_IO_ReadMultiple(TOUCH_CST340_I2C_ADDRESS, CST340_FINGER1_REG, dataxy, sizeof(dataxy)); + /* Send back ready X position to caller */ + *X = ((dataxy[1]<<4) + ((dataxy[3]>>4)&0x0f)); + *Y = ((dataxy[2]<<4) + ((dataxy[3])&0x0f)); + /* Send back ready Y position to caller */ + + *event = dataxy[0]; +} + +/** + * @brief Return if there is touches detected or not. + * Try to detect new touches and forget the old ones (reset internal global + * variables). + * @param DeviceAddr: Device address on communication Bus. + * @retval : Number of active touches detected + */ +static uint8_t cst340_TS_DetectTouch() +{ + uint8_t nbTouch; + uint8_t reg = TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST340_FINGER1_REG); + if( reg == 0x06 ) + nbTouch = 1; + else + nbTouch = 0; + + tc_handle.currActiveTouchNb = nbTouch; + + tc_handle.currActiveTouchIdx = 0; + return (nbTouch); +} + +void TouchReset() +{ + LL_GPIO_ResetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + delay_ms(10); + LL_GPIO_SetOutputPin(TOUCH_RST_GPIO, TOUCH_RST_GPIO_PIN); + delay_ms(300); +} + +static const TouchControllerDescriptor FT6236 = +{ + .read = ft6x06_TS_GetXY, + .detectTouch = ft6x06_TS_DetectTouch, + .printDebugInfo = touch_ft6236_debug_info, + .contactEvent = FT6206_TOUCH_EVT_FLAG_CONTACT +}; +static const TouchControllerDescriptor CST340 = +{ + .read = cst340_TS_GetXY, + .detectTouch = cst340_TS_DetectTouch, + .printDebugInfo = touch_cst340_debug_info, + .contactEvent = CST340_TOUCH_EVT_FLAG_CONTACT +}; + +void detectTouchController() +{ + if( stm32_i2c_is_dev_ready(TOUCH_I2C_BUS, TOUCH_CST340_I2C_ADDRESS, 3, 5) == 0) + { + TouchControllerType = 0; + touchController = TC_CST340; + tc = &CST340; + } else { + TouchControllerType = 1; + touchController = TC_FT6236; + tc = &FT6236; + } +} + +void TouchInit() +{ + TOUCH_AF_GPIOConfig(); // SET RST=OUT INT=IN INT=HIGH + I2C_Init_Radio(); + TouchReset(); + detectTouchController(); + TOUCH_AF_ExtiConfig(); + + tc->printDebugInfo(); +} + +void handleTouch() +{ + unsigned short touchX; + unsigned short touchY; + uint32_t tEvent = 0; + tc->read(&touchX, &touchY, &tEvent); + +#if defined(DEBUG) + TRACE("handleTouch: touchX=%d, touchY=%d, tEvent=%d", touchX, touchY, tEvent); +#endif + // touch sensor is rotated by 90 deg + unsigned short tmp = touchY; + touchY = 319 - touchX; + touchX = tmp; + + if (tEvent == tc->contactEvent) { + int dx = touchX - internalTouchState.x; + int dy = touchY - internalTouchState.y; + + internalTouchState.x = touchX; + internalTouchState.y = touchY; + + if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { + internalTouchState.startX = internalTouchState.x; + internalTouchState.startY = internalTouchState.y; + internalTouchState.event = TE_DOWN; + } + else if (internalTouchState.event == TE_DOWN) { + if (dx >= SLIDE_RANGE || dx <= -SLIDE_RANGE || dy >= SLIDE_RANGE || dy <= -SLIDE_RANGE) { + internalTouchState.event = TE_SLIDE; + internalTouchState.deltaX = (short) dx; + internalTouchState.deltaY = (short) dy; + } + else { + internalTouchState.event = TE_DOWN; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + } + } + else if (internalTouchState.event == TE_SLIDE) { + internalTouchState.event = TE_SLIDE; //no change + internalTouchState.deltaX = (short) dx; + internalTouchState.deltaY = (short) dy; + } + + } +} + +static bool lastHasTouch = false; +bool touchPanelEventOccured() +{ + bool result = touchEventOccured; + uint8_t hasTouch = false; + if (touchEventOccured) { + hasTouch = tc->detectTouch(); + if (!hasTouch && !lastHasTouch) { + touchEventOccured = false; + result = false; + } + lastHasTouch = hasTouch; + } + +#if defined(DEBUG) + TRACE("TouchEvent: %d, %d, %d, %d", touchEventOccured, lastHasTouch, hasTouch, result); +#endif + return result; +} + +TouchState touchPanelRead() +{ + if (!touchEventOccured) return internalTouchState; + + touchEventOccured = false; + + tmr10ms_t now = get_tmr10ms(); + internalTouchState.tapCount = 0; + + if (tc->detectTouch()) { + handleTouch(); + if (internalTouchState.event == TE_DOWN && downTime == 0) { + downTime = now; + } + } else { + if (internalTouchState.event == TE_DOWN) { + internalTouchState.event = TE_UP; + if (now - downTime <= TAP_TIME) { + if (now - tapTime > TAP_TIME) { + tapCount = 1; + } else { + tapCount++; + } + internalTouchState.tapCount = tapCount; + tapTime = now; + } else { + internalTouchState.tapCount = 0; // not a tap + } + downTime = 0; + } else { + tapCount = 0; + internalTouchState.tapCount = 0; + internalTouchState.event = TE_SLIDE_END; + } + } + TouchState ret = internalTouchState; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + if(internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) + internalTouchState.event = TE_NONE; + +#if defined(DEBUG) + TRACE("%s: Event = %d", touchController == TC_CST340 ? "CST340" : "FT6236", ret.event); +#endif + + return ret; +} + +TouchState getInternalTouchState() +{ + return internalTouchState; +} diff --git a/radio/src/targets/pl18/tp_cst340.h b/radio/src/targets/pl18/tp_cst340.h new file mode 100644 index 00000000000..ec5f1610749 --- /dev/null +++ b/radio/src/targets/pl18/tp_cst340.h @@ -0,0 +1,289 @@ +/* + * 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 "touch.h" + +typedef struct +{ + /* field holding the current number of simultaneous active touches */ + uint8_t currActiveTouchNb; + + /* field holding the touch index currently managed */ + uint8_t currActiveTouchIdx; + +} tc_handle_TypeDef; + + +#define TOUCH_FT6236_MAX_TOUCH_POINTS 2 + +#define TOUCH_FT6236_REG_TH_GROUP 0x80 +#define TOUCH_FT6236_REG_PERIODACTIVE 0x88 +#define TOUCH_FT6236_REG_LIB_VER_H 0xa1 +#define TOUCH_FT6236_REG_LIB_VER_L 0xa2 +#define TOUCH_FT6236_REG_CIPHER 0xa3 +#define TOUCH_FT6236_REG_FIRMID 0xa6 +#define TOUCH_FT6236_REG_FOCALTECH_ID 0xa8 +#define TOUCH_FT6236_REG_RELEASE_CODE_ID 0xaf + +#define TOUCH_FT6236_EVENT_PRESS_DOWN 0 +#define TOUCH_FT6236_EVENT_LIFT_UP 1 +#define TOUCH_FT6236_EVENT_CONTACT 2 +#define TOUCH_FT6236_EVENT_NO_EVENT 3 + +#define TOUCH_FT6236_GESTURE_MOVE_FLAG 0x10 +#define TOUCH_FT6236_GESTURE_MOVE_UP 0x10 +#define TOUCH_FT6236_GESTURE_MOVE_RIGHT 0x14 +#define TOUCH_FT6236_GESTURE_MOVE_DOWN 0x18 +#define TOUCH_FT6236_GESTURE_MOVE_LEFT 0x1C +#define TOUCH_FT6236_GESTURE_ZOOM_IN 0x48 +#define TOUCH_FT6236_GESTURE_ZOOM_OUT 0x49 +#define TOUCH_FT6236_GESTURE_NONE 0x00 + +#define TOUCH_GESTURE_UP ((TOUCH_FT6236_GESTURE_MOVE_UP & 0x0F)+1) +#define TOUCH_GESTURE_DOWN ((TOUCH_FT6236_GESTURE_MOVE_DOWN & 0x0F)+1) +#define TOUCH_GESTURE_LEFT ((TOUCH_FT6236_GESTURE_MOVE_LEFT & 0x0F)+1) +#define TOUCH_GESTURE_RIGHT ((TOUCH_FT6236_GESTURE_MOVE_RIGHT & 0x0F)+1) +#define TOUCH_GESTURE_MOUSE_DOWN (0x80+TOUCH_FT6236_EVENT_PRESS_DOWN) +#define TOUCH_GESTURE_MOUSE_UP (0x80+TOUCH_FT6236_EVENT_LIFT_UP) +#define TOUCH_GESTURE_MOUSE_MOVE (0x80+TOUCH_FT6236_EVENT_CONTACT) +#define TOUCH_GESTURE_MOUSE_NONE (0x80+TOUCH_FT6236_EVENT_NO_EVENT) + + + + /* Maximum border values of the touchscreen pad */ +#define FT_6206_MAX_WIDTH ((uint16_t)800) /* Touchscreen pad max width */ +#define FT_6206_MAX_HEIGHT ((uint16_t)480) /* Touchscreen pad max height */ + + /* Possible values of driver functions return status */ +#define FT6206_STATUS_OK 0 +#define FT6206_STATUS_NOT_OK 1 + + /* Max detectable simultaneous touches */ +#define FT6206_MAX_DETECTABLE_TOUCH 2 + + /** + * @brief : Definitions for FT6206 I2C register addresses on 8 bit + **/ + + /* Current mode register of the FT6206 (R/W) */ +#define FT6206_DEV_MODE_REG 0x00 + + /* Possible values of FT6206_DEV_MODE_REG */ +#define FT6206_DEV_MODE_WORKING 0x00 +#define FT6206_DEV_MODE_FACTORY 0x04 + +#define FT6206_DEV_MODE_MASK 0x7 +#define FT6206_DEV_MODE_SHIFT 4 + + /* Gesture ID register */ +#define FT6206_GEST_ID_REG 0x01 + + /* Possible values of FT6206_GEST_ID_REG */ +#define FT6206_GEST_ID_NO_GESTURE 0x00 +#define FT6206_GEST_ID_MOVE_UP 0x10 +#define FT6206_GEST_ID_MOVE_RIGHT 0x14 +#define FT6206_GEST_ID_MOVE_DOWN 0x18 +#define FT6206_GEST_ID_MOVE_LEFT 0x1C +#define FT6206_GEST_ID_ZOOM_IN 0x48 +#define FT6206_GEST_ID_ZOOM_OUT 0x49 + + /* Touch Data Status register : gives number of active touch points (0..2) */ +#define FT6206_TD_STAT_REG 0x02 + + /* Values related to FT6206_TD_STAT_REG */ +#define FT6206_TD_STAT_MASK 0x0F +#define FT6206_TD_STAT_SHIFT 0x00 + + /* Values Pn_XH and Pn_YH related */ +#define FT6206_TOUCH_EVT_FLAG_PRESS_DOWN 0x00 +#define FT6206_TOUCH_EVT_FLAG_LIFT_UP 0x01 +#define FT6206_TOUCH_EVT_FLAG_CONTACT 0x02 +#define FT6206_TOUCH_EVT_FLAG_NO_EVENT 0x03 + +#define FT6206_TOUCH_EVT_FLAG_SHIFT 6 +#define FT6206_TOUCH_EVT_FLAG_MASK (3 << FT6206_TOUCH_EVT_FLAG_SHIFT) + +#define FT6206_MSB_MASK 0x0F +#define FT6206_MSB_SHIFT 0 + + /* Values Pn_XL and Pn_YL related */ +#define FT6206_LSB_MASK 0xFF +#define FT6206_LSB_SHIFT 0 + +#define FT6206_P1_XH_REG 0x03 +#define FT6206_P1_XL_REG 0x04 +#define FT6206_P1_YH_REG 0x05 +#define FT6206_P1_YL_REG 0x06 + + /* Touch Pressure register value (R) */ +#define FT6206_P1_WEIGHT_REG 0x07 + + /* Values Pn_WEIGHT related */ +#define FT6206_TOUCH_WEIGHT_MASK 0xFF +#define FT6206_TOUCH_WEIGHT_SHIFT 0 + + /* Touch area register */ +#define FT6206_P1_MISC_REG 0x08 + + /* Values related to FT6206_Pn_MISC_REG */ +#define FT6206_TOUCH_AREA_MASK (0x04 << 4) +#define FT6206_TOUCH_AREA_SHIFT 0x04 + +#define FT6206_P2_XH_REG 0x09 +#define FT6206_P2_XL_REG 0x0A +#define FT6206_P2_YH_REG 0x0B +#define FT6206_P2_YL_REG 0x0C +#define FT6206_P2_WEIGHT_REG 0x0D +#define FT6206_P2_MISC_REG 0x0E + + /* Threshold for touch detection */ +#define FT6206_TH_GROUP_REG 0x80 + + /* Values FT6206_TH_GROUP_REG : threshold related */ +#define FT6206_THRESHOLD_MASK 0xFF +#define FT6206_THRESHOLD_SHIFT 0 + + /* Filter function coefficients */ +#define FT6206_TH_DIFF_REG 0x85 + + /* Control register */ +#define FT6206_CTRL_REG 0x86 + + /* Values related to FT6206_CTRL_REG */ + + /* Will keep the Active mode when there is no touching */ +#define FT6206_CTRL_KEEP_ACTIVE_MODE 0x00 + + /* Switching from Active mode to Monitor mode automatically when there is no touching */ +#define FT6206_CTRL_KEEP_AUTO_SWITCH_MONITOR_MODE 0x01 + + /* The time period of switching from Active mode to Monitor mode when there is no touching */ +#define FT6206_TIMEENTERMONITOR_REG 0x87 + + /* Report rate in Active mode */ +#define FT6206_PERIODACTIVE_REG 0x88 + + /* Report rate in Monitor mode */ +#define FT6206_PERIODMONITOR_REG 0x89 + + /* The value of the minimum allowed angle while Rotating gesture mode */ +#define FT6206_RADIAN_VALUE_REG 0x91 + + /* Maximum offset while Moving Left and Moving Right gesture */ +#define FT6206_OFFSET_LEFT_RIGHT_REG 0x92 + + /* Maximum offset while Moving Up and Moving Down gesture */ +#define FT6206_OFFSET_UP_DOWN_REG 0x93 + + /* Minimum distance while Moving Left and Moving Right gesture */ +#define FT6206_DISTANCE_LEFT_RIGHT_REG 0x94 + + /* Minimum distance while Moving Up and Moving Down gesture */ +#define FT6206_DISTANCE_UP_DOWN_REG 0x95 + + /* Maximum distance while Zoom In and Zoom Out gesture */ +#define FT6206_DISTANCE_ZOOM_REG 0x96 + + /* High 8-bit of LIB Version info */ +#define FT6206_LIB_VER_H_REG 0xA1 + + /* Low 8-bit of LIB Version info */ +#define FT6206_LIB_VER_L_REG 0xA2 + + /* Chip Selecting */ +#define FT6206_CIPHER_REG 0xA3 + + /* Interrupt mode register (used when in interrupt mode) */ +#define FT6206_GMODE_REG 0xA4 + +#define FT6206_G_MODE_INTERRUPT_MASK 0x03 +#define FT6206_G_MODE_INTERRUPT_SHIFT 0x00 + + /* Possible values of FT6206_GMODE_REG */ +#define FT6206_G_MODE_INTERRUPT_POLLING 0x00 +#define FT6206_G_MODE_INTERRUPT_TRIGGER 0x01 + + /* Current power mode the FT6206 system is in (R) */ +#define FT6206_PWR_MODE_REG 0xA5 + + /* FT6206 firmware version */ +#define FT6206_FIRMID_REG 0xA6 + + /* FT6206 Chip identification register */ +#define FT6206_CHIP_ID_REG 0xA8 + + /* Possible values of FT6206_CHIP_ID_REG */ +#define FT6206_ID_VALUE 0x11 + + /* Release code version */ +#define FT6206_RELEASE_CODE_ID_REG 0xAF + + /* Current operating mode the FT6206 system is in (R) */ +#define FT6206_STATE_REG 0xBC + + + +#define HAS_TOUCH_PANEL() touchCST340Flag == true + +#define CST340_MODE_DEBUG_INFO 0xD101 // To read out chip ID and firmware version +#define CST340_MODE_NORMAL 0xD109 // Normal mode +#define CST340_FINGER1_REG 0xD000 // Touch info register +#define CST340_CHIPTYPE_REG 0xD204 // uint16_t chip IC type & uint16_t project ID register +#define CST340_FWVER_REG 0xD208 // Firmware version register(uint8_t major, uint8_t minor, uint16_t build) +#define CST340_TOUCH_EVT_FLAG_CONTACT 6 + +#define CST340_CHIP_ID 0x011C // Expected answer to CST340_CHIPTYPE_REG query for the ChipID field + +#define TOUCH_POINTS_MAX 5 // Max touch points + +#define TOUCH_ACK 0 +#define TOUCH_NACK 1 + +#define CST340_I2C_ADDR 0x1A + +extern bool touchCST340Flag; +extern uint32_t touchI2Chiccups; +extern uint16_t touchICfwver; +void TouchInit(); + +struct TouchState touchPanelRead(); +bool touchPanelEventOccured(); + +PACK(typedef struct { + uint8_t track; + uint16_t x; + uint16_t y; + uint16_t size; + uint8_t reserved; +}) TouchPoint; + +PACK(struct TouchData { + union + { + TouchPoint points[TOUCH_POINTS_MAX]; + uint8_t data[TOUCH_POINTS_MAX * sizeof(TouchPoint)]; + }; +}); + +#define TPRST_LOW() do { TOUCH_RST_GPIO->BSRRH = TOUCH_RST_GPIO_PIN; } while(0) +#define TPRST_HIGH() do { TOUCH_RST_GPIO->BSRRL = TOUCH_RST_GPIO_PIN; } while(0) diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index 3c13292347f..4f51bc44bb2 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -712,6 +712,8 @@ class OpenTxSimulatorFactory: public SimulatorFactory return Board::BOARD_TARANIS_X9LITE; #elif defined(PCBNV14) return Board::BOARD_FLYSKY_NV14; +#elif defined(PCBPL18) + return Board::BOARD_FLYSKY_PL18; #else return Board::BOARD_TARANIS_X9D; #endif diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 76aaef71ee2..04e6dff25dd 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -535,7 +535,7 @@ void boardOff() void hapticOff() {} -#if defined(PCBFRSKY) || defined(PCBFLYSKY) +#if defined(PCBFRSKY) || defined(PCBNV14) HardwareOptions hardwareOptions; #endif diff --git a/radio/src/targets/simu/simufatfs.cpp b/radio/src/targets/simu/simufatfs.cpp index bd6abc90c0c..c1c15c63a97 100644 --- a/radio/src/targets/simu/simufatfs.cpp +++ b/radio/src/targets/simu/simufatfs.cpp @@ -616,6 +616,7 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs) #include "hal/storage.h" void storageInit() {} +void storageDeInit() {} void storagePreMountHook() {} bool storageIsPresent() { return true; } diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 7fc9ef2113c..4207ed87203 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -480,10 +480,6 @@ else() message( FATAL_ERROR "Unknown CPU_TYPE_FULL" ) endif() -if(NOT PCB STREQUAL PCBXLITE) - add_definitions(-DHARDWARE_TRAINER_JACK) -endif() - if(ENABLE_SERIAL_PASSTHROUGH) set(CLI ON "Enable CLI") endif() diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 26bfff7d1d1..d5400c773d3 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -479,4 +479,12 @@ void setTopBatteryValue(uint32_t volts); #define VOLTAGE_DROP 20 #endif +#if defined(RADIO_T20) +#define NUM_TRIMS 8 +#else +#define NUM_TRIMS 4 +#endif + +#define NUM_TRIMS_KEYS (NUM_TRIMS * 2) + #endif // _BOARD_H_ diff --git a/radio/src/tasks.h b/radio/src/tasks.h index 0e6181e5660..e12829108de 100644 --- a/radio/src/tasks.h +++ b/radio/src/tasks.h @@ -30,8 +30,15 @@ #else #define MENUS_STACK_SIZE 2000 #endif + +#if !defined(DEBUG) #define MIXER_STACK_SIZE 400 #define AUDIO_STACK_SIZE 400 +#else +#define MIXER_STACK_SIZE 512 +#define AUDIO_STACK_SIZE 512 +#endif + #define CLI_STACK_SIZE 1024 // only consumed with CLI build option #if defined(FREE_RTOS) diff --git a/radio/src/tests/images/color/bitmap_480x320.png b/radio/src/tests/images/color/bitmap_480x320.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d818510e9bf355b6c060f88fbacdc106dc64d1 GIT binary patch literal 23258 zcmY(qdt6N08$Z7G-g9g2(PgTsnaZV-nrhUP)SgP^KJG~}kxYa^G6;K`Zd4Q@oiHin z;G86cj)2&riF*h0{|k(ACv);e|ht>%oTvh;%OoB1)AuU$;N)9&Pl-0w(|eaN3uOGwRSGC zmsrF9`OtY$LRkZ?`M&>cvrDL-R1W|D7sU3I(4~fw+pDCM&|oF`K{n!l{QgfP7>lW- zPAO#kx1}7&zz6t^Di^!|4k#ykCHrd={yPD9yOfiij;k2|`N$mwmrNXJ{p9)I!GAQQ z|IUn@UT2EkEacz0vV)X~nJcAM`~UlOG1({CUHra(yMv|_(*B)@loD>DZMFEM+HUpl z&}75<|6iA2oqYf4|7#VRy~OX9M`3o@|5=qXm+X`5PWo@V68oQtSi_~MxQex1`rH3q z3EB9cWo2K{hduarjcgrXVA~^@)$s2+!h@ARx-0y*9f|(WL^#mAAmtB}M*q8#AC3Q6 z*8gMwbU7oNyD@uQ@4vfSYA+$X8+Z5L_Qe00NOn4K{y!#l|99yB$G+qq{IB`z#FWqs zS?IyR|LzsLjV~a(JJ|oj?S$|Csd1zeE2&_9ge=f6X7mhf9KmCAT&I^&?t5 z<41SXHvj8aq(}abAE472|1oLvzeE48eShr1|C(RI(6-v|YmN2)uTSZul!(xk=|g@I!Lo@&Wj`am8opTO949j{Y zw}T_H;$Bn;j&kO*-H+wXP*B%!IwlQ7wuL>5ioszbrD0l>0zc*3tORM}fthuj7 z1Xt~iYiYB(c8dLTwnyQ81=U{&ThrJMw7@ZDGVGWO4Kh(8R&#zvjyi%3#pqKP#U-@c zCo*=wg`u?1QEBxO{cV)B%9_7SKf}>_U=4NeGHyZ$i=oawTwACAVt=-q?ObuuyGYxg zOynucO$dV;>`T;UxC&&MY%!E?X3~eQ8RF%_kyuRZk;f%eY5cak+MgYjxS5ITd!FGY zmd-}!KTlZ%bS%sDxXltHp-YS|KKiDPo<3I+5sVd_v>NsMkj*`jV3#C3VuZ0VSE5$( z(ZX59i(UkGK2r7l#h9^}?AFATo1mpBcHn}Do(cXS=Qk6Btk0RDSY5s}C?rMOO>=7u zs`xbE)JV97=4Qps^xm{FI|^MHFZ*YB5N(v#I%0A;)E+@Q+{9B6D_^A86-}XpMqdad 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 literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/clipping_480x320.png b/radio/src/tests/images/color/clipping_480x320.png new file mode 100644 index 0000000000000000000000000000000000000000..f14ef839bd3236ba7de2c2a133f3627a535b914d GIT binary patch literal 5121 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?4jfECk=Ls%bQlF?(U4ElpwXGKgBzPOa#6d}>#jIa4PRT&{jBOX0s!RlX z+74uJfQbnLOpA42Tzq0A#RN73#4!Py1>qQS3`bl_b1~&5@&rudqk$F)YlN zC>j8bQ#%Xe>moU%r;R~V5gerYFsnh~i{veo@IS1;;?{2AHu)novUn~DNC1tS{0z$P z5kqo_n{&eyNRUp4S&b6Dz&Jn-{zPEJJIgFSSpkhJwoDF?ah}Ccz8g16LU8bc1nFs* z)yUxs3U=h+H{fWzsMvQg#Ri&=nt^5ljay;~OIN@YiIff$R9Qel`Z5(}HFEd@;{YZ2 zC0Y)=5ICM;BnM4Xj#5lLOoqm8%IPrO&Itl-43C@5<=vFqP6!xiL=+}Ev8+7EdWH3h zQ=8kPSl8gs?vsyaa0HmMXq*&$^7P1I$+?97LnKv_@>nHUCob3@ND9#2IE%j_DH z3*f}A!P9tgjf)RMA2522Zz1Bk-BoJA-Ls*KBU6B3By78rP3FSNvkVTNE=W<)0n6+F68t<);GU)=tYt=G>4nnO0k#EEdP1W; zP0(loO7M^NG{NN+p`PYw@*W*X0gYc#Hju)=kbT5_hSdwjr@)~-22WQ%mvv4FO#o%S B+vETM literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/lines_480x320.png b/radio/src/tests/images/color/lines_480x320.png new file mode 100644 index 0000000000000000000000000000000000000000..885a7a1e0caf191f29cd977e6f7ae6eeffc38469 GIT binary patch literal 5734 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 literal 0 HcmV?d00001 diff --git a/radio/src/tests/images/color/transparency_EN_480x320.png b/radio/src/tests/images/color/transparency_EN_480x320.png new file mode 100644 index 0000000000000000000000000000000000000000..54827cd16112e62f8a6a05e64fddbbd43ad7a28b GIT binary patch literal 10019 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 Qfd2+5|Bj~mi|%0ee-2<9Qvd(} literal 0 HcmV?d00001 diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index f338756d2e4..4f144dc0653 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -216,7 +216,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -1032,6 +1032,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index aa001fff4e2..a3e8df750a3 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -229,7 +229,7 @@ #if defined(PCBTARANIS) || defined(PCBHORUS) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[DALŠÍ]" #else #define TR_ENTER "[MENU]" @@ -330,7 +330,7 @@ #define TR_SLOWUP TR3("Zpomalení(+)", "Zpomal(\176)", "Zpomalení(\176)") #define TR_MIXES "MIXER" #define TR_CV "K" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GP" #else #define TR_GV TR("G", "GP") @@ -1053,6 +1053,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] pro vybrani souboru" #define TR_BL_FLASH_KEY "Drzet dlouze [R TRIM] pro nahrani" #define TR_BL_EXIT_KEY " [L TRIM] pro ukonceni" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/da.h b/radio/src/translations/da.h index 6b234030acb..ecffd00d5e1 100644 --- a/radio/src/translations/da.h +++ b/radio/src/translations/da.h @@ -1046,6 +1046,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] for at bruge fil" #define TR_BL_FLASH_KEY "[R TRIM] lang til for at starte" #define TR_BL_EXIT_KEY "[L TRIM] for at forlade" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index 9b1697762e1..e406ceabaf6 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -220,7 +220,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -321,7 +321,7 @@ #define TR_SLOWUP "Langs.Up" #define TR_MIXES "MISCHER" #define TR_CV "KV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1034,6 +1034,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] um Datei auszuwählen" #define TR_BL_FLASH_KEY "Halte [R TRIM] gedrückt, zum schreiben" #define TR_BL_EXIT_KEY " [L TRIM] zum beenden" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // Taranis Info Zeile Anzeigen diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index 05e9267a613..43750aa4d8f 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -220,7 +220,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -320,7 +320,7 @@ #define TR_SLOWUP "Slow up" #define TR_MIXES "MIXES" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1042,6 +1042,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index 71d1448c879..254c9866112 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -216,7 +216,7 @@ #if defined(PCBTARANIS) || defined(PCBHORUS) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -318,7 +318,7 @@ #define TR_SLOWUP "Subir lento" #define TR_MIXES "MIXES" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1044,6 +1044,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/fi.h b/radio/src/translations/fi.h index acc72488422..6814f004e32 100644 --- a/radio/src/translations/fi.h +++ b/radio/src/translations/fi.h @@ -1057,6 +1057,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/fr.h b/radio/src/translations/fr.h index 490924f143d..c72e0d68ab3 100644 --- a/radio/src/translations/fr.h +++ b/radio/src/translations/fr.h @@ -226,7 +226,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[SUIVANT]" #else #define TR_ENTER "[MENU]" @@ -327,7 +327,7 @@ #define TR_SLOWUP "Ralenti haut" #define TR_MIXES "MIXEUR" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "VG" #else #define TR_GV TR("G", "VG") @@ -1058,6 +1058,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] pour sélect. fichier" #define TR_BL_FLASH_KEY "Appui long [R TRIM] pour flasher" #define TR_BL_EXIT_KEY " [L TRIM] pour quitter" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/he.h b/radio/src/translations/he.h index 234335035be..a2c47087b76 100644 --- a/radio/src/translations/he.h +++ b/radio/src/translations/he.h @@ -1050,6 +1050,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] ליציאה" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index 0b87268d50b..2489dd2f376 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -220,7 +220,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -320,7 +320,7 @@ #define TR_SLOWUP "Rall.Su" #define TR_MIXES "MIXER" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1041,6 +1041,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] per scegliere il file" #define TR_BL_FLASH_KEY "Tener premuto [R TRIM] per scrivere" #define TR_BL_EXIT_KEY " [L TRIM] per uscire" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/jp.h b/radio/src/translations/jp.h index 1e961499e0a..540897e2037 100644 --- a/radio/src/translations/jp.h +++ b/radio/src/translations/jp.h @@ -1037,6 +1037,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index afe22cb2a53..e7850c53634 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -217,7 +217,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -317,7 +317,7 @@ #define TR_SLOWUP "Langz.Up" #define TR_MIXES "MIXER" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1049,6 +1049,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 7dcb4acd8c1..0ddc68d059c 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -216,7 +216,7 @@ #if defined(PCBTARANIS) || defined(PCBHORUS) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -318,7 +318,7 @@ #define TR_SLOWUP "Spowoln.(+)" #define TR_MIXES "MIKSERY" #define TR_CV "Kr" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "ZG" #else #define TR_GV TR("G", "ZG") @@ -1039,6 +1039,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] aby wybrac plik" #define TR_BL_FLASH_KEY "Przytrzymaj [R TRIM] aby flashowac" #define TR_BL_EXIT_KEY " [L TRIM] aby wyjsc" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 2000191a1e8..44d38b0295b 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -223,7 +223,7 @@ #if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) #define TR_ENTER "[NEXT]" #else #define TR_ENTER "[MENU]" @@ -323,7 +323,7 @@ #define TR_SLOWUP "Slow up" #define TR_MIXES "MIXES" #define TR_CV "CV" -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) #define TR_GV "GV" #else #define TR_GV TR("G", "GV") @@ -1045,6 +1045,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/ru.h b/radio/src/translations/ru.h index eff4b3afc01..9679e3aa863 100644 --- a/radio/src/translations/ru.h +++ b/radio/src/translations/ru.h @@ -1044,6 +1044,16 @@ #define TR_BL_SELECT_KEY "[П ТРИМ] для выбора файла" #define TR_BL_FLASH_KEY "Удерживайте [П ТРИМ] для прошивки" #define TR_BL_EXIT_KEY "[Л ТРИМ] для выхода" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/se.h b/radio/src/translations/se.h index 5af95224792..c3b427a1ecb 100644 --- a/radio/src/translations/se.h +++ b/radio/src/translations/se.h @@ -1071,6 +1071,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] foer att vaelja fil" #define TR_BL_FLASH_KEY "Tryck [R TRIM] foer att flasha" #define TR_BL_EXIT_KEY " [L TRIM] för att avsluta" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/src/translations/tw.h b/radio/src/translations/tw.h index c589312a46e..0792092d191 100644 --- a/radio/src/translations/tw.h +++ b/radio/src/translations/tw.h @@ -1037,6 +1037,16 @@ #define TR_BL_SELECT_KEY "[R TRIM] to select file" #define TR_BL_FLASH_KEY "Hold [R TRIM] long to flash" #define TR_BL_EXIT_KEY " [L TRIM] to exit" +#elif defined(PCBPL18) + // Bootloader PL18 specific - Ascii only + #define TR_BL_RF_USB_ACCESS "RF USB access" + #define TR_BL_ERASE_INT_FLASH "Erase Internal Flash Storage" + #define TR_BL_ERASE_FLASH "Erase Flash Storage" + #define TR_BL_ERASE_FLASH_MSG "This may take up to 200s" + #define TR_BL_SELECT_KEY " [TR4 Dn] to select file" + #define TR_BL_FLASH_KEY " Hold [TR4 Dn] long to flash" + #define TR_BL_ERASE_KEY " Hold [TR4 Dn] long to erase" + #define TR_BL_EXIT_KEY " [TR4 Up] to exit" #endif // About screen diff --git a/radio/util/hw_defs/hal_keys.jinja b/radio/util/hw_defs/hal_keys.jinja index 499a41a397c..15b563d94e3 100644 --- a/radio/util/hw_defs/hal_keys.jinja +++ b/radio/util/hw_defs/hal_keys.jinja @@ -14,14 +14,11 @@ const char* const _key_labels[] = { {% endfor %} }; -constexpr uint32_t _defined_keys = +constexpr uint32_t _defined_keys = 0 {% for key in keys %} - {% if loop.last %} - (1 << {{ key.key }}); - {% else %} - (1 << {{ key.key }}) | - {% endif %} + | (1 << {{ key.key }}) {% endfor %} +; constexpr uint8_t _n_keys = {{ keys | length }}; constexpr uint8_t _n_trims = {{ trims | length }}; diff --git a/radio/util/hw_defs/legacy_names.py b/radio/util/hw_defs/legacy_names.py index 77f21d27c08..a313326a791 100644 --- a/radio/util/hw_defs/legacy_names.py +++ b/radio/util/hw_defs/legacy_names.py @@ -338,6 +338,142 @@ } } }, + { + "targets": { + "pl18" + }, + "inputs": { + "LH": { + "yaml": "Rud" + }, + "LV": { + "yaml": "Ele" + }, + "RV": { + "yaml": "Thr" + }, + "RH": { + "yaml": "Ail" + }, + "P1": { + "yaml": "POT1", + "lua": "s1", + "label": "S1", + "short_label": "1", + "description": "Potentiometer 1" + }, + "P2": { + "yaml": "POT2", + "lua": "s2", + "label": "S2", + "short_label": "2", + "description": "Potentiometer 2" + }, + "P3": { + "yaml": "POT3", + "lua": "s3", + "label": "S3", + "short_label": "3", + "description": "Potentiometer 3" + }, + "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" + }, + } + }, + { + "targets": { + "pl18ev" + }, + "inputs": { + "LH": { + "yaml": "Rud" + }, + "LV": { + "yaml": "Ele" + }, + "RV": { + "yaml": "Thr" + }, + "RH": { + "yaml": "Ail" + }, + "P1": { + "yaml": "POT1", + "lua": "s1", + "label": "S1", + "short_label": "1", + "description": "Potentiometer 1" + }, + "P2": { + "yaml": "POT2", + "lua": "s2", + "label": "S2", + "short_label": "2", + "description": "Potentiometer 2" + }, + "P3": { + "yaml": "POT3", + "lua": "s3", + "label": "S3", + "short_label": "3", + "description": "Potentiometer 3" + }, + "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" + }, + "EXT3": { + "yaml": "EXT3", + "lua": "ext3", + "label": "EXT3", + "short_label": "E3", + "description": "Ext 3" + }, + "EXT4": { + "yaml": "EXT4", + "lua": "ext4", + "label": "EXT4", + "short_label": "E4", + "description": "Ext 4" + } + } + }, { "targets": {"t20"}, "inputs": { diff --git a/radio/util/hw_defs/pot_config.py b/radio/util/hw_defs/pot_config.py index 637ac16717e..f80cd6fa142 100644 --- a/radio/util/hw_defs/pot_config.py +++ b/radio/util/hw_defs/pot_config.py @@ -14,6 +14,24 @@ "P1": {"default": "POT_CENTER"}, "P2": {"default": "POT_CENTER"} }, + "pl18": { + "P1": {"default": "POT"}, + "P2": {"default": "POT"}, + "P3": {"default": "POT"}, + "SL1": {"default": "SLIDER"}, + "SL2": {"default": "SLIDER"} + }, + "pl18ev": { + "P1": {"default": "POT_CENTER"}, + "P2": {"default": "POT"}, + "P3": {"default": "POT_CENTER"}, + "SL1": {"default": "SLIDER"}, + "SL2": {"default": "SLIDER"}, + "EXT1": {"default": "POT_CENTER"}, + "EXT2": {"default": "POT_CENTER"}, + "EXT3": {"default": "MULTIPOS"}, + "EXT4": {"default": "MULTIPOS"} + }, "mt12": { "P1": {"default": "POT"}, "P2": {"default": "POT"}, diff --git a/radio/util/hw_defs/stm32_adc_inputs.jinja b/radio/util/hw_defs/stm32_adc_inputs.jinja index b30ce7d2c2c..fc1103021f5 100644 --- a/radio/util/hw_defs/stm32_adc_inputs.jinja +++ b/radio/util/hw_defs/stm32_adc_inputs.jinja @@ -6,6 +6,7 @@ static const stm32_adc_input_t _ADC_inputs[] = { {% for input in adc_inputs.inputs %} { + // {{ input.name }} // ADC_INPUT_{{ input.type }}, {{ input.gpio if input.gpio else 'nullptr' }}, {{ input.pin if input.gpio else '0' }}, diff --git a/radio/util/hw_defs/switch_config.py b/radio/util/hw_defs/switch_config.py index 3d7cb80cd25..de3461f53ca 100644 --- a/radio/util/hw_defs/switch_config.py +++ b/radio/util/hw_defs/switch_config.py @@ -29,6 +29,28 @@ "SG": { "default": "3POS" }, "SH": { "default": "TOGGLE" } }, + "pl18": { + "SA": { "default": "2POS" }, + "SB": { "default": "3POS" }, + "SC": { "default": "2POS" }, + "SD": { "default": "3POS" }, + "SE": { "default": "3POS" }, + "SF": { "default": "2POS" }, + "SG": { "default": "3POS" }, + "SH": { "default": "TOGGLE" } + }, + "pl18ev": { + "SA": { "default": "2POS" }, + "SB": { "default": "3POS" }, + "SC": { "default": "2POS" }, + "SD": { "default": "3POS" }, + "SE": { "default": "3POS" }, + "SF": { "default": "2POS" }, + "SG": { "default": "3POS" }, + "SH": { "default": "3POS" }, + "SI": { "default": "3POS" }, + "SJ": { "default": "3POS" } + }, "lr3pro": { # left side "SA": {"default": "3POS", "display": [0, 0]}, diff --git a/tools/build-companion.sh b/tools/build-companion.sh index 128fba24c5c..d1dc928b62e 100755 --- a/tools/build-companion.sh +++ b/tools/build-companion.sh @@ -70,7 +70,7 @@ declare -a simulator_plugins=(x9lite x9lites tlite tpro lr3pro x9d x9dp x9dp2019 x9e xlite xlites - nv14 + nv14 pl18 pl18ev x10 x10-access x12s t16 t18 tx16s) @@ -170,6 +170,12 @@ do commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; + pl18ev) + BUILD_OPTIONS+="-DPCB=PL18 -DPCBREV=PL18EV" + ;; *) echo "Unknown target: $target_name" exit 1 diff --git a/tools/build-flysky.py b/tools/build-flysky.py index c6c60ad74d0..22be920ccee 100644 --- a/tools/build-flysky.py +++ b/tools/build-flysky.py @@ -9,15 +9,10 @@ boards = { - "NV14": { - "PCB": "NV14", - "DEFAULT_MODE": "1", - }, - "EL18": { - "PCB": "NV14", - "PCBREV": "EL18", - "DEFAULT_MODE": "1", - }, + "NV14": { "PCB": "NV14" }, + "EL18": { "PCB": "NV14", "PCBREV": "EL18" }, + "PL18": { "PCB": "PL18" }, + "PL18EV": { "PCB": "PL18", "PCBREV": "PL18EV" }, } translations = [ diff --git a/tools/build-gh.sh b/tools/build-gh.sh index aa154af5959..16f0aa512ba 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -191,6 +191,12 @@ do el18) BUILD_OPTIONS+="-DPCB=NV14 -DPCBREV=EL18" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; + pl18ev) + BUILD_OPTIONS+="-DPCB=PL18 -DPCBREV=PL18EV" + ;; commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index 66ea77e8516..1d9c9634f6d 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -148,6 +148,12 @@ do el18) BUILD_OPTIONS+="-DPCB=NV14 -DPCBREV=EL18" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; + pl18ev) + BUILD_OPTIONS+="-DPCB=PL18 -DPCBREV=PL18EV" + ;; commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; diff --git a/tools/generate-yaml.sh b/tools/generate-yaml.sh index aa1b04c33ec..cb834c7a05f 100755 --- a/tools/generate-yaml.sh +++ b/tools/generate-yaml.sh @@ -8,7 +8,7 @@ if [[ -n ${GCC_ARM} ]] ; then export PATH=${GCC_ARM}:$PATH fi -: ${FLAVOR:="tx16s;x12s;nv14;x9d;x9dp;x9e;x9lite;xlites;x7;tpro;t20"} +: ${FLAVOR:="tx16s;x12s;nv14;pl18;x9d;x9dp;x9e;x9lite;xlites;x7;tpro;t20"} : ${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"} @@ -110,6 +110,9 @@ do nv14) BUILD_OPTIONS+="-DPCB=NV14" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; From cea924a400d29e135e4abe8f16be98e6f241c542 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 6 Dec 2023 10:24:40 +1100 Subject: [PATCH 2/6] perf(color): Remove multiple checks for model notes in quick menu (#4382) --- radio/src/gui/colorlcd/view_main_menu.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/view_main_menu.cpp b/radio/src/gui/colorlcd/view_main_menu.cpp index 331cde9fa59..8adf20d8047 100644 --- a/radio/src/gui/colorlcd/view_main_menu.cpp +++ b/radio/src/gui/colorlcd/view_main_menu.cpp @@ -47,8 +47,10 @@ ViewMainMenu::ViewMainMenu(Window* parent, std::function closeHandler) : Layer::push(this); coord_t width = VM_W; + bool hasNotes = modelHasNotes(); + #if LCD_W > LCD_H - if (modelHasNotes()) + if (hasNotes) width += FAB_BUTTON_SIZE; #endif @@ -65,7 +67,7 @@ ViewMainMenu::ViewMainMenu(Window* parent, std::function closeHandler) : return 0; }); - if (modelHasNotes()) { + if (hasNotes) { carousel->addButton(ICON_MODEL_NOTES, STR_MAIN_MENU_MODEL_NOTES, [=]() -> uint8_t { deleteLater(); From eb81c5ecbb9060012412b14bbe336eec42de6ab6 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 6 Dec 2023 11:22:56 +1100 Subject: [PATCH 3/6] fix(color): Incorrect focus selection in GVAR list (#4378) --- radio/src/gui/colorlcd/model_flightmodes.cpp | 6 ++--- radio/src/gui/colorlcd/model_gvars.cpp | 23 ++++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index b7464b0e55d..f54b2c47171 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -292,7 +292,7 @@ class FMStyle lv_obj_set_style_flex_grow(obj, 2, LV_PART_MAIN); lv_obj_set_style_pad_all(obj, 0, LV_PART_MAIN); lv_obj_set_height(obj, LV_SIZE_CONTENT); - lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE); lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); return obj; @@ -318,7 +318,7 @@ class FMStyle { auto obj = lv_obj_create(parent); lv_obj_add_style(obj, &fmTrimContStyle, LV_PART_MAIN); - lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE); return obj; } @@ -403,13 +403,11 @@ class FlightModeBtn : public Button fmID = fmStyle.newId(lvobj); lv_obj_t* container = fmStyle.newGroup(lvobj); - lv_obj_set_user_data(container, this); fmName = fmStyle.newName(container); fmSwitch = fmStyle.newSwitch(container); lv_obj_t* trims_cont = fmStyle.newTrimCont(container); - lv_obj_set_user_data(trims_cont, this); for (int i = 0; i < keysGetMaxTrims(); i += 1) { fmTrimMode[i] = fmStyle.newTrimMode(trims_cont, i); diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 8c81591679c..87d8691fd64 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -106,7 +106,7 @@ class GVarStyle lv_obj_set_width(obj, GVAR_VAL_W * GVAR_COLS); lv_obj_set_height(obj, GVAR_H); lv_obj_set_style_pad_all(obj, 0, LV_PART_MAIN); - lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE); return obj; } @@ -117,7 +117,7 @@ class GVarStyle lv_obj_add_style(obj, &fmContStyle, LV_PART_MAIN); lv_obj_add_style(obj, &fmContStyleChecked, LV_PART_MAIN|LV_STATE_CHECKED); lv_obj_set_pos(obj, (flightMode%GVAR_COLS)*GVAR_VAL_W, (flightMode/GVAR_COLS)*GVAR_VAL_H); - lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE); return obj; } @@ -176,12 +176,7 @@ class GVarButton : public Button lv_obj_t* target = lv_event_get_target(e); auto line = (GVarButton*)lv_obj_get_user_data(target); if (line) - line->build(); - - if (e) { - auto param = lv_event_get_param(e); - lv_event_send(line->getLvObj(), LV_EVENT_DRAW_MAIN, param); - } + line->build(e); } protected: @@ -217,7 +212,7 @@ class GVarButton : public Button } } - void build() + void build(lv_event_t* e) { if (init) return; @@ -228,11 +223,10 @@ class GVarButton : public Button gvarStyle.newName(lvobj, getGVarString(gvarIdx)); if (modelFMEnabled()) { - auto container = gvarStyle.newGroup(lvobj); + lv_obj_t* container = gvarStyle.newGroup(lvobj); for (int flightMode = 0; flightMode < MAX_FLIGHT_MODES; flightMode++) { fmCont[flightMode] = gvarStyle.newFMCont(container, flightMode); - lv_obj_set_user_data(fmCont[flightMode], this); if (flightMode == currentFlightMode) { lv_obj_add_state(fmCont[flightMode], LV_STATE_CHECKED); } @@ -251,11 +245,16 @@ class GVarButton : public Button updateValueText(0); } + init = true; + lv_obj_enable_style_refresh(true); lv_obj_update_layout(lvobj); - init = true; + if (e) { + auto param = lv_event_get_param(e); + lv_event_send(lvobj, LV_EVENT_DRAW_MAIN, param); + } } void updateValueText(uint8_t flightMode) From 8e8feca046c48cb6ad220d5660ba016cacb19070 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Wed, 6 Dec 2023 10:36:40 +1000 Subject: [PATCH 4/6] revert: Wrong MCU type for TX12MK2 Fixes #4377, regression introduced by #3846 --- radio/src/targets/taranis/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 4207ed87203..11d79c5d039 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -215,7 +215,7 @@ elseif(PCB STREQUAL X7) set(MODULE_SIZE_STD YES) set(FLAVOUR tx12mk2) set(NAVIGATION_TYPE x7) - set(CPU_TYPE_FULL STM32F413xG) + set(CPU_TYPE_FULL STM32F407xE) set(ROTARY_ENCODER YES) set(USB_CHARGER YES) if (NOT BLUETOOTH) From 5d8ffcc24446ac8fa5b1cf616c15a1da5e2a54fc Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 6 Dec 2023 11:45:24 +1100 Subject: [PATCH 5/6] chore(cpn): Increase number of profiles that can be created (#4373) --- companion/src/apppreferencesdialog.cpp | 2 + companion/src/apppreferencesdialog.ui | 283 +++++++++++++------------ companion/src/mainwindow.cpp | 17 +- companion/src/storage/appdata.cpp | 17 ++ companion/src/storage/appdata.h | 6 +- 5 files changed, 189 insertions(+), 136 deletions(-) diff --git a/companion/src/apppreferencesdialog.cpp b/companion/src/apppreferencesdialog.cpp index 4696cad1094..85e55312068 100644 --- a/companion/src/apppreferencesdialog.cpp +++ b/companion/src/apppreferencesdialog.cpp @@ -76,6 +76,7 @@ void AppPreferencesDialog::accept() Profile & profile = g.currentProfile(); g.showSplash(ui->showSplash->isChecked()); + g.sortProfiles(ui->sortProfiles->isChecked()); g.promptProfile(ui->chkPromptProfile->isChecked()); g.simuSW(ui->simuSW->isChecked()); g.disableJoystickWarning(ui->joystickWarningCB->isChecked()); @@ -232,6 +233,7 @@ void AppPreferencesDialog::initSettings() } ui->showSplash->setChecked(g.showSplash()); + ui->sortProfiles->setChecked(g.sortProfiles()); ui->chkPromptProfile->setChecked(g.promptProfile()); ui->historySize->setValue(g.historySize()); ui->backLightColor->setCurrentIndex(g.backLight()); diff --git a/companion/src/apppreferencesdialog.ui b/companion/src/apppreferencesdialog.ui index b69843db0cf..8813d7473d5 100644 --- a/companion/src/apppreferencesdialog.ui +++ b/companion/src/apppreferencesdialog.ui @@ -7,7 +7,7 @@ 0 0 865 - 692 + 703 @@ -45,7 +45,7 @@ - 0 + 1 @@ -748,24 +748,44 @@ Mode 4: 6 - - - - Qt::Horizontal + + + + + 0 + 0 + + + + Remember - - + + + + Update + + + + + + + Prompt to run installer after update + + + + + - + - false + true - + 0 @@ -773,14 +793,38 @@ Mode 4: - Select Folder + Select Executable - - + + + + + + <html><head/><body><p>Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.</p></body></html> + + + Application (Companion/Simulator) + + + + + + + <html><head/><body><p>Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator <span style=" font-style:italic;">Debug Output</span> window.</p></body></html> + + + Radio Firmware (in Simulator) + + + + + + + 0 @@ -788,14 +832,11 @@ Mode 4: - Automatic Backup Folder + Google Earth Executable - - - - + @@ -808,8 +849,11 @@ Mode 4: - - + + + + + 0 @@ -817,45 +861,21 @@ Mode 4: - Google Earth Executable + Automatic Backup Folder - - - - - - <html><head/><body><p>Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.</p></body></html> - - - Application (Companion/Simulator) - - - - - - - <html><head/><body><p>Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator <span style=" font-style:italic;">Debug Output</span> window.</p></body></html> - - - Radio Firmware (in Simulator) - - - - - - - + + - + - true + false - + 0 @@ -863,13 +883,20 @@ Mode 4: - Select Executable + Select Folder - + + + + Qt::Horizontal + + + + Qt::Horizontal @@ -883,14 +910,14 @@ Mode 4: - + Qt::Horizontal - + @@ -913,14 +940,7 @@ Mode 4: - - - - Prompt for radio profile - - - - + @@ -940,17 +960,47 @@ Mode 4: - - - - <html><head/><body><p>This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. </p><p>When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.</p></body></html> + + + + + + true + + + false + + + + + + + + 0 + 0 + + + + Select Folder + + + + + + + + + + 0 + 0 + - Remove empty model slots when deleting models (only applies for radios w/out labels) + Action on New Model - + @@ -963,45 +1013,25 @@ Mode 4: - - + + - + 0 0 - Action on New Model + Enable automatic backup before writing firmware - - - - - - true - - - false - - - - - - - - 0 - 0 - - - - Select Folder - - - - + + + + Prompt for radio profile + + @@ -1039,7 +1069,7 @@ Mode 4: - + @@ -1065,14 +1095,24 @@ Mode 4: - + Qt::Horizontal - + + + + <html><head/><body><p>This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. </p><p>When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.</p></body></html> + + + Remove empty model slots when deleting models (only applies for radios w/out labels) + + + + @@ -1093,43 +1133,24 @@ Mode 4: - - - - - 0 - 0 - - + + - Enable automatic backup before writing firmware + Radio Profiles - - - - - 0 - 0 - - + + - Remember + Move selected Radio Profile to the top of the list - - - - Prompt to run installer after update - - - - - - - Update + + + + Qt::Horizontal diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 95d47bfa014..1725be789dc 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -1248,6 +1248,7 @@ void MainWindow::onChangeWindowAction(QAction * act) void MainWindow::onCurrentProfileChanged() { + g.moveCurrentProfileToTop(); Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.currentProfile().fwType())); emit firmwareChanged(); QApplication::clipboard()->clear(); @@ -1259,8 +1260,10 @@ int MainWindow::newProfile(bool loadProfile) int i; for (i=0; i < MAX_PROFILES && g.profile[i].existsOnDisk(); i++) ; - if (i == MAX_PROFILES) //Failed to find free slot + if (i == MAX_PROFILES) { //Failed to find free slot + QMessageBox::warning(this, tr("Cannot add profile"), tr("There is no space left to add a new profile. Delete an exsting profile before adding a new one.")); return -1; + } Firmware *newfw = Firmware::getDefaultVariant(); g.profile[i].init(); @@ -1300,9 +1303,14 @@ void MainWindow::deleteProfile(const int pid) QMessageBox::warning(this, tr("Companion :: Open files warning"), tr("Please save or close modified file(s) before deleting the active profile.")); return; } + int newPid = 0; if (pid == 0) { - QMessageBox::warning(this, tr("Not possible to remove profile"), tr("The default profile can not be removed.")); - return; + // Find valid profile + for (newPid = 1; newPid < MAX_PROFILES && !g.profile[newPid].existsOnDisk(); newPid += 1); + if (newPid == MAX_PROFILES) { + QMessageBox::warning(this, tr("Not possible to remove profile"), tr("The default profile can not be removed.")); + return; + } } int ret = QMessageBox::question(this, tr("Confirm Delete Profile"), @@ -1311,7 +1319,8 @@ void MainWindow::deleteProfile(const int pid) return; g.getProfile(pid).resetAll(); - loadProfileId(0); + loadProfileId(newPid); + g.moveCurrentProfileToTop(); } void MainWindow::deleteCurrentProfile() diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index 9408cec2876..45c21d590d7 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -304,6 +304,11 @@ Profile::Profile() : CompStoreObj(), index(-1) CompStoreObj::addObjectMapping(propertyGroup(), this); } +Profile::Profile(const Profile & rhs) : CompStoreObj(), index(-1) +{ + *this = rhs; +} + // The default copy operator can not be used since the index variable would be destroyed Profile & Profile::operator= (const Profile & rhs) { @@ -661,6 +666,18 @@ QMap AppData::getActiveProfiles() const return active; } +void AppData::moveCurrentProfileToTop() +{ + if (g.sortProfiles() && m_sessionId > 0) { + Profile tmpProfile(g.profile[m_sessionId]); + for (int i = m_sessionId; i > 0; i -= 1) { + g.profile[i] = g.profile[i - 1]; + } + g.profile[0] = tmpProfile; + id(0); + } +} + void AppData::convertSettings(QSettings & settings) { quint32 savedVer = settings.value(SETTINGS_VERSION_KEY, 0).toUInt(); diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index 37fcffa9904..c544058b6fd 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -58,7 +58,7 @@ #define CPN_SETTINGS_INI_FILE QString(PRODUCT % " " % QCoreApplication::translate("Companion", "settings") % " %1.ini") #define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE) -#define MAX_PROFILES 20 +#define MAX_PROFILES 32 #define MAX_JS_AXES 10 #define MAX_JS_BUTTONS 32 #define MAX_COMPONENTS 10 @@ -472,6 +472,7 @@ class Profile: public CompStoreObj protected: explicit Profile(); + explicit Profile(const Profile & rhs); void setIndex(int idx) { index = idx; } inline QString propertyGroup() const override { return QStringLiteral("Profiles"); } inline QString settingsPath() const override { return QString("%1/profile%2/").arg(propertyGroup()).arg(index); } @@ -685,6 +686,8 @@ class AppData: public CompStoreObj const Profile & getProfile(int index) const; //! List of all active profiles mapped by index. QMap getActiveProfiles() const; + // Move the currently selected profile to the top of the list + void moveCurrentProfileToTop(); //! Get a modifiable (non-const) reference to the ComponentData at \a index. Returns component[0] if \a index is invalid. ComponentData & getComponent(int index); @@ -791,6 +794,7 @@ class AppData: public CompStoreObj PROPERTY4(bool, jsSupport, "js_support", false) PROPERTY4(bool, showSplash, "show_splash", true) + PROPERTY4(bool, sortProfiles, "sort_profiles", false) PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false) PROPERTY(UpdateCheckFreq, updateCheckFreq, UPDATE_CHECK_MANUAL) From 7206182742aa8f1136b2e5bd0eab825a0cb87b90 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 6 Dec 2023 12:51:01 +1100 Subject: [PATCH 6/6] fix(cpn): Tweak PL18 simulator layout (#4384) --- .../src/images/simulator/PL18/bottom.png | Bin 178 -> 1514 bytes companion/src/images/simulator/PL18/left.png | Bin 1273 -> 1839 bytes companion/src/images/simulator/PL18/right.png | Bin 1273 -> 1839 bytes companion/src/images/simulator/PL18/top.png | Bin 178 -> 1514 bytes .../src/simulation/simulateduiwidgetPL18.ui | 44 +++++++++--------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/companion/src/images/simulator/PL18/bottom.png b/companion/src/images/simulator/PL18/bottom.png index 5fc10a49d42fe209dea8c83e3b8a9eeae29e6b43..35710ce353e018c489d4cc569b72acb0dbbc9bf2 100644 GIT binary patch literal 1514 zcmbVM%Wl&^6rIw70w@*hvKhG>5ID9cPK`$x2yvm2aH&cp+8q;nl30yB#-5V23k2-= z09GvE3-|$q*zf@?SfFm;2M`+&Tdwo4S}0G;zPd9$_x*UhwtBsMV&McrsN7s}*Abfe z2s|kr0eaGU1{dgfva*>WH2)_5W}cjzKaJ4b*RZuAH@q9v7g2=;qRlJAC;>D=7cUPJ z=5KL{+q@gbj`I2CYXygaquj1}x|cL~FI?G8`K{g6mcP5@+ktZV623U3pdjLs;bGK| zGdgsXkzNXXeyu5Z1d&^gQqK+I4Q~xMM9Q&UF;rjI4Qv5tE|CkiZCTcNOmtG!^d*hd zR8pliiy9`LJPKG$gAQGHuTAX1$x(VzCREb~gF$6bt%$U%8MbX}I?)JG0ik9)v1CIv z&K9Q{T%P%9n8;AXIM>M9Vp}>2csmXwN(!}cHc1sEO&hXAGb(x>)(9B*1uof6`y=Cl zukk*Qcq}u(8U;4#2`RFk_=W1?_W=WtUe7CR9O_FH6(%ydd=J8yB;-(Z*4jz9w$3xL zo%($F9=N%fN0ZP-%9#{tONjooSZh<4F{xDuKI1VzjPoGQ?i1lIliX4Ax~T?H^`(}f zQ+RdzoMBL1A45G6gq@wAp@bS$TD1;9p>YBx*&o4yPdg%w7=#=~tjo0|?kc!QByEVk zNWn08R~>obd9)d4lEpr6x{d--SHdu$9a3d&|AMU&z1CJuQ@2!Rn;n%^iJ_aiX%N$z z%Dckf&TBiB5B^d9W*WkLvHpMS$)_%F3c3d^{JgL{ zP$t*me`jE8NVFe=9R1rjw^KMQgPbik z5gdwL#$#AuDQv3v{e!!(<(@X(dTY4%>D^2jq0%h=@OrPY*|19Wy?OM_eTyC)yRx5@ z7T`D`s6Ja-I6!a@j#DyM25vuj@O);jJSC@0;0ki|5bXU;@5|t&mA-nIZ{z0js{7*V Hok!mRC$_`K literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yoxj5K>FJwJW~toLXCIR&>9`SRV;vd+9R*W8oKXU<#Ju~$iVU9X3~GNIBM zkJP5DjnW*@mUZRYDCc5JYP2bPNfzKYFaN+Oi2{7b3!N}ul>KCRuaLL*R=Q$uOZXAK zb`@P2F>sJd&Cw_wWF;E~cGolWy6Q@b2XVW_KTFh;r>470)5yfn+ePbFAo&wz0I%k9ohr`;iUQ&wBK%sFWuK72?kzlbZQVuC6Dep;= zXFZInLb63QPz3}AcJ(ofFl3#q)I1Zi(+)6XT}zUP0d*l7bkmNbnA;7HQ=7-av4x!X zn{i!w^UcO=eOrlbqwQ>c1nXm3uhZD`Y$s~Cc0)9M+mAhJ`wfRSIdz*ppHukth^4x(T3ov)KWoO}n7|EvMOkNmW>qR+FQgrP~WNr#xxG7*M+ zuG#q8M`LIPhuLP#YOumXHJW7xt_v8v_wd`lVB-JKX)km~f4~0IowKY{N6@=o{qc`G kuOA$JPL_W8A0Gc^zjZG@d$IA~Q}avfEUmPEzrOMC1Ea(=!~g&Q literal 1273 zcmeAS@N?(olHy`uVBq!ia0vp^DGUsZUpUx+tka)676U2H0*}aI1_r*vAk26?e+5tl zgJg+oL`iUdT1k0gQ7S`udAVL@UUqSEVnM22eo^}DcQ#TC3@mM)E{-7;bKYKb6l72o zII!XIAMa@Y4{nULDqL2VO^tsmKdzTOaCU-(a}rOdp~|sQ#%Lgnri9U4K;>M(r3EU|i#(P* zTe6oF-MS}Bfoz^SWb4$Si|76U*)lgl{(u@t9`%DrTst+gmXN?l@_XO=-j92~?CyNM zwD8%2VHiuT?dBfbsbS2WeKZf&#i!2};r4O5{X=dT%jd;^?$PS<=Z5j&dDK482f?>Y zsJOyI)sdA^oC4Y~*49TU7l%@#j_gH=Z@&EFPZLF43>>%Bg{b>6%ll!MTZE<=i zywF_#3ayP85X4e*G>QjF&PKjD=4D_Pud#{75Pj&I8%02L5bUB&l}Y4Ph$U>BAQvpB zP8*)*y6zoBZCb^49n+dctE}cS;-KkeLc}cWvc2ZLX*_uH&A!$t!+1CxR)*Dz%6gc1 zo`-FUDYXD$QW(-Z4i!4fYq!Lsx@{T&vz6ok4X~bzso8;3{LDBGtrYn@?N}<2ZcHe@+;DlWB{{)AEbyXH~NI8mm zPvSJ`nW!uz+f)OUL10i<9jgce)=F~C6Cqno--N6yQ4}%~R%^9d!?x3(@Nc*@tjY$rh~V6Ex?O2CoI0_%W7B%Q?g~of%>TPA zg7d`(@6A)3x?(8Uc9cWAPNrvKU;a3?1`!(10pp^WZ{HL}V`XSgUq@F|;AR<2Xor0X zr0;0=1eU9=9`a0X^q^Wd7$|(5{G1=ZTl~%1cb)i41NeU8r==Brp7bOvO>7qF;9|8* z8iOzQZ2awwH8g|6ZY#`kvBE>Snq>mi87$t5-(LL+8~>A5bE7?a`rM(rH;GRVgVt+!s~okEtIcX_N({K&-?xHXm#a!{^aaQgiyY|?5rU) z^&WULcLeAu>nU8I6VdWUg3!$C^qYEoVde}%(_j3?x?Fc}5GH~G^~5GG^n(b{2rVr3 zBg(e8#GAb3hqm(R#VZB-o~_(2ySf|Ic-vpziTSOal?L0{ViiwWyo?w61QY~ZQrr(Z zVM6-0GSo|ePp>rv4?pj?CTOx)sk$6Uw9!#*ICrTuLyEG;be zl<~EH=nRb1_Szhj{-N9>SR$ITM`K=XLAL&(p)_z`OJ{KuiS~1l!+(3`b^?cGkh0k( zfqh{r*yEEvk^9DUUeq}Go z&BAd^Q1xhTb|1lQI8MlP9=N^a{zhgsuam= literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yoxj5K> 0 0 - 520 - 500 + 580 + 420 @@ -18,14 +18,14 @@ - 520 - 500 + 580 + 420 - 520 - 500 + 580 + 420 @@ -54,14 +54,14 @@ - 100 - 500 + 50 + 420 - 100 - 500 + 50 + 420 @@ -106,14 +106,14 @@ - 100 - 500 + 50 + 420 - 100 - 500 + 50 + 420 @@ -134,14 +134,14 @@ - 320 - 10 + 480 + 50 - 320 - 10 + 480 + 50 @@ -164,14 +164,14 @@ - 320 - 10 + 480 + 50 - 320 - 10 + 480 + 50