diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index c9cd80fc72f..1fa28f6f7bf 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -47,6 +47,7 @@ jobs: matrix: target: - nv14 + - pl18 - t12 - t16 - t18 @@ -93,6 +94,7 @@ jobs: matrix: target: - nv14 + - pl18 - t12 - t16 - t18 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index adb4a706100..4b382bdc3ed 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -19,6 +19,7 @@ jobs: matrix: target: - nv14 + - pl18 - t12 - t16 - t18 diff --git a/cmake.sh b/cmake.sh deleted file mode 100755 index 5b956fe7187..00000000000 --- a/cmake.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -cmake --target simu "$@" diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 87baaaa6a84..dca9c5f3e70 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -109,6 +109,8 @@ uint32_t Boards::getFourCC(Type board) return 0x4378746F; case BOARD_FLYSKY_NV14: return 0x3A78746F; + case BOARD_FLYSKY_PL18: + return 0x4878746F; default: return 0; } @@ -153,6 +155,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: case BOARD_FLYSKY_NV14: + case BOARD_FLYSKY_PL18: return 0; default: return 0; @@ -196,6 +199,7 @@ int Boards::getFlashSize(Type board) case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: case BOARD_FLYSKY_NV14: + case BOARD_FLYSKY_PL18: return FSIZE_HORUS; case BOARD_UNKNOWN: return FSIZE_MAX; @@ -356,6 +360,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"}, @@ -436,6 +454,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 3; else if (IS_FLYSKY_NV14(board)) return 2; + else if (IS_FLYSKY_PL18(board)) + return 3; else return 3; @@ -448,7 +468,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case Sliders: if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(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; @@ -469,7 +489,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return getCapability(board, Board::Sticks) + getCapability(board, Board::Pots) + getCapability(board, Board::Sliders) + getCapability(board, Board::MouseAnalogs) + getCapability(board, Board::GyroAnalogs); case MultiposPots: - if (IS_HORUS_OR_TARANIS(board) && !IS_FLYSKY_NV14(board)) + if (IS_HORUS_OR_TARANIS(board) && !IS_FLYSKY_NV14(board) && !IS_FLYSKY_PL18(board)) return getCapability(board, Board::Pots); else return 0; @@ -494,6 +514,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 4; else if (board == BOARD_FLYSKY_NV14) return 8; + else if (board == BOARD_FLYSKY_PL18) + return 8; else if (board == BOARD_RADIOMASTER_TX12_MK2 || board == BOARD_RADIOMASTER_BOXER) return 6; else if (IS_FAMILY_T12(board)) @@ -527,7 +549,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)) + if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) return getCapability(board, Board::Switches) * 3; else return 9; @@ -537,7 +559,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case NumTrims: - if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(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; @@ -551,7 +575,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); @@ -749,6 +773,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(), { @@ -863,6 +895,8 @@ QString Boards::getBoardName(Board::Type board) return "Radiomaster T8"; case BOARD_FLYSKY_NV14: return "FlySky NV14"; + case BOARD_FLYSKY_PL18: + return "FlySky PL18"; case BOARD_BETAFPV_LR3PRO: return "BETAFPV LR3PRO"; case BOARD_IFLIGHT_COMMANDO8: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index c38b106013f..ecdd18b007d 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -68,6 +68,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, @@ -360,6 +361,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; @@ -437,7 +443,7 @@ 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*/; + return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board); } inline bool IS_HORUS_OR_TARANIS(Board::Type board) @@ -447,7 +453,7 @@ 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); + return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(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 1e9b7958a10..965ab737ba6 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -146,6 +146,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)) @@ -277,7 +279,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; @@ -286,6 +288,10 @@ 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; + } 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 71545c14c75..290c8d6aaee 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -144,7 +144,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); } @@ -180,7 +180,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; @@ -208,10 +208,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) @@ -2640,7 +2640,7 @@ class TopBarField: public StructField { TopBarField(DataField * parent, TopBarPersistentData & topBar, Board::Type board, unsigned int version): StructField(parent, "Top Bar") { - Append(new WidgetsContainerPersistentField(this, topBar, IS_FLYSKY_NV14(board) ? 2 : 4, MAX_TOPBAR_OPTIONS, board, version)); + Append(new WidgetsContainerPersistentField(this, topBar, (IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) ? 2 : 4, MAX_TOPBAR_OPTIONS, board, version)); //dump(); } }; @@ -2942,7 +2942,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) { @@ -2989,7 +2989,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) { @@ -3035,7 +3035,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)); } @@ -3099,11 +3099,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)); } @@ -3394,7 +3394,7 @@ void OpenTxGeneralData::afterImport() { if (IS_FAMILY_HORUS_OR_T16(board)) { if (version < 220) { // re-initialise as no conversion possible - const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX"; + const char * themeName = (IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) ? "FlySky" : "EdgeTX"; RadioTheme::init(themeName, generalData.themeData); } } diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index aff7f3909cd..6538feb7d5d 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -116,6 +116,8 @@ const char * OpenTxEepromInterface::getName() return "EdgeTX for FrSky X10 Express"; case BOARD_FLYSKY_NV14: return "EdgeTX for FlySky NV14"; + case BOARD_FLYSKY_PL18: + return "EdgeTX for FlySky PL18"; case BOARD_BETAFPV_LR3PRO: return "EdgeTx for BETAFPV LR3PRO"; case BOARD_IFLIGHT_COMMANDO8: @@ -658,6 +660,8 @@ int OpenTxFirmware::getCapability(::Capability capability) case LcdWidth: if (IS_FLYSKY_NV14(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)) @@ -669,6 +673,8 @@ int OpenTxFirmware::getCapability(::Capability capability) case LcdHeight: if (IS_FLYSKY_NV14(board)) return 480; + else if (IS_FLYSKY_PL18(board)) + return 320; else if (IS_FAMILY_HORUS_OR_T16(board)) return 272; else @@ -770,7 +776,7 @@ int OpenTxFirmware::getCapability(::Capability capability) return IS_FAMILY_HORUS_OR_T16(board) || IS_RADIOMASTER_ZORRO(board) || IS_JUMPER_TPRO(board) || IS_RADIOMASTER_TX12_MK2(board) || IS_RADIOMASTER_BOXER(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)) ? true : false; + 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_PL18(board)) ? true : false; case HasADCJitterFilter: return IS_HORUS_OR_TARANIS(board); case HasTelemetryBaudrate: @@ -1412,6 +1418,13 @@ void registerOpenTxFirmwares() addOpenTxRfOptions(firmware, FLEX + AFHDS3); registerOpenTxFirmware(firmware); + /* FlySky PL18 board */ + firmware = new OpenTxFirmware("opentx-pl18", QCoreApplication::translate("Firmware", "FlySky PL18"), BOARD_FLYSKY_PL18); + addOpenTxFrskyOptions(firmware); + firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); + registerOpenTxFirmware(firmware); + /* BETAFPV LR3PRO board */ firmware = new OpenTxFirmware(FIRMWAREID("lr3pro"), QCoreApplication::translate("Firmware", "BETAFPV LiteRadio3 Pro"), BOARD_BETAFPV_LR3PRO); addOpenTxCommonOptions(firmware); diff --git a/companion/src/images/simulator/PL18/bottom.png b/companion/src/images/simulator/PL18/bottom.png new file mode 100644 index 00000000000..5fc10a49d42 Binary files /dev/null and b/companion/src/images/simulator/PL18/bottom.png differ diff --git a/companion/src/images/simulator/PL18/left.png b/companion/src/images/simulator/PL18/left.png new file mode 100644 index 00000000000..6294e9922da Binary files /dev/null and b/companion/src/images/simulator/PL18/left.png differ diff --git a/companion/src/images/simulator/PL18/right.png b/companion/src/images/simulator/PL18/right.png new file mode 100644 index 00000000000..6294e9922da Binary files /dev/null and b/companion/src/images/simulator/PL18/right.png differ diff --git a/companion/src/images/simulator/PL18/top.png b/companion/src/images/simulator/PL18/top.png new file mode 100644 index 00000000000..5fc10a49d42 Binary files /dev/null and b/companion/src/images/simulator/PL18/top.png differ diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 34f190f42f9..eda9d398fc7 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -25,6 +25,7 @@ set(simulation_SRCS simulateduiwidgetT8.cpp simulateduiwidgetTX16S.cpp simulateduiwidgetNV14.cpp + simulateduiwidgetPL18.cpp simulatorinterface.cpp simulatormainwindow.cpp simulatorstartupdialog.cpp @@ -61,6 +62,7 @@ set(simulation_UIS simulateduiwidgetT8.ui simulateduiwidgetTX16S.ui simulateduiwidgetNV14.ui + simulateduiwidgetPL18.ui simulatormainwindow.ui simulatorstartupdialog.ui simulatorwidget.ui diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index e4eb209ff6d..8840c7f9698 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -124,6 +124,7 @@ namespace Ui { class SimulatedUIWidgetBoxer; class SimulatedUIWidgetT8; class SimulatedUIWidgetNV14; + class SimulatedUIWidgetPL18; } class SimulatedUIWidget9X: public SimulatedUIWidget @@ -394,4 +395,16 @@ class SimulatedUIWidgetNV14: public SimulatedUIWidget Ui::SimulatedUIWidgetNV14 * 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 de23e3d6c0f..0c7f2b31cd4 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -128,6 +128,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_FLYSKY_NV14: radioUiWidget = new SimulatedUIWidgetNV14(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 790bc202311..3fa97e2f4ec 100644 --- a/fw.json +++ b/fw.json @@ -3,6 +3,7 @@ ["BETAFPV LiteRadio 3 Pro", "lr3pro-"], ["Flysky EL18", "nv14-"], ["Flysky NV14", "nv14-"], + ["Flysky PL18", "pl18-"], ["FrSky Horus X10", "x10-"], ["FrSky Horus X10 Access", "x10-access-"], ["FrSky Horus X12s", "x12s-"], diff --git a/prepare.sh b/prepare.sh new file mode 100755 index 00000000000..5ac25ed83bc --- /dev/null +++ b/prepare.sh @@ -0,0 +1,20 @@ +#!/bin/bash +mkdir -p _Release +cd _Release + +cmake -DPCB=X10 -DPCBREV=TX16S -DDEFAULT_MODE=2 -DGVARS=YES -DPPM_UNIT=US -DHELI=YES -DLUA=YES -DINTERNAL_GPS=NO -DCMAKE_BUILD_TYPE=Release ../ + +cd .. +mkdir -p _Debug +cd _Debug +cmake -DPCB=X10 -DPCBREV=TX16S -DDEFAULT_MODE=2 -DGVARS=YES -DPPM_UNIT=US -DHELI=YES -DLUA=YES -DINTERNAL_GPS=NO -DCMAKE_BUILD_TYPE=Debug ../ + +cd .. +mkdir -p _NV14 +cd _NV14 +cmake -DPCB=NV14 -DDEFAULT_MODE=2 -DGVARS=YES -DPPM_UNIT=US -DHELI=YES -DLUA=YES -DINTERNAL_GPS=NO -DCMAKE_BUILD_TYPE=Debug ../ + +cd .. +mkdir -p _PL18 +cd _PL18 +cmake -DPCB=PL18 -DDEFAULT_MODE=2 -DGVARS=YES -DPPM_UNIT=US -DHELI=YES -DLUA=YES -DINTERNAL_GPS=NO -DCMAKE_BUILD_TYPE=Debug ../ diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 1e59d512c69..5f415528c5d 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 SK SE PL HU NL TW) set(TTS_LANGUAGES CN CZ DA DE EN ES FR HE IT JP PT SK SE PL HU NL RU) @@ -59,6 +59,7 @@ option(IMRC_RELEASE "Used to build IMRC released firmware" OFF) option(HARDWARE_TRAINER_MULTI "Allow multi trainer" OFF) option(BOOTLOADER "Include Bootloader" ON) option(FWDRIVE "Attach also firmware drive with USB" OFF) +option(LITTLEFS "Use LittleFS for internal flash" OFF) if(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) option(USBJ_EX "Enable USB Joystick Extension" OFF) @@ -95,6 +96,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() @@ -251,9 +254,19 @@ endif() if(SDCARD) add_definitions(-DSDCARD) + set(SRC ${SRC} sdcard.cpp logs.cpp) +endif() + +if(SPI_FLASH) + add_definitions(-DLFS_NO_ASSERT) + add_definitions(-DSPI_FLASH) +endif() + +if(SDCARD OR (SPI_FLASH AND NOT LITTLEFS)) include_directories(${FATFS_DIR} ${FATFS_DIR}/option) - set(SRC ${SRC} sdcard.cpp rtc.cpp logs.cpp thirdparty/libopenui/src/libopenui_file.cpp) + set(SRC ${SRC} rtc.cpp VirtualFS.cpp) set(FIRMWARE_SRC ${FIRMWARE_SRC} ${FATFS_SRC}) + add_definitions(-DUSE_FATFS) endif() if(SHUTDOWN_CONFIRMATION) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp new file mode 100644 index 00000000000..c0155ff2051 --- /dev/null +++ b/radio/src/VirtualFS.cpp @@ -0,0 +1,1479 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "strhelpers.h" +#if !defined(BOOT) +#include "opentx.h" +#endif +#include "edgetx_assert.h" +#include "VirtualFS.h" +#include "board.h" +#include "audio.h" +#include "disk_cache.h" +#include "debug.h" + +#if defined(LIBOPENUI) && 0 + #include "libopenui.h" +#endif + +VirtualFS* VirtualFS::_instance = nullptr;; + +#if defined(SDCARD) +static FATFS sdFatFs __DMA; // initialized in boardInit() + +#if defined(LOG_TELEMETRY) +VfsFile g_telemetryFile = {}; +#endif + +#if defined(LOG_BLUETOOTH) +VfsFile g_bluetoothFile = {}; +#endif +#endif + +#if defined(SPI_FLASH) +static FATFS spiFatFs __DMA; +#endif + + +#if !defined(CLIPBOARD_PATH_LEN) +#define CLIPBOARD_PATH_LEN 32 +#endif + +static VfsError convertResult(FRESULT err) +{ + switch(err) + { + case FR_OK: return VfsError::OK; + case FR_DISK_ERR: return VfsError::IO; + case FR_INT_ERR: return VfsError::INVAL; + case FR_NOT_READY: return VfsError::NOT_READY; + case FR_NO_FILE: return VfsError::NOENT; + case FR_NO_PATH: return VfsError::NOENT; + case FR_INVALID_NAME: return VfsError::INVAL; + case FR_DENIED: return VfsError::INVAL; + case FR_EXIST: return VfsError::EXIST; + case FR_INVALID_OBJECT: return VfsError::BADF; + case FR_WRITE_PROTECTED: return VfsError::INVAL; + case FR_INVALID_DRIVE: return VfsError::INVAL; + case FR_NOT_ENABLED: return VfsError::INVAL; + case FR_NO_FILESYSTEM: return VfsError::INVAL; + case FR_MKFS_ABORTED: return VfsError::INVAL; + case FR_TIMEOUT: return VfsError::INVAL; + case FR_LOCKED: return VfsError::INVAL; + case FR_NOT_ENOUGH_CORE: return VfsError::INVAL; + case FR_TOO_MANY_OPEN_FILES: return VfsError::INVAL; + case FR_INVALID_PARAMETER: return VfsError::INVAL; + } + return VfsError::INVAL; +} + +static int convertOpenFlagsToFat(VfsOpenFlags flags) +{ + return (int)flags; +} + +VfsOpenFlags operator|(VfsOpenFlags lhs,VfsOpenFlags rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + | static_cast(rhs)); +} + +VfsOpenFlags operator|=(VfsOpenFlags lhs,VfsOpenFlags rhs) +{ + lhs = lhs|rhs; + return lhs; +} + +VfsOpenFlags operator&(VfsOpenFlags lhs,VfsOpenFlags rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + & static_cast(rhs)); +} + +VfsFileAttributes operator|(VfsFileAttributes lhs,VfsFileAttributes rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + | static_cast(rhs)); +} + +VfsFileAttributes operator&(VfsFileAttributes lhs,VfsFileAttributes rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + & static_cast(rhs)); +} + +const char* VfsFileInfo::getName() const +{ + switch(type) + { + case VfsFileType::ROOT: return name; + case VfsFileType::FAT: return(name != nullptr)?name:fatInfo.fname; + default: break; + } + return ""; +}; + +size_t VfsFileInfo::getSize() const +{ + switch(type) + { + case VfsFileType::ROOT: return 0; + case VfsFileType::FAT: return fatInfo.fsize; + default: break; + } + return 0; +} + +VfsType VfsFileInfo::getType() const +{ + switch(type) + { + case VfsFileType::ROOT: + return VfsType::DIR; + case VfsFileType::FAT: + if (name != nullptr) + return VfsType::DIR; + if(fatInfo.fattrib & AM_DIR) + return VfsType::DIR; + else + return VfsType::FILE; + default: break; + } + return VfsType::UNKOWN; +}; + +VfsFileAttributes VfsFileInfo::getAttrib() +{ + switch(type) + { + case VfsFileType::ROOT: + return VfsFileAttributes::DIR; + case VfsFileType::FAT: + return (VfsFileAttributes)fatInfo.fattrib; + default: break; + } + return VfsFileAttributes::NONE; +} + +int VfsFileInfo::getDate(){ + switch(type) + { + case VfsFileType::ROOT: + return 0; + case VfsFileType::FAT: + return fatInfo.fdate; + default: break; + } + return 0; +} + +int VfsFileInfo::getTime() +{ + switch(type) + { + case VfsFileType::ROOT: + return 0; + case VfsFileType::FAT: + return fatInfo.ftime; + default: break; + } + return 0; +} + +void VfsFileInfo::clear() { + type = VfsFileType::UNKNOWN; + fatInfo = {0}; + name = nullptr; +} + + +void VfsDir::clear() +{ + type = DIR_UNKNOWN; + fat.dir = {0}; + + readIdx = 0; +} + +VfsError VfsDir::read(VfsFileInfo& info) +{ + info.clear(); + switch(type) + { + case VfsDir::DIR_ROOT: + info.type = VfsFileType::ROOT; +#if defined(SPI_FLASH) + if(readIdx == 0) + info.name = "INTERNAL"; +#if defined(SDCARD) + else if(readIdx == 1) + info.name = "SDCARD"; +#endif + else + info.name = ""; +#elif defined(SDCARD) // SPI_FLASH + if(readIdx == 0) + info.name = "SDCARD"; + else + info.name = ""; +#endif // SDCARD + readIdx++; + return VfsError::OK; + case VfsDir::DIR_FAT: + { + info.type = VfsFileType::FAT; + if(readIdx == 0) // emulate ".." entry + { + readIdx++; + info.name = ".."; + return VfsError::OK; + } + VfsError ret = convertResult(f_readdir(&fat.dir, &info.fatInfo)); + return ret; + } + default: break; + } + return VfsError::INVAL; +} + +VfsError VfsDir::close() +{ + VfsError ret = VfsError::INVAL; + switch(type) + { + case VfsDir::DIR_ROOT: + ret = VfsError::OK; + break; + case VfsDir::DIR_FAT: + ret = convertResult(f_closedir(&fat.dir)); + break; + default: break; + } + clear(); + return ret; +} + +VfsError VfsDir::rewind() +{ + readIdx = 0; + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::OK; + case VfsDir::DIR_FAT: + { + return convertResult(f_readdir(&fat.dir, nullptr)); + } + default: break; + } + return VfsError::INVAL; +} + +void VfsFile::clear() { + type = VfsFileType::UNKNOWN; + fat.file = {0}; +} + +VfsError VfsFile::close() +{ + VfsError ret = VfsError::INVAL; + switch(type) + { + case VfsFileType::FAT: + ret = convertResult(f_close(&fat.file)); + break; + default: break; + } + + clear(); + return ret; +} + +int VfsFile::size() +{ + switch(type) + { + case VfsFileType::FAT: + return f_size(&fat.file); + break; + default: break; + } + + return -1; +} + +VfsError VfsFile::read(void* buf, size_t size, size_t& readSize) +{ + switch(type) + { + case VfsFileType::FAT: { + UINT rd = 0; + VfsError res = convertResult(f_read(&fat.file, buf, size, &rd)); + readSize = rd; + return res; + } + default: break; + } + + return VfsError::INVAL; +} + +char* VfsFile::gets(char* buf, size_t maxLen) +{ + switch(type) + { + case VfsFileType::FAT: + return f_gets(buf, maxLen, &fat.file); + default: break; + } + + return 0; +} + +#if !defined(BOOT) +VfsError VfsFile::write(const void* buf, size_t size, size_t& written) +{ + switch(type) + { + case VfsFileType::FAT: { + UINT wrt = 0; + VfsError res = convertResult(f_write(&fat.file, buf, size, &wrt)); + written = wrt; + return res; + } + default: break; + } + + return VfsError::INVAL; +} + +VfsError VfsFile::puts(const std::string& str) +{ + size_t written; + return this->write(str.data(), str.length(), written); +} + +VfsError VfsFile::putc(char c) +{ + size_t written; + return this->write(&c, 1, written); +} + +int VfsFile::fprintf(const char* str, ...) +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + { + char line[256]; + va_list args; + va_start(args, str); + vsprintf(line, str, args); + va_end(args); + + size_t written; + this->write(line, strlen(line), written); + return written; + } +#endif + default: break; + } + + return (int)VfsError::INVAL; +} +#endif + +size_t VfsFile::tell() +{ + switch(type) + { + case VfsFileType::FAT: + return f_tell(&fat.file); + default: break; + } + + return (size_t)VfsError::INVAL; +} + +VfsError VfsFile::lseek(size_t offset) +{ + switch(type) + { + case VfsFileType::FAT: + return convertResult(f_lseek(&fat.file, offset)); + break; + default: break; + } + + return VfsError::INVAL; +} + +int VfsFile::eof() +{ + switch(type) + { + case VfsFileType::FAT: + return f_eof(&fat.file); + default: break; + } + + return 0; +} + +const char * VirtualFS::getBasename(const char * path) +{ + for (int8_t i = strlen(path) - 1; i >= 0; i--) { + if (path[i] == '/') { + return &path[i + 1]; + } + } + return path; +} + +VirtualFS::VirtualFS() +{ +#if defined (SPI_FLASH) + + spiFatFs = {0}; +#endif // SPI_FLASH +#if defined (SDCARD) + sdFatFs = {0}; +#endif + + restart(); +} + +VirtualFS::~VirtualFS() +{ +#if defined (SPI_FLASH) + f_unmount("1:"); +#endif // SPI_FLASH +} + +void VirtualFS::stop() +{ + stopLogs(); + audioQueue.stopSD(); +#if defined (SDCARD) + if (sdCardMounted()) { + f_mount(nullptr, "", 0); // unmount SD + } +#endif + +#if defined (SPI_FLASH) + f_unmount("1:"); +#endif // SPI_FLASH +} +void VirtualFS::restart() +{ + TRACE("VirtualFS::restart()"); +#if defined (SDCARD) + mountSd(); +#endif +#if defined (SPI_FLASH) +#if !defined(BOOT) + diskCache[1].clear(); +#endif + if(f_mount(&spiFatFs, "1:", 1) != FR_OK) + { +#if !defined(BOOT) + BYTE work[FF_MAX_SS]; + FRESULT res = f_mkfs("1:", FM_ANY, 0, work, sizeof(work)); +#if !defined(BOOT) + switch(res) { + case FR_OK : + break; + case FR_DISK_ERR: + POPUP_WARNING("Format error"); + break; + case FR_NOT_READY: + POPUP_WARNING("Flash not ready"); + break; + case FR_WRITE_PROTECTED: + POPUP_WARNING("Flash write protected"); + break; + case FR_INVALID_PARAMETER: + POPUP_WARNING("Format param invalid"); + break; + case FR_INVALID_DRIVE: + POPUP_WARNING("Invalid drive"); + break; + case FR_MKFS_ABORTED: + POPUP_WARNING("Format aborted"); + break; + default: + POPUP_WARNING(STR_SDCARD_ERROR); + break; + } +#endif + if(f_mount(&spiFatFs, "1:", 1) != FR_OK) + { +#if !defined(BOOT) + POPUP_WARNING(STR_SDCARD_ERROR); +#endif + } +#endif + } +#endif // SPI_FLASH +#if !defined(BOOT) + checkAndCreateDirectory("/DEFAULT/RADIO"); + checkAndCreateDirectory("/DEFAULT/MODELS"); + checkAndCreateDirectory("/DEFAULT/LOGS"); + checkAndCreateDirectory("/DEFAULT/SCREENSHOTS"); + checkAndCreateDirectory("/DEFAULT/BACKUP"); +#endif + startLogs(); +} + +void VirtualFS::mountSd() +{ +#if defined(SDCARD) + if(sdCardMounted()) + return; + +#if defined(DISK_CACHE) && !defined(BOOT) + diskCache[0].clear(); +#endif + + if (f_mount(&sdFatFs, "", 1) == FR_OK) { +#if(!defined(BOOT)) + // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called + sdGetFreeSectors(); +#endif + } + else { + TRACE("SD Card f_mount() failed"); + } +#endif +} + +bool VirtualFS::defaultStorageAvailable() +{ +#if defined (SIMU) + return true; +#endif +#if (DEFAULT_STORAGE == INTERNAL) + return spiFatFs.fs_type != 0; +#elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE + return sdFatFs.fs_type != 0; +#endif + +} +#if !defined(BOOT) +bool VirtualFS::format() +{ +// TODO format + return false; +// BYTE work[FF_MAX_SS]; +// FRESULT res = f_mkfs("", FM_FAT32, 0, work, sizeof(work)); +// switch(res) { +// case FR_OK : +// return true; +// case FR_DISK_ERR: +// POPUP_WARNING("Format error"); +// return false; +// case FR_NOT_READY: +// POPUP_WARNING("SDCard not ready"); +// return false; +// case FR_WRITE_PROTECTED: +// POPUP_WARNING("SDCard write protected"); +// return false; +// case FR_INVALID_PARAMETER: +// POPUP_WARNING("Format param invalid"); +// return false; +// case FR_INVALID_DRIVE: +// POPUP_WARNING("Invalid drive"); +// return false; +// case FR_MKFS_ABORTED: +// POPUP_WARNING("Format aborted"); +// return false; +// default: +// POPUP_WARNING(STR_SDCARD_ERROR); +// return false; +// } +} +#endif + +VfsDir::DirType VirtualFS::getDirTypeAndPath(char* path) +{ + char tmpPath[2 * CLIPBOARD_PATH_LEN+1] = { 0 }; + char* tmp = &tmpPath[0]; + size_t pLen = strlen(path); + + if(strcmp(PATH_SEPARATOR, path) == 0) + { + return VfsDir::DIR_ROOT; +#if defined (SPI_FLASH) + } else if(strncmp("/INTERNAL", path, 9) == 0) + { + tmp = strAppend(tmpPath, "1:", 2); + if(pLen > 9) + strAppend(tmp, path+9, pLen-9); + else + *tmp = '/'; + strcpy(path, tmpPath); + return VfsDir::DIR_FAT; +#endif // SPI_FLASH +#if defined (SDCARD) + } else if(strncmp("/SDCARD", path, 7) == 0) { + if(pLen > 7) + strAppend(tmp, path+7, pLen-7); + else + *tmp = '/'; + strcpy(path, tmpPath); + return VfsDir::DIR_FAT; +#endif + } else if(strncmp("/DEFAULT", path, 8) == 0) { +#if (DEFAULT_STORAGE == INTERNAL) + tmp = strAppend(tmpPath, "1:", 2); + if(pLen > 8) + strAppend(tmp, path+8, pLen-8); + else + *tmp = '/'; + strcpy(path, tmpPath); + return VfsDir::DIR_FAT; +#elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE + if(pLen > 8) + strAppend(tmp, path+8, pLen-8); + else + *tmp = '/'; + strcpy(path, tmpPath); + return VfsDir::DIR_FAT; +#else // DEFAULT_STORAGE + #error No valid default storage selectd +#endif + } + return VfsDir::DIR_UNKNOWN; +} + +void VirtualFS::normalizePath(char* path) +{ + char buf[2 * CLIPBOARD_PATH_LEN+1]; + char* wPtr = buf; + + if(path[0] != '/') + { + wPtr = strAppend(wPtr, curWorkDir.c_str(), curWorkDir.length()); + *wPtr++ = PATH_SEPARATOR[0]; + } + + wPtr = strAppend(wPtr, path, std::min(strlen(path), sizeof(buf) - (wPtr - buf))); + buf[sizeof(buf)-1] = '\0'; + char* tokens[20]; + size_t tokenCount = 0; + + char* cur = strtok(buf, "/" ); + while(cur) + { + char* old = cur; + cur = strtok(nullptr, "/"); + if(old[0] == 0) + continue; + if(strcmp("..", old) == 0) + { + if(tokenCount > 0) + tokenCount--; + continue; + } + if(strcmp(".", old) == 0) + continue; + + tokens[tokenCount++] = old; + if(tokenCount >= sizeof(tokens)) + break; + } + + wPtr = path; + for(size_t i = 0; i < tokenCount; i++) + { + *wPtr++ = PATH_SEPARATOR[0]; + wPtr = strAppend(wPtr, tokens[i], strlen(tokens[i])); + } + *wPtr = '\0'; + if(path[0] == '\0') + { + path[0] = PATH_SEPARATOR[0]; + path[1] = '\0'; + } +} + +#if !defined(BOOT) +VfsError VirtualFS::unlink(const char* path) +{ + if(path == nullptr) + return VfsError::INVAL; + + char p[CLIPBOARD_PATH_LEN + 1]; + strncpy(p, path, sizeof(p)); + p[sizeof(p)-1] = '\0'; + + normalizePath(p); + VfsDir::DirType type = getDirTypeAndPath(p); + + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + case VfsDir::DIR_FAT: + return convertResult(f_unlink(p)); + } + + return VfsError::INVAL; +} +#endif + +VfsError VirtualFS::changeDirectory(const char* path) +{ + assert(path != nullptr); + + char p[CLIPBOARD_PATH_LEN + 1]; + strncpy(p, path, sizeof(p)); + p[sizeof(p)-1] = '\0'; + normalizePath(p); + curWorkDir = p; + + return VfsError::OK; +} + +VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) +{ + dir.clear(); + assert(path != nullptr); + + char dirPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(dirPath, path, sizeof(dirPath)); + dirPath[sizeof(dirPath)-1] = '\0'; + + normalizePath(dirPath); + + VfsDir::DirType type = getDirTypeAndPath(dirPath); + dir.type = type; + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::OK; + case VfsDir::DIR_FAT: + return convertResult(f_opendir(&dir.fat.dir, dirPath)); + default: break; + } + + return VfsError::INVAL; +} +#if !defined(BOOT) +VfsError VirtualFS::makeDirectory(const char* path) +{ + assert(path != nullptr); + + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; + + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + break; + case VfsDir::DIR_FAT: + { + DIR dir; + FRESULT result = f_opendir(&dir, normPath); + if (result == FR_OK) { + f_closedir(&dir); + return VfsError::OK; + } else { + if (result == FR_NO_PATH) + result = f_mkdir(normPath); + if (result != FR_OK) + return convertResult(result); + } + break; + } + default: break; + } + return VfsError::INVAL; +} +VfsError VirtualFS::rename(const char* oldPath, const char* newPath) +{ + assert(oldPath != nullptr); + assert(newPath != nullptr); + + char oldP[CLIPBOARD_PATH_LEN + 1]; + strncpy(oldP, oldPath, sizeof(oldP)); + oldP[sizeof(oldP)-1] = '\0'; + char newP[CLIPBOARD_PATH_LEN + 1]; + strncpy(newP, newPath, sizeof(newP)); + newP[sizeof(newP)-1] = '\0'; + + normalizePath(oldP); + normalizePath(newP); + + VfsDir::DirType oldType = getDirTypeAndPath(oldP); + VfsDir::DirType newType = getDirTypeAndPath(newP); + + if(oldType == newType) + { + switch(oldType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + case VfsDir::DIR_FAT: + return convertResult(f_rename(oldP, newP)); + } + } else { + VfsError err = copyFile(oldPath, newPath); + if(err == VfsError::OK) + return unlink(oldPath); + return err; + } + return VfsError::INVAL; +} + +VfsError VirtualFS::copyFile(const char* source, const char* destination) +{ + VfsFile src; + VfsFile dest; + + VfsError err = openFile(src, source, VfsOpenFlags::READ); + if(err != VfsError::OK) + return err; + + err = openFile(dest, destination, VfsOpenFlags::CREATE_NEW|VfsOpenFlags::WRITE); + if(err != VfsError::OK) + { + src.close(); + return err; + } + + err = VfsError::OK; + + char buf[256] = {0}; + size_t readBytes = 0; + size_t written = 0; + + err = src.read(buf, sizeof(buf), readBytes); + if(err != VfsError::OK) + goto cleanup; + + while(readBytes) + { + err = dest.write(buf, readBytes, written); + if(err != VfsError::OK) + goto cleanup; + err = src.read(buf, sizeof(buf), readBytes); + if(err != VfsError::OK) + goto cleanup; + } + +cleanup: + src.close(); + dest.close(); + return err; +} + +VfsError VirtualFS::copyFile(const char* srcFile, const char* srcDir, + const char* destDir, const char* destFile) +{ + assert(srcFile != nullptr); + assert(srcDir != nullptr); + assert(destFile != nullptr); + assert(destDir != nullptr); + + char srcPath[2*CLIPBOARD_PATH_LEN+1] = {0}; + char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); + *tmp++ = '/'; + strAppend(tmp, srcFile, CLIPBOARD_PATH_LEN); + + char destPath[2*CLIPBOARD_PATH_LEN+1] = {0}; + tmp = strAppend(destPath, destDir, CLIPBOARD_PATH_LEN); + *tmp++ = '/'; + strAppend(tmp, destFile, CLIPBOARD_PATH_LEN); + + return copyFile(srcPath, destPath); +} + +// Will overwrite if destination exists +const char * VirtualFS::moveFile(const char* srcPath, const char* destPath) +{ + assert(srcPath != nullptr); + assert(destPath != nullptr); + + auto res = copyFile(srcPath, destPath); + if(res != VfsError::OK) { + return STORAGE_ERROR(res); + } + + res = unlink(srcPath); + if(res != VfsError::OK) { + return STORAGE_ERROR(res); + } + return nullptr; +} + +// Will overwrite if destination exists +const char * VirtualFS::moveFile(const char* srcFilename, const char* srcDir, const char* destFilename, const char* destDir) +{ + auto res = copyFile(srcFilename, srcDir, destFilename, destDir); + if(res != VfsError::OK) { + return STORAGE_ERROR(res); + } + + char srcPath[2*CLIPBOARD_PATH_LEN+1] = { 0 }; + char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); + *tmp++ = '/'; + strAppend(tmp, srcFilename, CLIPBOARD_PATH_LEN); + res = unlink(srcPath); + if(res != VfsError::OK) { + return STORAGE_ERROR(res); + } + return nullptr; +} +#endif // !BOOT +#if !defined(SIMU) || defined(SIMU_DISKIO) +uint32_t sdGetNoSectors() +{ + static DWORD noSectors = 0; + if (noSectors == 0 ) { + disk_ioctl(0, GET_SECTOR_COUNT, &noSectors); + } + return noSectors; +} + +#endif +VfsError VirtualFS::fstat(const char* path, VfsFileInfo& fileInfo) +{ + assert(path != nullptr); + + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; + + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + case VfsDir::DIR_FAT: + fileInfo.type = VfsFileType::FAT; + return convertResult(f_stat(normPath, &fileInfo.fatInfo)); + default: break; + } + return VfsError::INVAL; +} +#if !defined(BOOT) +VfsError VirtualFS::utime(const char* path, const VfsFileInfo& fileInfo) +{ + assert(path != nullptr); + + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; + + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + case VfsDir::DIR_FAT: + return convertResult(f_utime(normPath, &fileInfo.fatInfo)); + default: break; + } + return VfsError::INVAL; +} +#endif +VfsError VirtualFS::openFile(VfsFile& file, const char* path, VfsOpenFlags flags) +{ + assert(path != nullptr); + + file.clear(); + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + VfsError ret = VfsError::INVAL; + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + break; + case VfsDir::DIR_FAT: + { + file.type = VfsFileType::FAT; + ret = convertResult(f_open(&file.fat.file, normPath, convertOpenFlagsToFat(flags))); + break; + } + default: break; + } + + return ret; +} +#if !defined(BOOT) +const char* VirtualFS::checkAndCreateDirectory(const char * path) +{ + VfsError res = makeDirectory(path); + + if(res == VfsError::OK) + return nullptr; +#if !defined(BOOT) + return STORAGE_ERROR(res); +#else + return "could not create directory"; +#endif +} + +bool VirtualFS::isFileAvailable(const char * path, bool exclDir) +{ + assert(path != nullptr); + + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; + + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return false; + break; + case VfsDir::DIR_FAT: + { + if (exclDir) { + FILINFO fno; + return (f_stat(normPath, &fno) == FR_OK && !(fno.fattrib & AM_DIR)); + } + return f_stat(normPath, nullptr) == FR_OK; + } + default: break; + } + + return false; +} + +const char * VirtualFS::getFileExtension(const char * filename, uint8_t size, uint8_t extMaxLen, uint8_t * fnlen, uint8_t * extlen) +{ + assert(filename != nullptr); + + int len = size; + if (!size) { + len = strlen(filename); + } + if (!extMaxLen) { + extMaxLen = LEN_FILE_EXTENSION_MAX; + } + if (fnlen != nullptr) { + *fnlen = (uint8_t)len; + } + for (int i=len-1; i >= 0 && len-i <= extMaxLen; --i) { + if (filename[i] == '.') { + if (extlen) { + *extlen = len-i; + } + return &filename[i]; + } + } + if (extlen != nullptr) { + *extlen = 0; + } + return nullptr; +} + +/** + Check if given extension exists in a list of extensions. + @param extension The extension to search for, including leading period. + @param pattern One or more file extensions concatenated together, including the periods. + The list is searched backwards and the first match, if any, is returned. + eg: ".gif.jpg.jpeg.png" + @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). + @retval true if a extension was found in the lost, false otherwise. +*/ +bool VirtualFS::isFileExtensionMatching(const char * extension, const char * pattern, char * match) +{ + assert(extension != nullptr); + assert(pattern != nullptr); + + const char *ext; + uint8_t extlen, fnlen; + int plen; + + ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); + plen = (int)fnlen; + while (plen > 0 && ext) { + if (!strncasecmp(extension, ext, extlen)) { + if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); + return true; + } + plen -= extlen; + if (plen > 0) { + ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); + } + } + return false; +} + + +/** + Search file system path for a file. Can optionally take a list of file extensions to search through. + Eg. find "splash.bmp", or the first occurrence of one of "splash.[bmp|jpeg|jpg|gif]". + + @param path String with path name, no trailing slash. eg; "/BITMAPS" + @param file String containing file name to search for, with or w/out an extension. + eg; "splash.bmp" or "splash" + @param pattern Optional list of one or more file extensions concatenated together, including the period(s). + The list is searched backwards and the first match, if any, is returned. If null, then only the actual filename + passed will be searched for. + eg: ".gif.jpg.jpeg.bmp" + @param exclDir true/false whether to allow directory matches (default true, excludes directories) + @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). + @retval true if a file was found, false otherwise. +*/ +bool VirtualFS::isFilePatternAvailable(const char * path, const char * file, const char * pattern, bool exclDir, char * match) +{ + uint8_t fplen; + char fqfp[LEN_FILE_PATH_MAX + FF_MAX_LFN + 1] = "\0"; + + fplen = strlen(path); + if (fplen > LEN_FILE_PATH_MAX) { + //TRACE_ERROR("isFilePatternAvailable(%s) = error: path too long.\n", path); + return false; + } + + strcpy(fqfp, path); + strcpy(fqfp + fplen, "/"); + strncat(fqfp + (++fplen), file, FF_MAX_LFN); + + if (pattern == nullptr) { + // no extensions list, just check the filename as-is + return isFileAvailable(fqfp, exclDir); + } + else { + // extensions list search + const char *ext; + uint16_t len; + uint8_t extlen, fnlen; + int plen; + + getFileExtension(file, 0, 0, &fnlen, &extlen); + len = fplen + fnlen - extlen; + fqfp[len] = '\0'; + ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); + plen = (int)fnlen; + while (plen > 0 && ext) { + strncat(fqfp + len, ext, extlen); + if (isFileAvailable(fqfp, exclDir)) { + if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); + return true; + } + plen -= extlen; + if (plen > 0) { + fqfp[len] = '\0'; + ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); + } + } + } + return false; +} + +char* VirtualFS::getFileIndex(char * filename, unsigned int & value) +{ + value = 0; + char * pos = (char *)getFileExtension(filename); + if (!pos || pos == filename) + return nullptr; + int multiplier = 1; + while (pos > filename) { + pos--; + char c = *pos; + if (c >= '0' && c <= '9') { + value += multiplier * (c - '0'); + multiplier *= 10; + } + else { + return pos+1; + } + } + return filename; +} + +static uint8_t _getDigitsCount(unsigned int value) +{ + uint8_t count = 1; + while (value >= 10) { + value /= 10; + ++count; + } + return count; +} + +unsigned int VirtualFS::findNextFileIndex(char * filename, uint8_t size, const char * directory) +{ + unsigned int index = 0; + uint8_t extlen; + char * indexPos = getFileIndex(filename, index); + char extension[LEN_FILE_EXTENSION_MAX+1] = "\0"; + char * p = (char *)getFileExtension(filename, 0, 0, nullptr, &extlen); + if (p) strncat(extension, p, sizeof(extension)-1); + while (true) { + index++; + if ((indexPos - filename) + _getDigitsCount(index) + extlen > size) { + return 0; + } + char * pos = strAppendUnsigned(indexPos, index); + strAppend(pos, extension); + if (!isFilePatternAvailable(directory, filename, nullptr, false)) { + return index; + } + } + return 0; +} +#endif +#if !defined(LIBOPENUI) && !defined(BOOT) +bool VirtualFS::listFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags) +{ + static uint16_t lastpopupMenuOffset = 0; + VfsFileInfo fno; + VfsDir dir; + const char * fnExt; + uint8_t fnLen, extLen; + char tmpExt[LEN_FILE_EXTENSION_MAX+1] = "\0"; + + popupMenuOffsetType = MENU_OFFSET_EXTERNAL; + + static uint8_t s_last_flags; + + if (selection) { + s_last_flags = flags; + if (!isFilePatternAvailable(path, selection, ((flags & LIST_SD_FILE_EXT) ? nullptr : extension))) selection = nullptr; + } + else { + flags = s_last_flags; + } + + if (popupMenuOffset == 0) { + lastpopupMenuOffset = 0; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (popupMenuOffset == popupMenuItemsCount - MENU_MAX_DISPLAY_LINES) { + lastpopupMenuOffset = 0xffff; + memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); + } + else if (popupMenuOffset == lastpopupMenuOffset) { + // should not happen, only there because of Murphy's law + return true; + } + else if (popupMenuOffset > lastpopupMenuOffset) { + memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0xff, MENU_LINE_LENGTH); + } + else { + memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + } + + popupMenuItemsCount = 0; + + VfsError res = openDirectory(dir, path); + if (res == VfsError::OK) { + + if (flags & LIST_NONE_SD_FILE) { + popupMenuItemsCount++; + if (selection) { + lastpopupMenuOffset++; + } + else if (popupMenuOffset==0 || popupMenuOffset < lastpopupMenuOffset) { + char * line = reusableBuffer.modelsel.menu_bss[0]; + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, "---"); + popupMenuItems[0] = line; + } + } + + for (;;) { + res = dir.read(fno); /* Read a directory item */ + if (res != VfsError::OK || strlen(fno.getName()) == 0) break; /* Break on error or end of dir */ + if (fno.getType() == VfsType::DIR) continue; /* Skip subfolders */ + if ((int)(fno.getAttrib() & VfsFileAttributes::HID) != 0) continue; /* Skip hidden files */ + if ((int)(fno.getAttrib() & VfsFileAttributes::SYS) != 0) continue; /* Skip system files */ + + fnExt = getFileExtension(fno.getName(), 0, 0, &fnLen, &extLen); + fnLen -= extLen; + +// TRACE_DEBUG("listSdFiles(%s, %s, %u, %s, %u): fn='%s'; fnExt='%s'; match=%d\n", +// path, extension, maxlen, (selection ? selection : "nul"), flags, fname.c_str(), (fnExt ? fnExt : "nul"), (fnExt && isExtensionMatching(fnExt, extension))); + // file validation checks + if (!fnLen || fnLen > maxlen || ( // wrong size + fnExt && extension && ( // extension-based checks follow... + !isFileExtensionMatching(fnExt, extension) || ( // wrong extension + !(flags & LIST_SD_FILE_EXT) && // only if we want unique file names... + strcasecmp(fnExt, getFileExtension(extension)) && // possible duplicate file name... + isFilePatternAvailable(path, fno.getName(), extension, true, tmpExt) && // find the first file from extensions list... + strncasecmp(fnExt, tmpExt, LEN_FILE_EXTENSION_MAX) // found file doesn't match, this is a duplicate + ) + ) + )) + { + continue; + } + + popupMenuItemsCount++; + + std::string fname = fno.getName(); + if (!(flags & LIST_SD_FILE_EXT)) { + fname = fname.substr(0,fnLen); // strip extension + } + + if (popupMenuOffset == 0) { + if (selection && strncasecmp(fname.c_str(), selection, maxlen) < 0) { + lastpopupMenuOffset++; + } + else { + for (uint8_t i=0; i=0; i--) { + char * line = reusableBuffer.modelsel.menu_bss[i]; + if (line[0] == '\0' || strcasecmp(fname.c_str(), line) > 0) { + if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); + memset(line, 0, MENU_LINE_LENGTH); + strcpy(line, fname.c_str()); + break; + } + } + for (uint8_t i=0; i lastpopupMenuOffset) { + if (strcasecmp(fname.c_str(), reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-2]) > 0 && strcasecmp(fname.c_str(), reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1]) < 0) { + memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], fname.c_str()); + } + } + else { + if (strcasecmp(fname.c_str(), reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fname.c_str(), reusableBuffer.modelsel.menu_bss[0]) > 0) { + memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); + strcpy(reusableBuffer.modelsel.menu_bss[0], fname.c_str()); + } + } + } + dir.close(); + } + + if (popupMenuOffset > 0) + lastpopupMenuOffset = popupMenuOffset; + else + popupMenuOffset = lastpopupMenuOffset; + + return popupMenuItemsCount; + return 0; +} + +#endif // !LIBOPENUI + +bool VirtualFS::sdCardMounted() +{ +#if defined (SDCARD) + return sdFatFs.fs_type != 0; +#else + return false; +#endif +} +size_t VirtualFS::sdGetFreeSectors() +{ +#if defined (SDCARD) + return ::sdGetFreeSectors(); +#else + return 0; +#endif +} + +void VirtualFS::startLogs() +{ + if(!sdCardMounted()) + return; +#if defined(SDCARD) +#if defined(LOG_TELEMETRY) + openFile(g_telemetryFile, LOGS_PATH "/telemetry.log", VfsOpenFlags::OPEN_ALWAYS | VfsOpenFlagsWRITE); + if (g_telemetryFile.size() > 0) { + g_telemetryFile.lseek(g_telemetryFile.size()); // append + } +#endif + +#if defined(LOG_BLUETOOTH) + openFile(g_bluetoothFile, LOGS_PATH "/bluetooth.log", VfsOpenFlags::OPEN_ALWAYS | VfsOpenFlagsWRITE); + if (&g_bluetoothFile.size() > 0) { + g_bluetoothFile.lseek(g_bluetoothFile.size()); // append + } +#endif +#endif +} + +void VirtualFS::stopLogs() +{ +#if defined(SDCARD) +#if defined(LOG_TELEMETRY) + g_telemetryFile.close(); +#endif + +#if defined(LOG_BLUETOOTH) + g_bluetoothFile.close(); +#endif +#endif +} diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h new file mode 100644 index 00000000000..0b01fae89ad --- /dev/null +++ b/radio/src/VirtualFS.h @@ -0,0 +1,435 @@ +/* + * 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 _VIRTUALFS_H_ +#define _VIRTUALFS_H_ + +#include + +#include "frftl.h" +#include "FatFs/ff.h" +#if defined (SDCARD) +#include "sdcard.h" +#endif + +#include "translations.h" + +#if defined(PCBX12S) + #define ETX_FOURCC 0x3478746F // etx for X12S +#elif defined(RADIO_T16) + #define ETX_FOURCC 0x3F78746F // etx for Jumper T16 +#elif defined(RADIO_T18) + #define ETX_FOURCC 0x4078746F // etx for Jumper T18 +#elif defined(RADIO_TX16S) + #define ETX_FOURCC 0x3878746F // etx for Radiomaster TX16S +#elif defined(PCBX10) + #define ETX_FOURCC 0x3778746F // etx for X10 +#elif defined(PCBX9E) + #define ETX_FOURCC 0x3578746F // etx for Taranis X9E +#elif defined(PCBXLITES) + #define ETX_FOURCC 0x3B78746F // etx for Taranis X-Lite S +#elif defined(PCBXLITE) + #define ETX_FOURCC 0x3978746F // etx for Taranis X-Lite +#elif defined(RADIO_T12) + #define ETX_FOURCC 0x3D78746F // etx for Jumper T12 +#elif defined(RADIO_TLITE) + #define ETX_FOURCC 0x4278746F // etx for Jumper TLite +#elif defined(RADIO_TPRO) + #define ETX_FOURCC 0x4678746F // etx for Jumper TPro +#elif defined(RADIO_TX12) + #define ETX_FOURCC 0x4178746F // etx for Radiomaster TX12 +#elif defined(RADIO_TX12MK2) + #define ETX_FOURCC 0x4878746F // etx for Radiomaster TX12MK2 +#elif defined(RADIO_ZORRO) + #define ETX_FOURCC 0x4778746F // otx for Radiomaster Zorro +#elif defined(RADIO_T8) + #define ETX_FOURCC 0x4378746F // etx for Radiomaster T8 +#elif defined(PCBX7) + #define ETX_FOURCC 0x3678746F // etx for Taranis X7 / X7S / X7 Express / X7S Express +#elif defined(PCBX9LITES) + #define ETX_FOURCC 0x3E78746F // etx for Taranis X9-Lite S +#elif defined(PCBX9LITE) + #define ETX_FOURCC 0x3C78746F // etx for Taranis X9-Lite +#elif defined(PCBX9D) || defined(PCBX9DP) + #define ETX_FOURCC 0x3378746F // etx for Taranis X9D +#elif defined(RADIO_LR3PRO) + #define ETX_FOURCC 0x4478746F // etx for BETAFPV LR3PRO +#elif defined(PCBNV14) + #define ETX_FOURCC 0x3A78746F // otx for NV14 +#elif defined(PCBPL18) + #define ETX_FOURCC 0x4878746F // otx for PL18 +#endif + +#define VFS_MAX_LFN 255 +constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. + +#define FILE_COPY_PREFIX "cp_" + +#define PATH_SEPARATOR "/" +#if defined(BOOT) +#define ROOT_PATH PATH_SEPARATOR +#else +#define ROOT_PATH PATH_SEPARATOR "DEFAULT" PATH_SEPARATOR +#endif +#define SDCARD_PATH PATH_SEPARATOR "SDCARD" PATH_SEPARATOR +#define INTERNAL_ST_PATH PATH_SEPARATOR "INTERNAL" PATH_SEPARATOR +#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important +#define DELETED_MODELS_PATH MODELS_PATH PATH_SEPARATOR "DELETED" +#define UNUSED_MODELS_PATH MODELS_PATH PATH_SEPARATOR "UNUSED" +#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important +#define TEMPLATES_PATH ROOT_PATH "TEMPLATES" +#define PERS_TEMPL_PATH TEMPLATES_PATH "/PERSONAL" +#define LOGS_PATH SDCARD_PATH "LOGS" +#define SCREENSHOTS_PATH ROOT_PATH "SCREENSHOTS" +#define SOUNDS_PATH ROOT_PATH "SOUNDS/en" +#define SOUNDS_PATH_LNG_OFS (sizeof(SOUNDS_PATH)-3) +#define SYSTEM_SUBDIR "SYSTEM" +#define BITMAPS_PATH ROOT_PATH "IMAGES" +#define FIRMWARES_PATH ROOT_PATH "FIRMWARE" +#define AUTOUPDATE_FILENAME FIRMWARES_PATH PATH_SEPARATOR "autoupdate.frsk" +#define EEPROMS_PATH ROOT_PATH "EEPROM" +#define BACKUP_PATH ROOT_PATH "BACKUP" +#define SCRIPTS_PATH ROOT_PATH "SCRIPTS" +#define WIZARD_PATH SCRIPTS_PATH PATH_SEPARATOR "WIZARD" +#define THEMES_PATH ROOT_PATH "THEMES" +#define LAYOUTS_PATH ROOT_PATH "LAYOUTS" +#define WIDGETS_PATH ROOT_PATH "WIDGETS" +#define WIZARD_NAME "wizard.lua" +#define SCRIPTS_MIXES_PATH SCRIPTS_PATH PATH_SEPARATOR "MIXES" +#define SCRIPTS_FUNCS_PATH SCRIPTS_PATH PATH_SEPARATOR "FUNCTIONS" +#define SCRIPTS_TELEM_PATH SCRIPTS_PATH PATH_SEPARATOR "TELEMETRY" +#define SCRIPTS_TOOLS_PATH SCRIPTS_PATH PATH_SEPARATOR "TOOLS" + +#define SDCARD_FIRMWARES_PATH SDCARD_PATH PATH_SEPARATOR "FIRMWARE" +#define INTERNAL_ST_FIRMWARES_PATH INTERNAL_ST_PATH PATH_SEPARATOR "FIRMWARE" + +#define LEN_FILE_PATH_MAX (sizeof(SCRIPTS_TELEM_PATH)+1) // longest + "/" + +#if defined(SDCARD_YAML) || defined(SDCARD_RAW) +#define RADIO_FILENAME "radio.bin" +const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt"; +const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME; +#if defined(SDCARD_YAML) +#define LABELS_FILENAME "labels.yml" +#define MODELS_FILENAME "models.yml" +const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR MODELS_FILENAME; +const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR MODELS_FILENAME; +const char LABELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR LABELS_FILENAME; +const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml"; +const char RADIO_SETTINGS_TMPFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_new.yml"; +const char RADIO_SETTINGS_ERRORFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_error.yml"; + +const char YAMLFILE_CHECKSUM_TAG_NAME[] = "checksum"; +#endif +#define SPLASH_FILE "splash.png" +#endif + + +#define MODELS_EXT ".bin" +#define LOGS_EXT ".csv" +#define SOUNDS_EXT ".wav" +#define BMP_EXT ".bmp" +#define PNG_EXT ".png" +#define JPG_EXT ".jpg" +#define SCRIPT_EXT ".lua" +#define SCRIPT_BIN_EXT ".luac" +#define TEXT_EXT ".txt" +#define FIRMWARE_EXT ".bin" +#define EEPROM_EXT ".bin" +#define SPORT_FIRMWARE_EXT ".frk" +#define FRSKY_FIRMWARE_EXT ".frsk" +#define MULTI_FIRMWARE_EXT ".bin" +#define ELRS_FIRMWARE_EXT ".elrs" +#define YAML_EXT ".yml" + +#if defined(COLORLCD) +#define BITMAPS_EXT BMP_EXT JPG_EXT PNG_EXT +#define LEN_BITMAPS_EXT 4 +#else +#define BITMAPS_EXT BMP_EXT +#endif + +#ifdef LUA_COMPILER + #define SCRIPTS_EXT SCRIPT_BIN_EXT SCRIPT_EXT +#else + #define SCRIPTS_EXT SCRIPT_EXT +#endif + +#define GET_FILENAME(filename, path, var, ext) \ + char filename[sizeof(path) + sizeof(var) + sizeof(ext)]; \ + memcpy(filename, path, sizeof(path) - 1); \ + filename[sizeof(path) - 1] = '/'; \ + memcpy(&filename[sizeof(path)], var, sizeof(var)); \ + filename[sizeof(path)+sizeof(var)] = '\0'; \ + strcat(&filename[sizeof(path)], ext) + +extern uint8_t logDelay100ms; // this does not belong here, this is just the way it was before VirtualFS + +class VirtualFS; + +enum class VfsType { UNKOWN, DIR, FILE }; +enum class VfsFileType { + UNKNOWN + ,ROOT + ,FAT +}; + +enum class VfsError { + OK = 0, // No error + IO = -5, // Error during device operation + CORRUPT = -84, // Corrupted + NOENT = -2, // No directory entry + EXIST = -17, // Entry already exists + NOTDIR = -20, // Entry is not a dir + ISDIR = -21, // Entry is a dir + NOTEMPTY = -39, // Dir is not empty + BADF = -9, // Bad file number + FBIG = -27, // File too large + INVAL = -22, // Invalid parameter + NOSPC = -28, // No space left on device + NOMEM = -12, // No more memory available + NOATTR = -61, // No data/attr available + NAMETOOLONG = -36, // File name too long + NOT_READY = -99 // storage not ready +}; + +#if !defined(BOOT) +inline const char * STORAGE_ERROR(VfsError result) +{ + if (result == VfsError::NOT_READY) + return STR_NO_SDCARD; + else + return STR_SDCARD_ERROR; +} +#endif + +enum class VfsOpenFlags { + NONE = 0x00, + READ = 0x01, + WRITE = 0x02, + OPEN_EXISTING = 0x00, + CREATE_NEW = 0x04, + CREATE_ALWAYS = 0x08, + OPEN_ALWAYS = 0x10, + OPEN_APPEND = 0x30, +}; + +VfsOpenFlags operator|(VfsOpenFlags lhs,VfsOpenFlags rhs); +VfsOpenFlags operator|=(VfsOpenFlags lhs,VfsOpenFlags rhs); +VfsOpenFlags operator&(VfsOpenFlags lhs,VfsOpenFlags rhs); + +//for compatibility reasons those are identical to the FAT implementation +enum class VfsFileAttributes { + NONE = 0x00, + RDO = 0x01, + HID = 0x02, + SYS = 0x04, + DIR = 0x10, + ARC = 0x20 +}; + +VfsFileAttributes operator|(VfsFileAttributes lhs,VfsFileAttributes rhs); +VfsFileAttributes operator&(VfsFileAttributes lhs,VfsFileAttributes rhs); + +struct VfsDir; + +struct VfsFileInfo +{ +public: + VfsFileInfo(){ clear(); } + + const char* getName() const; + size_t getSize() const; + VfsType getType() const; + VfsFileAttributes getAttrib(); + + int getDate(); + int getTime(); +private: + friend class VirtualFS; + friend struct VfsDir; + VfsFileInfo(const VfsFileInfo&); + + void clear(); + + VfsFileType type = VfsFileType::UNKNOWN; + union { + FILINFO fatInfo; + }; + + const char* name = nullptr; +}; + +struct VfsDir +{ +public: + VfsDir() { clear(); } + ~VfsDir() { if (type != DIR_UNKNOWN) close(); } + + VfsError read(VfsFileInfo& info); + VfsError close(); + VfsError rewind(); + +private: + friend class VirtualFS; + VfsDir(const VfsDir&); + + enum DirType {DIR_UNKNOWN, DIR_ROOT, DIR_FAT}; + + void clear(); + + DirType type = DIR_UNKNOWN; + union { + struct { + DIR dir; + } fat; + }; + + size_t readIdx = 0; +}; + +// for compatibility reasons (audio.h WavContext) the file does not close it self when destructed +// since the must not be a non trivial destructor +struct VfsFile +{ +public: + VfsFile() {clear();} + + VfsError close(); + + bool isOpen() const { return type != VfsFileType::UNKNOWN; } + int size(); + VfsError read(void* buf, size_t size, size_t& readSize); + char* gets(char* buf, size_t maxLen); +#if !defined(BOOT) + VfsError write(const void* buf, size_t size, size_t& written); + VfsError puts(const std::string& str); + VfsError putc(char c); + int fprintf(const char* str, ...); +#endif + + size_t tell(); + VfsError lseek(size_t offset); + int eof(); + +private: + friend class VirtualFS; + VfsFile(const VfsFile&); + + void clear(); + + VfsFileType type = VfsFileType::UNKNOWN; + union { + struct { + FIL file = {0}; + } fat; + }; + +}; + +extern VfsFile g_oLogFile; + + +class VirtualFS +{ +public: + VirtualFS(); + ~VirtualFS(); + VirtualFS(const VirtualFS&) = delete; + VirtualFS& operator=(VirtualFS&) = delete; + + void stop(); + void restart(); + void mountSd(); + + // NOTE: 'size' must = 0 or be a valid character position within 'filename' array -- it is NOT validated + static const char* getBasename(const char * path); + + static VirtualFS& instance() + { + if( _instance == nullptr) + _instance = new VirtualFS(); + return *_instance; + } + + bool defaultStorageAvailable(); +#if !defined(LIBOPENUI) && !defined(BOOT) + bool listFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags = 0); +#endif +#if !defined(BOOT) + bool format(); + const char * checkAndCreateDirectory(const char * path); + + bool isFileAvailable(const char * path, bool exclDir = false); + bool isFilePatternAvailable(const char * path, const char * file, const char * pattern = nullptr, bool exclDir = true, char * match = nullptr); + char* getFileIndex(char * filename, unsigned int & value); + + static const char* getFileExtension(const char * filename, uint8_t size = 0, uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); + static bool isFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); +#endif + const std::string& getCurWorkDir() const { return curWorkDir;} +#if !defined(BOOT) + VfsError unlink(const char* path); +#endif // !BOOT + VfsError changeDirectory(const char* path); + VfsError openDirectory(VfsDir& dir, const char * path); + VfsError makeDirectory(const char* path); + + VfsError fstat(const char* path, VfsFileInfo& fileInfo); + VfsError utime(const char* path, const VfsFileInfo& fileInfo); + VfsError openFile(VfsFile& file, const char* path, VfsOpenFlags flags); + +#if !defined(BOOT) + VfsError rename(const char* oldPath, const char* newPath); + VfsError copyFile(const char* source, const char* destination); + VfsError copyFile(const char* srcFile, const char* srcDir, + const char* destDir, const char* destFile); + const char * moveFile(const char* source, const char* destination); + const char * moveFile(const char* srcFile, const char* srcDir, + const char* destDir, const char* destFile); +#endif // !BOOT + + bool sdCardMounted(); + size_t sdGetFreeSectors(); + + uint32_t flashGetNoSectors() const; + uint32_t flashGetSize() const; + uint32_t flashGetFreeSectors() const; + + // NOTE: 'size' must = 0 or be a valid character position within 'filename' array -- it is NOT validated + unsigned int findNextFileIndex(char * filename, uint8_t size, const char * directory); + + #define LIST_NONE_SD_FILE 1 + #define LIST_SD_FILE_EXT 2 +private: + static VirtualFS* _instance; + + + std::string curWorkDir = "/"; + + void normalizePath(char* path); + + VfsDir::DirType getDirTypeAndPath(char* path); + + void startLogs(); + void stopLogs(); +}; + +#endif // _VIRTUALFS_H_ diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 2944274b32e..b7580cde396 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -137,8 +137,6 @@ const int16_t sineValues[] = -784, -588, -392, -196, }; -#if defined(SDCARD) - const char * const unitsFilenames[] = { "", "volt", @@ -276,33 +274,35 @@ void referenceSystemAudioFiles() { static_assert(sizeof(audioFilenames)==AU_SPECIAL_SOUND_FIRST*sizeof(char *), "Invalid audioFilenames size"); char path[AUDIO_FILENAME_MAXLEN+1]; - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; + VirtualFS& vfs = VirtualFS::instance(); sdAvailableSystemAudioFiles.reset(); char * filename = strAppendSystemAudioPath(path); *(filename-1) = '\0'; - FRESULT res = f_opendir(&dir, path); /* Open the directory */ - if (res == FR_OK) { + VfsError res = vfs.openDirectory(dir, path); /* Open the directory */ + if (res == VfsError::OK) { for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - uint8_t len = strlen(fno.fname); + res = dir.read(fno); /* Read a directory item */ + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + uint8_t len = strlen(name); // Eliminates directories / non wav files - if (len < 5 || strcasecmp(fno.fname+len-4, SOUNDS_EXT) || (fno.fattrib & AM_DIR)) continue; + if (len < 5 || strcasecmp(name+len-4, SOUNDS_EXT) || (fno.getType() == VfsType::DIR)) continue; for (int i=0; iid, 2); char *buf = strcat_currentmodelname(path + sizeof(SOUNDS_PATH), ' '); - if (!isFileAvailable(path)) { + if (!VirtualFS::instance().isFileAvailable(path)) { buf = strcat_currentmodelname(path + sizeof(SOUNDS_PATH), 0); } @@ -374,8 +374,9 @@ void getLogicalSwitchAudioFile(char * filename, int index, unsigned int event) void referenceModelAudioFiles() { char path[AUDIO_FILENAME_MAXLEN+1]; - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; + VirtualFS& vfs = VirtualFS::instance(); sdAvailableFlightmodeAudioFiles.reset(); sdAvailableSwitchAudioFiles.reset(); @@ -384,24 +385,25 @@ void referenceModelAudioFiles() char * filename = getModelAudioPath(path); *(filename-1) = '\0'; - FRESULT res = f_opendir(&dir, path); /* Open the directory */ - if (res == FR_OK) { + VfsError res = vfs.openDirectory(dir, path); /* Open the directory */ + if (res == VfsError::OK) { for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - uint8_t len = strlen(fno.fname); + res = dir.read(fno); /* Read a directory item */ + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + uint8_t len = strlen(name); bool found = false; // Eliminates directories / non wav files - if (len < 5 || strcasecmp(fno.fname+len-4, SOUNDS_EXT) || (fno.fattrib & AM_DIR)) continue; - TRACE("referenceModelAudioFiles(): using file: %s", fno.fname); + if (len < 5 || strcasecmp(name+len-4, SOUNDS_EXT) || (fno.getType() == VfsType::DIR)) continue; + TRACE("referenceModelAudioFiles(): using file: %s", name); // Flight modes Audio Files -[on|off].wav for (int i=0; i> fade) >> (16-AUDIO_BITS_PER_SAMPLE)), AUDIO_DATA_MAX); } -#if defined(SDCARD) - #define RIFF_CHUNK_SIZE 12 uint8_t wavBuffer[AUDIO_BUFFER_SIZE*2] __DMA; int WavContext::mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade) { - FRESULT result = FR_OK; - UINT read = 0; + VfsError result = VfsError::OK; + size_t read = 0; + VirtualFS& vfs = VirtualFS::instance(); if (fragment.file[1]) { - result = f_open(&state.file, fragment.file, FA_OPEN_EXISTING | FA_READ); + result = vfs.openFile(state.file, fragment.file, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); fragment.file[1] = 0; - if (result == FR_OK) { - result = f_read(&state.file, wavBuffer, RIFF_CHUNK_SIZE+8, &read); - if (result == FR_OK && read == RIFF_CHUNK_SIZE+8 && !memcmp(wavBuffer, "RIFF", 4) && !memcmp(wavBuffer+8, "WAVEfmt ", 8)) { + if (result == VfsError::OK) { + result = state.file.read(wavBuffer, RIFF_CHUNK_SIZE+8, read); + if (result == VfsError::OK && read == RIFF_CHUNK_SIZE+8 && !memcmp(wavBuffer, "RIFF", 4) && !memcmp(wavBuffer+8, "WAVEfmt ", 8)) { uint32_t size = *((uint32_t *)(wavBuffer+16)); - result = (size < 256 ? f_read(&state.file, wavBuffer, size+8, &read) : FR_DENIED); - if (result == FR_OK && read == size+8) { + result = (size < 256 ? state.file.read(wavBuffer, size+8, read) : VfsError::INVAL); + if (result == VfsError::OK && read == size+8) { state.codec = ((uint16_t *)wavBuffer)[0]; state.freq = ((uint16_t *)wavBuffer)[2]; uint32_t *wavSamplesPtr = (uint32_t *)(wavBuffer + size); @@ -574,13 +569,13 @@ int WavContext::mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade) state.readSize = (state.codec == CODEC_ID_PCM_S16LE ? 2*AUDIO_BUFFER_SIZE : AUDIO_BUFFER_SIZE) / state.resampleRatio; } else { - result = FR_DENIED; + result = VfsError::INVAL; } - while (result == FR_OK && memcmp(wavSamplesPtr, "data", 4) != 0) { - result = f_lseek(&state.file, f_tell(&state.file)+size); - if (result == FR_OK) { - result = f_read(&state.file, wavBuffer, 8, &read); - if (read != 8) result = FR_DENIED; + while (result == VfsError::OK && memcmp(wavSamplesPtr, "data", 4) != 0) { + result = state.file.lseek(state.file.tell()+size); + if (result == VfsError::OK) { + result = state.file.read(wavBuffer, 8, read); + if (read != 8) result = VfsError::INVAL; wavSamplesPtr = (uint32_t *)wavBuffer; size = wavSamplesPtr[1]; } @@ -588,26 +583,26 @@ int WavContext::mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade) state.size = size; } else { - result = FR_DENIED; + result = VfsError::INVAL; } } else { - result = FR_DENIED; + result = VfsError::INVAL; } } } - if (result == FR_OK) { + if (result == VfsError::OK) { read = 0; - result = f_read(&state.file, wavBuffer, state.readSize, &read); - if (result == FR_OK) { + result = state.file.read(wavBuffer, state.readSize, read); + if (result == VfsError::OK) { if (read > state.size) { read = state.size; } state.size -= read; if (read != state.readSize) { - f_close(&state.file); + state.file.close(); fragment.clear(); } @@ -625,17 +620,11 @@ int WavContext::mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade) } } - if (result != FR_OK) { + if (result != VfsError::OK) { clear(); } return 0; } -#else -int WavContext::mixBuffer(AudioBuffer *buffer, int volume, unsigned int fade) -{ - return 0; -} -#endif const unsigned int toneVolumes[] = { 10, 8, 6, 4, 2 }; inline float evalVolumeRatio(int freq, int volume) @@ -869,7 +858,6 @@ void AudioQueue::playTone(uint16_t freq, uint16_t len, uint16_t pause, uint8_t f RTOS_UNLOCK_MUTEX(audioMutex); } -#if defined(SDCARD) void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id) { #if defined(SIMU) @@ -883,7 +871,7 @@ void AudioQueue::playFile(const char * filename, uint8_t flags, uint8_t id) #endif #endif - if (!sdMounted()) + if (!VirtualFS::instance().defaultStorageAvailable()) return; if (g_eeGeneral.beepMode == e_mode_quiet) @@ -932,8 +920,6 @@ void AudioQueue::stopSD() playTone(0, 0, 100, PLAY_NOW); // insert a 100ms pause } -#endif - void AudioQueue::stopAll() { flush(); @@ -1063,14 +1049,12 @@ void audioEvent(unsigned int index) } if (g_eeGeneral.beepMode >= e_mode_nokeys || (g_eeGeneral.beepMode >= e_mode_alarms && index <= AU_ERROR)) { -#if defined(SDCARD) char filename[AUDIO_FILENAME_MAXLEN + 1]; if (index < AU_SPECIAL_SOUND_FIRST && isAudioFileReferenced(index, filename)) { audioQueue.stopPlay(ID_PLAY_PROMPT_BASE + index); audioQueue.playFile(filename, 0, ID_PLAY_PROMPT_BASE + index); return; } -#endif switch (index) { case AU_INACTIVITY: audioQueue.playTone(2250, 80, 20, PLAY_REPEAT(2)); @@ -1222,7 +1206,6 @@ void audioEvent(unsigned int index) } } -#if defined(SDCARD) void pushUnit(uint8_t unit, uint8_t idx, uint8_t id) { if (unit < DIM(unitsFilenames)) { @@ -1236,11 +1219,9 @@ void pushUnit(uint8_t unit, uint8_t idx, uint8_t id) TRACE("pushUnit: out of bounds unit : %d", unit); // We should never get here, but given the nature of TTS files, this prevent segfault in case of bug there. } } -#endif void pushPrompt(uint16_t prompt, uint8_t id) { -#if defined(SDCARD) char filename[AUDIO_FILENAME_MAXLEN+1]; char * str = strAppendSystemAudioPath(filename); strcpy(str, "0000" SOUNDS_EXT); @@ -1249,7 +1230,6 @@ void pushPrompt(uint16_t prompt, uint8_t id) prompt /= 10; } audioQueue.playFile(filename, 0, id); -#endif } void onKeyError() diff --git a/radio/src/audio.h b/radio/src/audio.h index 44a0deac9fd..765d6f5daff 100644 --- a/radio/src/audio.h +++ b/radio/src/audio.h @@ -24,9 +24,9 @@ #include #include -#include "ff.h" #include "opentx_types.h" #include "dataconstants.h" +#include "VirtualFS.h" /* Implements a bit field, number of bits is set by the template, @@ -69,7 +69,7 @@ template class BitField #define INDEX_PHASE_AUDIO_FILE(index, event) (2*(index)+(event)) // max length (example: /SOUNDS/fr/123456789012/1234567890-off.wav) -constexpr uint8_t AUDIO_MODEL_FILENAME_MAXLEN = (sizeof("/SOUNDS/fr/") - 1) + LEN_MODEL_NAME + 1 + LEN_FLIGHT_MODE_NAME + (sizeof("-off.wav") - 1); +constexpr uint8_t AUDIO_MODEL_FILENAME_MAXLEN = (sizeof(INTERNAL_ST_PATH) - 1) + (sizeof("/SOUNDS/fr/") - 1) + LEN_MODEL_NAME + 1 + LEN_FLIGHT_MODE_NAME + (sizeof("-off.wav") - 1); constexpr uint8_t AUDIO_LUA_FILENAME_MAXLEN = 42; // Some scripts use long audio paths, even on 128x64 boards constexpr uint8_t AUDIO_FILENAME_MAXLEN = (AUDIO_LUA_FILENAME_MAXLEN > AUDIO_MODEL_FILENAME_MAXLEN ? AUDIO_LUA_FILENAME_MAXLEN : AUDIO_MODEL_FILENAME_MAXLEN); @@ -242,7 +242,7 @@ class WavContext { AudioFragment fragment; struct { - FIL file; + VfsFile file; uint8_t codec; uint32_t freq; uint32_t size; @@ -631,26 +631,16 @@ void playModelName(); #define AUDIO_RESET() audioQueue.stopAll() #define AUDIO_FLUSH() audioQueue.flush() -#if defined(SDCARD) - extern tmr10ms_t timeAutomaticPromptsSilence; - void playModelEvent(uint8_t category, uint8_t index, event_t event=0); - #define PLAY_PHASE_OFF(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_OFF) - #define PLAY_PHASE_ON(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_ON) - #define PLAY_SWITCH_MOVED(sw) playModelEvent(SWITCH_AUDIO_CATEGORY, sw) - #define PLAY_LOGICAL_SWITCH_OFF(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_OFF) - #define PLAY_LOGICAL_SWITCH_ON(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_ON) - #define PLAY_MODEL_NAME() playModelName() - #define START_SILENCE_PERIOD() timeAutomaticPromptsSilence = get_tmr10ms() - #define IS_SILENCE_PERIOD_ELAPSED() (get_tmr10ms()-timeAutomaticPromptsSilence > 50) -#else - #define PLAY_PHASE_OFF(phase) - #define PLAY_PHASE_ON(phase) - #define PLAY_SWITCH_MOVED(sw) - #define PLAY_LOGICAL_SWITCH_OFF(sw) - #define PLAY_LOGICAL_SWITCH_ON(sw) - #define PLAY_MODEL_NAME() - #define START_SILENCE_PERIOD() -#endif +extern tmr10ms_t timeAutomaticPromptsSilence; +void playModelEvent(uint8_t category, uint8_t index, event_t event=0); +#define PLAY_PHASE_OFF(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_OFF) +#define PLAY_PHASE_ON(phase) playModelEvent(PHASE_AUDIO_CATEGORY, phase, AUDIO_EVENT_ON) +#define PLAY_SWITCH_MOVED(sw) playModelEvent(SWITCH_AUDIO_CATEGORY, sw) +#define PLAY_LOGICAL_SWITCH_OFF(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_OFF) +#define PLAY_LOGICAL_SWITCH_ON(sw) playModelEvent(LOGICAL_SWITCH_AUDIO_CATEGORY, sw, AUDIO_EVENT_ON) +#define PLAY_MODEL_NAME() playModelName() +#define START_SILENCE_PERIOD() timeAutomaticPromptsSilence = get_tmr10ms() +#define IS_SILENCE_PERIOD_ELAPSED() (get_tmr10ms()-timeAutomaticPromptsSilence > 50) char * getAudioPath(char * path); diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 9432649ad1f..c1619e4d570 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/bitmaps/480x272/splash_480x320.png b/radio/src/bitmaps/480x272/splash_480x320.png new file mode 100644 index 00000000000..43e59de80d4 Binary files /dev/null and b/radio/src/bitmaps/480x272/splash_480x320.png differ diff --git a/radio/src/bitmaps/480x272/splash_chr_480x320.png b/radio/src/bitmaps/480x272/splash_chr_480x320.png new file mode 100644 index 00000000000..7cc10bc0b21 Binary files /dev/null and b/radio/src/bitmaps/480x272/splash_chr_480x320.png differ diff --git a/radio/src/bluetooth.cpp b/radio/src/bluetooth.cpp index 6aba80ac3c7..a9f68150c0c 100644 --- a/radio/src/bluetooth.cpp +++ b/radio/src/bluetooth.cpp @@ -26,12 +26,10 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif #if defined(LOG_BLUETOOTH) -extern FIL g_bluetoothFile; +extern VfsFile g_bluetoothFile; #endif #if defined(PCBX9E) @@ -745,9 +743,9 @@ const char * Bluetooth::bootloaderWriteFlash(const uint8_t * data, uint32_t size const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler progressHandler) { const char * result; - FIL file; + VfsFile file; uint8_t buffer[CC26XX_MAX_BYTES_PER_TRANSFER * 4]; - UINT count; + size_t count; // Dummy command bootloaderSendCommand(0); @@ -766,26 +764,26 @@ const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler p result = bootloaderWaitResponseData(id, 4); bootloaderSendCommandResponse(result == nullptr ? BLUETOOTH_ACK : BLUETOOTH_NACK); - if (f_open(&file, filename, FA_READ) != FR_OK) { + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) { return "Error opening file"; } FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *)buffer; - if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || count != sizeof(FrSkyFirmwareInformation)) { - f_close(&file); + if (file.read(buffer, sizeof(FrSkyFirmwareInformation), count) != VfsError::OK || count != sizeof(FrSkyFirmwareInformation)) { + file.close(); return "Format error"; } - progressHandler(getBasename(filename), STR_FLASH_ERASE, 0, 0); + progressHandler(VirtualFS::VirtualFS::getBasename(filename), STR_FLASH_ERASE, 0, 0); result = bootloaderEraseFlash(CC26XX_FIRMWARE_BASE, information->size); if (result) { - f_close(&file); + file.close(); return result; } uint32_t size = information->size; - progressHandler(getBasename(filename), STR_FLASH_WRITE, 0, size); + progressHandler(VirtualFS::getBasename(filename), STR_FLASH_WRITE, 0, size); result = bootloaderStartWriteFlash(CC26XX_FIRMWARE_BASE, size); if (result) @@ -793,9 +791,9 @@ const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler p uint32_t done = 0; while (1) { - progressHandler(getBasename(filename), STR_FLASH_WRITE, done, size); - if (f_read(&file, buffer, min(sizeof(buffer), size - done), &count) != FR_OK) { - f_close(&file); + progressHandler(VirtualFS::getBasename(filename), STR_FLASH_WRITE, done, size); + if (file.read(buffer, min(sizeof(buffer), size - done), count) != VfsError::OK) { + file.close(); return "Error reading file"; } result = bootloaderWriteFlash(buffer, count); @@ -803,7 +801,7 @@ const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler p return result; done += count; if (done >= size) { - f_close(&file); + file.close(); return nullptr; } } @@ -811,7 +809,7 @@ const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler p const char * Bluetooth::flashFirmware(const char * filename, ProgressHandler progressHandler) { - progressHandler(getBasename(filename), STR_MODULE_RESET, 0, 0); + progressHandler(VirtualFS::getBasename(filename), STR_MODULE_RESET, 0, 0); state = BLUETOOTH_STATE_FLASH_FIRMWARE; @@ -837,7 +835,7 @@ const char * Bluetooth::flashFirmware(const char * filename, ProgressHandler pro POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS); } - progressHandler(getBasename(filename), STR_MODULE_RESET, 0, 0); + progressHandler(VirtualFS::getBasename(filename), STR_MODULE_RESET, 0, 0); /* wait 1s off */ watchdogSuspend(500 /*5s*/); diff --git a/radio/src/bluetooth.h b/radio/src/bluetooth.h index 29047e8b99c..c7a0fb65847 100644 --- a/radio/src/bluetooth.h +++ b/radio/src/bluetooth.h @@ -57,7 +57,7 @@ enum BluetoothStates { #if defined(LOG_BLUETOOTH) #define BLUETOOTH_TRACE(...) \ - f_printf(&g_bluetoothFile, __VA_ARGS__); \ + g_bluetoothFile.fprintf(__VA_ARGS__); \ TRACE_NOCRLF(__VA_ARGS__); #else #if defined(DEBUG_BLUETOOTH) diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 754c8d1cbfc..da2616f6008 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -23,6 +23,7 @@ #include #include "opentx.h" +#include "VirtualFS.h" #include "diskio.h" #include "timers_driver.h" #include "watchdog_driver.h" @@ -256,17 +257,18 @@ int cliPlay(const char ** argv) int cliLs(const char ** argv) { - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; - FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */ - if (res == FR_OK) { + VfsError res = VirtualFS::instance().openDirectory(dir, argv[1]); /* Open the directory */ + if (res == VfsError::OK) { for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - cliSerialPrint(fno.fname); + res = dir.read(fno); /* Read a directory item */ + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + cliSerialPrint(name); } - f_closedir(&dir); + dir.close(); } else { cliSerialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]); @@ -276,7 +278,7 @@ int cliLs(const char ** argv) int cliRead(const char ** argv) { - FIL file; + VfsFile file; uint32_t bytesRead = 0; int bufferSize; if (toInt(argv, 2, &bufferSize) == 0 || bufferSize < 0 ) { @@ -290,8 +292,8 @@ int cliRead(const char ** argv) return 0; } - FRESULT result = f_open(&file, argv[1], FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + VfsError result = VirtualFS::instance().openFile(file, argv[1], VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result != VfsError::OK) { free(buffer); cliSerialPrint("%s: File not found \"%s\"", argv[0], argv[1]); return 0; @@ -300,12 +302,12 @@ int cliRead(const char ** argv) tmr10ms_t start = get_tmr10ms(); while (true) { - UINT read; - result = f_read(&file, buffer, sizeof(buffer), &read); - if (result == FR_OK) { + size_t read; + result = file.read(buffer, sizeof(buffer), read); + if (result == VfsError::OK) { if (read == 0) { // end of file - f_close(&file); + file.close(); break; } bytesRead += read; @@ -1036,6 +1038,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) @@ -1056,6 +1059,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*); @@ -1380,9 +1384,12 @@ int cliDisplay(const char ** argv) #endif #if defined(DISK_CACHE) else if (!strcmp(argv[1], "dc")) { - DiskCacheStats stats = diskCache.getStats(); - uint32_t hitRate = diskCache.getHitRate(); - cliSerialPrint("Disk Cache stats: w:%u r: %u, h: %u(%0.1f%%), m: %u", stats.noWrites, (stats.noHits + stats.noMisses), stats.noHits, hitRate*0.1f, stats.noMisses); + for (uint8_t drv = 0; drv < sizeof(diskCache) / sizeof(diskCache[0]); drv++) + { + DiskCacheStats stats = diskCache[drv].getStats(); + uint32_t hitRate = diskCache[drv].getHitRate(); + cliSerialPrint("Disk Cache [%d] stats: w:%u r: %u, h: %u(%0.1f%%), m: %u", drv, stats.noWrites, (stats.noHits + stats.noMisses), stats.noHits, hitRate*0.1f, stats.noMisses); + } } #endif else if (toLongLongInt(argv, 1, &address) > 0) { @@ -1579,7 +1586,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; @@ -1651,7 +1658,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 08ea06e26d3..04b6bb1e047 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 @@ -268,6 +268,7 @@ enum TelemetryProtocol PROTOCOL_TELEMETRY_AFHDS3, PROTOCOL_TELEMETRY_GHOST, PROTOCOL_TELEMETRY_FLYSKY_NV14, + PROTOCOL_TELEMETRY_FLYSKY_PL18, PROTOCOL_TELEMETRY_DSMP, PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_DSMP, PROTOCOL_TELEMETRY_LUA @@ -425,6 +426,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, @@ -514,10 +522,9 @@ enum MixSources { //#if defined(PCBHORUS) MIXSRC_TrimT5, MIXSRC_TrimT6, - MIXSRC_LAST_TRIM SKIP = MIXSRC_TrimT6, - //#else - //MIXSRC_LAST_TRIM SKIP = MIXSRC_TrimAil, - //#endif + MIXSRC_TrimT7, + MIXSRC_TrimT8, + MIXSRC_LAST_TRIM SKIP = MIXSRC_TrimT8, MIXSRC_FIRST_SWITCH SKIP, MIXSRC_LAST_SWITCH SKIP = MIXSRC_FIRST_SWITCH + MAX_SWITCHES - 1, diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 4907f81ab1d..da421b23b0c 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 727974e9202..8cac868cbfa 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -601,7 +601,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/disk_cache.cpp b/radio/src/disk_cache.cpp index d54f6af5ac9..a6f13823088 100644 --- a/radio/src/disk_cache.cpp +++ b/radio/src/disk_cache.cpp @@ -43,7 +43,7 @@ #define BLOCK_SIZE FF_MAX_SS #define DISK_CACHE_BLOCK_SIZE (DISK_CACHE_BLOCK_SECTORS * BLOCK_SIZE) -DiskCache diskCache; +DiskCache diskCache[2]; class DiskCacheBlock { @@ -143,7 +143,12 @@ DRESULT DiskCache::read(BYTE drv, BYTE * buff, DWORD sector, UINT count) } // if block + cache block size is beyond the end of the disk, then read it directly without using cache - if (sector + DISK_CACHE_BLOCK_SECTORS >= sdGetNoSectors()) { + size_t sectors = 0; + DRESULT res = RES_OK; +#if !defined(SIMU) + res = disk_ioctl(drv, GET_SECTOR_COUNT, §ors); +#endif + if (res != RES_OK || sector + DISK_CACHE_BLOCK_SECTORS >= sectors) { TRACE_DISK_CACHE("\t\t cache would be beyond end of disk %u (%u)", (uint32_t)sector, sdGetNoSectors()); return __disk_read(drv, buff, sector, count); } @@ -197,11 +202,11 @@ int DiskCache::getHitRate() const DRESULT disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) { - return diskCache.read(drv, buff, sector, count); + return diskCache[drv].read(drv, buff, sector, count); } DRESULT disk_write(BYTE drv, const BYTE * buff, DWORD sector, UINT count) { - return diskCache.write(drv, buff, sector, count); + return diskCache[drv].write(drv, buff, sector, count); } diff --git a/radio/src/disk_cache.h b/radio/src/disk_cache.h index ef5fbaeca65..509f2fc981a 100644 --- a/radio/src/disk_cache.h +++ b/radio/src/disk_cache.h @@ -56,6 +56,6 @@ class DiskCache DiskCacheBlock * blocks; }; -extern DiskCache diskCache; +extern DiskCache diskCache[2]; #endif // _DISK_CACHE_H_ diff --git a/radio/src/edgetx_assert.h b/radio/src/edgetx_assert.h new file mode 100644 index 00000000000..7eb19928078 --- /dev/null +++ b/radio/src/edgetx_assert.h @@ -0,0 +1,32 @@ +/* + * 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 + +#if defined(SIMU) + #include +#else // SIMU + #if !defined(NDEBUG) + #define assert(x) {if(!(x)) __asm__("BKPT");} + #else // DEBUG + #define assert(x) + #endif +#endif // SIMU diff --git a/radio/src/frftl.cpp b/radio/src/frftl.cpp new file mode 100644 index 00000000000..208b0f3bfe2 --- /dev/null +++ b/radio/src/frftl.cpp @@ -0,0 +1,990 @@ +/* + * Copyright (C) EdgeTX + * + * Authors: + * Dr. Richard Li + * + * 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. + */ + +/* + * FrFTL - Flash Resident Flash Translation Layer + * + * This is a FTL designed for NOR flash, logical to physical mapping uses 2 layers + * of translation tables all resident in flash. It comes with mechanisms to ensure + * the integrity of the data in previous state when power out occurs in the middle + * of flash programming. + * + * It can be used to back the FatFS library by ChaN and included the support of + * CTRL_SYNC and CTRL_TRIM functions for best performance. + * + */ + +#include +#include +#include +#include "frftl.h" +#include "crc.h" + +#define SECTOR_SIZE 512 +#define PAGE_SIZE 4096 +#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE) +#define TT_PAGE_MAGIC 0xEF87364A +#define TT_RECORDS_PER_PAGE 1024 +#define BUFFER_SIZE_MULTIPLIER 4 // The multiplier for cache buffers, min recommendation is 2 +#define RESERVED_PAGES_MULTIPLIER 16 // Reserve pages to minimize the erase cycles when the FS is full, + // should be at least 2 times of BUFFER_SIZE_MULTIPLIER + +typedef enum +{ + UNKNOWN, + USED, + ERASE_REQUIRED, + ERASED +} PhysicalPageState; + +typedef enum +{ + NONE, + PROGRAM, + ERASE_PROGRAM, + RELOCATE_ERASE_PROGRAM +} ProgramMode; + +typedef struct +{ + int16_t physicalPageNo; + uint8_t sectStatus; +} __attribute__((__packed__)) PageInfo; + +typedef struct +{ + uint32_t magicStart; + uint32_t logicalPageNo; + uint32_t serial; + uint16_t padding; + uint16_t crc16; +} __attribute__((__packed__)) TransTableHeader; + +typedef struct +{ + TransTableHeader header; + PageInfo pageInfos[TT_RECORDS_PER_PAGE]; +} TransTable; + +typedef union +{ + TransTable tt; + uint8_t data[PAGE_SIZE]; +} Page; + +typedef struct +{ + int16_t logicalPageNo; // Required for first program or reprogram + int16_t physicalPageNo; + uint8_t lru; // Index for physicalPageNo, 0 is most used + bool lock; // Page locked for delayed update + uint8_t sectorEraseRequired; // Record which sector need to be erased before update + ProgramMode pMode; + Page page; +} __attribute__((__packed__)) PageBuffer; + +static const uint8_t supportedFlashSizes[] = { 4, 8, 16, 32, 64, 128 }; + +static PhysicalPageState getPhysicalPageState(FrFTL* ftl, int16_t physicalPageNo) +{ + uint32_t idx = physicalPageNo >> 4; + uint32_t result = (ftl->physicalPageState[idx] >> ((physicalPageNo & 0xf) * 2)); + return (PhysicalPageState)(result & 0x3); +} + +static void setPhysicalPageState(FrFTL* ftl, int16_t physicalPageNo, PhysicalPageState state) +{ + uint32_t idx = physicalPageNo >> 4; + uint32_t mask = 0x3 << ((physicalPageNo & 0xf) * 2); + ftl->physicalPageState[idx] &= ~mask; + ftl->physicalPageState[idx] |= ((state & 0x3) << ((physicalPageNo & 0xf) * 2)); +} + +static uint16_t calcCRC(TransTableHeader* header) +{ + header->padding = 0xffff; + uint16_t crc = crc16(CRC_1021, (uint8_t*)header, sizeof(TransTableHeader) - 2, 0xffff); + return crc; +} + +static void resolveUnknownState(FrFTL* ftl, uint16_t count) +{ + if (ftl->physicalPageStateResolved) + { + return; + } + PhysicalPageState state; + uint16_t idx = ftl->writeFrontier; + bool earlyEnd = false; + for (int i = 0; i < ftl->physicalPageCount; i++) + { + if (getPhysicalPageState(ftl, idx) == UNKNOWN) + { + PhysicalPageState state = ftl->isFlashErased(idx * PAGE_SIZE) ? ERASED : ERASE_REQUIRED; + setPhysicalPageState(ftl, idx, state); + count--; + if (count == 0) + { + earlyEnd = true; + break; + } + } + idx++; + if (idx >= ftl->physicalPageCount) + { + idx = 0; + } + } + if (!earlyEnd) + { + ftl->physicalPageStateResolved = true; + } +} + +static PageBuffer* findPhysicalPageInBuffer(FrFTL* ftl, uint16_t physicalPageNo) +{ + PageBuffer* pageBuffer = ((PageBuffer*)(ftl->pageBuffer)); + for (uint8_t i = 0; i < ftl->pageBufferSize; i++) + { + PageBuffer* iBuffer = (pageBuffer + i); + if (iBuffer->physicalPageNo == physicalPageNo) + { + // Found physical page in buffer + // Update LRU + for (uint8_t j = 0; j < ftl->pageBufferSize; j++) + { + if ((pageBuffer + j)->lru == i && j > 0) + { + uint8_t temp = i; + while (j > 0) + { + (pageBuffer + j)->lru = (pageBuffer + j - 1)->lru; + j--; + } + pageBuffer->lru = temp; + break; + } + } + + return iBuffer; + } + } + return NULL; +} + +static PageBuffer* loadPhysicalPageInBuffer(FrFTL* ftl, uint16_t logicalPageNo, uint16_t physicalPageNo) +{ + // Find page in buffer + PageBuffer* pageBuffer = ((PageBuffer*)(ftl->pageBuffer)); + PageBuffer* currentBuffer = findPhysicalPageInBuffer(ftl, physicalPageNo); + if (currentBuffer == NULL) + { + // Page not in buffer, load page in buffer + uint8_t bufferIdx = 0; + for (int i = ftl->pageBufferSize - 1; i >= 0; i--) + { + bufferIdx = (pageBuffer + i)->lru; + currentBuffer = (pageBuffer + bufferIdx); + if (!currentBuffer->lock) + { + break; + } + } + if (currentBuffer->lock) + { + return NULL; + } + currentBuffer->physicalPageNo = -1; + if (!ftl->flashRead(physicalPageNo * PAGE_SIZE, currentBuffer->page.data, PAGE_SIZE)) + { + return NULL; + } + currentBuffer->logicalPageNo = logicalPageNo; + currentBuffer->physicalPageNo = physicalPageNo; + currentBuffer->lock = false; + currentBuffer->sectorEraseRequired = 0; + currentBuffer->pMode = NONE; + + // Update LRU + for (uint8_t j = 0; j < ftl->pageBufferSize; j++) + { + if ((pageBuffer + j)->lru == bufferIdx && j > 0) + { + uint8_t temp = bufferIdx; + while (j > 0) + { + (pageBuffer + j)->lru = (pageBuffer + j - 1)->lru; + j--; + } + pageBuffer->lru = temp; + break; + } + } + } + + return currentBuffer; +} + +static PageBuffer* initPhysicalPageInBuffer(FrFTL* ftl, uint16_t logicalPageNo, uint16_t physicalPageNo) +{ + // Find page in buffer + PageBuffer* currentBuffer = findPhysicalPageInBuffer(ftl, physicalPageNo); + if (currentBuffer == NULL) + { + // Page not in buffer, load page in buffer + PageBuffer* pageBuffer = ((PageBuffer*)(ftl->pageBuffer)); + uint8_t bufferIdx; + for (int i = ftl->pageBufferSize - 1; i >= 0; i--) + { + bufferIdx = (pageBuffer + i)->lru; + currentBuffer = (pageBuffer + bufferIdx); + if (!currentBuffer->lock) + { + break; + } + } + if (currentBuffer->lock) + { + return NULL; + } + currentBuffer->logicalPageNo = logicalPageNo; + currentBuffer->physicalPageNo = physicalPageNo; + currentBuffer->lock = true; + currentBuffer->sectorEraseRequired = 0; + currentBuffer->pMode = ERASE_PROGRAM; + + memset(currentBuffer->page.data, 0xff, PAGE_SIZE); + } + + return currentBuffer; +} + +static bool hasFreeBuffers(FrFTL* ftl, uint16_t bufferCount) +{ + uint16_t freeBufferFound = 0; + PageBuffer* pageBuffer = ((PageBuffer*)(ftl->pageBuffer)); + for (uint8_t i = 0; i < ftl->pageBufferSize; i++) + { + if (!(pageBuffer + i)->lock) + { + freeBufferFound++; + if (freeBufferFound >= bufferCount) + { + return true; + } + } + } + return false; +} + +static bool readPhysicalSector(FrFTL* ftl, uint8_t* buffer, uint16_t logicalPageNo, uint16_t physicalPageNo, uint8_t pageSectorNo) +{ + PageBuffer* pageBuffer = loadPhysicalPageInBuffer(ftl, logicalPageNo, physicalPageNo); + if (pageBuffer != NULL) + { + memcpy(buffer, pageBuffer->page.data + pageSectorNo * SECTOR_SIZE, SECTOR_SIZE); + return true; + } + return false; +} + +static bool readPhysicalPageInfo(FrFTL* ftl, PageInfo* pageInfo, uint16_t logicalPageNo, uint16_t physicalPageNo, uint16_t recordNo) +{ +// printf("readPhysicalPageInfo: %d, %d", physicalPageNo, recordNo); + PageBuffer* pageBuffer = loadPhysicalPageInBuffer(ftl, logicalPageNo, physicalPageNo); + if (pageBuffer != NULL) + { + // Page found in buffer + memcpy(pageInfo, &pageBuffer->page.tt.pageInfos[recordNo], sizeof(PageInfo)); + return true; + } + return false; +} + +static bool readPageInfo(FrFTL* ftl, PageInfo* pageInfo, uint16_t logicalPageNo) +{ + if (logicalPageNo < TT_RECORDS_PER_PAGE) + { + // Read from master TT + return readPhysicalPageInfo(ftl, pageInfo, 0, ftl->mttPhysicalPageNo, logicalPageNo); + } + else + { + // Lookup from secondary TT from master TT + PageInfo secondaryTTInfo; + uint16_t sttLogicalPageNo = logicalPageNo / TT_RECORDS_PER_PAGE; + if (!readPhysicalPageInfo(ftl, &secondaryTTInfo, 0, ftl->mttPhysicalPageNo, sttLogicalPageNo)) + { + return false; + } + + // Read from secondary TT + return readPhysicalPageInfo(ftl, pageInfo, sttLogicalPageNo, secondaryTTInfo.physicalPageNo, logicalPageNo % TT_RECORDS_PER_PAGE); + } +} + +static bool updatePhysicalPageInfo(FrFTL* ftl, PageInfo* pageInfo, uint16_t logicalPageNo, uint16_t physicalPageNo, uint16_t recordNo) +{ + PageBuffer* pageBuffer = loadPhysicalPageInBuffer(ftl, logicalPageNo, physicalPageNo); + if (!pageBuffer) + { + return false; + } + + // Update info, need to lock and ensure update + pageBuffer->lock = true; + if (pageBuffer->pMode == NONE) + { + pageBuffer->pMode = PROGRAM; + } + memcpy(&pageBuffer->page.tt.pageInfos[recordNo], pageInfo, sizeof(PageInfo)); + + return true; +} + +static bool updatePageInfo(FrFTL* ftl, PageInfo* pageInfo, uint16_t logicalPageNo) +{ + if (logicalPageNo < TT_RECORDS_PER_PAGE) + { + // Update to master TT + return updatePhysicalPageInfo(ftl, pageInfo, 0, ftl->mttPhysicalPageNo, logicalPageNo); + } + else + { + // Lookup from secondary TT from master TT + PageInfo secondaryTTInfo; + uint16_t sttLogicalPageNo = logicalPageNo / TT_RECORDS_PER_PAGE; + if (!readPhysicalPageInfo(ftl, &secondaryTTInfo, 0, ftl->mttPhysicalPageNo, sttLogicalPageNo)) + { + return false; + } + + // Program to secondary TT + return updatePhysicalPageInfo(ftl, pageInfo, sttLogicalPageNo, secondaryTTInfo.physicalPageNo, logicalPageNo % TT_RECORDS_PER_PAGE); + } +} + +static int16_t allocatePhysicalPage(FrFTL* ftl) +{ + uint16_t lookupCount = 0; + while (getPhysicalPageState(ftl, ftl->writeFrontier) == USED) + { + ftl->writeFrontier++; + if (ftl->writeFrontier >= ftl->physicalPageCount) + { + ftl->writeFrontier = 0; + } + lookupCount++; + if (lookupCount > ftl->physicalPageCount) + { + printf("BUG: writeFrontier = %d\n", ftl->writeFrontier); + return -1; // BUG + } + } + + uint16_t physicalPageNo = ftl->writeFrontier++; + if (ftl->writeFrontier >= ftl->physicalPageCount) + { + ftl->writeFrontier = 0; + } + + return physicalPageNo; +} + +static bool programPageInBuffer(FrFTL* ftl, PageBuffer* buffer) +{ + uint32_t addr; + int16_t newPhysicalPageNo; + switch (buffer->pMode) + { + case PROGRAM: + // Program only + if (!ftl->flashProgram(buffer->physicalPageNo * PAGE_SIZE, buffer->page.data, PAGE_SIZE)) + { + return false; + } + setPhysicalPageState(ftl, buffer->physicalPageNo, USED); + break; + case ERASE_PROGRAM: + addr = buffer->physicalPageNo * PAGE_SIZE; + if (getPhysicalPageState(ftl, buffer->physicalPageNo) != ERASED) + { + // Do erase on the fly + if (!ftl->flashErase(addr)) + { + return false; + } + } + if (!ftl->flashProgram(addr, buffer->page.data, PAGE_SIZE)) + { + return false; + } + setPhysicalPageState(ftl, buffer->physicalPageNo, USED); + break; + case RELOCATE_ERASE_PROGRAM: + // Reprogram + newPhysicalPageNo = allocatePhysicalPage(ftl); + if (newPhysicalPageNo < 0) + { + return false; + } + + if (buffer->logicalPageNo < ftl->ttPageCount) + { + if (buffer->logicalPageNo == 0) + { + // MTT need update physicalPageNo + buffer->page.tt.pageInfos[0].physicalPageNo = newPhysicalPageNo; + } + + // TT page, need update serial and CRC + buffer->page.tt.header.serial++; + buffer->page.tt.header.crc16 = calcCRC(&buffer->page.tt.header); + } + else + { + // Data page, need to check if any sectors are mark trimmed and fill it with 0xff + for (uint8_t i = 0; i < SECTORS_PER_PAGE; i++) + { + uint8_t sectMask = 1 << i; + if ((buffer->sectorEraseRequired & sectMask) != 0) + { + memset(buffer->page.data + i * SECTOR_SIZE, 0xff, SECTOR_SIZE); + } + } + } + + addr = newPhysicalPageNo * PAGE_SIZE; + if (getPhysicalPageState(ftl, buffer->physicalPageNo) != ERASED) + { + // Do erase on the fly + if (!ftl->flashErase(addr)) + { + return false; + } + } + if (!ftl->flashProgram(addr, buffer->page.data, PAGE_SIZE)) + { + return false; + } + setPhysicalPageState(ftl, buffer->physicalPageNo, ERASE_REQUIRED); + buffer->physicalPageNo = newPhysicalPageNo; + setPhysicalPageState(ftl, buffer->physicalPageNo, USED); + if (buffer->logicalPageNo == 0) + { + // MTT page, need update mttPhysicalPageNo + ftl->mttPhysicalPageNo = buffer->physicalPageNo; + } + break; + } + return true; +} + +bool ftlSync(FrFTL* ftl) +{ + PageBuffer* pageBuffer = ((PageBuffer*)(ftl->pageBuffer)); + + // First program data pages + for (int i = 0; i < ftl->pageBufferSize; i++) + { + PageBuffer* currentBuffer = pageBuffer + i; + if (currentBuffer->lock) + { + if (currentBuffer->logicalPageNo >= ftl->ttPageCount) + { + if (!programPageInBuffer(ftl, currentBuffer)) + { + return false; + } + + // Update PageInfo in TT pages + PageInfo pageInfo; + if (!readPageInfo(ftl, &pageInfo, currentBuffer->logicalPageNo)) + { + return false; + } + pageInfo.physicalPageNo = currentBuffer->physicalPageNo; + if (!updatePageInfo(ftl, &pageInfo, currentBuffer->logicalPageNo)) + { + return false; + } + + // Unlock buffer + currentBuffer->lock = false; + currentBuffer->sectorEraseRequired = 0; + currentBuffer->pMode = NONE; + } + } + } + + // Second program STT pages + PageBuffer* mttBuffer = loadPhysicalPageInBuffer(ftl, 0, ftl->mttPhysicalPageNo); + if (!mttBuffer) + { + return false; + } + for (int i = 0; i < ftl->pageBufferSize; i++) + { + PageBuffer* currentBuffer = pageBuffer + i; + if (currentBuffer->lock) + { + if (currentBuffer->logicalPageNo > 0 && currentBuffer->logicalPageNo < ftl->ttPageCount) + { + if (!programPageInBuffer(ftl, currentBuffer)) + { + return false; + } + + // Update PageInfo in MTT page + mttBuffer->page.tt.pageInfos[currentBuffer->logicalPageNo].physicalPageNo = currentBuffer->physicalPageNo; + + // Unlock buffer + currentBuffer->lock = false; + currentBuffer->pMode = NONE; + } + } + } + + // Finally program MTT page + if (mttBuffer->lock) + { + if (!programPageInBuffer(ftl, mttBuffer)) + { + return false; + } + + // Unlock buffer + mttBuffer->lock = false; + mttBuffer->pMode = NONE; + } + + return true; +} + +bool ftlWrite(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors, const uint8_t* buf) +{ + resolveUnknownState(ftl, ftl->ttPageCount); + if (startSectorNo + noOfSectors > ftl->usableSectorCount) + { + return false; + } + + uint32_t sectorNo = startSectorNo; + while (noOfSectors > 0) + { + if (!hasFreeBuffers(ftl, 3)) // Max no. of sectors need to be rewritten is 3, need to ensure has enough free buffers + { + // Flush the buffers first if free space is not found + if (!ftlSync(ftl)) + { + return false; + } + } + + uint16_t logicalPageNo = sectorNo / SECTORS_PER_PAGE + ftl->ttPageCount; + uint8_t pageSectorNo = sectorNo % SECTORS_PER_PAGE; + + // Read page info + PageInfo pageInfo; + readPageInfo(ftl, &pageInfo, logicalPageNo); + PageBuffer* dataBuffer; + + // Allocate new physical page for uninitialized logical page + if (pageInfo.physicalPageNo < 0) + { + // Need allocate physical page + if ((pageInfo.physicalPageNo = allocatePhysicalPage(ftl)) < 0) + { + return false; + } + + // Init page in in buffer, locked for delayed program + dataBuffer = initPhysicalPageInBuffer(ftl, logicalPageNo, pageInfo.physicalPageNo); + if (!dataBuffer) + { + return false; + } + + pageInfo.sectStatus = 0xff; + + if (!updatePageInfo(ftl, &pageInfo, logicalPageNo)) + { + return false; + } + } + else + { + dataBuffer = loadPhysicalPageInBuffer(ftl, logicalPageNo, pageInfo.physicalPageNo); + if (!dataBuffer) + { + return false; + } + } + + uint8_t sectMask = 1 << pageSectorNo; + if ((pageInfo.sectStatus & sectMask) != 0) + { + // Sector never write, append information + pageInfo.sectStatus &= ~sectMask; + dataBuffer->sectorEraseRequired &= ~sectMask; + if (!updatePageInfo(ftl, &pageInfo, logicalPageNo)) + { + return false; + } + + // Update sector, locked for delayed program + dataBuffer->lock = true; + if (dataBuffer->pMode == NONE) + { + dataBuffer->pMode = PROGRAM; + } + memcpy(dataBuffer->page.data + pageSectorNo * SECTOR_SIZE, buf, SECTOR_SIZE); + } + else + { + // Sector already written, use replace write + // Lock page for delayed update with reprogram + dataBuffer->lock = true; + dataBuffer->pMode = RELOCATE_ERASE_PROGRAM; + memcpy(dataBuffer->page.data + pageSectorNo * SECTOR_SIZE, buf, SECTOR_SIZE); + + // Read TT pages and lock it for later update + PageBuffer* ttBuffer; + PageInfo ttPageInfo; + uint16_t ttPageNo = logicalPageNo / TT_RECORDS_PER_PAGE; + if (!readPageInfo(ftl, &ttPageInfo, ttPageNo)) + { + return false; + } + ttBuffer = loadPhysicalPageInBuffer(ftl, ttPageNo, ttPageInfo.physicalPageNo); + ttBuffer->lock = true; + ttBuffer->pMode = RELOCATE_ERASE_PROGRAM; + if (ttPageNo > 0) + { + ttBuffer = loadPhysicalPageInBuffer(ftl, 0, ftl->mttPhysicalPageNo); + ttBuffer->lock = true; + ttBuffer->pMode = RELOCATE_ERASE_PROGRAM; + } + } + + noOfSectors--; + sectorNo++; + buf += SECTOR_SIZE; + } + + return true; +} + +bool ftlRead(FrFTL* ftl, uint32_t sectorNo, uint8_t* buffer) +{ +// doGC(ftl, ftl->ttPageCount, 1); + if (sectorNo >= ftl->usableSectorCount) + { + return false; + } + + uint16_t logicalPageNo = sectorNo / SECTORS_PER_PAGE + ftl->ttPageCount; + uint8_t pageSectorNo = sectorNo % SECTORS_PER_PAGE; + + // Read page info + PageInfo pageInfo; + readPageInfo(ftl, &pageInfo, logicalPageNo); + + // Check if sector written before + uint8_t sectMask = 1 << pageSectorNo; + if ((pageInfo.sectStatus & sectMask) != 0) + { + // Sector never write, return init content + memset(buffer, 0xff, SECTOR_SIZE); + return true; + } + else + { + return readPhysicalSector(ftl, buffer, logicalPageNo, pageInfo.physicalPageNo, pageSectorNo); + } +} + +bool ftlTrim(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors) +{ + resolveUnknownState(ftl, ftl->ttPageCount); + if (startSectorNo + noOfSectors > ftl->usableSectorCount) + { + return false; + } + + uint32_t sectorNo = startSectorNo; + while (noOfSectors > 0) + { + if (!hasFreeBuffers(ftl, 3)) // Max no. of sectors need to be rewritten is 3, need to ensure has enough free buffers + { + // Flush the buffers first if free space is not found + if (!ftlSync(ftl)) + { + return false; + } + } + + uint16_t logicalPageNo = sectorNo / SECTORS_PER_PAGE + ftl->ttPageCount; + uint8_t pageSectorNo = sectorNo % SECTORS_PER_PAGE; + + // Read page info + PageInfo pageInfo; + readPageInfo(ftl, &pageInfo, logicalPageNo); + + // Check if physical page in used + if (pageInfo.physicalPageNo >= 0) + { + uint8_t sectMask = 1 << pageSectorNo; + if ((pageInfo.sectStatus & sectMask) != 1) + { + // Sector used, free it + PageBuffer* dataBuffer = loadPhysicalPageInBuffer(ftl, logicalPageNo, pageInfo.physicalPageNo); + if (!dataBuffer) + { + return false; + } + + pageInfo.sectStatus |= sectMask; + if (pageInfo.sectStatus == 0xff) + { + // Free whole page + setPhysicalPageState(ftl, pageInfo.physicalPageNo, ERASE_REQUIRED); + pageInfo.physicalPageNo = -1; // Invalidate page info + dataBuffer->physicalPageNo = -1; // Invalidate buffer + dataBuffer->lock = false; + } + else + { + // Locked for delayed relocate, fill and program + dataBuffer->lock = true; + dataBuffer->pMode = RELOCATE_ERASE_PROGRAM; + dataBuffer->sectorEraseRequired |= sectMask; + } + + // Update page info + if (!updatePageInfo(ftl, &pageInfo, logicalPageNo)) + { + return false; + } + + // Read TT pages and lock it for later update + PageBuffer* ttBuffer; + PageInfo ttPageInfo; + uint16_t ttPageNo = logicalPageNo / TT_RECORDS_PER_PAGE; + if (!readPageInfo(ftl, &ttPageInfo, ttPageNo)) + { + return false; + } + ttBuffer = loadPhysicalPageInBuffer(ftl, ttPageNo, ttPageInfo.physicalPageNo); + ttBuffer->lock = true; + ttBuffer->pMode = RELOCATE_ERASE_PROGRAM; + if (ttPageNo > 0) + { + ttBuffer = loadPhysicalPageInBuffer(ftl, 0, ftl->mttPhysicalPageNo); + ttBuffer->lock = true; + ttBuffer->pMode = RELOCATE_ERASE_PROGRAM; + } + } + } + noOfSectors--; + sectorNo++; + } + + return true; +} + +static void initPageBuffer(FrFTL* ftl) +{ + size_t bufferSize = sizeof(PageBuffer) * ftl->pageBufferSize; + ftl->memoryUsed += bufferSize; + ftl->pageBuffer = (PageBuffer*)malloc(bufferSize); + for (int8_t i = 0; i < ftl->pageBufferSize; i++) + { + PageBuffer* currentBuffer = ((PageBuffer*)ftl->pageBuffer) + i; + currentBuffer->logicalPageNo = -1; + currentBuffer->physicalPageNo = -1; + currentBuffer->lru = i; + currentBuffer->lock = false; + currentBuffer->pMode = NONE; + } +} + +static void initTransTablePage(FrFTL* ftl, Page* page, uint32_t logicalPageNo) +{ + memset(page->data, 0xff, PAGE_SIZE); + page->tt.header.magicStart = TT_PAGE_MAGIC; + page->tt.header.logicalPageNo = logicalPageNo; + page->tt.header.serial = 1; + page->tt.header.crc16 = calcCRC(&page->tt.header); +} + +void createFTL(FrFTL* ftl) +{ + // Resolve the first few blocks for proper startup + ftl->writeFrontier = 0; + resolveUnknownState(ftl, ftl->pageBufferSize); + + Page mtt; + initTransTablePage(ftl, &mtt, 0); + mtt.tt.pageInfos[0].physicalPageNo = 0; + mtt.tt.pageInfos[0].sectStatus = 0; + + Page stt; + for (int i = 1; i < ftl->ttPageCount; i++) + { + initTransTablePage(ftl, &stt, i); + uint32_t addr = i * PAGE_SIZE; + if (getPhysicalPageState(ftl, i) != ERASED) + { + ftl->flashErase(addr); + } + ftl->flashProgram(addr, stt.data, PAGE_SIZE); + setPhysicalPageState(ftl, i, USED); + mtt.tt.pageInfos[i].physicalPageNo = i; + mtt.tt.pageInfos[i].sectStatus = 0; + } + + if (getPhysicalPageState(ftl, 0) != ERASED) + { + ftl->flashErase(0); + } + ftl->flashProgram(0, mtt.data, PAGE_SIZE); + setPhysicalPageState(ftl, 0, USED); + + ftl->writeFrontier = ftl->ttPageCount; +} + +static bool loadFTL(FrFTL* ftl) +{ + // Scan for MTT + uint32_t currentSerial = 0; + int16_t currentPhysicalMTTPageNo = -1; + for (int16_t i = 0; i < ftl->physicalPageCount; i++) + { + TransTableHeader header; + ftl->flashRead(i * PAGE_SIZE, (uint8_t*)&header, sizeof(header)); + if (header.magicStart == TT_PAGE_MAGIC && header.logicalPageNo == 0 && header.crc16 == calcCRC(&header)) + { + // MTT detected + if (header.serial > currentSerial) + { + // Newer MTT found + currentSerial = header.serial; + currentPhysicalMTTPageNo = i; + } + } + } + + if (currentPhysicalMTTPageNo >= 0) + { + // MTT found, load data + ftl->mttPhysicalPageNo = currentPhysicalMTTPageNo; + setPhysicalPageState(ftl, currentPhysicalMTTPageNo, USED); + ftl->writeFrontier = currentPhysicalMTTPageNo + 1; + if (ftl->writeFrontier >= ftl->physicalPageCount) + { + ftl->writeFrontier = 0; + } + + PageBuffer* mtt = loadPhysicalPageInBuffer(ftl, 0, currentPhysicalMTTPageNo); + for (int i = 1; i < TT_RECORDS_PER_PAGE; i++) + { + int16_t currentPhysicalPageNo = mtt->page.tt.pageInfos[i].physicalPageNo; + if (currentPhysicalPageNo >= 0) + { + // Used page + setPhysicalPageState(ftl, currentPhysicalPageNo, USED); + } + if (i < ftl->ttPageCount) + { + // TT pages + PageBuffer* stt = loadPhysicalPageInBuffer(ftl, i, currentPhysicalPageNo); + for (int j = 0; j < TT_RECORDS_PER_PAGE; j++) + { + currentPhysicalPageNo = stt->page.tt.pageInfos[j].physicalPageNo; + if (currentPhysicalPageNo >= 0) + { + // Used page + setPhysicalPageState(ftl, currentPhysicalPageNo, USED); + } + } + } + } + + // Walk forward to ensure some pages are resolved + resolveUnknownState(ftl, ftl->pageBufferSize); + return true; + } + + return false; +} + +FrFTL* ftlInit(FlashReadCB rf, FlashProgramCB pf, FlashEraseCB ef, IsFlashErasedCB ief, uint8_t flashSizeInMB) +{ + // Check flash size + bool found = false; + for (uint8_t i = 0; i < sizeof(supportedFlashSizes); i++) + { + if (flashSizeInMB == supportedFlashSizes[i]) + { + found = true; + break; + } + } + if (!found) + { + return NULL; + } + + FrFTL* ftl = (FrFTL*)calloc(sizeof(FrFTL), 1); + ftl->memoryUsed = sizeof(FrFTL); + ftl->flashRead = rf; + ftl->flashProgram = pf; + ftl->flashErase = ef; + ftl->isFlashErased = ief; + ftl->mttPhysicalPageNo = 0; + ftl->physicalPageCount = flashSizeInMB * 1024 * 1024 / PAGE_SIZE; + ftl->ttPageCount = ftl->physicalPageCount / TT_RECORDS_PER_PAGE; + ftl->usableSectorCount = (ftl->physicalPageCount - ftl->ttPageCount * RESERVED_PAGES_MULTIPLIER) * SECTORS_PER_PAGE; + uint32_t stateSize = ftl->physicalPageCount / 16 + (ftl->physicalPageCount % 16 > 0 ? 1 : 0); + ftl->physicalPageState = (uint32_t*)calloc(stateSize, sizeof(uint32_t)); + ftl->physicalPageStateResolved = false; + ftl->memoryUsed += stateSize * sizeof(uint32_t); + ftl->pageBufferSize = ftl->ttPageCount * BUFFER_SIZE_MULTIPLIER; + initPageBuffer(ftl); + + if (!loadFTL(ftl)) + { + createFTL(ftl); + } + return ftl; +} + +void ftlDeInit(FrFTL* ftl) +{ + free(ftl->pageBuffer); + free(ftl->physicalPageState); + free(ftl); +} + diff --git a/radio/src/frftl.h b/radio/src/frftl.h new file mode 100644 index 00000000000..11da14fd28f --- /dev/null +++ b/radio/src/frftl.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) EdgeTX + * + * Authors: + * Dr. Richard Li + * + * 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. + */ + +/* + * FrFTL - Flash Resident Flash Translation Layer + * + * This is a FTL designed for NOR flash, logical to physical mapping uses 2 layers + * of translation tables all resident in flash. It comes with mechanisms to ensure + * the integrity of the data in previous state when power out occurs in the middle + * of flash programming. + * + * It can be used to back the FatFS library by ChaN and included the support of + * CTRL_SYNC and CTRL_TRIM functions for best performance. + * + */ + +#ifndef _FRFTL_H_ +#define _FRFTL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef bool (*FlashReadCB)(uint32_t addr, uint8_t* buf, uint32_t len); + typedef bool (*FlashProgramCB)(uint32_t addr, const uint8_t* buf, uint32_t len); + typedef bool (*FlashEraseCB)(uint32_t addr); + typedef bool (*IsFlashErasedCB)(uint32_t addr); + + typedef struct + { + FlashReadCB flashRead; + FlashProgramCB flashProgram; + FlashEraseCB flashErase; + IsFlashErasedCB isFlashErased; + uint32_t mttPhysicalPageNo; + uint16_t physicalPageCount; + uint8_t ttPageCount; + uint32_t usableSectorCount; + uint16_t writeFrontier; + uint32_t* physicalPageState; + bool physicalPageStateResolved; + uint16_t pageBufferSize; + void *pageBuffer; + size_t memoryUsed; + } FrFTL; + + FrFTL* ftlInit(FlashReadCB rf, FlashProgramCB pf, FlashEraseCB ef, IsFlashErasedCB ief, uint8_t flashSizeInMB); + void ftlDeInit(FrFTL *ftl); + bool ftlWrite(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors, const uint8_t* buf); + bool ftlRead(FrFTL* ftl, uint32_t sectorNo, uint8_t* buffer); + bool ftlTrim(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors); + bool ftlSync(FrFTL* ftl); + +#ifdef __cplusplus +} +#endif + +#endif // _FRFTL_H_ diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index 0a1a9322524..fb9189e4b95 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "switches.h" +#include "logs.h" #if defined(COLORLCD) void setRequestedMainView(uint8_t view); @@ -287,7 +288,7 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext break; } -#if defined(SDCARD) +#if defined(SDCARD)||1 case FUNC_PLAY_SOUND: case FUNC_PLAY_TRACK: case FUNC_PLAY_VALUE: diff --git a/radio/src/gui/128x64/bmp.cpp b/radio/src/gui/128x64/bmp.cpp index 4e82d1bc3e7..dc1ab9078e1 100644 --- a/radio/src/gui/128x64/bmp.cpp +++ b/radio/src/gui/128x64/bmp.cpp @@ -20,11 +20,12 @@ */ #include "opentx.h" +#include "VirtualFS.h" uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uint8_t height) { - FIL bmpFile; - UINT read; + VfsFile bmpFile; + size_t read; uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ uint8_t * buf = &bmpBuf[0]; @@ -32,24 +33,24 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin return nullptr; } - FRESULT result = f_open(&bmpFile, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + VfsError result = VirtualFS::instance().openFile(bmpFile, filename, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result != VfsError::OK) { return nullptr; } - if (f_size(&bmpFile) < 14) { - f_close(&bmpFile); + if (bmpFile.size() < 14) { + bmpFile.close(); return nullptr; } - result = f_read(&bmpFile, buf, 14, &read); - if (result != FR_OK || read != 14) { - f_close(&bmpFile); + result = bmpFile.read(buf, 14, read); + if (result != VfsError::OK || read != 14) { + bmpFile.close(); return nullptr; } if (buf[0] != 'B' || buf[1] != 'M') { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -57,9 +58,9 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); - result = f_read(&bmpFile, buf, len, &read); - if (result != FR_OK || read != len) { - f_close(&bmpFile); + result = bmpFile.read(buf, len, read); + if (result != VfsError::OK || read != len) { + bmpFile.close(); return nullptr; } @@ -67,18 +68,18 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin /* invalid header size */ if (ihsize + 14 > hsize) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } /* sometimes file size is set to some headers size, set a real size in that case */ if (fsize == 14 || fsize == ihsize + 14) { - fsize = f_size(&bmpFile) - 2; + fsize = bmpFile.size() - 2; } /* declared file size less than header size */ if (fsize <= hsize) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -100,17 +101,17 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin buf += 8; break; default: - f_close(&bmpFile); + bmpFile.close(); return nullptr; } if (*((uint16_t *)&buf[0]) != 1) { /* planes */ - f_close(&bmpFile); + bmpFile.close(); return nullptr; } if (w > width || h > height) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -118,8 +119,8 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin buf = &bmpBuf[0]; - if (f_lseek(&bmpFile, hsize) != FR_OK) { - f_close(&bmpFile); + if (bmpFile.lseek(hsize) != VfsError::OK) { + bmpFile.close(); return nullptr; } @@ -136,9 +137,9 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin case 1: rowSize = ((w + 31) / 32) * 4; for (int8_t i=h-1; i>=0; i--) { - result = f_read(&bmpFile, buf, rowSize, &read); - if (result != FR_OK || read != rowSize) { - f_close(&bmpFile); + result = bmpFile.read(buf, rowSize, read); + if (result != VfsError::OK || read != rowSize) { + bmpFile.close(); return nullptr; } @@ -152,10 +153,10 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint8_t width, uin break; default: - f_close(&bmpFile); + bmpFile.close(); return nullptr; } - f_close(&bmpFile); + bmpFile.close(); return bmp; } diff --git a/radio/src/gui/128x64/gui.h b/radio/src/gui/128x64/gui.h index 477155f70a3..14416e1b70b 100644 --- a/radio/src/gui/128x64/gui.h +++ b/radio/src/gui/128x64/gui.h @@ -132,14 +132,10 @@ extern uint8_t s_copySrcCh; extern int8_t s_currCh; extern uint8_t s_maxLines; -#if defined(SDCARD) #define STATUS_LINE_LENGTH 32 extern char statusLineMsg[STATUS_LINE_LENGTH]; void showStatusLine(); void drawStatusLine(); -#else -#define drawStatusLine() -#endif void menuTextView(event_t event); void pushMenuTextView(const char *filename); diff --git a/radio/src/gui/128x64/lcd.cpp b/radio/src/gui/128x64/lcd.cpp index 0a7f8739cd7..966cbc8f0bb 100644 --- a/radio/src/gui/128x64/lcd.cpp +++ b/radio/src/gui/128x64/lcd.cpp @@ -22,6 +22,7 @@ #include #include +#include "edgetx_assert.h" #include "lcd.h" #include "thirdparty/libopenui/src/bitfield.h" #include "gui/common/stdlcd/fonts.h" diff --git a/radio/src/gui/128x64/model_custom_scripts.cpp b/radio/src/gui/128x64/model_custom_scripts.cpp index 7727034a62e..7219d167d06 100644 --- a/radio/src/gui/128x64/model_custom_scripts.cpp +++ b/radio/src/gui/128x64/model_custom_scripts.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" void copySelection(char * dst, const char * src, uint8_t size) { @@ -34,7 +35,7 @@ void onModelCustomScriptMenu(const char *result) ScriptData &sd = g_model.scriptsData[s_currIdx]; if (result == STR_UPDATE_LIST) { - if (!sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), nullptr)) { + if (!VirtualFS::instance().listFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), nullptr)) { POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); } } @@ -81,7 +82,7 @@ void menuModelCustomScriptOne(event_t event) lcdDrawTextAtIndex(SCRIPT_ONE_2ND_COLUMN_POS, y, STR_VCSWFUNC, 0, attr); if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && !READ_ONLY()) { s_editMode = 0; - if (sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { + if (VirtualFS::instance().listFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { POPUP_MENU_START(onModelCustomScriptMenu); } else { diff --git a/radio/src/gui/128x64/model_display.cpp b/radio/src/gui/128x64/model_display.cpp index bf5365b52be..e59d295857c 100644 --- a/radio/src/gui/128x64/model_display.cpp +++ b/radio/src/gui/128x64/model_display.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" enum MenuModelDisplayItems { ITEM_DISPLAY_SCREEN_LABEL1, @@ -93,7 +94,7 @@ void onTelemetryScriptFileSelectionMenu(const char * result) int screenIndex = DISPLAY_CURRENT_SCREEN(menuVerticalPosition - HEADER_LINE); if (result == STR_UPDATE_LIST) { - if (!sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), nullptr)) { + if (!VirtualFS::instance().listFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), nullptr)) { POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); } } @@ -164,7 +165,7 @@ void menuModelDisplay(event_t event) if (menuHorizontalPosition==1 && attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { s_editMode = 0; - if (sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), g_model.screens[screenIndex].script.file)) { + if (VirtualFS::instance().listFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), g_model.screens[screenIndex].script.file)) { POPUP_MENU_START(onTelemetryScriptFileSelectionMenu); } else { diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index 9e4fcec2c73..eca13a1d014 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" #define MODELSEL_W LCD_W @@ -53,7 +54,6 @@ void onModelSelectMenu(const char * result) s_copyTgtOfs = 0; s_copySrcRow = -1; } -#if defined(SDCARD) else if (result == STR_BACKUP_MODEL) { storageCheck(true); // force writing of current model data before this is changed POPUP_WARNING(backupModel(sub)); @@ -68,17 +68,15 @@ void onModelSelectMenu(const char * result) ext = STR_MODELS_EXT; path = STR_MODELS_PATH; #endif - if (sdListFiles(path, ext, MENU_LINE_LENGTH-1, nullptr)) + if (VirtualFS::instance().listFiles(path, ext, MENU_LINE_LENGTH-1, nullptr)) POPUP_MENU_START(onModelSelectMenu); else POPUP_WARNING(STR_NO_MODELS_ON_SD); } -#endif else if (result == STR_DELETE_MODEL) { POPUP_CONFIRMATION(STR_DELETEMODEL, onDeleteModelConfirm); SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), 0); } -#if defined(SDCARD) else if (result != STR_EXIT) { // The user choosed a file on SD to restore storageCheck(true); @@ -87,7 +85,6 @@ void onModelSelectMenu(const char * result) loadModel(sub); } } -#endif } static void moveToFreeModelSlot(bool forward, int8_t& sub, int8_t oldSub) @@ -218,12 +215,8 @@ void menuModelSelect(event_t event) POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL); } else { -#if defined(SDCARD) POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL); POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL); -#else - selectModel(sub); -#endif } } else { diff --git a/radio/src/gui/128x64/model_special_functions.cpp b/radio/src/gui/128x64/model_special_functions.cpp index 94920244757..5b0be31c3f3 100644 --- a/radio/src/gui/128x64/model_special_functions.cpp +++ b/radio/src/gui/128x64/model_special_functions.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" #define MODEL_SPECIAL_FUNC_1ST_COLUMN (0) #define MODEL_SPECIAL_FUNC_2ND_COLUMN (4*FW-1) @@ -31,7 +32,6 @@ #define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF (18*FW+2) #endif -#if defined(SDCARD) #define SD_LOGS_PERIOD_MIN 1 // 0.1s fastest period #define SD_LOGS_PERIOD_MAX 255 // 25.5s slowest period #define SD_LOGS_PERIOD_DEFAULT 10 // 1s default period for newly created SF @@ -62,7 +62,7 @@ void onCustomFunctionsFileSelectionMenu(const char * result) strcpy(directory, SOUNDS_PATH); strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); } - if (!sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), nullptr)) { + if (!VirtualFS::instance().listFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), nullptr)) { POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD); } } @@ -72,7 +72,6 @@ void onCustomFunctionsFileSelectionMenu(const char * result) storageDirty(eeFlags); } } -#endif // SDCARD #if defined(PCBTARANIS) void onAdjustGvarSourceLongEnterPress(const char * result) @@ -304,7 +303,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); } #endif -#if defined(SDCARD) else if (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT) { if (ZEXIST(cfn->play.name)) lcdDrawSizedText(MODEL_SPECIAL_FUNC_3RD_COLUMN-6, y, cfn->play.name, sizeof(cfn->play.name), attr); @@ -320,7 +318,7 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF strcpy(directory, SOUNDS_PATH); strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); } - if (sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) { + if (VirtualFS::instance().listFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) { POPUP_MENU_START(onCustomFunctionsFileSelectionMenu); } else { @@ -337,7 +335,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(functionsContext == &globalFunctionsContext ? isSourceAvailableInGlobalFunctions : isSourceAvailable); } } -#endif // SDCARD else if (func == FUNC_VOLUME) { val_max = MIXSRC_LAST_CH; drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); @@ -354,7 +351,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(isSourceAvailable); } } -#if defined(SDCARD) else if (func == FUNC_LOGS) { val_min = SD_LOGS_PERIOD_MIN; val_max = SD_LOGS_PERIOD_MAX; @@ -366,7 +362,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|PREC1|LEFT); lcdDrawChar(lcdLastRightPos, y, 's'); } -#endif #if defined(GVARS) else if (func == FUNC_ADJUST_GVAR) { switch (CFN_GVAR_MODE(cfn)) { diff --git a/radio/src/gui/128x64/model_telemetry_sensor.cpp b/radio/src/gui/128x64/model_telemetry_sensor.cpp index 1ce8173f400..d6d4deb3623 100644 --- a/radio/src/gui/128x64/model_telemetry_sensor.cpp +++ b/radio/src/gui/128x64/model_telemetry_sensor.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "logs.h" enum SensorFields { SENSOR_FIELD_NAME, diff --git a/radio/src/gui/128x64/widgets.cpp b/radio/src/gui/128x64/widgets.cpp index 459678eb642..292538668d1 100644 --- a/radio/src/gui/128x64/widgets.cpp +++ b/radio/src/gui/128x64/widgets.cpp @@ -251,7 +251,6 @@ int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int } #endif -#if defined(SDCARD) char statusLineMsg[STATUS_LINE_LENGTH]; tmr10ms_t statusLineTime = 0; uint8_t statusLineHeight = 0; @@ -280,4 +279,3 @@ void drawStatusLine() lcdDrawFilledRect(0, LCD_H-statusLineHeight, LCD_W, FH, SOLID); } } -#endif diff --git a/radio/src/gui/212x64/bmp.cpp b/radio/src/gui/212x64/bmp.cpp index 4cf78d7823b..25c4ca766d9 100644 --- a/radio/src/gui/212x64/bmp.cpp +++ b/radio/src/gui/212x64/bmp.cpp @@ -20,11 +20,12 @@ */ #include "opentx.h" +#include "VirtualFS.h" uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, uint16_t height) { - FIL bmpFile; - UINT read; + VfsFile bmpFile; + size_t read; uint8_t palette[16]; uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ uint8_t * buf = &bmpBuf[0]; @@ -33,24 +34,24 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui return nullptr; } - FRESULT result = f_open(&bmpFile, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + VfsError result = VirtualFS::instance().openFile(bmpFile, filename, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result != VfsError::OK) { return nullptr; } - if (f_size(&bmpFile) < 14) { - f_close(&bmpFile); + if (bmpFile.size() < 14) { + bmpFile.close(); return nullptr; } - result = f_read(&bmpFile, buf, 14, &read); - if (result != FR_OK || read != 14) { - f_close(&bmpFile); + result = bmpFile.read(buf, 14, read); + if (result != VfsError::OK || read != 14) { + bmpFile.close(); return nullptr; } if (buf[0] != 'B' || buf[1] != 'M') { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -58,9 +59,9 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); - result = f_read(&bmpFile, buf, len, &read); - if (result != FR_OK || read != len) { - f_close(&bmpFile); + result = bmpFile.read(buf, len, read); + if (result != VfsError::OK || read != len) { + bmpFile.close(); return nullptr; } @@ -68,17 +69,17 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui /* invalid header size */ if (ihsize + 14 > hsize) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } /* sometimes file size is set to some headers size, set a real size in that case */ if (fsize == 14 || fsize == ihsize + 14) - fsize = f_size(&bmpFile) - 2; + fsize = bmpFile.size() - 2; /* declared file size less than header size */ if (fsize <= hsize) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -100,17 +101,17 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui buf += 8; break; default: - f_close(&bmpFile); + bmpFile.close(); return nullptr; } if (*((uint16_t *)&buf[0]) != 1) { /* planes */ - f_close(&bmpFile); + bmpFile.close(); return nullptr; } if (w > width || h > height) { - f_close(&bmpFile); + bmpFile.close(); return nullptr; } @@ -119,8 +120,8 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui buf = &bmpBuf[0]; if (depth == 4) { - if (f_lseek(&bmpFile, hsize-64) != FR_OK || f_read(&bmpFile, buf, 64, &read) != FR_OK || read != 64) { - f_close(&bmpFile); + if (bmpFile.lseek(hsize-64) != VfsError::OK || bmpFile.read(buf, 64, read) != VfsError::OK || read != 64) { + bmpFile.close(); return nullptr; } for (uint8_t i=0; i<16; i++) { @@ -128,8 +129,8 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui } } else { - if (f_lseek(&bmpFile, hsize) != FR_OK) { - f_close(&bmpFile); + if (bmpFile.lseek(hsize) != VfsError::OK) { + bmpFile.close(); return nullptr; } } @@ -147,9 +148,9 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui case 1: rowSize = ((w+31)/32)*4; for (uint32_t i=0; i=0; i--) { - result = f_read(&bmpFile, buf, rowSize, &read); - if (result != FR_OK || read != rowSize) { - f_close(&bmpFile); + result = bmpFile.read(buf, rowSize, read); + if (result != VfsError::OK || read != rowSize) { + bmpFile.close(); return nullptr; } uint8_t * dst = dest + (i/2)*w; @@ -181,11 +182,11 @@ uint8_t * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, ui break; default: - f_close(&bmpFile); + bmpFile.close(); return nullptr; } - f_close(&bmpFile); + bmpFile.close(); return bmp; } diff --git a/radio/src/gui/212x64/lcd.cpp b/radio/src/gui/212x64/lcd.cpp index 4c4d9a80f90..0e0d82171f6 100644 --- a/radio/src/gui/212x64/lcd.cpp +++ b/radio/src/gui/212x64/lcd.cpp @@ -22,13 +22,12 @@ #include #include +#include "edgetx_assert.h" #include "lcd.h" #include "common/stdlcd/fonts.h" #include "common/stdlcd/utf8.h" -#if !defined(SIMU) - #define assert(x) -#else +#if defined(SIMU) #include #endif diff --git a/radio/src/gui/212x64/model_custom_scripts.cpp b/radio/src/gui/212x64/model_custom_scripts.cpp index 33fffd37415..5f4942c68c7 100644 --- a/radio/src/gui/212x64/model_custom_scripts.cpp +++ b/radio/src/gui/212x64/model_custom_scripts.cpp @@ -20,13 +20,14 @@ */ #include "opentx.h" +#include "VirtualFS.h" void onModelCustomScriptMenu(const char *result) { ScriptData &sd = g_model.scriptsData[s_currIdx]; if (result == STR_UPDATE_LIST) { - if (!sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), nullptr)) { + if (!VirtualFS::instance().listFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), nullptr)) { POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); } } @@ -74,7 +75,7 @@ void menuModelCustomScriptOne(event_t event) lcdDrawTextAtIndex(SCRIPT_ONE_2ND_COLUMN_POS, y, STR_VCSWFUNC, 0, attr); if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && !READ_ONLY()) { s_editMode = 0; - if (sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { + if (VirtualFS::instance().listFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) { POPUP_MENU_START(onModelCustomScriptMenu); } else { diff --git a/radio/src/gui/212x64/model_display.cpp b/radio/src/gui/212x64/model_display.cpp index d9115e29c39..af138cb8a33 100644 --- a/radio/src/gui/212x64/model_display.cpp +++ b/radio/src/gui/212x64/model_display.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" enum MenuModelDisplayItems { ITEM_DISPLAY_TOP_BAR_LABEL, @@ -92,7 +93,7 @@ void onTelemetryScriptFileSelectionMenu(const char *result) int screenIndex = DISPLAY_CURRENT_SCREEN(menuVerticalPosition); if (result == STR_UPDATE_LIST) { - if (!sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), nullptr)) { + if (!VirtualFS::instance().listFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), nullptr)) { POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); } } @@ -167,7 +168,7 @@ void menuModelDisplay(event_t event) if (menuHorizontalPosition==1 && attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { s_editMode = 0; - if (sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), g_model.screens[screenIndex].script.file)) { + if (VirtualFS::instance().listFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.screens[screenIndex].script.file), g_model.screens[screenIndex].script.file)) { POPUP_MENU_START(onTelemetryScriptFileSelectionMenu); } else { diff --git a/radio/src/gui/212x64/model_select.cpp b/radio/src/gui/212x64/model_select.cpp index fe24899c8c8..b066371abf6 100644 --- a/radio/src/gui/212x64/model_select.cpp +++ b/radio/src/gui/212x64/model_select.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" #define MODELSEL_W 133 @@ -58,7 +59,7 @@ void onModelSelectMenu(const char * result) ext = STR_MODELS_EXT; path = STR_MODELS_PATH; #endif - if (sdListFiles(path, ext, MENU_LINE_LENGTH-1, nullptr)) + if (VirtualFS::instance().listFiles(path, ext, MENU_LINE_LENGTH-1, nullptr)) POPUP_MENU_START(onModelSelectMenu); else POPUP_WARNING(STR_NO_MODELS_ON_SD); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 5d86a23f8c4..1d902976fcd 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -26,6 +26,7 @@ #include "opentx.h" #include "mixer_scheduler.h" #include "switches.h" +#include "VirtualFS.h" #if defined(USBJ_EX) #include "usb_joystick.h" @@ -236,7 +237,7 @@ void copySelection(char * dst, const char * src, uint8_t size) void onModelSetupBitmapMenu(const char * result) { if (result == STR_UPDATE_LIST) { - if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), nullptr)) { + if (!VirtualFS::instance().listFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), nullptr)) { POPUP_WARNING(STR_NO_BITMAPS_ON_SD); } } @@ -641,7 +642,7 @@ void menuModelSetup(event_t event) lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { s_editMode = 0; - if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), g_model.header.bitmap, LIST_NONE_SD_FILE)) { + if (VirtualFS::instance().listFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap), g_model.header.bitmap, LIST_NONE_SD_FILE)) { POPUP_MENU_START(onModelSetupBitmapMenu); } else { diff --git a/radio/src/gui/212x64/model_special_functions.cpp b/radio/src/gui/212x64/model_special_functions.cpp index 88ac985d060..d4c2b57edbb 100644 --- a/radio/src/gui/212x64/model_special_functions.cpp +++ b/radio/src/gui/212x64/model_special_functions.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "VirtualFS.h" #define MODEL_SPECIAL_FUNC_1ST_COLUMN (4*FW+2) #define MODEL_SPECIAL_FUNC_2ND_COLUMN (8*FW+2) @@ -57,7 +58,7 @@ void onCustomFunctionsFileSelectionMenu(const char * result) strcpy(directory, SOUNDS_PATH); strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); } - if (!sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), nullptr)) { + if (!VirtualFS::instance().listFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), nullptr)) { POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD); } } @@ -294,7 +295,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); } #endif -#if defined(SDCARD) else if (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT) { coord_t x = MODEL_SPECIAL_FUNC_3RD_COLUMN; if (ZEXIST(cfn->play.name)) @@ -311,7 +311,7 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF strcpy(directory, SOUNDS_PATH); strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); } - if (sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) { + if (VirtualFS::instance().listFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) { POPUP_MENU_START(onCustomFunctionsFileSelectionMenu); } else { @@ -328,7 +328,6 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(functionsContext == &globalFunctionsContext ? isSourceAvailableInGlobalFunctions : isSourceAvailable); } } -#endif else if (func == FUNC_VOLUME) { val_max = MIXSRC_LAST_CH; drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); diff --git a/radio/src/gui/212x64/model_telemetry_sensor.cpp b/radio/src/gui/212x64/model_telemetry_sensor.cpp index 5135deb9249..c41ad12a3ae 100644 --- a/radio/src/gui/212x64/model_telemetry_sensor.cpp +++ b/radio/src/gui/212x64/model_telemetry_sensor.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "logs.h" enum SensorFields { SENSOR_FIELD_NAME, diff --git a/radio/src/gui/212x64/widgets.cpp b/radio/src/gui/212x64/widgets.cpp index 7ef61fc54f5..638d2260639 100644 --- a/radio/src/gui/212x64/widgets.cpp +++ b/radio/src/gui/212x64/widgets.cpp @@ -170,7 +170,6 @@ int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int } #endif -#if defined(SDCARD) char statusLineMsg[STATUS_LINE_LENGTH]; tmr10ms_t statusLineTime = 0; uint8_t statusLineHeight = 0; @@ -199,4 +198,3 @@ void drawStatusLine() lcdDrawFilledRect(0, LCD_H-statusLineHeight, LCD_W, FH, SOLID); } } -#endif diff --git a/radio/src/gui/colorlcd/file_browser.cpp b/radio/src/gui/colorlcd/file_browser.cpp index 2a3aa4e5aa5..d9b4e518e9d 100644 --- a/radio/src/gui/colorlcd/file_browser.cpp +++ b/radio/src/gui/colorlcd/file_browser.cpp @@ -20,8 +20,9 @@ */ #include "file_browser.h" -#include "libopenui_file.h" +#include "VirtualFS.h" #include "font.h" +#include "strhelpers.h" #include #include @@ -54,8 +55,9 @@ static void fb_event(lv_event_t* e) static const char* getFullPath(const char* filename) { - static char full_path[FF_MAX_LFN + 1]; - f_getcwd((TCHAR*)full_path, FF_MAX_LFN); + static char full_path[VFS_MAX_LFN + 1]; + std::string dir = VirtualFS::instance().getCurWorkDir(); + strncpy(full_path, dir.c_str(), VFS_MAX_LFN); strcat(full_path, "/"); strcat(full_path, filename); return full_path; @@ -63,8 +65,9 @@ static const char* getFullPath(const char* filename) static const char* getCurrentPath() { - static char path[FF_MAX_LFN + 1]; - f_getcwd((TCHAR*)path, FF_MAX_LFN); + static char path[VFS_MAX_LFN + 1]; + std::string dir = VirtualFS::instance().getCurWorkDir(); + strncpy(path, dir.c_str(), VFS_MAX_LFN); return path; } @@ -130,28 +133,36 @@ static bool natural_compare_nocase(const std::string & first, const std::string static int scan_files(std::list& files, std::list& directories) { - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; + VirtualFS& vfs = VirtualFS::instance(); - FRESULT res = f_opendir(&dir, "."); // Open the directory - if (res != FR_OK) return -1; + VfsError res = vfs.openDirectory(dir, "."); // Open the directory + if (res != VfsError::OK) return -1; // read all entries - bool firstTime = true; for (;;) { - res = sdReadDir(&dir, &fno, firstTime); + res = dir.read(fno); - if (res != FR_OK || fno.fname[0] == 0) + if (res != VfsError::OK || strlen(fno.getName()) == 0) break; // Break on error or end of dir - // if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH) - // continue; - if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */ - if (fno.fname[0] == '.' && fno.fname[1] != '.') continue; // Ignore hidden files under UNIX, but not .. - if (fno.fattrib & AM_DIR) { - directories.push_back((char*)fno.fname); + auto fname = fno.getName(); + auto attribs = fno.getAttrib(); + + // Ignore hidden files + if ((attribs & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) + continue; + + // ".*" but not ".." + if (fname[0] == '.' && fname[1] != '.') + continue; + + if (fno.getType() == VfsType::DIR) { + directories.push_back(fname); } else { - files.push_back((char*)fno.fname); + files.push_back(fname); } } @@ -168,7 +179,7 @@ FileBrowser::FileBrowser(Window* parent, const rect_t& rect, const char* dir) : setColumnCount(1); - f_chdir(dir); + VirtualFS::instance().changeDirectory(dir); if (lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { lv_group_t* g = (lv_group_t*)lv_obj_get_group(lvobj); @@ -278,7 +289,7 @@ void FileBrowser::onPress(const char* name, bool is_dir) const char* path = getCurrentPath(); const char* fullpath = getFullPath(name); if (is_dir) { - f_chdir(fullpath); + VirtualFS::instance().changeDirectory(fullpath); refresh(); return; } diff --git a/radio/src/gui/colorlcd/file_preview.cpp b/radio/src/gui/colorlcd/file_preview.cpp index 7a4d324734b..814c49cd1f6 100644 --- a/radio/src/gui/colorlcd/file_preview.cpp +++ b/radio/src/gui/colorlcd/file_preview.cpp @@ -39,8 +39,8 @@ void FilePreview::setFile(const char *filename) bitmap = nullptr; if (filename) { - const char *ext = getFileExtension(filename); - if (ext && isExtensionMatching(ext, BITMAPS_EXT)) { + const char *ext = VirtualFS::getFileExtension(filename); + if (ext && VirtualFS::isFileExtensionMatching(ext, BITMAPS_EXT)) { bitmap = BitmapBuffer::loadBitmap(filename); } else { bitmap = nullptr; diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index ef656cf4bcb..3eeae7f8bfe 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -89,8 +89,8 @@ static void flushLcd(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_ #endif #if defined(DEBUG_WINDOWS) - if (area->x1 != 0 || area->x2 != LCD_W-1 || area->y1 != 0 || - area->y2 != LCD_H-1) { + if (refr_area.x1 != 0 || refr_area.x2 != LCD_PHYS_W-1 || refr_area.y1 != 0 || + refr_area.y2 != LCD_PHYS_H-1) { TRACE("partial refresh @ 0x%p {%d,%d,%d,%d}", color_p, area->x1, area->y1, area->x2, area->y2); } else { @@ -159,9 +159,9 @@ void lcdInitDisplayDriver() disp_drv.flush_cb = flushLcd; /*Set a flush callback to draw to the display*/ disp_drv.wait_cb = lcd_wait_cb; /*Set a wait callback*/ - disp_drv.hor_res = LCD_W; /*Set the horizontal resolution in pixels*/ - disp_drv.ver_res = LCD_H; /*Set the vertical resolution in pixels*/ - disp_drv.full_refresh = 0; + disp_drv.hor_res = LCD_PHYS_W; /*Set the horizontal resolution in pixels*/ + disp_drv.ver_res = LCD_PHYS_H; /*Set the vertical resolution in pixels*/ + disp_drv.full_refresh = 1; #if !defined(LCD_VERTICAL_INVERT) disp_drv.direct_mode = 1; @@ -169,6 +169,10 @@ void lcdInitDisplayDriver() disp_drv.direct_mode = 0; #endif +#if defined (PCBPL18) + disp_drv.rotated = LV_DISP_ROT_90; +#endif + // Register the driver and save the created display object disp = lv_disp_drv_register(&disp_drv); diff --git a/radio/src/gui/colorlcd/libopenui.h b/radio/src/gui/colorlcd/libopenui.h index 546c4a6c035..29dadd7c099 100644 --- a/radio/src/gui/colorlcd/libopenui.h +++ b/radio/src/gui/colorlcd/libopenui.h @@ -23,7 +23,6 @@ #if !defined(BOOT) #include "libopenui_defines.h" -#include "libopenui_file.h" #include "font.h" #include "window.h" #include "mainwindow.h" diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index bad8f4166d9..74dc1a6c7f0 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -416,14 +416,14 @@ void ModelsPageBody::duplicateModel(ModelCell *model) std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { storageFlushCurrentModel(); storageCheck(true); - + VirtualFS &vfs = VirtualFS::instance(); char duplicatedFilename[LEN_MODEL_FILENAME + 1]; memcpy(duplicatedFilename, model->modelFilename, sizeof(duplicatedFilename)); - if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, - MODELS_PATH)) { - sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, - MODELS_PATH); + if (vfs.findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, + MODELS_PATH)) { + vfs.copyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, + MODELS_PATH); // Make a new model which is a copy of the selected one, set the same // labels auto new_model = modelslist.addModel(duplicatedFilename, true, model); @@ -454,6 +454,7 @@ void ModelsPageBody::saveAsTemplate(ModelCell *model) new ConfirmDialog( parent, STR_SAVE_TEMPLATE, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { + VirtualFS &vfs = VirtualFS::instance(); storageDirty(EE_MODEL); storageCheck(true); constexpr size_t size = sizeof(model->modelName) + sizeof(YAML_EXT); @@ -462,16 +463,16 @@ void ModelsPageBody::saveAsTemplate(ModelCell *model) char templatePath[FF_MAX_LFN]; snprintf(templatePath, FF_MAX_LFN, "%s%c%s", PERS_TEMPL_PATH, '/', modelName); - sdCheckAndCreateDirectory(TEMPLATES_PATH); - sdCheckAndCreateDirectory(PERS_TEMPL_PATH); - if (isFileAvailable(templatePath)) { + vfs.checkAndCreateDirectory(TEMPLATES_PATH); + vfs.checkAndCreateDirectory(PERS_TEMPL_PATH); + if (vfs.isFileAvailable(templatePath)) { new ConfirmDialog(parent, STR_FILE_EXISTS, STR_ASK_OVERWRITE, [=] { - sdCopyFile(model->modelFilename, MODELS_PATH, modelName, - PERS_TEMPL_PATH); + VirtualFS::instance().copyFile(model->modelFilename, MODELS_PATH, + modelName, PERS_TEMPL_PATH); }); } else { - sdCopyFile(model->modelFilename, MODELS_PATH, modelName, - PERS_TEMPL_PATH); + vfs.copyFile(model->modelFilename, MODELS_PATH, modelName, + PERS_TEMPL_PATH); } }); } diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 07f0027b6fa..6132a44b6a1 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -346,7 +346,7 @@ void ModelSetupPage::build(FormWindow * window) line = window->newLine(&grid2); line->padTop(2); - +#endif // Timer buttons new SubScreenButton(line, TR_TIMER "1", []() { new TimerWindow(0); }); new SubScreenButton(line, TR_TIMER "2", []() { new TimerWindow(1); }); diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index e38bb624a77..30d9eba03df 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -23,6 +23,7 @@ #include #include "model_telemetry.h" #include "opentx.h" +#include "logs.h" #include "libopenui.h" #define SET_DIRTY() storageDirty(EE_MODEL) @@ -719,7 +720,9 @@ class SensorEditWindow : public Page { new StaticText(line, rect_t{}, STR_LOGS, 0, COLOR_THEME_PRIMARY1); new ToggleSwitch(line, rect_t{}, GET_DEFAULT(sensor->logs), [=](int32_t newValue) { sensor->logs = newValue; +#if defined(SDCARD) logsClose(); +#endif SET_DIRTY(); }); diff --git a/radio/src/gui/colorlcd/model_templates.cpp b/radio/src/gui/colorlcd/model_templates.cpp index d2c46ef2090..fc9f046ece1 100644 --- a/radio/src/gui/colorlcd/model_templates.cpp +++ b/radio/src/gui/colorlcd/model_templates.cpp @@ -56,12 +56,12 @@ TemplatePage::TemplatePage() : Page(ICON_MODEL_SELECT) void TemplatePage::updateInfo() { if (buffer[0]) { - FIL fp; - FRESULT res = f_open(&fp, buffer, FA_READ); - unsigned int bytesRead = 0; - if (res == FR_OK) { - f_read(&fp, infoText, LEN_INFO_TEXT, &bytesRead); - f_close(&fp); + VfsFile fp; + VfsError res = VirtualFS::instance().openFile(fp, buffer, VfsOpenFlags::READ); + size_t bytesRead = 0; + if (res == VfsError::OK) { + fp.read(infoText, LEN_INFO_TEXT, bytesRead); + fp.close(); } infoText[bytesRead] = '\0'; } @@ -101,30 +101,36 @@ class SelectTemplate : public TemplatePage snprintf(path, LEN_PATH, "%s/%s", TEMPLATES_PATH, folder.c_str()); std::list files; - FILINFO fno; - DIR dir; - FRESULT res = f_opendir(&dir, path); + VirtualFS& vfs = VirtualFS::instance(); + VfsFileInfo fno; + VfsDir dir; + VfsError res = vfs.openDirectory(dir, path); Button* firstButton = nullptr; - if (res == FR_OK) { + if (res == VfsError::OK) { // read all entries for (;;) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) + res = dir.read(fno); + const char* fName = fno.getName(); + if (res != VfsError::OK || fName[0] == 0) break; // Break on error or end of dir - if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH) continue; - if (fno.fattrib & (AM_DIR | AM_HID | AM_SYS)) - continue; /* Ignore folders, hidden and system files */ - if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */ - - const char* ext = getFileExtension(fno.fname); - if (ext && !strcasecmp(ext, YAML_EXT)) { - int len = ext - fno.fname; - if (len < FF_MAX_LFN) { - char name[FF_MAX_LFN] = {0}; - strncpy(name, fno.fname, len); - files.push_back(name); + if (strlen(fName) > STORAGE_SCREEN_FILE_LENGTH) + continue; + if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) + continue; + if (fName[0] == '.') + continue; + if (fno.getType() == VfsType::FILE) { + const char *ext = vfs.getFileExtension(fName); + if (ext && !strcasecmp(ext, YAML_EXT)) { + int len = ext - fName; + if (len < FF_MAX_LFN) { + char name[FF_MAX_LFN] = {0}; + strncpy(name, fName, len); + files.push_back(name); + } } } } @@ -151,7 +157,7 @@ class SelectTemplate : public TemplatePage } } - f_closedir(&dir); + dir.close(); if (files.size() == 0) { new StaticText(listWindow, rect_t{0, 0, lv_pct(100), lv_pct(50)}, @@ -187,21 +193,26 @@ SelectTemplateFolder::SelectTemplateFolder(std::function directories; - FILINFO fno; - DIR dir; - FRESULT res = f_opendir(&dir, TEMPLATES_PATH); + VfsFileInfo fno; + VfsDir dir; + VfsError res = VirtualFS::instance().openDirectory(dir, TEMPLATES_PATH); - if (res == FR_OK) { + if (res == VfsError::OK) { // read all entries for (;;) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) + res = dir.read(fno); + const char* fName = fno.getName(); + if (res != VfsError::OK || fName[0] == 0) break; // Break on error or end of dir - if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH) continue; - if (fno.fattrib & (AM_HID | AM_SYS)) - continue; /* Ignore hidden and system files */ - if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */ - if (fno.fattrib & AM_DIR) directories.push_back((char*)fno.fname); + if (strlen(fName) > STORAGE_SCREEN_FILE_LENGTH) + continue; + if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) + continue; + if (fName[0] == '.') + continue; + if (fno.getType() == VfsType::DIR) + directories.push_back(fName); } directories.sort(compare_nocase); @@ -230,7 +241,7 @@ SelectTemplateFolder::SelectTemplateFolder(std::functionsetSlidersVisible(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 9e93cc0d427..819532a23bd 100644 --- a/radio/src/gui/colorlcd/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio_diaganas.cpp @@ -174,7 +174,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 8814e19064c..a1eea38900d 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -80,7 +80,7 @@ class RadioKeyDiagsWindow : public Window void paint(BitmapBuffer * dc) override { constexpr coord_t KEY_COLUMN = 6; -#if !defined(PCBNV14) +#if !defined(PCBNV14) && !defined(PCBPL18) // TODO! Remove PL18 after rotating the screen constexpr coord_t SWITCHES_COLUMN = LCD_W / 2 - 20; constexpr coord_t TRIM_COLUMN = LCD_W - 120; #else @@ -108,8 +108,7 @@ class RadioKeyDiagsWindow : public Window dc->drawText(KEY_COLUMN, y, STR_ROTARY_ENCODER, COLOR_THEME_PRIMARY1); dc->drawNumber(70, y, rotaryEncoderGetValue(), COLOR_THEME_PRIMARY1); #endif -#else // defined(PCBNV14) - // KEYS +#else // PCBNV14 does NOT have physical keys, only remapped trim keys { coord_t y = 1; dc->drawText(KEY_COLUMN, y, keysGetLabel(KEY_ENTER), COLOR_THEME_PRIMARY1); @@ -133,8 +132,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 a328687ef3a..67c1e033250 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_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 6ff1de9f3bf..6018b38402a 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -27,7 +27,7 @@ #include "io/multi_firmware_update.h" #include "io/bootloader_flash.h" #include "standalone_lua.h" -#include "sdcard.h" +#include "VirtualFS.h" #include "view_text.h" #include "file_preview.h" #include "file_browser.h" @@ -73,26 +73,26 @@ class FileNameEditWindow : public Page char extension[LEN_FILE_EXTENSION_MAX + 1]; memset(extension, 0, sizeof(extension)); const char *ext = - getFileExtension(name.c_str(), 0, 0, &nameLength, &extLength); + VirtualFS::getFileExtension(name.c_str(), 0, 0, &nameLength, &extLength); if (extLength > LEN_FILE_EXTENSION_MAX) extLength = LEN_FILE_EXTENSION_MAX; if (ext) strncpy(extension, ext, extLength); - const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength; + const uint8_t maxNameLength = STORAGE_SCREEN_FILE_LENGTH - extLength; nameLength -= extLength; if (nameLength > maxNameLength) nameLength = maxNameLength; - memset(reusableBuffer.sdManager.originalName, 0, SD_SCREEN_FILE_LENGTH); + memset(reusableBuffer.sdManager.originalName, 0, STORAGE_SCREEN_FILE_LENGTH); strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); reusableBuffer.sdManager.originalName[nameLength] = '\0'; auto newFileName = new TextEdit( form, rect_t{0, 0, LCD_W-8, 0}, reusableBuffer.sdManager.originalName, - SD_SCREEN_FILE_LENGTH - extLength, LcdFlags(0)); + STORAGE_SCREEN_FILE_LENGTH - extLength, LcdFlags(0)); newFileName->setChangeHandler([=]() { char *newValue = reusableBuffer.sdManager.originalName; size_t totalSize = strlen(newValue); - char changedName[SD_SCREEN_FILE_LENGTH + 1]; + char changedName[STORAGE_SCREEN_FILE_LENGTH + 1]; memset(changedName, 0, sizeof(changedName)); strncpy(changedName, newValue, totalSize); changedName[totalSize] = '\0'; @@ -100,13 +100,13 @@ class FileNameEditWindow : public Page strncpy(changedName + totalSize, extension, extLength); } changedName[totalSize + extLength] = '\0'; - f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); + VirtualFS::instance().rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); }); }; }; RadioSdManagerPage::RadioSdManagerPage() : - PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) + PageTab(STR_SD_CARD, ICON_RADIO_SD_MANAGER) { } @@ -333,7 +333,7 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, { auto window = Layer::back(); auto menu = new Menu(window); - const char* ext = getFileExtension(name); + const char* ext = VirtualFS::getFileExtension(name); if (ext) { if (!strcasecmp(ext, SOUNDS_EXT)) { menu->addLine(STR_PLAY_FILE, [=]() { @@ -363,17 +363,18 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, MultiFirmwareUpdate(fullpath, EXTERNAL_MODULE, MULTI_TYPE_ELRS); }); } else if (!strcasecmp(BITMAPS_PATH, path) && - isExtensionMatching(ext, BITMAPS_EXT)) { + VirtualFS::isFileExtensionMatching(ext, BITMAPS_EXT)) { menu->addLine(STR_ASSIGN_BITMAP, [=]() { memcpy(g_model.header.bitmap, name, sizeof(g_model.header.bitmap)); storageDirty(EE_MODEL); }); } else if (!strcasecmp(ext, TEXT_EXT) || !strcasecmp(ext, LOGS_EXT)) { menu->addLine(STR_VIEW_TEXT, [=]() { - FIL file; - if (FR_OK == f_open(&file, fullpath, FA_OPEN_EXISTING | FA_READ)) { - const int fileLength = file.obj.objsize; - f_close(&file); + VfsFile file; + VirtualFS& vfs = VirtualFS::instance(); + if (VfsError::OK == vfs.openFile(file, fullpath, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ)) { + const int fileLength = file.size(); + file.close(); if (fileLength > WARN_FILE_LENGTH) { char buf[64]; @@ -483,7 +484,7 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, } } #if defined(LUA) - else if (isExtensionMatching(ext, SCRIPTS_EXT)) { + else if (VirtualFS::isFileExtensionMatching(ext, SCRIPTS_EXT)) { menu->addLine(STR_EXECUTE_FILE, [=]() { luaExec(fullpath); StandaloneLuaWindow::instance()->attach(); @@ -493,25 +494,28 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, } if (!READ_ONLY()) { menu->addLine(STR_COPY_FILE, [=]() { - clipboard.type = CLIPBOARD_TYPE_SD_FILE; - f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); - strncpy(clipboard.data.sd.filename, name, CLIPBOARD_PATH_LEN - 1); + clipboard.type = CLIPBOARD_TYPE_STORAGE_FILE; + const std::string& dir = VirtualFS::instance().getCurWorkDir(); + strncpy(clipboard.data.storage.directory, dir.c_str(), CLIPBOARD_PATH_LEN-1); + strncpy(clipboard.data.storage.filename, name, CLIPBOARD_PATH_LEN - 1); }); - if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) { + if (clipboard.type == CLIPBOARD_TYPE_STORAGE_FILE) { menu->addLine(STR_PASTE, [=]() { - static char lfn[FF_MAX_LFN + 1]; // TODO optimize that! - f_getcwd((TCHAR*)lfn, FF_MAX_LFN); + static char lfn[VFS_MAX_LFN + 1]; // TODO optimize that! + VirtualFS& vfs = VirtualFS::instance(); + const std::string& dir = vfs.getCurWorkDir(); + strncpy((char *)&lfn[0], dir.c_str(), VFS_MAX_LFN-1); // prevent copying to the same directory with the same name - char* destNamePtr = clipboard.data.sd.filename; - if (!strcmp(clipboard.data.sd.directory, lfn)) { + char* destNamePtr = clipboard.data.storage.filename; + if (!strcmp(clipboard.data.storage.directory, lfn)) { char destFileName[2 * CLIPBOARD_PATH_LEN + 1]; destNamePtr = strAppend(destFileName, FILE_COPY_PREFIX, CLIPBOARD_PATH_LEN); - destNamePtr = strAppend(destNamePtr, clipboard.data.sd.filename, + destNamePtr = strAppend(destNamePtr, clipboard.data.storage.filename, CLIPBOARD_PATH_LEN); destNamePtr = destFileName; } - sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, + vfs.copyFile(clipboard.data.storage.filename, clipboard.data.storage.directory, destNamePtr, lfn); clipboard.type = CLIPBOARD_TYPE_NONE; @@ -523,7 +527,7 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, few->setCloseHandler([=]() { browser->refresh(); }); }); menu->addLine(STR_DELETE_FILE, [=]() { - f_unlink(fullpath); + VirtualFS::instance().unlink(fullpath); browser->refresh(); }); } diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 86654a6cf3d..ffac9c6cabf 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -24,6 +24,7 @@ #include "radio_spectrum_analyser.h" #include "radio_ghost_module_config.h" #include "opentx.h" +#include "VirtualFS.h" #include "libopenui.h" #include "lua/lua_api.h" #include "standalone_lua.h" @@ -92,10 +93,10 @@ inline bool tool_compare_nocase(const ToolEntry& first, const ToolEntry& second) #if defined(LUA) static void run_lua_tool(Window* parent, const std::string& path) { - char toolPath[FF_MAX_LFN + 1]; + char toolPath[VFS_MAX_LFN + 1]; strncpy(toolPath, path.c_str(), sizeof(toolPath)-1); - *((char *)getBasename(toolPath)-1) = '\0'; - f_chdir(toolPath); + *((char *)VirtualFS::getBasename(toolPath)-1) = '\0'; + VirtualFS::instance().changeDirectory(toolPath); luaExec(path.c_str()); auto lua_win = StandaloneLuaWindow::instance(); @@ -105,29 +106,31 @@ static void run_lua_tool(Window* parent, const std::string& path) // LUA scripts in TOOLS static void scanLuaTools(std::list& scripts) { - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; - FRESULT res = f_opendir(&dir, SCRIPTS_TOOLS_PATH); - if (res == FR_OK) { + VfsError res = VirtualFS::instance().openDirectory(dir, SCRIPTS_TOOLS_PATH); + if (res == VfsError::OK) { for (;;) { - TCHAR path[FF_MAX_LFN+1] = SCRIPTS_TOOLS_PATH "/"; - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (fno.fattrib & (AM_DIR|AM_HID|AM_SYS)) continue; // skip subfolders, hidden files and system files - if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */ - - strcat(path, fno.fname); - if (isRadioScriptTool(fno.fname)) { + TCHAR path[FF_MAX_LFN+1] = ":" SCRIPTS_TOOLS_PATH "/"; + res = dir.read(fno); /* Read a directory item */ + const char* fName = fno.getName(); + if (res != VfsError::OK || fName[0] == 0) break; /* Break on error or end of dir */ + if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) continue; + if (fno.getType() == VfsType::DIR) continue; /* Skip subfolders */ + if (fName[0] == '.') continue; + strcat(path, fName); + if (isRadioScriptTool(fName)) { char toolName[RADIO_TOOL_NAME_MAXLEN + 1] = {0}; const char * label; - char * ext = (char *)getFileExtension(path); - if (readToolName(toolName, path)) { + char * ext = (char *)VirtualFS::getFileExtension(path+1); + if (readToolName(toolName, path+1)) { label = toolName; } else { *ext = '\0'; - label = getBasename(path); + label = VirtualFS::getBasename(path); } scripts.emplace_back(ToolEntry{ label, path, run_lua_tool }); diff --git a/radio/src/gui/colorlcd/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp index 6c5f23c1d88..3cfa04f73b2 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( @@ -159,11 +160,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, @@ -303,11 +306,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], @@ -326,7 +331,7 @@ RadioVersionPage::RadioVersionPage(): { } -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) extern const char* boardLcdType; #endif @@ -349,7 +354,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/splash.cpp b/radio/src/gui/colorlcd/splash.cpp index 64ec63a7349..ba270d0e439 100644 --- a/radio/src/gui/colorlcd/splash.cpp +++ b/radio/src/gui/colorlcd/splash.cpp @@ -44,8 +44,6 @@ void draw_splash_cb(lv_event_t * e) auto draw_ctx = lv_event_get_draw_ctx(e); auto splashImg = (BitmapBuffer*)lv_event_get_user_data(e); - // lcd->clear(splash_background_color); - if (splashImg) { lv_draw_img_dsc_t img_dsc; lv_draw_img_dsc_init(&img_dsc); @@ -70,7 +68,6 @@ void drawSplash() // try splash from SD card first if (loadSplashImg && splashImg == nullptr) { - if (!sdMounted()) sdInit(); splashImg = BitmapBuffer::loadBitmap(BITMAPS_PATH "/" SPLASH_FILE, BMP_RGB565); loadSplashImg = false; diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index df99cad2fdb..2d702cb130c 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -33,7 +33,7 @@ enum IconState { // TODO: hotfix, through FatFS out of libopenui instead #if !defined(YAML_GENERATOR) -#include "ffconf.h" +#include "VirtualFS.h" #else #define FF_MAX_LFN 255 #endif diff --git a/radio/src/gui/colorlcd/theme_manager.cpp b/radio/src/gui/colorlcd/theme_manager.cpp index 7e442fe67e0..8fc2315be34 100644 --- a/radio/src/gui/colorlcd/theme_manager.cpp +++ b/radio/src/gui/colorlcd/theme_manager.cpp @@ -145,7 +145,7 @@ ThemeFile::ThemeFile(std::string themePath, bool loadYAML) : int n = 0; while (n < MAX_FILES) { auto baseFileName(path.substr(0, found + 1) + (n != 0 ? "screenshot" + std::to_string(n) : "logo") + ".png"); - if (isFileAvailable(baseFileName.c_str(), true)) { + if (VirtualFS::instance().isFileAvailable(baseFileName.c_str(), true)) { _imageFileNames.emplace_back(baseFileName); } else { break; @@ -239,17 +239,19 @@ void ThemeFile::applyBackground() std::string backgroundImageFileName(getPath()); auto pos = backgroundImageFileName.rfind('/'); if (pos != std::string::npos) { + VirtualFS& vfs = VirtualFS::instance(); auto rootDir = backgroundImageFileName.substr(0, pos + 1); rootDir = rootDir + "background_" + std::to_string(LCD_W) + "x" + std::to_string(LCD_H) + ".png"; - if (isFileAvailable(rootDir.c_str())) { + + if (vfs.isFileAvailable(rootDir.c_str())) { instance->setBackgroundImageFileName((char *)rootDir.c_str()); } else { // TODO: This needs to be made user configurable, not // require the file be deleted to remove global background std::string fileName = THEMES_PATH PATH_SEPARATOR "EdgeTX/background.png"; - if (isFileAvailable(fileName.c_str())) { + if (vfs.isFileAvailable(fileName.c_str())) { instance->setBackgroundImageFileName(fileName.c_str()); } else { instance->setBackgroundImageFileName(""); @@ -285,7 +287,7 @@ void ThemePersistance::clearThemes() void ThemePersistance::scanThemeFolder(char *fullPath) { strncat(fullPath, "/theme.yml", FF_MAX_LFN); - if (isFileAvailable(fullPath, true)) { + if (VirtualFS::instance().isFileAvailable(fullPath, true)) { TRACE("scanForThemes: found file %s", fullPath); themes.emplace_back(new ThemeFile(fullPath)); } @@ -295,36 +297,35 @@ void ThemePersistance::scanForThemes() { clearThemes(); - DIR dir; - FILINFO fno; + VfsDir dir; + VfsFileInfo fno; char fullPath[FF_MAX_LFN + 1]; strAppend(fullPath, THEMES_PATH, FF_MAX_LFN); TRACE("opening directory: %s", fullPath); - FRESULT res = f_opendir(&dir, fullPath); // Open the directory - if (res == FR_OK) { + VfsError res = VirtualFS::instance().openDirectory(dir, fullPath); // Open the directory + if (res == VfsError::OK) { TRACE("scanForThemes: open successful"); // read all entries - bool firstTime = true; for (;;) { - res = sdReadDir(&dir, &fno, firstTime); - - if (res != FR_OK || fno.fname[0] == 0) + res = dir.read(fno); + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; // Break on error or end of dir - if (strlen((const char *)fno.fname) > SD_SCREEN_FILE_LENGTH) continue; - if (fno.fattrib & AM_DIR) { + if (strlen(name) > STORAGE_SCREEN_FILE_LENGTH) continue; + if (fno.getType() == VfsType::DIR) { char themePath[FF_MAX_LFN + 1]; char *s = strAppend(themePath, fullPath, FF_MAX_LFN); s = strAppend(s, "/", FF_MAX_LFN - (s - themePath)); - strAppend(s, fno.fname, FF_MAX_LFN - (s - themePath)); + strAppend(s, name, FF_MAX_LFN - (s - themePath)); scanThemeFolder(themePath); } } - f_closedir(&dir); + dir.close(); std::sort(themes.begin(), themes.end(), [](ThemeFile *a, ThemeFile *b) { return strcmp(a->getName(), b->getName()) < 0; @@ -343,16 +344,16 @@ void ThemePersistance::loadDefaultTheme() // TODO: remove this sometime in the future if (g_eeGeneral.selectedTheme[0] == 0) { constexpr const char* SELECTED_THEME_FILE = THEMES_PATH "/selectedtheme.txt"; - - FIL file; - FRESULT status = f_open(&file, SELECTED_THEME_FILE, FA_READ); - - if (status == FR_OK) { + + VirtualFS& vfs = VirtualFS::instance(); + VfsFile file; + VfsError status = vfs.openFile(file, SELECTED_THEME_FILE, VfsOpenFlags::READ); + if (status == VfsError::OK) { char line[256]; unsigned int len; - status = f_read(&file, line, 256, &len); - if (status == FR_OK) { + status = file.read(line, 256, len); + if (status == VfsError::OK) { line[len] = '\0'; for (auto theme : themes) { @@ -368,10 +369,10 @@ void ThemePersistance::loadDefaultTheme() index = 0; } - f_close(&file); + file.close(); // Delete old file - f_unlink(SELECTED_THEME_FILE); + vfs.unlink(SELECTED_THEME_FILE); } // Save selected theme (sets to default if nothing found) @@ -414,14 +415,14 @@ bool ThemePersistance::deleteThemeByIndex(int index) strcat(newFile, ".deleted"); // for now we are just renaming the file so we don't find it - FRESULT status = f_rename(theme->getPath().c_str(), newFile); + VfsError status = VirtualFS::instance().rename(theme->getPath().c_str(), newFile); refresh(); // make sure currentTheme stays in bounds if (getThemeIndex() >= (int) themes.size()) setThemeIndex(themes.size() - 1); - return status == FR_OK; + return status == VfsError::OK; } return false; } @@ -433,14 +434,16 @@ bool ThemePersistance::createNewTheme(std::string name, ThemeFile &theme) s = strAppend(s, "/", FF_MAX_LFN - (s - fullPath)); s = strAppend(s, name.c_str(), FF_MAX_LFN - (s - fullPath)); - if (!isFileAvailable(THEMES_PATH)) + VirtualFS& vfs = VirtualFS::instance(); + VfsError result; + if (!vfs.isFileAvailable(THEMES_PATH)) { - FRESULT result = f_mkdir(THEMES_PATH); - if (result != FR_OK) return false; + result = vfs.makeDirectory(THEMES_PATH); + if (result != VfsError::OK) return false; } - FRESULT result = f_mkdir(fullPath); - if (result != FR_OK) return false; + result = vfs.makeDirectory(fullPath); + if (result != VfsError::OK) return false; s = strAppend(s, "/", FF_MAX_LFN - (s - fullPath)); strAppend(s, "theme.yml", FF_MAX_LFN - (s - fullPath)); theme.setPath(fullPath); diff --git a/radio/src/gui/colorlcd/theme_manager.h b/radio/src/gui/colorlcd/theme_manager.h index c81e48fb5f5..67c8f27da9d 100644 --- a/radio/src/gui/colorlcd/theme_manager.h +++ b/radio/src/gui/colorlcd/theme_manager.h @@ -26,7 +26,6 @@ #include "colors.h" #include "debug.h" #include "opentx.h" -#include "sdcard.h" #include "str_functions.h" #define COLOR_COUNT 13 diff --git a/radio/src/gui/colorlcd/view_text.cpp b/radio/src/gui/colorlcd/view_text.cpp index acf47397d58..56249b88c9f 100644 --- a/radio/src/gui/colorlcd/view_text.cpp +++ b/radio/src/gui/colorlcd/view_text.cpp @@ -30,7 +30,7 @@ void ViewTextWindow::extractNameSansExt() uint8_t extLength; const char *ext = - getFileExtension(name.c_str(), 0, 0, &nameLength, &extLength); + VirtualFS::getFileExtension(name.c_str(), 0, 0, &nameLength, &extLength); extension = std::string(ext); if (nameLength > TEXT_FILENAME_MAXLEN) nameLength = TEXT_FILENAME_MAXLEN; @@ -41,7 +41,7 @@ void ViewTextWindow::extractNameSansExt() void ViewTextWindow::buildBody(Window *window) { - FILINFO info; + VfsFileInfo info; if (buffer) { free(buffer); @@ -49,18 +49,20 @@ void ViewTextWindow::buildBody(Window *window) bufSize = 0; } - auto res = f_stat((TCHAR *)fullPath.c_str(), &info); - if (res == FR_OK) { - fileLength = int(info.fsize); + VirtualFS &vfs = VirtualFS::instance(); + auto res = vfs.fstat(fullPath.c_str(), info); + if (res == VfsError::OK) { + int fsize = int(info.getSize()); + fileLength = int(fsize); bufSize = std::min(fileLength, maxTxtBuffSize) + 1; buffer = (char *)malloc(bufSize); if (buffer) { offset = - std::max(int(openFromEnd ? int(info.fsize) - bufSize + 1 : 0), 0); - TRACE("info.fsize=%d\tbufSize=%d\toffset=%d", info.fsize, bufSize, - int(info.fsize) - bufSize + 1); - if (sdReadTextFileBlock(bufSize, offset) == FR_OK) { + std::max(int(openFromEnd ? int(fsize) - bufSize + 1 : 0), 0); + TRACE("info.fsize=%d\tbufSize=%d\toffset=%d", info.getSize(), bufSize, + int(info.getSize()) - bufSize + 1); + if (sdReadTextFileBlock(bufSize, offset) == VfsError::OK) { auto obj = window->getLvObj(); lv_obj_add_flag( obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW | @@ -87,23 +89,24 @@ void ViewTextWindow::buildBody(Window *window) } } -FRESULT ViewTextWindow::sdReadTextFileBlock(const uint32_t bufSize, +VfsError ViewTextWindow::sdReadTextFileBlock(const uint32_t bufSize, const uint32_t offset) { - FIL file; + VirtualFS &vfs = VirtualFS::instance(); + VfsFile file; char escape_chars[4]; int escape = 0; - auto res = f_open(&file, (TCHAR *)fullPath.c_str(), FA_OPEN_EXISTING | FA_READ); - if (res == FR_OK) { - res = f_lseek(&file, offset); - if (res == FR_OK) { - UINT br; + auto res = vfs.openFile(file, fullPath.c_str(), VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (res == VfsError::OK) { + res = file.lseek(offset); + if (res == VfsError::OK) { + size_t br; char c; char *ptr = buffer; for (int i = 0; i < (int)bufSize; i++) { - res = f_read(&file, &c, 1, &br); - if (res == FR_OK && br == 1) { + res = file.read(&c, 1, br); + if (res == VfsError::OK && br == 1) { if (c == '\\' && escape == 0) { escape = 1; continue; @@ -145,7 +148,7 @@ FRESULT ViewTextWindow::sdReadTextFileBlock(const uint32_t bufSize, } *ptr = '\0'; } - f_close(&file); + file.close(); } return res; } @@ -196,7 +199,7 @@ bool openNotes(const char buf[], std::string modelNotesName) { std::string fullPath = std::string(buf) + PATH_SEPARATOR + modelNotesName; - if (isFileAvailable(fullPath.c_str())) { + if (VirtualFS::instance().isFileAvailable(modelNotesName.c_str())) { new ViewTextWindow(std::string(buf), modelNotesName, ICON_MODEL); return true; } else { diff --git a/radio/src/gui/colorlcd/view_text.h b/radio/src/gui/colorlcd/view_text.h index 77bb7be0254..0fe1e8f50a8 100644 --- a/radio/src/gui/colorlcd/view_text.h +++ b/radio/src/gui/colorlcd/view_text.h @@ -20,12 +20,11 @@ #pragma once -#include "ff.h" #include "lcd.h" #include "menus.h" #include "page.h" #include "static.h" -#include "sdcard.h" +#include "VirtualFS.h" #include "LvglWrapper.h" @@ -45,7 +44,7 @@ class ViewTextWindow : public Page buildBody(&body); }; - FRESULT sdReadTextFileBlock(const uint32_t bufSize, + VfsError sdReadTextFileBlock(const uint32_t bufSize, const uint32_t offset); ~ViewTextWindow() diff --git a/radio/src/gui/common/stdlcd/menus.cpp b/radio/src/gui/common/stdlcd/menus.cpp index d331935fb9c..2ca2f2595dd 100644 --- a/radio/src/gui/common/stdlcd/menus.cpp +++ b/radio/src/gui/common/stdlcd/menus.cpp @@ -20,6 +20,7 @@ */ #include "opentx.h" +#include "edgetx_assert.h" MenuHandlerFunc menuHandlers[5]; event_t menuEvent = 0; diff --git a/radio/src/gui/common/stdlcd/model_notes.cpp b/radio/src/gui/common/stdlcd/model_notes.cpp index bf2bb09f21e..f86b552b2d3 100644 --- a/radio/src/gui/common/stdlcd/model_notes.cpp +++ b/radio/src/gui/common/stdlcd/model_notes.cpp @@ -28,7 +28,7 @@ void menuModelNotes(event_t event) char *buf = strcat_currentmodelname( &reusableBuffer.viewText.filename[sizeof(MODELS_PATH)], ' '); strcpy(buf, TEXT_EXT); - if (!isFileAvailable(reusableBuffer.viewText.filename)) { + if (!VirtualFS::instance().isFileAvailable(reusableBuffer.viewText.filename)) { buf = strcat_currentmodelname( &reusableBuffer.viewText.filename[sizeof(MODELS_PATH)], 0); strcpy(buf, TEXT_EXT); diff --git a/radio/src/gui/common/stdlcd/popups.cpp b/radio/src/gui/common/stdlcd/popups.cpp index d3e52ea4b95..1fdb78318b5 100644 --- a/radio/src/gui/common/stdlcd/popups.cpp +++ b/radio/src/gui/common/stdlcd/popups.cpp @@ -118,31 +118,25 @@ const char * runPopupMenu(event_t event) #endif else { popupMenuSelectedItem = min(display_count, MENU_MAX_DISPLAY_LINES) - 1; -#if defined(SDCARD) if (popupMenuItemsCount > MENU_MAX_DISPLAY_LINES) { popupMenuOffset = popupMenuItemsCount - display_count; result = STR_UPDATE_LIST; } -#endif } } else if (IS_NEXT_EVENT(eventTemp)) { if (popupMenuSelectedItem < display_count - 1 && popupMenuOffset + popupMenuSelectedItem + 1 < popupMenuItemsCount) { popupMenuSelectedItem++; } -#if defined(SDCARD) else if (popupMenuItemsCount > popupMenuOffset + display_count) { popupMenuOffset++; result = STR_UPDATE_LIST; } -#endif else { popupMenuSelectedItem = 0; -#if defined(SDCARD) if (popupMenuOffset) { popupMenuOffset = 0; result = STR_UPDATE_LIST; } -#endif } } else if (eventTemp == EVT_KEY_BREAK(KEY_ENTER)) { result = popupMenuItems[popupMenuSelectedItem + (popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)]; diff --git a/radio/src/gui/common/stdlcd/popups.h b/radio/src/gui/common/stdlcd/popups.h index 84165115ac0..6ed926f1fa7 100644 --- a/radio/src/gui/common/stdlcd/popups.h +++ b/radio/src/gui/common/stdlcd/popups.h @@ -164,11 +164,7 @@ inline void POPUP_MENU_ADD_ITEM(const char * s) } } -#if defined(SDCARD) - #define POPUP_MENU_ADD_SD_ITEM(s) POPUP_MENU_ADD_ITEM(s) -#else - #define POPUP_MENU_ADD_SD_ITEM(s) -#endif +#define POPUP_MENU_ADD_SD_ITEM(s) POPUP_MENU_ADD_ITEM(s) inline void POPUP_MENU_SELECT_ITEM(uint8_t index) { diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index badbbb7ecee..840985232e6 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -21,12 +21,13 @@ #include #include "opentx.h" +#include "VirtualFS.h" +#include "logs.h" #include "io/frsky_firmware_update.h" #include "io/multi_firmware_update.h" #include "io/bootloader_flash.h" -#include "libopenui/src/libopenui_file.h" -#define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1] +#define NODE_TYPE(fname) fname[STORAGE_SCREEN_FILE_LENGTH+1] #define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname))) #define IS_FILE(fname) ((bool)(NODE_TYPE(fname))) @@ -73,7 +74,9 @@ inline bool isFilenameLower(bool isfile, const char * fn, const char * line) void getSelectionFullPath(char * lfn) { - f_getcwd(lfn, FF_MAX_LFN); + const std::string& curWorkDir = VirtualFS::instance().getCurWorkDir(); + strncpy(lfn, curWorkDir.c_str(), FF_MAX_LFN); + lfn[FF_MAX_LFN] = 0; strcat(lfn, "/"); strcat(lfn, reusableBuffer.sdManager.lines[menuVerticalPosition - HEADER_LINE - menuVerticalOffset]); } @@ -120,6 +123,7 @@ void onUpdateStateChanged() void onSdManagerMenu(const char * result) { TCHAR lfn[FF_MAX_LFN+1]; + VirtualFS& vfs = VirtualFS::instance(); // TODO possible buffer overflows here! @@ -130,44 +134,46 @@ void onSdManagerMenu(const char * result) pushMenu(menuRadioSdManagerInfo); } else if (result == STR_COPY_FILE) { - clipboard.type = CLIPBOARD_TYPE_SD_FILE; - f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); - strncpy(clipboard.data.sd.filename, line, CLIPBOARD_PATH_LEN-1); + clipboard.type = CLIPBOARD_TYPE_STORAGE_FILE; + const std::string& curWorkDir = vfs.getCurWorkDir(); + strncpy(clipboard.data.storage.directory, curWorkDir.c_str(), CLIPBOARD_PATH_LEN); + strncpy(clipboard.data.storage.filename, line, CLIPBOARD_PATH_LEN-1); } else if (result == STR_PASTE) { - f_getcwd(lfn, FF_MAX_LFN); + const std::string& curWorkDir = vfs.getCurWorkDir(); + strncpy(lfn, curWorkDir.c_str(), FF_MAX_LFN); // if destination is dir, copy into that dir if (IS_DIRECTORY(line)) { strcat(lfn, "/"); strcat(lfn, line); } - char *destNamePtr = clipboard.data.sd.filename; - if (!strcmp(clipboard.data.sd.directory, lfn)) { + char *destNamePtr = clipboard.data.storage.filename; + if (!strcmp(clipboard.data.storage.directory, lfn)) { // prevent copying to the same directory under the same name char destFileName[2 * CLIPBOARD_PATH_LEN + 1]; destNamePtr = strAppend(destFileName, FILE_COPY_PREFIX, CLIPBOARD_PATH_LEN); - destNamePtr = strAppend(destNamePtr, clipboard.data.sd.filename, + destNamePtr = strAppend(destNamePtr, clipboard.data.storage.filename, CLIPBOARD_PATH_LEN); destNamePtr = destFileName; } - POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, - clipboard.data.sd.directory, destNamePtr, lfn)); + POPUP_WARNING(STORAGE_ERROR(vfs.copyFile(clipboard.data.storage.filename, + clipboard.data.storage.directory, destNamePtr, lfn))); REFRESH_FILES(); } else if (result == STR_RENAME_FILE) { memcpy(reusableBuffer.sdManager.originalName, line, sizeof(reusableBuffer.sdManager.originalName)); uint8_t fnlen = 0, extlen = 0; - getFileExtension(line, 0, LEN_FILE_EXTENSION_MAX, &fnlen, &extlen); + VirtualFS::getFileExtension(line, 0, LEN_FILE_EXTENSION_MAX, &fnlen, &extlen); // write spaces to allow extending the length of a filename - memset(line + fnlen - extlen, ' ', SD_SCREEN_FILE_LENGTH - fnlen + extlen); - line[SD_SCREEN_FILE_LENGTH-extlen] = '\0'; + memset(line + fnlen - extlen, ' ', STORAGE_SCREEN_FILE_LENGTH - fnlen + extlen); + line[STORAGE_SCREEN_FILE_LENGTH-extlen] = '\0'; s_editMode = EDIT_MODIFY_STRING; editNameCursorPos = 0; } else if (result == STR_DELETE_FILE) { getSelectionFullPath(lfn); - f_unlink(lfn); + vfs.unlink(lfn); strncpy(statusLineMsg, line, 13); strcpy(statusLineMsg+min((uint8_t)strlen(statusLineMsg), (uint8_t)13), STR_REMOVED); showStatusLine(); @@ -258,7 +264,9 @@ void onSdManagerMenu(const char * result) #if defined(LUA) else if (result == STR_EXECUTE_FILE) { getSelectionFullPath(lfn); - luaExec(lfn); + std::string p = ":"; + p += lfn; + luaExec(p.c_str()); } #endif } @@ -302,7 +310,7 @@ void menuRadioSdManager(event_t _event) switch (_event) { case EVT_ENTRY: - f_chdir(ROOT_PATH); + VirtualFS::instance().changeDirectory(ROOT_PATH); #if LCD_DEPTH > 1 lastPos = -1; #endif @@ -337,7 +345,7 @@ void menuRadioSdManager(event_t _event) else { int index = menuVerticalPosition - HEADER_LINE - menuVerticalOffset; if (IS_DIRECTORY(reusableBuffer.sdManager.lines[index])) { - f_chdir(reusableBuffer.sdManager.lines[index]); + VirtualFS::instance().changeDirectory(reusableBuffer.sdManager.lines[index]); menuVerticalOffset = 0; menuVerticalPosition = HEADER_LINE; REFRESH_FILES(); @@ -366,20 +374,20 @@ void menuRadioSdManager(event_t _event) if (!strcmp(line, "..")) { break; // no menu for parent dir } - const char * ext = getFileExtension(line); + const char * ext = VirtualFS::getFileExtension(line); if (ext) { if (!strcasecmp(ext, SOUNDS_EXT)) { POPUP_MENU_ADD_ITEM(STR_PLAY_FILE); } #if LCD_DEPTH > 1 - else if (isExtensionMatching(ext, BITMAPS_EXT)) { + else if (VirtualFS::isFileExtensionMatching(ext, BITMAPS_EXT)) { if (!READ_ONLY() && (ext-line) <= (int)sizeof(g_model.header.bitmap)) { POPUP_MENU_ADD_ITEM(STR_ASSIGN_BITMAP); } } #endif #if defined(LUA) - else if (isExtensionMatching(ext, SCRIPTS_EXT)) { + else if (VirtualFS::isFileExtensionMatching(ext, SCRIPTS_EXT)) { POPUP_MENU_ADD_ITEM(STR_EXECUTE_FILE); } #endif @@ -457,14 +465,14 @@ void menuRadioSdManager(event_t _event) } } #endif - if (isExtensionMatching(ext, TEXT_EXT) || isExtensionMatching(ext, SCRIPTS_EXT)) { + if (VirtualFS::isFileExtensionMatching(ext, TEXT_EXT) || VirtualFS::isFileExtensionMatching(ext, SCRIPTS_EXT)) { POPUP_MENU_ADD_ITEM(STR_VIEW_TEXT); } } if (!READ_ONLY()) { if (IS_FILE(line)) POPUP_MENU_ADD_ITEM(STR_COPY_FILE); - if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) + if (clipboard.type == CLIPBOARD_TYPE_STORAGE_FILE) POPUP_MENU_ADD_ITEM(STR_PASTE); POPUP_MENU_ADD_ITEM(STR_RENAME_FILE); if (IS_FILE(line)) @@ -477,12 +485,12 @@ void menuRadioSdManager(event_t _event) if (SD_CARD_PRESENT()) { if (reusableBuffer.sdManager.offset != menuVerticalOffset) { - FILINFO fno; - DIR dir; + VfsFileInfo fno; + VfsDir dir; if (menuVerticalOffset == reusableBuffer.sdManager.offset + 1) { memmove(reusableBuffer.sdManager.lines[0], reusableBuffer.sdManager.lines[1], (NUM_BODY_LINES-1)*sizeof(reusableBuffer.sdManager.lines[0])); - memset(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], 0xff, SD_SCREEN_FILE_LENGTH); + memset(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], 0xff, STORAGE_SCREEN_FILE_LENGTH); NODE_TYPE(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1]) = 1; } else if (menuVerticalOffset == reusableBuffer.sdManager.offset - 1) { @@ -496,27 +504,29 @@ void menuRadioSdManager(event_t _event) reusableBuffer.sdManager.count = 0; - FRESULT res = f_opendir(&dir, "."); // Open the directory - if (res == FR_OK) { - bool firstTime = true; + VfsError res = VirtualFS::instance().openDirectory(dir, "."); // Open the directory + if (res == VfsError::OK) { for (;;) { - res = sdReadDir(&dir, &fno, firstTime); - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (strlen(fno.fname) > SD_SCREEN_FILE_LENGTH) continue; - if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */ - if (fno.fname[0] == '.' && fno.fname[1] != '.') continue; /* Ignore UNIX hidden files, but not .. */ + res = dir.read(fno); + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + if (strlen(name) > STORAGE_SCREEN_FILE_LENGTH) continue; + /* Ignore hidden and system files */ + if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) continue; + if (name[0] == '.' && name[1] != '.') continue; /* Ignore UNIX hidden files, but not .. */ reusableBuffer.sdManager.count++; - bool isfile = !(fno.fattrib & AM_DIR); + bool isfile = (fno.getType() != VfsType::DIR); if (menuVerticalOffset == 0) { for (uint8_t i=0; i=0; i--) { char * line = reusableBuffer.sdManager.lines[i]; - if (line[0] == '\0' || isFilenameGreater(isfile, fno.fname, line)) { + if (line[0] == '\0' || isFilenameGreater(isfile, name, line)) { if (i > 0) memmove(reusableBuffer.sdManager.lines[0], reusableBuffer.sdManager.lines[1], sizeof(reusableBuffer.sdManager.lines[0]) * i); memset(line, 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(line, fno.fname); + strcpy(line, name); NODE_TYPE(line) = isfile; break; } } } else if (menuVerticalOffset > reusableBuffer.sdManager.offset) { - if (isFilenameGreater(isfile, fno.fname, reusableBuffer.sdManager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, fno.fname, reusableBuffer.sdManager.lines[NUM_BODY_LINES-1])) { + if (isFilenameGreater(isfile, name, reusableBuffer.sdManager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, name, reusableBuffer.sdManager.lines[NUM_BODY_LINES-1])) { memset(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], fno.fname); + strcpy(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], name); NODE_TYPE(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1]) = isfile; } } else { - if (isFilenameLower(isfile, fno.fname, reusableBuffer.sdManager.lines[1]) && isFilenameGreater(isfile, fno.fname, reusableBuffer.sdManager.lines[0])) { + if (isFilenameLower(isfile, name, reusableBuffer.sdManager.lines[1]) && isFilenameGreater(isfile, name, reusableBuffer.sdManager.lines[0])) { memset(reusableBuffer.sdManager.lines[0], 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(reusableBuffer.sdManager.lines[0], fno.fname); + strcpy(reusableBuffer.sdManager.lines[0], name); NODE_TYPE(reusableBuffer.sdManager.lines[0]) = isfile; } } } - f_closedir(&dir); + dir.close(); } } @@ -566,13 +576,13 @@ void menuRadioSdManager(event_t _event) } if (s_editMode == EDIT_MODIFY_STRING && attr) { uint8_t extlen, efflen; - const char * ext = getFileExtension(reusableBuffer.sdManager.originalName, 0, 0, nullptr, &extlen); + const char * ext = VirtualFS::getFileExtension(reusableBuffer.sdManager.originalName, 0, 0, nullptr, &extlen); editName(lcdNextPos, y, reusableBuffer.sdManager.lines[i], - SD_SCREEN_FILE_LENGTH - extlen, _event, attr, 0, old_editMode); + STORAGE_SCREEN_FILE_LENGTH - extlen, _event, attr, 0, old_editMode); efflen = effectiveLen(reusableBuffer.sdManager.lines[i], - SD_SCREEN_FILE_LENGTH - extlen); + STORAGE_SCREEN_FILE_LENGTH - extlen); if (s_editMode == 0) { if (ext) { @@ -581,7 +591,7 @@ void menuRadioSdManager(event_t _event) else { reusableBuffer.sdManager.lines[i][efflen] = 0; } - f_rename(reusableBuffer.sdManager.originalName, reusableBuffer.sdManager.lines[i]); + VirtualFS::instance().rename(reusableBuffer.sdManager.originalName, reusableBuffer.sdManager.lines[i]); REFRESH_FILES(); } } @@ -616,8 +626,8 @@ void menuRadioSdManager(event_t _event) #endif #if LCD_DEPTH > 1 - const char * ext = getFileExtension(reusableBuffer.sdManager.lines[index]); - if (ext && isExtensionMatching(ext, BITMAPS_EXT)) { + const char * ext = VirtualFS::getFileExtension(reusableBuffer.sdManager.lines[index]); + if (ext && VirtualFS::isFileExtensionMatching(ext, BITMAPS_EXT)) { if (lastPos != menuVerticalPosition) { if (!lcdLoadBitmap(modelBitmap, reusableBuffer.sdManager.lines[index], MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT)) { memcpy(modelBitmap, logo_taranis, MODEL_BITMAP_SIZE); diff --git a/radio/src/gui/common/stdlcd/radio_tools.cpp b/radio/src/gui/common/stdlcd/radio_tools.cpp index c47e40ceb6e..3218e042e84 100644 --- a/radio/src/gui/common/stdlcd/radio_tools.cpp +++ b/radio/src/gui/common/stdlcd/radio_tools.cpp @@ -22,6 +22,7 @@ #include #include #include "opentx.h" +#include "VirtualFS.h" #include "hal/module_port.h" extern uint8_t g_moduleIdx; @@ -73,8 +74,8 @@ void addRadioScriptTool(std::vector luaScripts) if (addRadioTool(index++, luaScript.label.c_str())) { char toolPath[FF_MAX_LFN + 1]; strncpy(toolPath, luaScript.path.c_str(), sizeof(toolPath) - 1); - *((char *)getBasename(toolPath) - 1) = '\0'; - f_chdir(toolPath); + *((char *)VirtualFS::getBasename(toolPath) - 1) = '\0'; + VirtualFS::instance().changeDirectory(toolPath); luaExec(luaScript.path.c_str()); } @@ -86,14 +87,14 @@ void addRadioScriptTool(uint8_t index, const char * path) char toolName[RADIO_TOOL_NAME_MAXLEN + 1]; if (!readToolName(toolName, path)) { - strAppendFilename(toolName, getBasename(path), RADIO_TOOL_NAME_MAXLEN); + strAppendFilename(toolName, VirtualFS::getBasename(path), RADIO_TOOL_NAME_MAXLEN); } if (addRadioTool(index, toolName)) { char toolPath[FF_MAX_LFN]; strcpy(toolPath, path); - *((char *)getBasename(toolPath)-1) = '\0'; - f_chdir(toolPath); + *((char *)VirtualFS::getBasename(toolPath)-1) = '\0'; + VirtualFS::instance().changeDirectory(toolPath); luaExec(path); } } @@ -117,37 +118,42 @@ void menuRadioTools(event_t event) uint8_t index = 0; #if defined(LUA) - FILINFO fno; - DIR dir; + VirtualFS &vfs = VirtualFS::instance(); + VfsFileInfo fno; + VfsDir dir; - FRESULT res = f_opendir(&dir, SCRIPTS_TOOLS_PATH); - if (res == FR_OK) { + VfsError res = vfs.openDirectory(dir, SCRIPTS_TOOLS_PATH); + if (res == VfsError::OK) { std::vector luaScripts; // gather and sort before adding to menu for (;;) { - TCHAR path[FF_MAX_LFN + 1] = SCRIPTS_TOOLS_PATH "/"; - - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (fno.fattrib & (AM_DIR|AM_HID|AM_SYS)) continue; // skip subfolders, hidden files and system files - if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */ - - strcat(path, fno.fname); - if (isRadioScriptTool(fno.fname)) { + char path[FF_MAX_LFN + 1] = SCRIPTS_TOOLS_PATH "/"; + + res = dir.read(fno); /* Read a directory item */ + constchar *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + if (fno.getType() == VfsType::DIR) continue; /* Skip subfolders */ + /* Skip hidden and system files */ + if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + != VfsFileAttributes::NONE) continue; + if (name[0] == '.') continue; /* Ignore UNIX hidden files */ + strcat(path, name); + if (isRadioScriptTool(name)) { char toolName[RADIO_TOOL_NAME_MAXLEN + 1] = {0}; const char *label; - char *ext = (char *)getFileExtension(path); + char *ext = (char *)VirtualFS::getFileExtension(path); if (readToolName(toolName, path)) { label = toolName; } else { *ext = '\0'; - label = getBasename(path); + label = VirtualFS::getBasename(path); } - - luaScripts.emplace_back(LuaScript{path, label}); + std::string p = ":"; + p += path; + luaScripts.emplace_back(LuaScript{p.c_str(), label}); } } - f_closedir(&dir); + dir.close(); std::sort(luaScripts.begin(), luaScripts.end(), LuaScript_compare_nocase); addRadioScriptTool(luaScripts); diff --git a/radio/src/gui/common/stdlcd/view_text.cpp b/radio/src/gui/common/stdlcd/view_text.cpp index 5185074ee36..2afc5c55182 100644 --- a/radio/src/gui/common/stdlcd/view_text.cpp +++ b/radio/src/gui/common/stdlcd/view_text.cpp @@ -20,15 +20,16 @@ */ #include "opentx.h" +#include "VirtualFS.h" constexpr uint32_t TEXT_FILE_MAXSIZE = 2048; -static void sdReadTextFile(const char * filename, char lines[TEXT_VIEWER_LINES][LCD_COLS + 1], int & lines_count) +static void storageReadTextFile(const char * filename, char lines[TEXT_VIEWER_LINES][LCD_COLS + 1], int & lines_count) { - FIL file; - int result; + VfsFile file; + VfsError result; char c; - unsigned int sz; + size_t sz; int line_length = 0; uint8_t escape = 0; char escape_chars[4] = {0}; @@ -36,9 +37,9 @@ static void sdReadTextFile(const char * filename, char lines[TEXT_VIEWER_LINES][ memclear(lines, TEXT_VIEWER_LINES * (LCD_COLS + 1)); - result = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ); - if (result == FR_OK) { - for (uint32_t i = 0; i < TEXT_FILE_MAXSIZE && f_read(&file, &c, 1, &sz) == FR_OK && sz == 1 && (lines_count == 0 || current_line - menuVerticalOffset < int(TEXT_VIEWER_LINES)); i++) { + result = VirtualFS::instance().openFile(file, filename, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result == VfsError::OK) { + for (uint32_t i = 0; i < TEXT_FILE_MAXSIZE && file.read(&c, 1, sz) == VfsError::OK && sz == 1 && (lines_count == 0 || current_line - menuVerticalOffset < int(TEXT_VIEWER_LINES)); i++) { if (c == '\n') { ++current_line; line_length = 0; @@ -84,7 +85,7 @@ static void sdReadTextFile(const char * filename, char lines[TEXT_VIEWER_LINES][ if (c != '\n') { current_line += 1; } - f_close(&file); + file.close(); } if (lines_count == 0) { @@ -127,16 +128,16 @@ void menuTextView(event_t event) if (event == EVT_ENTRY) { menuVerticalOffset = 0; reusableBuffer.viewText.linesCount = 0; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); + storageReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); } else if (IS_PREVIOUS_EVENT(event)) { if (menuVerticalOffset > 0) { menuVerticalOffset--; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); + storageReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); } } else if (IS_NEXT_EVENT(event)) { if (menuVerticalOffset + LCD_LINES-1 < reusableBuffer.viewText.linesCount) { ++menuVerticalOffset; - sdReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); + storageReadTextFile(reusableBuffer.viewText.filename, reusableBuffer.viewText.lines, reusableBuffer.viewText.linesCount); } } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { popMenu(); @@ -152,7 +153,7 @@ void menuTextView(event_t event) #else // TODO? #endif - lcdDrawText(LCD_W/2, 0, getBasename(title), CENTERED); + lcdDrawText(LCD_W/2, 0, VirtualFS::getBasename(title), CENTERED); lcdInvertLine(0); if (reusableBuffer.viewText.linesCount > LCD_LINES-1) { diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 694749e7cf7..24afbff54cf 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -1109,16 +1109,17 @@ bool isTrainerModeAvailable(int mode) bool modelHasNotes() { + VirtualFS& vfs = VirtualFS::instance(); char filename[sizeof(MODELS_PATH)+1+sizeof(g_model.header.name)+sizeof(TEXT_EXT)] = MODELS_PATH "/"; char *buf = strcat_currentmodelname(&filename[sizeof(MODELS_PATH)], 0); strcpy(buf, TEXT_EXT); - if (isFileAvailable(filename)) { + if (vfs.isFileAvailable(filename)) { return true; } buf = strcat_currentmodelname(&filename[sizeof(MODELS_PATH)], ' '); strcpy(buf, TEXT_EXT); - if (isFileAvailable(filename)) { + if (vfs.isFileAvailable(filename)) { return true; } @@ -1126,7 +1127,7 @@ bool modelHasNotes() buf = strAppendFilename(&filename[sizeof(MODELS_PATH)], g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); strcpy(buf, TEXT_EXT); - if (isFileAvailable(filename)) { + if (vfs.isFileAvailable(filename)) { return true; } #endif diff --git a/radio/src/gui/screenshot.cpp b/radio/src/gui/screenshot.cpp index a82ac8a413c..0bf29db7328 100644 --- a/radio/src/gui/screenshot.cpp +++ b/radio/src/gui/screenshot.cpp @@ -52,8 +52,9 @@ const uint8_t BMP_HEADER[] = { const char * writeScreenshot() { - FIL bmpFile; - UINT written; + VirtualFS& vfs = VirtualFS::instance(); + VfsFile bmpFile; + size_t written; char filename[42]; // /SCREENSHOTS/screen-2013-01-01-123540.bmp if (sdIsFull()) { @@ -63,7 +64,7 @@ const char * writeScreenshot() // check and create folder here strcpy(filename, SCREENSHOTS_PATH); - const char * error = sdCheckAndCreateDirectory(filename); + const char * error = vfs.checkAndCreateDirectory(filename); if (error) { return error; } @@ -74,20 +75,20 @@ const char * writeScreenshot() strcpy(tmp, BMP_EXT); #endif - FRESULT result = f_open(&bmpFile, filename, FA_CREATE_ALWAYS | FA_WRITE); - if (result != FR_OK) { - return SDCARD_ERROR(result); + VfsError result = vfs.openFile(bmpFile, filename, VfsOpenFlags::CREATE_ALWAYS | VfsOpenFlags::WRITE); + if (result != VfsError::OK) { + return STORAGE_ERROR(result); } - result = f_write(&bmpFile, BMP_HEADER, sizeof(BMP_HEADER), &written); - if (result != FR_OK || written != sizeof(BMP_HEADER)) { - f_close(&bmpFile); - return SDCARD_ERROR(result); + result = bmpFile.write(BMP_HEADER, sizeof(BMP_HEADER), written); + if (result != VfsError::OK || written != sizeof(BMP_HEADER)) { + bmpFile.close(); + return STORAGE_ERROR(result); } #if defined(COLORLCD) lv_img_dsc_t* snapshot = lv_snapshot_take(lv_scr_act(), LV_IMG_CF_TRUE_COLOR); - if (!snapshot) { f_close(&bmpFile); return nullptr; } + if (!snapshot) { bmpFile.close(); return nullptr; } auto w = snapshot->header.w; auto h = snapshot->header.h; @@ -102,10 +103,10 @@ const char * writeScreenshot() | (pixel.ch.green << 10) | (pixel.ch.blue << 3); - if (f_write(&bmpFile, &dst, sizeof(dst), &written) != FR_OK || written != sizeof(dst)) { + if (bmpFile.write(&dst, sizeof(dst), written) != VfsError::OK || written != sizeof(dst)) { lv_snapshot_free(snapshot); - f_close(&bmpFile); - return SDCARD_ERROR(result); + bmpFile.close(); + return STORAGE_ERROR(result); } } } @@ -117,15 +118,15 @@ const char * writeScreenshot() for (int y=LCD_H-1; y>=0; y-=1) { for (int x=0; x<8*((LCD_W+7)/8); x+=2) { pixel_t byte = getPixel(x+1, y) + (getPixel(x, y) << 4); - if (f_write(&bmpFile, &byte, 1, &written) != FR_OK || written != 1) { - f_close(&bmpFile); - return SDCARD_ERROR(result); + if (bmpFile.write(&byte, 1, written) != VfsError::OK || written != 1) { + bmpFile.close(); + return STORAGE_ERROR(result); } } } #endif - f_close(&bmpFile); + bmpFile.close(); return nullptr; } diff --git a/radio/src/io/bootloader_flash.cpp b/radio/src/io/bootloader_flash.cpp index 6f0c738a055..4c0643056b1 100644 --- a/radio/src/io/bootloader_flash.cpp +++ b/radio/src/io/bootloader_flash.cpp @@ -28,18 +28,16 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif bool isBootloader(const char * filename) { - FIL file; - f_open(&file, filename, FA_READ); + VfsFile file; + VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ); uint8_t buffer[1024]; - UINT count; + size_t count; - if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK || count != sizeof(buffer)) { + if (file.read(buffer, sizeof(buffer), count) != VfsError::OK || count != sizeof(buffer)) { return false; } @@ -48,13 +46,13 @@ bool isBootloader(const char * filename) void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler) { - FIL file; + VfsFile file; uint8_t buffer[1024]; - UINT count; + size_t count; pulsesStop(); - f_open(&file, filename, FA_READ); + VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ); static uint8_t unlocked = 0; if (!unlocked) { @@ -62,7 +60,7 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand unlockFlash(); } - UINT flash_size = file.obj.objsize; + size_t flash_size = file.size(); if (flash_size > BOOTLOADER_SIZE) { flash_size = BOOTLOADER_SIZE; } @@ -72,12 +70,12 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand watchdogSuspend(1000/*10s*/); memset(buffer, 0xFF, sizeof(buffer)); - if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) { + if (file.read(buffer, sizeof(buffer), count) != VfsError::OK) { POPUP_WARNING(STR_SDCARD_ERROR); break; } if (count != sizeof(buffer) - && !f_eof(&file)) { + && !file.eof()) { POPUP_WARNING(STR_SDCARD_ERROR); break; } @@ -85,13 +83,13 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand POPUP_WARNING(STR_INCOMPATIBLE); break; } - for (UINT j = 0; j < count; j += FLASH_PAGESIZE) { + for (size_t j = 0; j < count; j += FLASH_PAGESIZE) { flashWrite(CONVERT_UINT_PTR(FIRMWARE_ADDRESS + i + j), CONVERT_UINT_PTR(buffer + j)); } progressHandler("Bootloader", STR_WRITING, i, flash_size); // Reached end-of-file - if (f_eof(&file)) break; + if (file.eof()) break; #if defined(SIMU) // add an artificial delay and check for simu quit @@ -110,7 +108,7 @@ void BootloaderFirmwareUpdate::flashFirmware(const char * filename, ProgressHand unlocked = 0; } - f_close(&file); + file.close(); pulsesStart(); } diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index b690c03e723..00c52f543eb 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -28,8 +28,6 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif #include "hal/module_port.h" @@ -48,20 +46,20 @@ const char * readFrSkyFirmwareInformation(const char * filename, FrSkyFirmwareInformation & data) { - FIL file; - UINT count; + VfsFile file; + size_t count; - if (f_open(&file, filename, FA_READ) != FR_OK) { + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) { return STR_NEEDS_FILE; } - if (f_read(&file, &data, sizeof(data), &count) != FR_OK || count != sizeof(data)) { - f_close(&file); + if (file.read(&data, sizeof(data), count) != VfsError::OK || count != sizeof(data)) { + file.close(); return STR_DEVICE_FILE_ERROR; } - uint32_t size = f_size(&file); - f_close(&file); + size_t size = file.size(); + file.close(); if (data.headerVersion != 1 && data.fourcc != 0x4B535246) { return STR_DEVICE_FILE_ERROR; @@ -271,12 +269,12 @@ static const etx_serial_init serialInitParams = { const char *FrskyDeviceFirmwareUpdate::doFlashFirmware( const char *filename, ProgressHandler progressHandler) { - FIL file; + VfsFile file; const char *result; FrSkyFirmwareInformation information; - UINT count; + size_t count; - if (f_open(&file, filename, FA_READ) != FR_OK) { + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) { return STR_NEEDS_FILE; } @@ -296,12 +294,11 @@ const char *FrskyDeviceFirmwareUpdate::doFlashFirmware( etx_serial_init cfg(serialInitParams); cfg.baudrate = 57600; - const char *ext = getFileExtension(filename); + const char * ext = VirtualFS::getFileExtension(filename); if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { - auto ret = - f_read(&file, &information, sizeof(FrSkyFirmwareInformation), &count); - if (ret != FR_OK || count != sizeof(FrSkyFirmwareInformation)) { - f_close(&file); + auto ret = file.read(&information, sizeof(FrSkyFirmwareInformation), count); + if (ret != VfsError::OK || count != sizeof(FrSkyFirmwareInformation)) { + file.close(); return STR_DEVICE_FILE_ERROR; } @@ -358,9 +355,9 @@ const char *FrskyDeviceFirmwareUpdate::doFlashFirmware( // Special update method for X12S / X10 iXJT if (module == INTERNAL_MODULE && port == ETX_MOD_PORT_UART && set_bootcmd != nullptr) { - result = uploadFileToHorusXJT(filename, &file, progressHandler); + result = uploadFileToHorusXJT(filename, file, progressHandler); } else { - result = uploadFileNormal(filename, &file, progressHandler); + result = uploadFileNormal(filename, file, progressHandler); } if (set_pwr) set_pwr(false); @@ -371,10 +368,10 @@ const char *FrskyDeviceFirmwareUpdate::doFlashFirmware( } const char *FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT( - const char *filename, FIL *file, ProgressHandler progressHandler) + const char *filename, VfsFile& file, ProgressHandler progressHandler) { uint32_t buffer[1024 / sizeof(uint32_t)]; - UINT count; + size_t count; uint8_t frame[8]; if (!readBuffer(frame, 8, 100) || frame[0] != 0x01) { @@ -392,10 +389,11 @@ const char *FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT( readBuffer(frame, 1, 100); uint8_t index = 0; + size_t fSize = file.size(); while (true) { - progressHandler(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); + progressHandler(VirtualFS::getBasename(filename), STR_WRITING, file.tell(), fSize); - if (f_read(file, buffer, 1024, &count) != FR_OK) { + if (file.read(buffer, 1024, count) != VfsError::OK) { return STR_DEVICE_FILE_ERROR; } @@ -439,10 +437,10 @@ void FrskyDeviceFirmwareUpdate::sendDataTransfer(uint32_t* buffer) } const char *FrskyDeviceFirmwareUpdate::uploadFileNormal( - const char *filename, FIL *file, ProgressHandler progressHandler) + const char *filename, VfsFile& file, ProgressHandler progressHandler) { uint32_t buffer[1024 / sizeof(uint32_t)]; - UINT count; + size_t count; const char * result = sendPowerOn(); if (result) @@ -461,8 +459,9 @@ const char *FrskyDeviceFirmwareUpdate::uploadFileNormal( uint8_t retries = 0; + size_t fSize = file.size(); while (true) { - if (f_read(file, buffer, 1024, &count) != FR_OK) { + if (file.read(buffer, 1024, count) != VfsError::OK) { return STR_DEVICE_FILE_ERROR; } @@ -482,7 +481,7 @@ const char *FrskyDeviceFirmwareUpdate::uploadFileNormal( sendDataTransfer(buffer); if (i == 0) { - progressHandler(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); + progressHandler(VirtualFS::getBasename(filename), STR_WRITING, file.tell(), fSize); } } @@ -514,7 +513,7 @@ const char *FrskyDeviceFirmwareUpdate::flashFirmware( // switch S.PORT power OFF if supported modulePortSetPower(SPORT_MODULE, false); - progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0); + progressHandler(VirtualFS::getBasename(filename), STR_DEVICE_RESET, 0, 0); /* wait 2s off */ watchdogSuspend(1000 /*10s*/); diff --git a/radio/src/io/frsky_firmware_update.h b/radio/src/io/frsky_firmware_update.h index 754e5fdbf11..3b8aa7b67d9 100644 --- a/radio/src/io/frsky_firmware_update.h +++ b/radio/src/io/frsky_firmware_update.h @@ -154,8 +154,8 @@ class FrskyDeviceFirmwareUpdate { const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler); const char * sendPowerOn(); const char * sendReqVersion(); - const char * uploadFileNormal(const char * filename, FIL * file, ProgressHandler progressHandler); - const char * uploadFileToHorusXJT(const char * filename, FIL * file, ProgressHandler progressHandler); + const char * uploadFileNormal(const char * filename, VfsFile& file, ProgressHandler progressHandler); + const char * uploadFileToHorusXJT(const char * filename, VfsFile& file, ProgressHandler progressHandler); const char * endTransfer(); }; diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index b3651a24048..8fa8fac2310 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -37,8 +37,6 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif #if defined(MULTI_PROTOLIST) @@ -75,7 +73,7 @@ class MultiFirmwareUpdateDriver { } - const char* flashFirmware(FIL* file, const char* label, + const char* flashFirmware(VfsFile& file, const char* label, ProgressHandler progressHandler); }; @@ -311,7 +309,7 @@ void MultiFirmwareUpdateDriver::leaveProgMode() } const char* MultiFirmwareUpdateDriver::flashFirmware( - FIL* file, const char* label, ProgressHandler progressHandler) + VfsFile& file, const char* label, ProgressHandler progressHandler) { #if defined(SIMU) for (uint16_t i = 0; i < 100; i++) { @@ -356,12 +354,14 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( writeOffset = 0x1000; // start offset (word address) } - while (!f_eof(file)) { - progressHandler(label, STR_WRITING, file->fptr, file->obj.objsize); + size_t fSize = file.size(); - UINT count = 0; + while (!file.eof()) { + progressHandler(label, STR_WRITING, file.tell(), fSize); + + size_t count = 0; memclear(buffer, pageSize); - if (f_read(file, buffer, pageSize, &count) != FR_OK) { + if (file.read(buffer, pageSize, count) != VfsError::OK) { result = STR_DEVICE_FILE_ERROR; break; } @@ -384,8 +384,8 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( writeOffset += pageSize / 2; } - if (f_eof(file)) { - progressHandler(label, STR_WRITING, file->fptr, file->obj.objsize); + if (file.eof()) { + progressHandler(label, STR_WRITING, file.tell(), fSize); } leaveProgMode(); @@ -475,26 +475,26 @@ const char * MultiFirmwareInformation::readV2Signature(const char * buffer) const char * MultiFirmwareInformation::readMultiFirmwareInformation(const char * filename) { - FIL file; - if (f_open(&file, filename, FA_READ) != FR_OK) + VfsFile file; + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) return STR_DEVICE_FILE_ERROR; - const char * err = readMultiFirmwareInformation(&file); - f_close(&file); + const char * err = readMultiFirmwareInformation(file); + file.close(); return err; } -const char * MultiFirmwareInformation::readMultiFirmwareInformation(FIL * file) +const char * MultiFirmwareInformation::readMultiFirmwareInformation(VfsFile& file) { char buffer[MULTI_SIGN_SIZE]; - UINT count; + size_t count; - if (f_size(file) < MULTI_SIGN_SIZE) + if (file.size() < MULTI_SIGN_SIZE) return STR_DEVICE_FILE_ERROR; - f_lseek(file, f_size(file) - MULTI_SIGN_SIZE); - if (f_read(file, buffer, MULTI_SIGN_SIZE, &count) != FR_OK || count != MULTI_SIGN_SIZE) { + file.lseek(file.size() - MULTI_SIGN_SIZE); + if (file.read(buffer, MULTI_SIGN_SIZE, count) != VfsError::OK || count != MULTI_SIGN_SIZE) { return STR_DEVICE_FILE_ERROR; } @@ -507,26 +507,26 @@ const char * MultiFirmwareInformation::readMultiFirmwareInformation(FIL * file) bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler) { - FIL file; + VfsFile file; - if (f_open(&file, filename, FA_READ) != FR_OK) { + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) { POPUP_WARNING(STR_DEVICE_FILE_ERROR); return false; } if (type == MULTI_TYPE_MULTIMODULE) { MultiFirmwareInformation firmwareFile; - if (firmwareFile.readMultiFirmwareInformation(&file)) { - f_close(&file); + if (firmwareFile.readMultiFirmwareInformation(file)) { + file.close(); POPUP_WARNING(STR_DEVICE_FILE_ERROR); return false; } - f_lseek(&file, 0); + file.lseek(0); #if defined(HARDWARE_EXTERNAL_MODULE) if (module == EXTERNAL_MODULE) { if (!firmwareFile.isMultiExternalFirmware()) { - f_close(&file); + file.close(); POPUP_WARNING(STR_NEEDS_FILE, STR_EXT_MULTI_SPEC); return false; } @@ -536,7 +536,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan #if defined(HARDWARE_INTERNAL_MODULE) if (module == INTERNAL_MODULE) { if (!firmwareFile.isMultiInternalFirmware()) { - f_close(&file); + file.close(); POPUP_WARNING(STR_NEEDS_FILE, STR_INT_MULTI_SPEC); return false; } @@ -555,15 +555,15 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan // switch S.PORT power OFF if supported modulePortSetPower(SPORT_MODULE, false); - progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0); + progressHandler(VirtualFS::getBasename(filename), STR_DEVICE_RESET, 0, 0); /* wait 2s off */ watchdogSuspend(500 /*5s*/); RTOS_WAIT_MS(3000); MultiFirmwareUpdateDriver driver(module, type); - const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler); - f_close(&file); + const char * result = driver.flashFirmware(file, VirtualFS::instance().getBasename(filename), progressHandler); + file.close(); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1); BACKLIGHT_ENABLE(); diff --git a/radio/src/io/multi_firmware_update.h b/radio/src/io/multi_firmware_update.h index 29a210818e5..d8f63722d6e 100644 --- a/radio/src/io/multi_firmware_update.h +++ b/radio/src/io/multi_firmware_update.h @@ -21,7 +21,7 @@ #pragma once -#include "ff.h" +#include "VirtualFS.h" /* Signature format is multi-[board type]-[bootloader support][check for bootloader][multi telemetry type][telemetry inversion][debug]-[firmware version] Where: @@ -87,7 +87,7 @@ class MultiFirmwareInformation } const char * readMultiFirmwareInformation(const char * filename); - const char * readMultiFirmwareInformation(FIL * file); + const char * readMultiFirmwareInformation(VfsFile& file); private: bool optibootSupport:1; diff --git a/radio/src/libopenui_conf.h b/radio/src/libopenui_conf.h new file mode 100644 index 00000000000..ccd7c5d99ae --- /dev/null +++ b/radio/src/libopenui_conf.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) EdgeTX + * + * Source: + * https://github.com/EdgeTX/libopenui + * + * Based on code named + * opentx - https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#if !defined(_OPENUI_CONF_H) +#define _OPENUI_CONF_H + +#include "VirtualFS.h" + +#define OpenUiFile VfsFile +#define OpenUiDir VfsDir +#define OpenUiFileInfo VfsFileInfo +#define OpenUiFsRetType VfsError + +#define OPENUI_FS_OPEN_FLAG_READ VfsOpenFlags::READ +#define OPENUI_FS_OPEN_FLAG_OPENEXISTING VfsOpenFlags::OPEN_EXISTING + +#define openUiOpenFile(fileHandle, path, flags) (VirtualFS::instance().openFile(*(fileHandle), path, flags)) +#define openUiCloseFile(fileHandle) ((fileHandle)->close()) +#define openUiReadFile(fileHandle, buf, bytes, read) ((fileHandle)->read(buf, bytes, read)) +#define openUiFileGetSize(fileHandle) ((fileHandle)->size()) +#define openUiFileLSeek(fileHandle, offset) ((fileHandle)->lseek(offset)) + +#define openUiOpenDir(dirHandle, path) (VirtualFS::instance().openDirectory(*(dirHandle), path)) +#define openUiCloseDir(dirHandle) ((dirHandle)->close()) +#define openUiReadDir(dirHandle, fileInfo) ((dirHandle)->read(*(fileInfo))) + +#define openUiFsIsDir(fileInfo) ((fileInfo)->getType() == VfsType::DIR) +#define openUiFsIsHiddenFile(fileHandle) (false) +#define openUiFsIsSystemFile(fileHandle) (false) +#define openUiFsGetName(fileInfo) ((fileInfo)->getName()) + +#define openUiGetFileExtension(filename, size, extMaxLen, fnlen, extlen) (VirtualFS::getFileExtension(filename, size, extMaxLen, fnlen, extlen)) +#define openUiIsFileExtensionMatching(extension, pattern, match) (VirtualFS::isFileExtensionMatching(extension, pattern, match)) + +#define OPENUI_FS_OK VfsError::OK + +#endif // _OPENUI_CONF_H diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index fd1bbd240b9..97531e483ee 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -20,18 +20,20 @@ */ #include "opentx.h" -#include "ff.h" +#include "VirtualFS.h" #include "analogs.h" #include "switches.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" +#include "logs.h" + #if defined(LIBOPENUI) #include "libopenui.h" #endif -FIL g_oLogFile __DMA; +VfsFile g_oLogFile __DMA; uint8_t logDelay100ms; static tmr10ms_t lastLogTime = 0; @@ -109,23 +111,24 @@ int getSwitchState(uint8_t swtch) { void logsInit() { - memset(&g_oLogFile, 0, sizeof(g_oLogFile)); + g_oLogFile.close(); } const char * logsOpen() { // Determine and set log file filename - FRESULT result; + VfsError result; + VirtualFS& vfs = VirtualFS::instance(); // /LOGS/modelnamexxxxxx_YYYY-MM-DD-HHMMSS.log char filename[sizeof(LOGS_PATH) + LEN_MODEL_NAME + 18 + 4 + 1]; - if (!sdMounted()) + if (!VirtualFS::instance().sdCardMounted()) return STR_NO_SDCARD; // check and create folder here strcpy(filename, STR_LOGS_PATH); - const char * error = sdCheckAndCreateDirectory(filename); + const char * error = vfs.checkAndCreateDirectory(filename); if (error) { return error; } @@ -167,12 +170,12 @@ const char * logsOpen() strcpy(tmp, STR_LOGS_EXT); - result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE | FA_OPEN_APPEND); - if (result != FR_OK) { - return SDCARD_ERROR(result); + result = vfs.openFile(g_oLogFile, filename, VfsOpenFlags::OPEN_ALWAYS | VfsOpenFlags::WRITE | VfsOpenFlags::OPEN_APPEND); + if (result != VfsError::OK) { + return STORAGE_ERROR(result); } - if (f_size(&g_oLogFile) == 0) { + if (g_oLogFile.size() == 0) { writeHeader(); } @@ -181,11 +184,8 @@ const char * logsOpen() void logsClose() { - if (g_oLogFile.obj.fs && sdMounted()) { - if (f_close(&g_oLogFile) != FR_OK) { - // close failed, forget file - g_oLogFile.obj.fs = 0; - } + if (g_oLogFile.isOpen() && VirtualFS::instance().sdCardMounted()) { + g_oLogFile.close(); lastLogTime = 0; } @@ -194,9 +194,9 @@ void logsClose() void writeHeader() { #if defined(RTCLOCK) - f_puts("Date,Time,", &g_oLogFile); + g_oLogFile.puts("Date,Time,"); #else - f_puts("Time,", &g_oLogFile); + g_oLogFile.puts("Time,"); #endif @@ -215,7 +215,7 @@ void writeHeader() strcat(label, ")"); } strcat(label, ","); - f_puts(label, &g_oLogFile); + g_oLogFile.puts(label); } } } @@ -223,16 +223,16 @@ void writeHeader() auto n_inputs = adcGetMaxInputs(ADC_INPUT_MAIN); for (uint8_t i = 0; i < n_inputs; i++) { const char* p = analogGetCanonicalName(ADC_INPUT_MAIN, i); - while (*p) { f_putc(*(p++), &g_oLogFile); } - f_putc(',', &g_oLogFile); + while (*p) { g_oLogFile.putc(*(p++)); } + g_oLogFile.putc(','); } n_inputs = adcGetMaxInputs(ADC_INPUT_POT); for (uint8_t i = 0; i < n_inputs; i++) { if (!IS_POT_AVAILABLE(i)) continue; const char* p = analogGetCanonicalName(ADC_INPUT_POT, i); - while (*p) { f_putc(*(p++), &g_oLogFile); } - f_putc(',', &g_oLogFile); + while (*p) { g_oLogFile.putc(*(p++)); } + g_oLogFile.putc(','); } for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { @@ -242,16 +242,16 @@ void writeHeader() temp = getSwitchName(s, i); *temp++ = ','; *temp = '\0'; - f_puts(s, &g_oLogFile); + g_oLogFile.puts(s); } } - f_puts("LSW,", &g_oLogFile); + g_oLogFile.puts("LSW,"); for (uint8_t channel = 0; channel < MAX_OUTPUT_CHANNELS; channel++) { - f_printf(&g_oLogFile, "CH%d(us),", channel+1); + g_oLogFile.fprintf("CH%d(us),", channel+1); } - f_puts("TxBat(V)\n", &g_oLogFile); + g_oLogFile.puts("TxBat(V)\n"); } uint32_t getLogicalSwitchesStates(uint8_t first) @@ -267,7 +267,7 @@ void logsWrite() { static const char * error_displayed = nullptr; - if (!sdMounted()) { + if (!VirtualFS::instance().sdCardMounted()) { return; } @@ -283,7 +283,7 @@ void logsWrite() bool sdCardFull = sdIsFull(); // check if file needs to be opened - if (!g_oLogFile.obj.fs) { + if (!g_oLogFile.isOpen()) { const char *result = sdCardFull ? STR_SDCARD_FULL_EXT : logsOpen(); // SD card is full or file open failed @@ -313,10 +313,10 @@ void logsWrite() lastRtcTime = g_rtcTime; gettime(&utm); } - f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); + g_oLogFile.fprintf("%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); } #else - f_printf(&g_oLogFile, "%d,", tmr10ms); + g_oLogFile.fprintf("%d,", tmr10ms); #endif for (int i=0; igcrunning) { luaC_fullgc(L, 1); /* try to free some memory... */ - *b = BitmapBuffer::loadBitmap(filename); /* try again */ + *b = BitmapBuffer::loadBitmap(file.c_str()); /* try again */ } } diff --git a/radio/src/lua/api_filesystem.cpp b/radio/src/lua/api_filesystem.cpp index 47cf67bd331..8ea46b7f510 100644 --- a/radio/src/lua/api_filesystem.cpp +++ b/radio/src/lua/api_filesystem.cpp @@ -22,29 +22,32 @@ #define LUA_LIB #include - +#include +#include "VirtualFS.h" #include "lua_api.h" +#include "lua_file_api.h" + #include "api_filesystem.h" // garbage collector for luaDir static int dir_gc(lua_State* L) { - DIR* dir = (DIR*)lua_touserdata(L, 1); - if (dir) f_closedir(dir); + VfsDir* dir = (VfsDir*)lua_touserdata(L, 1); + if (dir) dir->close(); return 0; } static int dir_iter(lua_State* L) { - DIR* dir = (DIR*)lua_touserdata(L, lua_upvalueindex(1)); + VfsDir* dir = (VfsDir*)lua_touserdata(L, lua_upvalueindex(1)); - FILINFO info; - FRESULT res = f_readdir(dir, &info); - if (res != FR_OK || info.fname[0] == 0) { /* Break on error or end of dir */ + VfsFileInfo info; + VfsError res = dir->read(info); + if (res != VfsError::OK || strlen(info.getName()) == 0) { /* Break on error or end of dir */ return 0; } - lua_pushstring(L, info.fname); + lua_pushstring(L, info.getName()); return 1; } @@ -68,13 +71,14 @@ static int dir_iter(lua_State* L) int luaDir(lua_State* L) { const char* path = luaL_optstring(L, 1, nullptr); - DIR* dir = (DIR*)lua_newuserdata(L, sizeof(DIR)); + VfsDir* dir = (VfsDir*)lua_newuserdata(L, sizeof(VfsDir)); luaL_getmetatable(L, DIR_METATABLE); lua_setmetatable(L, -2); - FRESULT res = f_opendir(dir, path); - if (res != FR_OK) { + std::string p = normalizeLuaPath(path); + VfsError res = VirtualFS::instance().openDirectory(*dir, p.c_str()); + if (res != VfsError::OK) { printf("luaDir cannot open %s\n", path); } @@ -124,25 +128,29 @@ int luaFstat(lua_State* L) { const char * path = luaL_optstring(L, 1, nullptr); - FRESULT res; - FILINFO info; + VirtualFS& vfs = VirtualFS::instance(); + VfsError res; + VfsFileInfo info; + std::string p = normalizeLuaPath(path); - res = f_stat(path, &info); - if (res != FR_OK) { + res = vfs.fstat(p.c_str(), info); + if (res != VfsError::OK) { printf("luaFstat cannot open %s\n", path); return 0; } lua_newtable(L); - lua_pushtableinteger(L, "size", info.fsize); - lua_pushtableinteger(L, "attrib", info.fattrib); - - uint32_t year = (info.fdate >> 9) + 1980; - uint32_t month = info.fdate >> 5 & 15; - uint32_t day = info.fdate & 31; - uint32_t hour = info.ftime >> 11; - uint32_t mn = info.ftime >> 5 & 63; - uint32_t sec = (info.ftime & 31) * 2; + lua_pushtableinteger(L, "size", info.getSize()); + lua_pushtableinteger(L, "attrib", (int)info.getAttrib()); + + int date = info.getDate(); + int time = info.getTime(); + uint32_t year = (date >> 9) + 1980; + uint32_t month = date >> 5 & 15; + uint32_t day = date & 31; + uint32_t hour = time >> 11; + uint32_t mn = time >> 5 & 63; + uint32_t sec = (time & 31) * 2; lua_pushstring(L, "time"); luaPushDateTime(L, year, month, day, hour, mn, sec); @@ -171,13 +179,12 @@ int luaFstat(lua_State* L) int luaDelete(lua_State* L) { const char* filename = luaL_optstring(L, 1, nullptr); - - FRESULT res = f_unlink(filename); - if (res != FR_OK) { + VfsError res = VirtualFS::instance().unlink(filename); + if (res != VfsError::OK) { printf("luaDelete cannot delete file/folder %s\n", filename); } - lua_pushunsigned(L, res); + lua_pushunsigned(L, (lua_Unsigned) res); return 1; } diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 852721f2ae0..566190fe70a 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -33,6 +33,8 @@ #include "switches.h" #include "input_mapping.h" +#include "lua_file_api.h" + #if defined(LIBOPENUI) #include "libopenui.h" #include "api_colorlcd.h" @@ -1424,7 +1426,7 @@ static int luaGetFlightMode(lua_State * L) Play a file from the SD card -@param path (string) full path to wav file (i.e. “/SOUNDS/en/system/tada.wav”) +@param path (string) full path to wav file (i.e. 鈥?SOUNDS/en/system/tada.wav鈥? Introduced in 2.1.0: If you use a relative path, the current language is appended to the path (example: for English language: `/SOUNDS/en` is appended) @@ -1673,9 +1675,9 @@ Raises a pop-up on screen that allows uses input @param event (number) the event variable that is passed in from the Run function (key pressed) -@param input (number) value that can be adjusted by the +/­- keys +@param input (number) value that can be adjusted by the +/颅- keys -@param min (number) min value that input can reach (by pressing the -­ key) +@param min (number) min value that input can reach (by pressing the -颅 key) @param max (number) max value that input can reach @@ -1985,7 +1987,18 @@ static int luaGetRSSI(lua_State * L) static int luaChdir(lua_State * L) { const char * directory = luaL_optstring(L, 1, nullptr); - f_chdir(directory); + std::string dir; + if(directory[0] == '/') + { + dir = ROOT_PATH; + dir += directory; + } else if (directory[0] == ':') { + dir += directory+1; + } else { + dir = directory; + } + + VirtualFS::instance().changeDirectory(dir.c_str()); return 0; } @@ -2048,7 +2061,7 @@ static int luaLoadScript(lua_State * L) const char *mode = luaL_optstring(L, 2, NULL); int env = (!lua_isnone(L, 3) ? 3 : 0); // 'env' index or 0 if no 'env' lua_settop(L, 0); - if (fname != NULL && luaLoadScriptFileToState(L, fname , mode) == SCRIPT_OK) { + if (fname != NULL && luaLoadScriptFileToState(L, normalizeLuaPath(fname).c_str() , mode) == SCRIPT_OK) { if (env != 0) { // 'env' parameter? lua_pushvalue(L, env); // environment for loaded function if (!lua_setupvalue(L, -2, 1)) // set it as 1st upvalue @@ -2138,7 +2151,7 @@ static int luaResetGlobalTimer(lua_State * L) /*luadoc @function multiBuffer(address[,value]) -This function reads/writes the Multi protocol buffer to interact with a protocol². +This function reads/writes the Multi protocol buffer to interact with a protocol虏. @param address to read/write in the buffer @param (optional): value to write in the buffer @@ -3091,7 +3104,7 @@ LROT_BEGIN(etxcst, NULL, 0) #if defined(KEYS_GPIO_REG_PAGEUP) LROT_NUMENTRY( EVT_VIRTUAL_PREV_PAGE, EVT_KEY_BREAK(KEY_PAGEUP) ) LROT_NUMENTRY( EVT_VIRTUAL_NEXT_PAGE, EVT_KEY_BREAK(KEY_PAGEDN) ) -#elif defined(PCBNV14) +#elif defined(PCBNV14) || defined(PCBPL18) LROT_NUMENTRY( EVT_VIRTUAL_PREV_PAGE, EVT_KEY_BREAK(KEY_LEFT) ) LROT_NUMENTRY( EVT_VIRTUAL_NEXT_PAGE, EVT_KEY_BREAK(KEY_RIGHT) ) #else diff --git a/radio/src/lua/api_stdlcd.cpp b/radio/src/lua/api_stdlcd.cpp index 19332eb72af..2687695f045 100644 --- a/radio/src/lua/api_stdlcd.cpp +++ b/radio/src/lua/api_stdlcd.cpp @@ -406,7 +406,17 @@ static int luaLcdDrawPixmap(lua_State *L) const char * filename = luaL_checkstring(L, 3); uint8_t bitmap[BITMAP_BUFFER_SIZE(LCD_W/2, LCD_H)]; // width max is LCD_W/2 pixels for saving stack and avoid a malloc here - if (lcdLoadBitmap(bitmap, filename, LCD_W/2, LCD_H)) { + std::string file; + if(filename[0] == '/') + { + file = ROOT_PATH; + file += filename; + } else if (filename[0] == ':') { + file += filename+1; + } else { + file = filename; + } + if (lcdLoadBitmap(bitmap, file.c_str(), LCD_W/2, LCD_H)) { lcdDrawBitmap(x, y, bitmap); } diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index bd24e49ff1b..4a054ef10ad 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -27,18 +27,16 @@ #include "opentx.h" #include "bin_allocator.h" - +#include "VirtualFS.h" #include "lua_api.h" #include "lua_event.h" +#include "lua_file_api.h" -#include "sdcard.h" #include "api_filesystem.h" #include "switches.h" #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif extern "C" { @@ -357,9 +355,9 @@ void luaFree(lua_State * L, ScriptInternalData & sid) static int luaDumpWriter(lua_State * L, const void* p, size_t size, void* u) { UNUSED(L); - UINT written; - FRESULT result = f_write((FIL *)u, p, size, &written); - return (result != FR_OK && !written); + size_t written; + VfsError result = ((VfsFile *)u)->write(p, size, written); + return (result != VfsError::OK && !written); } /* @@ -372,16 +370,18 @@ static int luaDumpWriter(lua_State * L, const void* p, size_t size, void* u) 1 = remove debug info from bytecode (smaller but errors are less informative) 0 = keep debug info */ -static void luaDumpState(lua_State * L, const char * filename, const FILINFO * finfo, int stripDebug) +static void luaDumpState(lua_State * L, const char * filename, const VfsFileInfo* finfo, int stripDebug) { - FIL D; - if (f_open(&D, filename, FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) { + VfsFile D; + VirtualFS& vfs = VirtualFS::instance(); + std::string path = normalizeLuaPath(filename); + if (vfs.openFile(D, path.c_str(), VfsOpenFlags::WRITE | VfsOpenFlags::CREATE_ALWAYS) == VfsError::OK) { lua_lock(L); luaU_dump(L, getproto(L->top - 1), luaDumpWriter, &D, stripDebug); lua_unlock(L); - if (f_close(&D) == FR_OK) { + if (D.close() == VfsError::OK) { if (finfo != nullptr) - f_utime(filename, finfo); // set the file mod time + vfs.utime(path.c_str(), *finfo); // set the file mod time TRACE("luaDumpState(%s): Saved bytecode to file.", filename); } } else @@ -433,9 +433,10 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * #if defined(LUA_COMPILER) uint16_t fnamelen; uint8_t extlen; - char filenameFull[LEN_FILE_PATH_MAX + FF_MAX_LFN + 1] = "\0"; - FILINFO fnoLuaS, fnoLuaC; - FRESULT frLuaS, frLuaC; + char filenameFull[LEN_FILE_PATH_MAX + FF_MAX_LFN + 1] = ":\0"; + VirtualFS& vfs = VirtualFS::instance(); + VfsFileInfo fnoLuaS, fnoLuaC; + VfsError frLuaS, frLuaC; bool scriptNeedsCompile = false; uint8_t loadFileType = 0; // 1=text, 2=binary @@ -445,35 +446,36 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * fnamelen = strlen(filename); // check if file extension is already in the file name and strip it - getFileExtension(filename, fnamelen, 0, nullptr, &extlen); + VirtualFS::getFileExtension(filename, fnamelen, 0, nullptr, &extlen); fnamelen -= extlen; if (fnamelen > sizeof(filenameFull) - sizeof(SCRIPT_BIN_EXT)) { TRACE_ERROR("luaLoadScriptFileToState(%s, %s): Error loading script: filename buffer overflow.\n", filename, lmode); return ret; } + fnamelen++; // for the added colon in filename strncat(filenameFull, filename, fnamelen); // check if binary version exists strcpy(filenameFull + fnamelen, SCRIPT_BIN_EXT); - frLuaC = f_stat(filenameFull, &fnoLuaC); + frLuaC = vfs.fstat(filenameFull+1, fnoLuaC); // check if text version exists strcpy(filenameFull + fnamelen, SCRIPT_EXT); - frLuaS = f_stat(filenameFull, &fnoLuaS); + frLuaS = vfs.fstat(filenameFull+1, fnoLuaS); // decide which version to load, text or binary - if (frLuaC != FR_OK && frLuaS == FR_OK) { + if (frLuaC != VfsError::OK && frLuaS == VfsError::OK) { // only text version exists loadFileType = 1; scriptNeedsCompile = true; } - else if (frLuaC == FR_OK && frLuaS != FR_OK) { + else if (frLuaC == VfsError::OK && frLuaS != VfsError::OK) { // only binary version exists loadFileType = 2; } - else if (frLuaS == FR_OK) { + else if (frLuaS == VfsError::OK) { // both versions exist, compare them - if (strchr(lmode, 'c') || (uint32_t)((fnoLuaC.fdate << 16) + fnoLuaC.ftime) < (uint32_t)((fnoLuaS.fdate << 16) + fnoLuaS.ftime)) { + if (strchr(lmode, 'c') || (uint32_t)((fnoLuaC.getDate() << 16) + fnoLuaC.getTime()) < (uint32_t)((fnoLuaS.getDate() << 16) + fnoLuaS.getTime())) { // text version is newer than binary or forced by "c" mode flag, rebuild it scriptNeedsCompile = true; } @@ -499,9 +501,9 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * // TRACE_DEBUG("luaLoadScriptFileToState(%s, %s):\n", filename, lmode); // TRACE_DEBUG("\tldfile='%s'; ldtype=%u; compile=%u;\n", filenameFull, loadFileType, scriptNeedsCompile); -// TRACE_DEBUG("\t%-5s: %s; mtime: %04X%04X = %u/%02u/%02u %02u:%02u:%02u;\n", SCRIPT_EXT, (frLuaS == FR_OK ? "ok" : "nf"), fnoLuaS.fdate, fnoLuaS.ftime, +// TRACE_DEBUG("\t%-5s: %s; mtime: %04X%04X = %u/%02u/%02u %02u:%02u:%02u;\n", SCRIPT_EXT, (frLuaS == VfsError::OK ? "ok" : "nf"), fnoLuaS.fdate, fnoLuaS.ftime, // (fnoLuaS.fdate >> 9) + 1980, (fnoLuaS.fdate >> 5) & 15, fnoLuaS.fdate & 31, fnoLuaS.ftime >> 11, (fnoLuaS.ftime >> 5) & 63, (fnoLuaS.ftime & 31) * 2); -// TRACE_DEBUG("\t%-5s: %s; mtime: %04X%04X = %u/%02u/%02u %02u:%02u:%02u;\n", SCRIPT_BIN_EXT, (frLuaC == FR_OK ? "ok" : "nf"), fnoLuaC.fdate, fnoLuaC.ftime, +// TRACE_DEBUG("\t%-5s: %s; mtime: %04X%04X = %u/%02u/%02u %02u:%02u:%02u;\n", SCRIPT_BIN_EXT, (frLuaC == VfsError::OK ? "ok" : "nf"), fnoLuaC.fdate, fnoLuaC.ftime, // (fnoLuaC.fdate >> 9) + 1980, (fnoLuaC.fdate >> 5) & 15, fnoLuaC.fdate & 31, fnoLuaC.ftime >> 11, (fnoLuaC.ftime >> 5) & 63, (fnoLuaC.ftime & 31) * 2); // final check that file exists and is allowed by mode flags @@ -523,7 +525,7 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * lstatus = luaL_loadfilex(L, filenameFull, nullptr); #if defined(LUA_COMPILER) // Check for bytecode encoding problem, eg. compiled for x64. Unfortunately Lua doesn't provide a unique error code for this. See Lua/src/lundump.c. - if (lstatus == LUA_ERRSYNTAX && loadFileType == 2 && frLuaS == FR_OK && strstr(lua_tostring(L, -1), "precompiled")) { + if (lstatus == LUA_ERRSYNTAX && loadFileType == 2 && frLuaS == VfsError::OK && strstr(lua_tostring(L, -1), "precompiled")) { loadFileType = 1; scriptNeedsCompile = true; strcpy(filenameFull + fnamelen, SCRIPT_EXT); @@ -544,7 +546,7 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * #endif else { TRACE_ERROR("luaLoadScriptFileToState(%s, %s): Error loading script: %s\n", filename, lmode, lua_tostring(L, -1)); - if (lstatus == LUA_ERRFILE) { + if (lstatus == LUA_ERR_FILE) { ret = SCRIPT_NOFILE; } else if (lstatus == LUA_ERRSYNTAX) { @@ -586,7 +588,9 @@ static const char * getScriptName(uint8_t idx) static bool luaLoad(const char * pathname, ScriptInternalData & sid) { - sid.state = luaLoadScriptFileToState(lsScripts, pathname, LUA_SCRIPT_LOAD_MODE); + std::string path = normalizeLuaPath(pathname); + + sid.state = luaLoadScriptFileToState(lsScripts, path.c_str(), LUA_SCRIPT_LOAD_MODE); if (sid.state != SCRIPT_OK) { luaFree(lsScripts, sid); @@ -1185,7 +1189,8 @@ static bool resumeLua(bool init, bool allowLcdUsage) } else if (lua_isstring(lsScripts, -1)) { char nextScript[FF_MAX_LFN+1]; - strncpy(nextScript, lua_tostring(lsScripts, -1), FF_MAX_LFN); + const char* luaFile = lua_tostring(lsScripts, -1); + strncpy(nextScript, luaFile, FF_MAX_LFN); nextScript[FF_MAX_LFN] = '\0'; luaExec(nextScript); return scriptWasRun; @@ -1365,18 +1370,18 @@ void luaInit() bool readToolName(char * toolName, const char * filename) { - FIL file; + VfsFile file; char buffer[1024]; - UINT count; + size_t count; - if (f_open(&file, filename, FA_READ) != FR_OK) { + if (VirtualFS::instance().openFile(file, filename, VfsOpenFlags::READ) != VfsError::OK) { return "Error opening file"; } - FRESULT res = f_read(&file, &buffer, sizeof(buffer), &count); - f_close(&file); + VfsError res = file.read(&buffer, sizeof(buffer), count); + file.close(); - if (res != FR_OK) + if (res != VfsError::OK) return false; const char * tns = "TNS|"; @@ -1403,6 +1408,7 @@ bool readToolName(char * toolName, const char * filename) bool isRadioScriptTool(const char * filename) { - const char * ext = getFileExtension(filename); + std::string path = normalizeLuaPath(filename); + const char * ext = VirtualFS::getFileExtension(path.c_str()); return ext && !strcasecmp(ext, SCRIPT_EXT); } diff --git a/radio/src/lua/lua_file_api.cpp b/radio/src/lua/lua_file_api.cpp new file mode 100644 index 00000000000..de1345b2bbe --- /dev/null +++ b/radio/src/lua/lua_file_api.cpp @@ -0,0 +1,535 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#if !defined(SIMU) +#include +#include +#endif +#include +#include + +#include "lua_file_api.h" +#include "VirtualFS.h" + +// TODO: places this somewhere else +// and check if we really need that many files +#define MAX_OPEN_FILES 4 + +struct open_files_t +{ + int handle = -1; + int pos = 0; + int flags = 0; + VfsFile* vfs_file = nullptr; +}; + +static open_files_t open_files[MAX_OPEN_FILES]; + +static int findslot(int fh) +{ + static int slot; + static int lastfh = -1; + + if ((fh != -1) && (fh == lastfh)) + return slot; + + for (slot = 0; slot < MAX_OPEN_FILES; slot++) + if (open_files[slot].handle == fh) + break; + + lastfh = fh; + + return slot; +} + +static int remap_vfs_errors(VfsError e) +{ + switch (e) { + // case OK: // No error + + case VfsError::IO: // Error during device operation + case VfsError::CORRUPT: // Corrupted + case VfsError::NOT_READY: // storage not ready + errno = EIO; + break; + + case VfsError::NOENT: // No directory entry + errno = ENOENT; + break; + + case VfsError::EXIST: // Entry already exists + errno = EEXIST; + break; + + case VfsError::NOTDIR: // Entry is not a dir + case VfsError::ISDIR: // Entry is a dir + case VfsError::NOTEMPTY: // Dir is not empty + case VfsError::INVAL: // Invalid parameter + case VfsError::NOATTR: // No data/attr available + case VfsError::NAMETOOLONG: // File name too long + errno = EINVAL; + break; + + case VfsError::BADF: // Bad file number + errno = EBADF; + break; + + case VfsError::FBIG: // File too large + case VfsError::NOSPC: // No space left on device + case VfsError::NOMEM: // No more memory available + default: + errno = EIO; + break; + } + + return -1; +} + +#define FILE_HANDLE_OFFSET (0x20) + +#if 0 +static int remap_handle(int fh) +{ + return fh - FILE_HANDLE_OFFSET; +} +#endif + +static int set_errno(int errval) +{ + errno = errval; + return -1; +} + +static int convertOpenMode(VfsOpenFlags &vfsFlags, const char* mode) +{ + size_t modeLen = strlen(mode); + if(modeLen < 1 || modeLen > 3) + { + set_errno(EINVAL); + return -EINVAL; + } + + if(mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') + { + set_errno(EINVAL); + return -EINVAL; + } + + bool update = false; + bool binary __attribute__((unused)) = false; + if(modeLen>1) + { + if(mode[1] == '+') + update = true; + else if (mode[1] == 'b') + binary = true; + else { + set_errno(EINVAL); + return -EINVAL; + } + } + if(modeLen>2) + { + if(mode[1] == mode[2]) + { + set_errno(EINVAL); + return -EINVAL; + } + if(mode[2] == '+') + update = true; + else if (mode[2] == 'b') + binary = true; + else { + set_errno(EINVAL); + return -EINVAL; + } + } + + vfsFlags = VfsOpenFlags::NONE; + + switch(mode[0]) + { + case 'r': + vfsFlags = VfsOpenFlags::READ; + if(update) + vfsFlags |= VfsOpenFlags::WRITE; + break; + case 'w': + vfsFlags = VfsOpenFlags::WRITE | VfsOpenFlags::CREATE_ALWAYS; + break; + case 'a': + vfsFlags = VfsOpenFlags::WRITE | VfsOpenFlags::CREATE_NEW; + if(update) + vfsFlags |= VfsOpenFlags::READ; + break; + } + + return 0; +} + +std::string normalizeLuaPath(const char* path) +{ + std::string n; + if(path[0] == '/') + { + n = ROOT_PATH; + n += path; + } else if(path[0] == ':') { + n += &path[1]; + } else { + n = path; + } + return n; +} + +extern "C" { +static int _lua_fopen(open_files_t* file, const char* name, const char *mode) +{ + VfsOpenFlags vfsFlags = VfsOpenFlags::NONE; + int ret = convertOpenMode(vfsFlags, mode); + if( ret != 0) + return ret; + + std::string n = normalizeLuaPath(name); + VfsError res = VirtualFS::instance().openFile(*file->vfs_file, n.c_str(), vfsFlags); + if(res == VfsError::OK) + { + set_errno(0); + return 0; + } + + int err = remap_vfs_errors(res); + set_errno(err); + return -err; +} + +open_files_t* lua_fopen(const char* name, const char *mode) +{ + if(strlen(name) == 0) + { + set_errno(EINVAL); + return nullptr; + } + + int slot = findslot(-1); + if(slot < MAX_OPEN_FILES) + { + open_files[slot].vfs_file = new VfsFile(); + int res = _lua_fopen(&open_files[slot], name, mode); + if(res == 0) + { + set_errno(0); + return &open_files[slot]; + } + } else { + set_errno(ENFILE); + } + + open_files[slot].handle = -1; + open_files[slot].pos = 0; + open_files[slot].flags = 0; + return nullptr; +} + +int lua_fclose(open_files_t* file) +{ + file->vfs_file->close(); + delete file->vfs_file; + file->vfs_file = nullptr; + file->handle = -1; + file->pos = 0; + file->flags = 0; + return 0; +} + +open_files_t *lua_freopen(const char *pathname, const char *mode, open_files_t *stream) +{ + stream->vfs_file->close(); + stream->pos = 0; + stream->flags = 0; + int res = _lua_fopen(stream, pathname, mode); + if(res == 0) + return stream; + + stream->handle = -1; + return nullptr; +} + +int lua_feof(open_files_t *stream) +{ + return stream->vfs_file->eof(); +} + +int lua_fseek(open_files_t *stream, long offset, int whence) +{ + int err = 0; + switch(whence) + { + case SEEK_SET: + err = remap_vfs_errors(stream->vfs_file->lseek(offset)); + break; + case SEEK_CUR: + err = remap_vfs_errors(stream->vfs_file->lseek(stream->vfs_file->tell() + offset)); + break; + case SEEK_END: + err = remap_vfs_errors(stream->vfs_file->lseek(stream->vfs_file->size() - offset)); + break; + default: + err = EINVAL; + break; + } + set_errno(err); + return -err; +} + +int lua_ferror(open_files_t *stream) +{ + return 0; +} + +size_t lua_fread(void *ptr, size_t size, size_t nmemb, open_files_t *stream) +{ + size_t count = size*nmemb; + size_t readSize = 0; + + stream->vfs_file->read(ptr, count, readSize); + return readSize/size; +} + +size_t lua_fwrite(const void *ptr, size_t size, size_t nmemb, + open_files_t *stream) +{ + size_t count = size*nmemb; + size_t writtenSize = 0; +#if !defined(BOOT) + stream->vfs_file->write(ptr, count, writtenSize); +#endif + return writtenSize/size; +} + +char *lua_fgets(char *s, int size, open_files_t *stream) +{ + return stream->vfs_file->gets(s, size); +} + +char lua_fgetc(open_files_t *stream) +{ + char c; + size_t result; + if (stream->vfs_file->read(&c, 1, result) == VfsError::OK && result == 1) + return c; + else + return -1; +} + +int lua_fputs(const char *s, open_files_t *stream) +{ +#if !defined(BOOT) + stream->vfs_file->puts(s); +#endif + return strlen(s); +} + + +} + + +#if 0 +extern "C" int _open(const char *name, int flags, ...) +{ + int fh = 0; + int slot; + + if ((slot = findslot(-1)) == MAX_OPEN_FILES) + return set_errno(ENFILE); + + if (flags & O_APPEND) + flags &= ~O_TRUNC; + + VfsOpenFlags vfs_flags = VfsOpenFlags::NONE; + if (((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) && + (flags & (O_RDWR | O_WRONLY))) + vfs_flags = VfsOpenFlags::CREATE_ALWAYS; + else if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + vfs_flags = VfsOpenFlags::OPEN_EXISTING; + else if ((flags & O_CREAT) == O_CREAT) + vfs_flags = VfsOpenFlags::OPEN_ALWAYS; + else if ((flags == O_RDONLY) || (flags == O_WRONLY) || (flags == O_RDWR)) + vfs_flags = VfsOpenFlags::OPEN_EXISTING; + else + return set_errno(EINVAL); + + if ((flags & O_ACCMODE) == O_RDONLY) + vfs_flags = vfs_flags | VfsOpenFlags::READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + vfs_flags = vfs_flags | VfsOpenFlags::WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) + vfs_flags = vfs_flags | VfsOpenFlags::READ | VfsOpenFlags::WRITE; + else + return set_errno(EINVAL); + + fh = -1; + errno = EIO; + + VfsError err; + if (!open_files[slot].vfs_file) + if ((open_files[slot].vfs_file = new VfsFile()) != nullptr) + if ((fh = ((err = VirtualFS::instance().openFile( + *open_files[slot].vfs_file, name, vfs_flags)) == + VfsError::OK) + ? slot + : -1) == -1) + remap_vfs_errors(err); + + if (fh >= 0) { + open_files[slot].handle = fh; + open_files[slot].pos = 0; + open_files[slot].flags = 0; + + if (flags & O_APPEND) { + auto f = open_files[slot].vfs_file; + int s = f->size(); + if (f->lseek(s) != VfsError::OK) + fh = -1; + else + open_files[slot].pos = f->tell(); + } + } + + if ((fh < 0) && open_files[slot].vfs_file) { + delete open_files[slot].vfs_file; + open_files[slot].vfs_file = nullptr; + } + + return fh >= 0 ? (fh + FILE_HANDLE_OFFSET) : -1; +} + +extern "C" int _close(int fd) +{ + int slot = findslot(remap_handle(fd)); + if (slot == MAX_OPEN_FILES) + return set_errno(EBADF); + + open_files[slot].handle = -1; + + if (open_files[slot].vfs_file) { + + auto f = open_files[slot].vfs_file; + auto err = f->close(); + + open_files[slot].vfs_file = nullptr; + delete f; + + if (err != VfsError::OK) + return remap_vfs_errors(err); + } + + return 0; +} + +extern "C" int _fstat(int fd, struct stat * st) +{ + (void)fd; + (void)st; + return set_errno(ENOSYS); +} + +extern "C" int _lseek(int fd, int ptr, int dir) +{ + int slot = findslot(remap_handle(fd)); + if (slot == MAX_OPEN_FILES || !open_files[slot].vfs_file) + return set_errno(EBADF); + + // We support only SEEK_SET for now + if (dir != SEEK_SET) + return set_errno(EINVAL); + + auto err = open_files[slot].vfs_file->lseek(ptr); + if (err != VfsError::OK) + return remap_vfs_errors(err); + + return open_files[slot].pos = open_files[slot].vfs_file->tell(); +} + +extern "C" int _read(int fd, char *ptr, int len) +{ + int slot = findslot(remap_handle(fd)); + if (slot == MAX_OPEN_FILES || !open_files[slot].vfs_file) + return set_errno(EBADF); + + if (open_files[slot].flags & O_WRONLY) + return set_errno(EBADF); + + auto f = open_files[slot].vfs_file; + size_t bytesRead; + VfsError err; + + if ((err = f->read(ptr, len, bytesRead)) != VfsError::OK) + return remap_vfs_errors(err); + + int bytesUnRead = len - bytesRead; + if (bytesUnRead < 0) + return -1; + + open_files[slot].pos += len - bytesUnRead; + return len - bytesUnRead; +} + +extern "C" int _write(int fd, char *ptr, int len) +{ +#if !defined(BOOT) + int slot = findslot(remap_handle(fd)); + if (slot == MAX_OPEN_FILES || !open_files[slot].vfs_file) + return set_errno(EBADF); + + if (open_files[slot].flags & O_RDONLY) + return set_errno(EBADF); + + auto f = open_files[slot].vfs_file; + size_t bytesWritten; + VfsError err; + + if ((err = f->write(ptr, len, bytesWritten)) != VfsError::OK) + return remap_vfs_errors(err); + + int bytesUnWritten = len - bytesWritten; + if (bytesUnWritten < 0 || bytesUnWritten == len) + return -1; + + open_files[slot].pos += len - bytesUnWritten; + + return len - bytesUnWritten; +#else + return set_errno(ENOSYS); +#endif +} + +// Not Implemented: +// int _mkdir(const char *path, mode_t mode __attribute__ ((unused))) +// int _chmod(const char *path, mode_t mode) + +#endif diff --git a/radio/src/lua/lua_file_api.h b/radio/src/lua/lua_file_api.h new file mode 100644 index 00000000000..fba5b3bd1d5 --- /dev/null +++ b/radio/src/lua/lua_file_api.h @@ -0,0 +1,16 @@ +/* + * lua_file_api.h + * + * Created on: Apr 19, 2022 + * Author: gagarin + */ + +#ifndef RADIO_SRC_LUA_LUA_FILE_API_H_ +#define RADIO_SRC_LUA_LUA_FILE_API_H_ + +#include + +std::string normalizeLuaPath(const char* path); + + +#endif /* RADIO_SRC_LUA_LUA_FILE_API_H_ */ diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index db1f864d009..3087ed7aa54 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -23,10 +23,11 @@ #include #include "opentx.h" +#include "VirtualFS.h" +#include "bin_allocator.h" #include "lua_api.h" #include "widget.h" -#include "libopenui_file.h" #include "view_main.h" #include "lua_widget.h" @@ -297,36 +298,38 @@ void luaLoadFile(const char * filename, void (*callback)()) void luaLoadFiles(const char * directory, void (*callback)()) { char path[LUA_FULLPATH_MAXLEN+1]; - FILINFO fno; - DIR dir; + VirtualFS& vfs = VirtualFS::instance(); + VfsFileInfo fno; + VfsDir dir; strcpy(path, directory); TRACE("luaLoadFiles() %s", path); - FRESULT res = f_opendir(&dir, path); /* Open the directory */ + VfsError res = vfs.openDirectory(dir, path); /* Open the directory */ - if (res == FR_OK) { + if (res == VfsError::OK) { int pathlen = strlen(path); path[pathlen++] = '/'; for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - uint8_t len = strlen(fno.fname); + res = dir.read(fno); /* Read a directory item */ + std::string fname = fno.getName(); + if (res != VfsError::OK || fname.length() == 0) break; /* Break on error or end of dir */ + uint8_t len = fname.length(); if (len > 0 && (unsigned int)(len + pathlen + sizeof(LUA_WIDGET_FILENAME)) <= sizeof(path) && - fno.fname[0]!='.' && (fno.fattrib & AM_DIR)) { - strcpy(&path[pathlen], fno.fname); + fname[0]!='.' && (fno.getType() == VfsType::DIR)) { + strcpy(&path[pathlen], fname.c_str()); strcat(&path[pathlen], LUA_WIDGET_FILENAME); - if (isFileAvailable(path)) { + if (vfs.isFileAvailable(path)) { luaLoadFile(path, callback); } } } } else { - TRACE("f_opendir(%s) failed, code=%d", path, res); + TRACE("VirtualFS::openDirectory(%s) failed, code=%d", path, res); } - f_closedir(&dir); + dir.close(); } #if defined(LUA_ALLOCATOR_TRACER) diff --git a/radio/src/lv_conf.h b/radio/src/lv_conf.h index 120cab1597e..1392cf5347f 100644 --- a/radio/src/lv_conf.h +++ b/radio/src/lv_conf.h @@ -807,7 +807,11 @@ *----------*/ /*1: Enable API to take snapshot for object*/ +#if !defined(BOOT) #define LV_USE_SNAPSHOT 1 +#else +#define LV_USE_SNAPSHOT 0 +#endif /*1: Enable Monkey test*/ #define LV_USE_MONKEY 0 diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 0957794f4fc..b2ebe9f0a38 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "hal/adc_driver.h" +#include "logs.h" #if defined(LIBOPENUI) #include "libopenui.h" @@ -487,7 +488,7 @@ void guiMain(event_t evt) } #endif -#if !defined(SIMU) +#if defined(SDCARD) && !defined(SIMU) void initLoggingTimer(); #endif @@ -499,12 +500,14 @@ void perMain() if (!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { checkEeprom(); - + +#if defined(SDCARD) #if !defined(SIMU) // use FreeRTOS software timer if radio firmware initLoggingTimer(); // initialize software timer for logging #else logsWrite(); // call logsWrite the old way for simu #endif +#endif // SDCARD } handleUsbConnection(); @@ -537,11 +540,11 @@ void perMain() #endif if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) - && SD_CARD_PRESENT() && !sdMounted()) { - sdMount(); + && SD_CARD_PRESENT()) { + VirtualFS::instance().mountSd(); } -#if !defined(EEPROM) +#if defined(SDCARD) // In case the SD card is removed during the session if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) && !SD_CARD_PRESENT() && !globalData.unexpectedShutdown) { diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index 03fbc83bd2d..8cb71e5b2e7 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -19,6 +19,7 @@ * GNU General Public License for more details. */ +#include "edgetx_assert.h" #include "opentx.h" #include "timers.h" #include "switches.h" diff --git a/radio/src/model_init.cpp b/radio/src/model_init.cpp index 5d190f49fe6..9e0513d65fb 100644 --- a/radio/src/model_init.cpp +++ b/radio/src/model_init.cpp @@ -150,8 +150,9 @@ void setModelDefaults(uint8_t id) strAppendUnsigned(strAppend(g_model.header.name, STR_MODEL), id, 2); #if defined(LUA) && defined(PCBTARANIS) // Horus uses menuModelWizard() for wizard - if (isFileAvailable(WIZARD_PATH "/" WIZARD_NAME)) { - f_chdir(WIZARD_PATH); + VirtualFS &vfs = VirtualFS::instance(); + if (VirtualFS::instance().isFileAvailable(WIZARD_PATH "/" WIZARD_NAME)) { + vfs.changeDirectory(WIZARD_PATH); luaExec(WIZARD_NAME); } #endif diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 90982531459..ea8072e60b1 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/nor_flash.cpp b/radio/src/nor_flash.cpp new file mode 100644 index 00000000000..5aa5a7977a0 --- /dev/null +++ b/radio/src/nor_flash.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "opentx.h" + +#include "nor_flash.h" + +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + +SpiFlashStorage* SpiFlashStorage::_instance = nullptr;; + +size_t flashSpiRead(size_t address, uint8_t* data, size_t size); +size_t flashSpiWrite(size_t address, const uint8_t* data, size_t size); + +int flashSpiErase(size_t address); +void flashSpiEraseAll(); + + +extern "C" +{ +int flashRead(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) +{ + flashSpiRead((block * c->block_size) + off, (uint8_t*)buffer, size); + return LFS_ERR_OK; +} + +int flashWrite(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) +{ + flashSpiWrite((block * c->block_size) + off, (uint8_t*)buffer, size); + return LFS_ERR_OK; +} + +int flashErase(const struct lfs_config *c, lfs_block_t block) +{ + flashSpiErase(block * c->block_size); + return LFS_ERR_OK; +} + +int flashSync(const struct lfs_config *c) +{ + return LFS_ERR_OK; +} +} + + +SpiFlashStorage::SpiFlashStorage() +{ + // configuration of the filesystem is provided by this struct + lfsCfg.read = flashRead; + lfsCfg.prog = flashWrite; + lfsCfg.erase = flashErase; + lfsCfg.sync = flashSync; + + // block device configuration + lfsCfg.read_size = 16; + lfsCfg.prog_size = 256; + lfsCfg.block_size = 4096; + lfsCfg.block_count = 4096; + lfsCfg.block_cycles = 500; + lfsCfg.cache_size = 512; + lfsCfg.lookahead_size = 32; + + int err = lfs_mount(&lfs, &lfsCfg); + if(err) { + flashSpiEraseAll(); + lfs_format(&lfs, &lfsCfg); + lfs_mount(&lfs, &lfsCfg); + } + lfsCfg.context = this; + checkAndCreateDirectory("/test"); + checkAndCreateDirectory("/test/foo"); +} + +SpiFlashStorage::~SpiFlashStorage() +{ + lfs_unmount(&lfs); +} + + +#ifdef LFS_THREADSAFE +// Lock the underlying block device. Negative error codes +// are propogated to the user. +int (*lock)(const struct lfs_config *c); + +// Unlock the underlying block device. Negative error codes +// are propogated to the user. +int (*unlock)(const struct lfs_config *c); +#endif + +bool SpiFlashStorage::format() +{ + flashSpiEraseAll(); + lfs_format(&lfs, &lfsCfg); + return true; + +// BYTE work[FF_MAX_SS]; +// FRESULT res = f_mkfs("", FM_FAT32, 0, work, sizeof(work)); +// switch(res) { +// case FR_OK : +// return true; +// case FR_DISK_ERR: +// POPUP_WARNING("Format error"); +// return false; +// case FR_NOT_READY: +// POPUP_WARNING("SDCard not ready"); +// return false; +// case FR_WRITE_PROTECTED: +// POPUP_WARNING("SDCard write protected"); +// return false; +// case FR_INVALID_PARAMETER: +// POPUP_WARNING("Format param invalid"); +// return false; +// case FR_INVALID_DRIVE: +// POPUP_WARNING("Invalid drive"); +// return false; +// case FR_MKFS_ABORTED: +// POPUP_WARNING("Format aborted"); +// return false; +// default: +// POPUP_WARNING(STR_SDCARD_ERROR); +// return false; +// } +} + +int SpiFlashStorage::openDirectory(lfs_dir_t* dir, const char * path) +{ + return lfs_dir_open(&lfs, dir, path); +} + +int SpiFlashStorage::readDirectory(lfs_dir_t* dir, lfs_info* info) +{ + return lfs_dir_read(&lfs, dir, info); +} + +int SpiFlashStorage::closeDirectory(lfs_dir_t* dir) +{ + return lfs_dir_close(&lfs, dir); +} + +int SpiFlashStorage::rename(const char* oldPath, const char* newPath) +{ + return lfs_rename(&lfs, oldPath, newPath); +} + + +const char* SpiFlashStorage::checkAndCreateDirectory(const char * path) +{ + lfs_dir_t dir; + int res = lfs_dir_open(&lfs, &dir, path); + if(res != LFS_ERR_OK) + { + if(res == LFS_ERR_NOENT) + res = lfs_mkdir(&lfs, path); + if(res != LFS_ERR_OK) + { + // return SDCARD_ERROR(result); + return "ERROR"; + } + } else { + lfs_dir_close(&lfs, &dir); + } + +// DIR archiveFolder; +// +// FRESULT result = f_opendir(&archiveFolder, path); +// if (result != FR_OK) { +// if (result == FR_NO_PATH) +// result = f_mkdir(path); +// if (result != FR_OK) +// return SDCARD_ERROR(result); +// } +// else { +// f_closedir(&archiveFolder); +// } +// + return nullptr; +} + +bool SpiFlashStorage::isFileAvailable(const char * path, bool exclDir) +{ + lfs_file_t file; + int res = lfs_file_open(&lfs, &file, path, LFS_O_RDONLY); + if(res != LFS_ERR_OK) + { + if(res == LFS_ERR_ISDIR) + return(!exclDir); + return false; + } else { + lfs_file_close(&lfs, &file); + } + return false; +} + +/** + Search file system path for a file. Can optionally take a list of file extensions to search through. + Eg. find "splash.bmp", or the first occurrence of one of "splash.[bmp|jpeg|jpg|gif]". + + @param path String with path name, no trailing slash. eg; "/BITMAPS" + @param file String containing file name to search for, with or w/out an extension. + eg; "splash.bmp" or "splash" + @param pattern Optional list of one or more file extensions concatenated together, including the period(s). + The list is searched backwards and the first match, if any, is returned. If null, then only the actual filename + passed will be searched for. + eg: ".gif.jpg.jpeg.bmp" + @param exclDir true/false whether to allow directory matches (default true, excludes directories) + @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). + @retval true if a file was found, false otherwise. +*/ +bool SpiFlashStorage::isFilePatternAvailable(const char * path, const char * file, const char * pattern, bool exclDir, char * match) +{ +// uint8_t fplen; +// char fqfp[LEN_FILE_PATH_MAX + FF_MAX_LFN + 1] = "\0"; +// +// fplen = strlen(path); +// if (fplen > LEN_FILE_PATH_MAX) { +// TRACE_ERROR("isFilePatternAvailable(%s) = error: path too long.\n", path); +// return false; +// } +// +// strcpy(fqfp, path); +// strcpy(fqfp + fplen, "/"); +// strncat(fqfp + (++fplen), file, FF_MAX_LFN); +// +// if (pattern == nullptr) { +// // no extensions list, just check the filename as-is +// return isFileAvailable(fqfp, exclDir); +// } +// else { +// // extensions list search +// const char *ext; +// uint16_t len; +// uint8_t extlen, fnlen; +// int plen; +// +// getFileExtension(file, 0, 0, &fnlen, &extlen); +// len = fplen + fnlen - extlen; +// fqfp[len] = '\0'; +// ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); +// plen = (int)fnlen; +// while (plen > 0 && ext) { +// strncat(fqfp + len, ext, extlen); +// if (isFileAvailable(fqfp, exclDir)) { +// if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); +// return true; +// } +// plen -= extlen; +// if (plen > 0) { +// fqfp[len] = '\0'; +// ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); +// } +// } +// } + return false; +} + +char* SpiFlashStorage::getFileIndex(char * filename, unsigned int & value) +{ +// value = 0; +// char * pos = (char *)getFileExtension(filename); +// if (!pos || pos == filename) +// return nullptr; +// int multiplier = 1; +// while (pos > filename) { +// pos--; +// char c = *pos; +// if (c >= '0' && c <= '9') { +// value += multiplier * (c - '0'); +// multiplier *= 10; +// } +// else { +// return pos+1; +// } +// } +// return filename; + return nullptr; +} + +static uint8_t _getDigitsCount(unsigned int value) +{ + uint8_t count = 1; + while (value >= 10) { + value /= 10; + ++count; + } + return count; +} + +unsigned int SpiFlashStorage::findNextFileIndex(char * filename, uint8_t size, const char * directory) +{ +// unsigned int index; +// uint8_t extlen; +// char * indexPos = getFileIndex(filename, index); +// char extension[LEN_FILE_EXTENSION_MAX+1] = "\0"; +// char * p = (char *)getFileExtension(filename, 0, 0, nullptr, &extlen); +// if (p) strncat(extension, p, sizeof(extension)-1); +// while (true) { +// index++; +// if ((indexPos - filename) + _getDigitsCount(index) + extlen > size) { +// return 0; +// } +// char * pos = strAppendUnsigned(indexPos, index); +// strAppend(pos, extension); +// if (!isFilePatternAvailable(directory, filename, nullptr, false)) { +// return index; +// } +// } + return 0; +} + +//static const char * getBasename(const char * path) +//{ +// for (int8_t i = strlen(path) - 1; i >= 0; i--) { +// if (path[i] == '/') { +// return &path[i + 1]; +// } +// } +// return path; +//} + +#if !defined(LIBOPENUI) +bool flashListFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags) +{ +// static uint16_t lastpopupMenuOffset = 0; +// FILINFO fno; +// DIR dir; +// const char * fnExt; +// uint8_t fnLen, extLen; +// char tmpExt[LEN_FILE_EXTENSION_MAX+1] = "\0"; +// +// popupMenuOffsetType = MENU_OFFSET_EXTERNAL; +// +// static uint8_t s_last_flags; +// +// if (selection) { +// s_last_flags = flags; +// if (!isFilePatternAvailable(path, selection, ((flags & LIST_SD_FILE_EXT) ? nullptr : extension))) selection = nullptr; +// } +// else { +// flags = s_last_flags; +// } +// +// if (popupMenuOffset == 0) { +// lastpopupMenuOffset = 0; +// memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); +// } +// else if (popupMenuOffset == popupMenuItemsCount - MENU_MAX_DISPLAY_LINES) { +// lastpopupMenuOffset = 0xffff; +// memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); +// } +// else if (popupMenuOffset == lastpopupMenuOffset) { +// // should not happen, only there because of Murphy's law +// return true; +// } +// else if (popupMenuOffset > lastpopupMenuOffset) { +// memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); +// memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0xff, MENU_LINE_LENGTH); +// } +// else { +// memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); +// memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); +// } +// +// popupMenuItemsCount = 0; +// +// FRESULT res = f_opendir(&dir, path); +// if (res == FR_OK) { +// +// if (flags & LIST_NONE_SD_FILE) { +// popupMenuItemsCount++; +// if (selection) { +// lastpopupMenuOffset++; +// } +// else if (popupMenuOffset==0 || popupMenuOffset < lastpopupMenuOffset) { +// char * line = reusableBuffer.modelsel.menu_bss[0]; +// memset(line, 0, MENU_LINE_LENGTH); +// strcpy(line, "---"); +// popupMenuItems[0] = line; +// } +// } +// +// for (;;) { +// res = f_readdir(&dir, &fno); /* Read a directory item */ +// if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ +// if (fno.fattrib & AM_DIR) continue; /* Skip subfolders */ +// if (fno.fattrib & AM_HID) continue; /* Skip hidden files */ +// if (fno.fattrib & AM_SYS) continue; /* Skip system files */ +// +// fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); +// fnLen -= extLen; +// +//// TRACE_DEBUG("listSdFiles(%s, %s, %u, %s, %u): fn='%s'; fnExt='%s'; match=%d\n", +//// path, extension, maxlen, (selection ? selection : "nul"), flags, fno.fname, (fnExt ? fnExt : "nul"), (fnExt && isExtensionMatching(fnExt, extension))); +// // file validation checks +// if (!fnLen || fnLen > maxlen || ( // wrong size +// fnExt && extension && ( // extension-based checks follow... +// !isExtensionMatching(fnExt, extension) || ( // wrong extension +// !(flags & LIST_SD_FILE_EXT) && // only if we want unique file names... +// strcasecmp(fnExt, getFileExtension(extension)) && // possible duplicate file name... +// isFilePatternAvailable(path, fno.fname, extension, true, tmpExt) && // find the first file from extensions list... +// strncasecmp(fnExt, tmpExt, LEN_FILE_EXTENSION_MAX) // found file doesn't match, this is a duplicate +// ) +// ) +// )) +// { +// continue; +// } +// +// popupMenuItemsCount++; +// +// if (!(flags & LIST_SD_FILE_EXT)) { +// fno.fname[fnLen] = '\0'; // strip extension +// } +// +// if (popupMenuOffset == 0) { +// if (selection && strncasecmp(fno.fname, selection, maxlen) < 0) { +// lastpopupMenuOffset++; +// } +// else { +// for (uint8_t i=0; i=0; i--) { +// char * line = reusableBuffer.modelsel.menu_bss[i]; +// if (line[0] == '\0' || strcasecmp(fno.fname, line) > 0) { +// if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); +// memset(line, 0, MENU_LINE_LENGTH); +// strcpy(line, fno.fname); +// break; +// } +// } +// for (uint8_t i=0; i lastpopupMenuOffset) { +// if (strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-2]) > 0 && strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1]) < 0) { +// memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0, MENU_LINE_LENGTH); +// strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], fno.fname); +// } +// } +// else { +// if (strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[0]) > 0) { +// memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); +// strcpy(reusableBuffer.modelsel.menu_bss[0], fno.fname); +// } +// } +// } +// f_closedir(&dir); +// } +// +// if (popupMenuOffset > 0) +// lastpopupMenuOffset = popupMenuOffset; +// else +// popupMenuOffset = lastpopupMenuOffset; +// +// return popupMenuItemsCount; +// return 0; +} + +#endif // !LIBOPENUI + +#if defined(SDCARD) +const char * flashCopyFile(const char * srcPath, const char * destPath) +{ +// FIL srcFile; +// FIL destFile; +// char buf[256]; +// UINT read = sizeof(buf); +// UINT written = sizeof(buf); +// +// FRESULT result = f_open(&srcFile, srcPath, FA_OPEN_EXISTING | FA_READ); +// if (result != FR_OK) { +// return SDCARD_ERROR(result); +// } +// +// result = f_open(&destFile, destPath, FA_CREATE_ALWAYS | FA_WRITE); +// if (result != FR_OK) { +// f_close(&srcFile); +// return SDCARD_ERROR(result); +// } +// +// while (result==FR_OK && read==sizeof(buf) && written==sizeof(buf)) { +// result = f_read(&srcFile, buf, sizeof(buf), &read); +// if (result == FR_OK) { +// result = f_write(&destFile, buf, read, &written); +// } +// } +// +// f_close(&destFile); +// f_close(&srcFile); +// +// if (result != FR_OK) { +// return SDCARD_ERROR(result); +// } +// + return nullptr; +} + +const char * flashCopyFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir) +{ +// char srcPath[2*CLIPBOARD_PATH_LEN+1]; +// char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); +// *tmp++ = '/'; +// strAppend(tmp, srcFilename, CLIPBOARD_PATH_LEN); +// +// char destPath[2*CLIPBOARD_PATH_LEN+1]; +// tmp = strAppend(destPath, destDir, CLIPBOARD_PATH_LEN); +// *tmp++ = '/'; +// strAppend(tmp, destFilename, CLIPBOARD_PATH_LEN); +// +// return sdCopyFile(srcPath, destPath); + return nullptr; +} +#endif // defined(SDCARD) + + +#if !defined(SIMU) || defined(SIMU_DISKIO) +uint32_t flashGetNoSectors() +{ + static DWORD noSectors = 0; +// if (noSectors == 0 ) { +// disk_ioctl(0, GET_SECTOR_COUNT, &noSectors); +// } + return noSectors; +} + +uint32_t flashGetSize() +{ + return (flashGetNoSectors() / 1000000) * BLOCK_SIZE; +} + +uint32_t flashGetFreeSectors() +{ +// DWORD nofree; +// FATFS * fat; +// if (f_getfree("", &nofree, &fat) != FR_OK) { +// return 0; +// } +// return nofree * fat->csize; + return 10; +} + +#else // #if !defined(SIMU) || defined(SIMU_DISKIO) + +uint32_t flashGetNoSectors() +{ + return 0; +} + +uint32_t flashGetSize() +{ + return 0; +} + +uint32_t flashGetFreeSectors() +{ + return 10; +} + +#endif // #if !defined(SIMU) || defined(SIMU_DISKIO) diff --git a/radio/src/nor_flash.h b/radio/src/nor_flash.h new file mode 100644 index 00000000000..f525cd34a89 --- /dev/null +++ b/radio/src/nor_flash.h @@ -0,0 +1,206 @@ +/* + * 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 _NOR_FLASH_H_ +#define _NOR_FLASH_H_ + +#include "littlefs_v2.4.1/lfs.h" + +#include "translations.h" + +#define FILE_COPY_PREFIX "cp_" + +#define PATH_SEPARATOR "/" +#define ROOT_PATH PATH_SEPARATOR +#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important +#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important +#define LOGS_PATH ROOT_PATH "LOGS" +#define SCREENSHOTS_PATH ROOT_PATH "SCREENSHOTS" +#define SOUNDS_PATH ROOT_PATH "SOUNDS/en" +#define SOUNDS_PATH_LNG_OFS (sizeof(SOUNDS_PATH)-3) +#define SYSTEM_SUBDIR "SYSTEM" +#define BITMAPS_PATH ROOT_PATH "IMAGES" +#define FIRMWARES_PATH ROOT_PATH "FIRMWARE" +#define AUTOUPDATE_FILENAME FIRMWARES_PATH PATH_SEPARATOR "autoupdate.frsk" +#define EEPROMS_PATH ROOT_PATH "EEPROM" +#define BACKUP_PATH ROOT_PATH "BACKUP" +#define SCRIPTS_PATH ROOT_PATH "SCRIPTS" +#define WIZARD_PATH SCRIPTS_PATH PATH_SEPARATOR "WIZARD" +#define THEMES_PATH ROOT_PATH "THEMES" +#define LAYOUTS_PATH ROOT_PATH "LAYOUTS" +#define WIDGETS_PATH ROOT_PATH "WIDGETS" +#define WIZARD_NAME "wizard.lua" +#define SCRIPTS_MIXES_PATH SCRIPTS_PATH PATH_SEPARATOR "MIXES" +#define SCRIPTS_FUNCS_PATH SCRIPTS_PATH PATH_SEPARATOR "FUNCTIONS" +#define SCRIPTS_TELEM_PATH SCRIPTS_PATH PATH_SEPARATOR "TELEMETRY" +#define SCRIPTS_TOOLS_PATH SCRIPTS_PATH PATH_SEPARATOR "TOOLS" + +#define LEN_FILE_PATH_MAX (sizeof(SCRIPTS_TELEM_PATH)+1) // longest + "/" +#if 0 +#if defined(SDCARD_YAML) || defined(SDCARD_RAW) +#define RADIO_FILENAME "radio.bin" +const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt"; +const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME; +#if defined(SDCARD_YAML) +const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR "models.yml"; +const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "models.yml"; +const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml"; +#endif +#define SPLASH_FILE "splash.png" +#endif + +#define MODELS_EXT ".bin" +#define LOGS_EXT ".csv" +#define SOUNDS_EXT ".wav" +#define BMP_EXT ".bmp" +#define PNG_EXT ".png" +#define JPG_EXT ".jpg" +#define SCRIPT_EXT ".lua" +#define SCRIPT_BIN_EXT ".luac" +#define TEXT_EXT ".txt" +#define FIRMWARE_EXT ".bin" +#define EEPROM_EXT ".bin" +#define SPORT_FIRMWARE_EXT ".frk" +#define FRSKY_FIRMWARE_EXT ".frsk" +#define MULTI_FIRMWARE_EXT ".bin" +#define ELRS_FIRMWARE_EXT ".elrs" +#define YAML_EXT ".yml" + +#if defined(COLORLCD) +#define BITMAPS_EXT BMP_EXT JPG_EXT PNG_EXT +#define LEN_BITMAPS_EXT 4 +#else +#define BITMAPS_EXT BMP_EXT +#endif + +#ifdef LUA_COMPILER + #define SCRIPTS_EXT SCRIPT_BIN_EXT SCRIPT_EXT +#else + #define SCRIPTS_EXT SCRIPT_EXT +#endif + +#define GET_FILENAME(filename, path, var, ext) \ + char filename[sizeof(path) + sizeof(var) + sizeof(ext)]; \ + memcpy(filename, path, sizeof(path) - 1); \ + filename[sizeof(path) - 1] = '/'; \ + memcpy(&filename[sizeof(path)], var, sizeof(var)); \ + filename[sizeof(path)+sizeof(var)] = '\0'; \ + strcat(&filename[sizeof(path)], ext) +#endif +class SpiFlashStorage +{ +public: + SpiFlashStorage(); + ~SpiFlashStorage(); + + static SpiFlashStorage* instance() + { + if( _instance == nullptr) + _instance = new SpiFlashStorage(); + return _instance; + } + + bool format(); + const char * checkAndCreateDirectory(const char * path); + bool isFileAvailable(const char * path, bool exclDir); + bool isFilePatternAvailable(const char * path, const char * file, const char * pattern = nullptr, bool exclDir = true, char * match = nullptr); + char* getFileIndex(char * filename, unsigned int & value); + + int openDirectory(lfs_dir_t* dir, const char * path); + int readDirectory(lfs_dir_t* dir, lfs_info* info); + int closeDirectory(lfs_dir_t* dir); + + int rename(const char* oldPath, const char* newPath); + + uint32_t flashGetNoSectors(); + uint32_t flashGetSize(); + uint32_t flashGetFreeSectors(); + + //#if !defined(BOOT) + //inline const char * SDCARD_ERROR(FRESULT result) + //{ + // if (result == FR_NOT_READY) + // return STR_NO_SDCARD; + // else + // return STR_SDCARD_ERROR; + //} + //#endif + + // NOTE: 'size' must = 0 or be a valid character position within 'filename' array -- it is NOT validated + const char * flashGetBasename(const char * path); + + #if defined(PCBX12S) + #define OTX_FOURCC 0x3478746F // otx for X12S + #elif defined(RADIO_T16) + #define OTX_FOURCC 0x3F78746F // otx for Jumper T16 + #elif defined(RADIO_T18) + #define OTX_FOURCC 0x4078746F // otx for Jumper T18 + #elif defined(RADIO_TX16S) + #define OTX_FOURCC 0x3878746F // otx for Radiomaster TX16S + #elif defined(PCBX10) + #define OTX_FOURCC 0x3778746F // otx for X10 + #elif defined(PCBX9E) + #define OTX_FOURCC 0x3578746F // otx for Taranis X9E + #elif defined(PCBXLITES) + #define OTX_FOURCC 0x3B78746F // otx for Taranis X-Lite S + #elif defined(PCBXLITE) + #define OTX_FOURCC 0x3978746F // otx for Taranis X-Lite + #elif defined(RADIO_T12) + #define OTX_FOURCC 0x3D78746F // otx for Jumper T12 + #elif defined(RADIO_TLITE) + #define OTX_FOURCC 0x4278746F // otx for Jumper TLite + #elif defined(RADIO_TPRO) + #define OTX_FOURCC 0x4678746F // otx for Jumper TPro + #elif defined(RADIO_TX12) + #define OTX_FOURCC 0x4178746F // otx for Radiomaster TX12 + #elif defined(RADIO_ZORRO) + #define OTX_FOURCC 0x4778746F // otx for Radiomaster Zorro + #elif defined(RADIO_T8) + #define OTX_FOURCC 0x4378746F // otx for Radiomaster T8 + #elif defined(PCBX7) + #define OTX_FOURCC 0x3678746F // otx for Taranis X7 / X7S / X7 Express / X7S Express + #elif defined(PCBX9LITES) + #define OTX_FOURCC 0x3E78746F // otx for Taranis X9-Lite S + #elif defined(PCBX9LITE) + #define OTX_FOURCC 0x3C78746F // otx for Taranis X9-Lite + #elif defined(PCBX9D) || defined(PCBX9DP) + #define OTX_FOURCC 0x3378746F // otx for Taranis X9D + #elif defined(PCBNV14) + #define OTX_FOURCC 0x3A78746F // otx for NV14 + #elif defined(PCBSKY9X) + #define OTX_FOURCC 0x3278746F // otx for sky9x + #endif + + unsigned int findNextFileIndex(char * filename, uint8_t size, const char * directory); + + const char * sdCopyFile(const char * src, const char * dest); + const char * flashCopyFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir); + + #define LIST_NONE_SD_FILE 1 + #define LIST_SD_FILE_EXT 2 + bool flashListFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags=0); +private: + static SpiFlashStorage* _instance; + + lfs_config lfsCfg = {0}; + lfs_t lfs = {0}; +}; + +#endif // _NOR_FLASH_H_ diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 40fdf6f42b0..627b558a2d6 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -20,9 +20,12 @@ */ #include "opentx.h" +#include "VirtualFS.h" #include "io/frsky_firmware_update.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" +#include "logs.h" + #include "timers_driver.h" #include "watchdog_driver.h" @@ -54,9 +57,7 @@ RadioData g_eeGeneral; ModelData g_model; -#if defined(SDCARD) Clipboard clipboard; -#endif GlobalData globalData; @@ -939,7 +940,6 @@ void alert(const char * title, const char * msg , uint8_t sound) #elif MAX_TRIMS == 2 int8_t trimGvar[MAX_TRIMS] = { -1, -1 }; #endif -#endif void checkTrims() { @@ -1214,16 +1214,14 @@ void opentxClose(uint8_t shutdown) #endif #endif -#if defined(SDCARD) - sdDone(); -#endif + VirtualFS::instance().stop(); } void opentxResume() { TRACE("opentxResume"); - sdMount(); + VirtualFS::instance().restart(); #if defined(COLORLCD) && defined(LUA) // reload widgets luaInitThemesAndWidgets(); @@ -1497,15 +1495,13 @@ void opentxInit() SET_POWER_REASON(0); #endif -#if defined(SDCARD) - // SDCARD related stuff, only done if not unexpectedShutdown + // storage related stuff, only done if not unexpectedShutdown if (!globalData.unexpectedShutdown) { - if (!sdMounted()) - sdInit(); + VirtualFS& vfs __attribute__((unused)) = VirtualFS::instance(); // initialize storage subsystem #if !defined(COLORLCD) - if (!sdMounted()) { + if (!vfs.defaultStorageAvailable()) { g_eeGeneral.pwrOffSpeed = 2; runFatalErrorScreen(STR_NO_SDCARD); } @@ -1513,22 +1509,22 @@ void opentxInit() #if defined(AUTOUPDATE) sportStopSendByteLoop(); - if (f_stat(AUTOUPDATE_FILENAME, nullptr) == FR_OK) { + if (vfs.fstat(AUTOUPDATE_FILENAME, nullptr) == VfsError::OK) { FrSkyFirmwareInformation information; if (readFrSkyFirmwareInformation(AUTOUPDATE_FILENAME, information) == nullptr) { #if defined(BLUETOOTH) if (information.productFamily == FIRMWARE_FAMILY_BLUETOOTH_CHIP) { if (bluetooth.flashFirmware(AUTOUPDATE_FILENAME) == nullptr) - f_unlink(AUTOUPDATE_FILENAME); + vfs.unlink(AUTOUPDATE_FILENAME); } #endif } } #endif - +#if defined(SDCARD) logsInit(); - } #endif + } #if defined(EEPROM) if (!radioSettingsValid) @@ -1656,7 +1652,7 @@ int main() } #endif -#if defined(COLORLCD) +#if defined(COLORLCD) && defined(SDCARD) // SD_CARD_PRESENT() does not work properly on most // B&W targets, so that we need to delay the detection // until the SD card is mounted (requires RTOS scheduler running) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 16276fdf04b..39e607a1d13 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -37,8 +37,6 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif #if defined(SIMU) @@ -144,11 +142,7 @@ #define CASE_PXX2(x) #endif -#if defined(SDCARD) - #define CASE_SDCARD(x) x, -#else - #define CASE_SDCARD(x) -#endif +#define CASE_SDCARD(x) x, #if defined(BLUETOOTH) #define CASE_BLUETOOTH(x) x, @@ -320,7 +314,6 @@ struct CustomFunctionsContext { #include "gui.h" #if !defined(SIMU) - #define assert(x) #if !defined(DEBUG) #define printf printf_not_allowed #endif @@ -368,7 +361,7 @@ extern uint8_t heartbeat; #include "keys.h" #include "pwr.h" -// #if defined(PCBFRSKY) || defined(PCBNV14) +// #if defined(PCBFRSKY) || defined(PCBNV14) || defined(PCBPL18) // extern uint8_t potsPos[NUM_XPOTS]; // #endif @@ -655,9 +648,7 @@ enum FunctionsActive { FUNCTION_TRAINER_CHANNELS = FUNCTION_TRAINER_STICK1 + MAX_STICKS, FUNCTION_INSTANT_TRIM, FUNCTION_VARIO, -#if defined(SDCARD) FUNCTION_LOGS, -#endif FUNCTION_BACKGND_MUSIC, FUNCTION_BACKGND_MUSIC_PAUSE, FUNCTION_BACKLIGHT, @@ -790,9 +781,7 @@ enum AUDIO_SOUNDS { #include "haptic.h" #endif -#if defined(SDCARD) -#include "sdcard.h" -#endif +#include "VirtualFS.h" #if defined(RTCLOCK) #include "rtc.h" @@ -825,9 +814,9 @@ constexpr uint8_t OPENTX_START_NO_CHECKS = 0x04; // Re-useable byte array to save having multiple buffers #if LCD_W <= 212 -constexpr uint8_t SD_SCREEN_FILE_LENGTH = 32; +constexpr uint8_t STORAGE_SCREEN_FILE_LENGTH = 32; #else -constexpr uint8_t SD_SCREEN_FILE_LENGTH = 64; +constexpr uint8_t STORAGE_SCREEN_FILE_LENGTH = 64; #endif #if defined(BLUETOOTH) @@ -889,19 +878,17 @@ union ReusableBuffer } inputs[MAX_ANALOG_INPUTS]; } calib; -#if defined(SDCARD) struct { - char lines[NUM_BODY_LINES][SD_SCREEN_FILE_LENGTH+1+1]; // the last char is used to store the flags (directory) of the line + char lines[NUM_BODY_LINES][STORAGE_SCREEN_FILE_LENGTH+1+1]; // the last char is used to store the flags (directory) of the line uint32_t available; uint16_t offset; uint16_t count; - char originalName[SD_SCREEN_FILE_LENGTH+1]; + char originalName[STORAGE_SCREEN_FILE_LENGTH+1]; #if defined(PXX2) OtaUpdateInformation otaUpdateInformation; char otaReceiverVersion[sizeof(TR_CURRENT_VERSION) + 12]; #endif } sdManager; -#endif struct { @@ -1077,12 +1064,11 @@ void varioWakeup(); #include "lua/lua_api.h" -#if defined(SDCARD) enum ClipboardType { CLIPBOARD_TYPE_NONE, CLIPBOARD_TYPE_CUSTOM_SWITCH, CLIPBOARD_TYPE_CUSTOM_FUNCTION, - CLIPBOARD_TYPE_SD_FILE, + CLIPBOARD_TYPE_STORAGE_FILE, }; #if defined(SIMU) @@ -1099,12 +1085,11 @@ struct Clipboard { struct { char directory[CLIPBOARD_PATH_LEN]; char filename[CLIPBOARD_PATH_LEN]; - } sd; + } storage; } data; }; extern Clipboard clipboard; -#endif #if defined(INTERNAL_GPS) #include "gps.h" 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/flysky.cpp b/radio/src/pulses/flysky.cpp index 6c99e324302..964b17c38a9 100644 --- a/radio/src/pulses/flysky.cpp +++ b/radio/src/pulses/flysky.cpp @@ -24,6 +24,7 @@ #include "opentx.h" #include "flysky.h" + #include "telemetry/flysky_nv14.h" #define IS_VALID_COMMAND_ID(id) ((id) < CMD_LAST) @@ -202,6 +203,7 @@ inline void initFlySkyCRC() inline void putFlySkyByte(uint8_t*& p_buf, uint8_t byte) { +#if defined(HARDWARE_INTERNAL_MODULE) if (END == byte) { *p_buf++ = ESC; *p_buf++ = ESC_END; @@ -211,6 +213,7 @@ inline void putFlySkyByte(uint8_t*& p_buf, uint8_t byte) } else { *p_buf++ = byte; } +#endif } inline void putFlySkyFrameByte(uint8_t*& p_buf, uint8_t byte) @@ -348,6 +351,7 @@ inline void debugFrame(const uint8_t* rxBuffer, uint8_t rxBufferCount) inline void parseResponse(uint8_t* buffer, uint8_t dataLen) { +#if defined(HARDWARE_INTERNAL_MODULE) afhds2Resp* resp = reinterpret_cast(buffer); if (resp->startByte != END || dataLen < 2) return; @@ -468,10 +472,12 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) break; } } +#endif } void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* len) { +#if defined(HARDWARE_INTERNAL_MODULE) if (byte == END && *len > 0) { parseResponse(buffer, *len); *len = 0; @@ -493,10 +499,12 @@ void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* } } } +#endif } void resetPulsesAFHDS2() { +#if defined(HARDWARE_INTERNAL_MODULE) NV14internalModuleFwVersion = 0; _flysky_frame_index = 1; setFlyskyState(STATE_SET_TX_POWER); @@ -507,10 +515,12 @@ void resetPulsesAFHDS2() if (50 > rx_freq || 400 < rx_freq) { gRomData.rx_freq[0] = 50; } +#endif } void setupPulsesAFHDS2(uint8_t*& p_buf) { +#if defined(HARDWARE_INTERNAL_MODULE) auto buffer_start = p_buf; putFlySkyFrameHeader(p_buf); @@ -639,6 +649,7 @@ void setupPulsesAFHDS2(uint8_t*& p_buf) TRACE_NOCRLF(";" CRLF); } } +#endif } // void usbDownloadTransmit(uint8_t *buffer, uint32_t size) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 1d8fbd12232..f777c3d4bc6 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -116,7 +116,7 @@ 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) +#if defined(PCBTARANIS) || defined(PCBHORUS) || defined(PCBNV14) || defined(PCBPL18) 0x08 //external #else 0x00 //external diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index 688d6c0dc1e..ba21be490ca 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -22,7 +22,7 @@ #include #include "opentx.h" #include "io/frsky_firmware_update.h" -#include "libopenui/src/libopenui_file.h" +#include "VirtualFS.h" #include "mixer_scheduler.h" #include "heartbeat_driver.h" #include "timers_driver.h" diff --git a/radio/src/pulses/pxx2_ota.cpp b/radio/src/pulses/pxx2_ota.cpp index 74d81b0e741..cc256b7fc20 100644 --- a/radio/src/pulses/pxx2_ota.cpp +++ b/radio/src/pulses/pxx2_ota.cpp @@ -77,9 +77,11 @@ const char* Pxx2OtaUpdate::nextStep(uint8_t step, const char* rxName, const char* Pxx2OtaUpdate::doFlashFirmware(const char* filename, ProgressHandler progressHandler) { - FIL file; + VfsError error = VfsError::OK; + VirtualFS& vfs = VirtualFS::instance(); + VfsFile file; uint8_t buffer[32]; - UINT count; + size_t count; const char * result; result = nextStep(OTA_UPDATE_START, rxName, 0, nullptr); @@ -87,30 +89,32 @@ const char* Pxx2OtaUpdate::doFlashFirmware(const char* filename, return result; } - if (f_open(&file, filename, FA_READ) != FR_OK) { + error = vfs.openFile(file, filename, VfsOpenFlags::READ); + if (error != VfsError::OK) { return "Open file failed"; } uint32_t size; - const char * ext = getFileExtension(filename); + const char * ext = vfs.getFileExtension(filename); if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *) buffer; - if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || - count != sizeof(FrSkyFirmwareInformation)) { - f_close(&file); + error = file.read(buffer, sizeof(FrSkyFirmwareInformation), count); + if (error != VfsError::OK || count != sizeof(FrSkyFirmwareInformation)) { + file.close(); return "Format error"; } size = information->size; } else { - size = f_size(&file); + size = file.size(); } uint32_t done = 0; while (1) { - progressHandler(getBasename(filename), STR_OTA_UPDATE, done, size); - if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) { - f_close(&file); + progressHandler(VirtualFS::getBasename(filename), STR_OTA_UPDATE, done, size); + error = file.read(buffer, sizeof(buffer), count); + if (error != VfsError::OK) { + file.close(); return "Read file failed"; } @@ -120,7 +124,7 @@ const char* Pxx2OtaUpdate::doFlashFirmware(const char* filename, } if (count < sizeof(buffer)) { - f_close(&file); + file.close(); break; } diff --git a/radio/src/rtos.h b/radio/src/rtos.h index 96a61cc0c65..390d51d311b 100644 --- a/radio/src/rtos.h +++ b/radio/src/rtos.h @@ -164,6 +164,11 @@ inline void RTOS_CREATE_TASK(pthread_t &taskId, void * (*task)(void *), const ch StaticSemaphore_t mutex_struct; } RTOS_MUTEX_HANDLE; + typedef struct { + SemaphoreHandle_t rtos_handle; + StaticSemaphore_t semaphore_struct; + } RTOS_SEMAPHORE_HANDLE; + typedef RTOS_MUTEX_HANDLE RTOS_FLAG_HANDLE; static inline void RTOS_START() @@ -224,6 +229,37 @@ inline void RTOS_CREATE_TASK(pthread_t &taskId, void * (*task)(void *), const ch #define RTOS_UNLOCK_MUTEX(handle) _RTOS_UNLOCK_MUTEX(&handle) + static inline void _RTOS_CREATE_SEMAPHORE(RTOS_SEMAPHORE_HANDLE* h) + { + h->rtos_handle = xSemaphoreCreateBinaryStatic(&h->semaphore_struct); + } + + #define RTOS_CREATE_SEAPHORE(handle) _RTOS_CREATE_SEMAPHORE(&handle) + + static inline void _RTOS_TAKE_SEMAPHORE(RTOS_SEMAPHORE_HANDLE* h) + { + xSemaphoreTake(h->rtos_handle, portMAX_DELAY); + } + + #define RTOS_TAKE_SEMAPHORE(handle) _RTOS_TAKE_SEMAPHORE(&handle) + + static inline void _RTOS_GIVE_SEMAPHORE(RTOS_SEMAPHORE_HANDLE* h) + { + xSemaphoreGive(h->rtos_handle); + } + + #define RTOS_GIVE_SEMAPHORE(handle) _RTOS_GIVE_SEMAPHORE(&handle) + + static inline void _RTOS_GIVE_SEMAPHORE_ISR(RTOS_SEMAPHORE_HANDLE* h) + { + signed long pxHigherPriorityTaskWoken; + xSemaphoreGiveFromISR(h->rtos_handle, &pxHigherPriorityTaskWoken); + + portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); + } + + #define RTOS_GIVE_SEMAPHORE_ISR(handle) _RTOS_GIVE_SEMAPHORE_ISR(&handle) + static inline uint32_t getStackAvailable(void * address, uint32_t size) { uint32_t * array = (uint32_t *)address; diff --git a/radio/src/sdcard.cpp b/radio/src/sdcard.cpp index 07ffe88ad43..92984e76f69 100644 --- a/radio/src/sdcard.cpp +++ b/radio/src/sdcard.cpp @@ -27,8 +27,6 @@ #if defined(LIBOPENUI) #include "libopenui.h" -#else - #include "libopenui/src/libopenui_file.h" #endif #if FF_MAX_SS != FF_MIN_SS @@ -39,410 +37,8 @@ #define SDCARD_MIN_FREE_SPACE_MB 50 // Maintain a 50MB free space buffer to prevent crashes -const char * sdCheckAndCreateDirectory(const char * path) -{ - DIR archiveFolder; - - FRESULT result = f_opendir(&archiveFolder, path); - if (result != FR_OK) { - if (result == FR_NO_PATH) - result = f_mkdir(path); - if (result != FR_OK) - return SDCARD_ERROR(result); - } - else { - f_closedir(&archiveFolder); - } - - return nullptr; -} - -bool isFileAvailable(const char * path, bool exclDir) -{ - if (exclDir) { - FILINFO fno; - return (f_stat(path, &fno) == FR_OK && !(fno.fattrib & AM_DIR)); - } - return f_stat(path, nullptr) == FR_OK; -} - -/** - Search file system path for a file. Can optionally take a list of file extensions to search through. - Eg. find "splash.bmp", or the first occurrence of one of "splash.[bmp|jpeg|jpg|gif]". - - @param path String with path name, no trailing slash. eg; "/BITMAPS" - @param file String containing file name to search for, with or w/out an extension. - eg; "splash.bmp" or "splash" - @param pattern Optional list of one or more file extensions concatenated together, including the period(s). - The list is searched backwards and the first match, if any, is returned. If null, then only the actual filename - passed will be searched for. - eg: ".gif.jpg.jpeg.bmp" - @param exclDir true/false whether to allow directory matches (default true, excludes directories) - @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). - @retval true if a file was found, false otherwise. -*/ -bool isFilePatternAvailable(const char * path, const char * file, const char * pattern = nullptr, bool exclDir = true, char * match = nullptr) -{ - uint8_t fplen; - char fqfp[LEN_FILE_PATH_MAX + FF_MAX_LFN + 1] = "\0"; - - fplen = strlen(path); - if (fplen > LEN_FILE_PATH_MAX) { - TRACE_ERROR("isFilePatternAvailable(%s) = error: path too long.\n", path); - return false; - } - - strcpy(fqfp, path); - strcpy(fqfp + fplen, "/"); - strncat(fqfp + (++fplen), file, FF_MAX_LFN); - - if (pattern == nullptr) { - // no extensions list, just check the filename as-is - return isFileAvailable(fqfp, exclDir); - } - else { - // extensions list search - const char *ext; - uint16_t len; - uint8_t extlen, fnlen; - int plen; - - getFileExtension(file, 0, 0, &fnlen, &extlen); - len = fplen + fnlen - extlen; - fqfp[len] = '\0'; - ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); - plen = (int)fnlen; - while (plen > 0 && ext) { - strncat(fqfp + len, ext, extlen); - if (isFileAvailable(fqfp, exclDir)) { - if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); - return true; - } - plen -= extlen; - if (plen > 0) { - fqfp[len] = '\0'; - ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); - } - } - } - return false; -} - -char * getFileIndex(char * filename, unsigned int & value) -{ - value = 0; - char * pos = (char *)getFileExtension(filename); - if (!pos || pos == filename) - return nullptr; - int multiplier = 1; - while (pos > filename) { - pos--; - char c = *pos; - if (c >= '0' && c <= '9') { - value += multiplier * (c - '0'); - multiplier *= 10; - } - else { - return pos+1; - } - } - return filename; -} - -uint8_t getDigitsCount(unsigned int value) -{ - uint8_t count = 1; - while (value >= 10) { - value /= 10; - ++count; - } - return count; -} - -unsigned int findNextFileIndex(char * filename, uint8_t size, const char * directory) -{ - unsigned int index; - uint8_t extlen; - char * indexPos = getFileIndex(filename, index); - char extension[LEN_FILE_EXTENSION_MAX+1] = "\0"; - char * p = (char *)getFileExtension(filename, 0, 0, nullptr, &extlen); - if (p) strncat(extension, p, sizeof(extension)-1); - while (true) { - index++; - if ((indexPos - filename) + getDigitsCount(index) + extlen > size) { - return 0; - } - char * pos = strAppendUnsigned(indexPos, index); - strAppend(pos, extension); - if (!isFilePatternAvailable(directory, filename, nullptr, false)) { - return index; - } - } -} - -const char * getBasename(const char * path) -{ - for (int8_t i = strlen(path) - 1; i >= 0; i--) { - if (path[i] == '/') { - return &path[i + 1]; - } - } - return path; -} - -#if !defined(LIBOPENUI) -bool sdListFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags) -{ - static uint16_t lastpopupMenuOffset = 0; - FILINFO fno; - DIR dir; - const char * fnExt; - uint8_t fnLen, extLen; - char tmpExt[LEN_FILE_EXTENSION_MAX+1] = "\0"; - - popupMenuOffsetType = MENU_OFFSET_EXTERNAL; - - static uint8_t s_last_flags; - - if (selection) { - s_last_flags = flags; - if (!isFilePatternAvailable(path, selection, ((flags & LIST_SD_FILE_EXT) ? nullptr : extension))) selection = nullptr; - } - else { - flags = s_last_flags; - } - - if (popupMenuOffset == 0) { - lastpopupMenuOffset = 0; - memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); - } - else if (popupMenuOffset == popupMenuItemsCount - MENU_MAX_DISPLAY_LINES) { - lastpopupMenuOffset = 0xffff; - memset(reusableBuffer.modelsel.menu_bss, 0, sizeof(reusableBuffer.modelsel.menu_bss)); - } - else if (popupMenuOffset == lastpopupMenuOffset) { - // should not happen, only there because of Murphy's law - return true; - } - else if (popupMenuOffset > lastpopupMenuOffset) { - memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); - memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0xff, MENU_LINE_LENGTH); - } - else { - memmove(reusableBuffer.modelsel.menu_bss[1], reusableBuffer.modelsel.menu_bss[0], (MENU_MAX_DISPLAY_LINES-1)*MENU_LINE_LENGTH); - memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); - } - - popupMenuItemsCount = 0; - - FRESULT res = f_opendir(&dir, path); - if (res == FR_OK) { - - if (flags & LIST_NONE_SD_FILE) { - popupMenuItemsCount++; - if (selection) { - lastpopupMenuOffset++; - } - else if (popupMenuOffset==0 || popupMenuOffset < lastpopupMenuOffset) { - char * line = reusableBuffer.modelsel.menu_bss[0]; - memset(line, 0, MENU_LINE_LENGTH); - strcpy(line, "---"); - popupMenuItems[0] = line; - } - } - - for (;;) { - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (fno.fattrib & (AM_DIR|AM_HID|AM_SYS)) continue; // skip subfolders, hidden files and system files - if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */ - - fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); - fnLen -= extLen; - -// TRACE_DEBUG("listSdFiles(%s, %s, %u, %s, %u): fn='%s'; fnExt='%s'; match=%d\n", -// path, extension, maxlen, (selection ? selection : "nul"), flags, fno.fname, (fnExt ? fnExt : "nul"), (fnExt && isExtensionMatching(fnExt, extension))); - // file validation checks - if (!fnLen || fnLen > maxlen || ( // wrong size - fnExt && extension && ( // extension-based checks follow... - !isExtensionMatching(fnExt, extension) || ( // wrong extension - !(flags & LIST_SD_FILE_EXT) && // only if we want unique file names... - strcasecmp(fnExt, getFileExtension(extension)) && // possible duplicate file name... - isFilePatternAvailable(path, fno.fname, extension, true, tmpExt) && // find the first file from extensions list... - strncasecmp(fnExt, tmpExt, LEN_FILE_EXTENSION_MAX) // found file doesn't match, this is a duplicate - ) - ) - )) - { - continue; - } - - popupMenuItemsCount++; - - if (!(flags & LIST_SD_FILE_EXT)) { - fno.fname[fnLen] = '\0'; // strip extension - } - - if (popupMenuOffset == 0) { - if (selection && strncasecmp(fno.fname, selection, maxlen) < 0) { - lastpopupMenuOffset++; - } - else { - for (uint8_t i=0; i=0; i--) { - char * line = reusableBuffer.modelsel.menu_bss[i]; - if (line[0] == '\0' || strcasecmp(fno.fname, line) > 0) { - if (i > 0) memmove(reusableBuffer.modelsel.menu_bss[0], reusableBuffer.modelsel.menu_bss[1], sizeof(reusableBuffer.modelsel.menu_bss[i]) * i); - memset(line, 0, MENU_LINE_LENGTH); - strcpy(line, fno.fname); - break; - } - } - for (uint8_t i=0; i lastpopupMenuOffset) { - if (strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-2]) > 0 && strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1]) < 0) { - memset(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], 0, MENU_LINE_LENGTH); - strcpy(reusableBuffer.modelsel.menu_bss[MENU_MAX_DISPLAY_LINES-1], fno.fname); - } - } - else { - if (strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[1]) < 0 && strcasecmp(fno.fname, reusableBuffer.modelsel.menu_bss[0]) > 0) { - memset(reusableBuffer.modelsel.menu_bss[0], 0, MENU_LINE_LENGTH); - strcpy(reusableBuffer.modelsel.menu_bss[0], fno.fname); - } - } - } - f_closedir(&dir); - } - - if (popupMenuOffset > 0) - lastpopupMenuOffset = popupMenuOffset; - else - popupMenuOffset = lastpopupMenuOffset; - - return popupMenuItemsCount; -} - -#endif // !LIBOPENUI - -#if defined(SDCARD) -const char * sdCopyFile(const char * srcPath, const char * destPath) -{ - FIL srcFile; - FIL destFile; - char buf[256]; - UINT read = sizeof(buf); - UINT written = sizeof(buf); - - FRESULT result = f_open(&srcFile, srcPath, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - - result = f_open(&destFile, destPath, FA_CREATE_ALWAYS | FA_WRITE); - if (result != FR_OK) { - f_close(&srcFile); - return SDCARD_ERROR(result); - } - - while (result==FR_OK && read==sizeof(buf) && written==sizeof(buf)) { - result = f_read(&srcFile, buf, sizeof(buf), &read); - if (result == FR_OK) { - result = f_write(&destFile, buf, read, &written); - } - } - - f_close(&destFile); - f_close(&srcFile); - - if (result != FR_OK) { - return SDCARD_ERROR(result); - } - - return nullptr; -} - -const char * sdCopyFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir) -{ - char srcPath[2*CLIPBOARD_PATH_LEN+1]; - char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); - *tmp++ = '/'; - strAppend(tmp, srcFilename, CLIPBOARD_PATH_LEN); - - char destPath[2*CLIPBOARD_PATH_LEN+1]; - tmp = strAppend(destPath, destDir, CLIPBOARD_PATH_LEN); - *tmp++ = '/'; - strAppend(tmp, destFilename, CLIPBOARD_PATH_LEN); - - return sdCopyFile(srcPath, destPath); -} - -// Will overwrite if destination exists -const char * sdMoveFile(const char * srcPath, const char * destPath) -{ - const char *result; - result = sdCopyFile(srcPath, destPath); - if(result != 0) { - return result; - } - - FRESULT fres = f_unlink(srcPath); - if(fres != FR_OK) { - return SDCARD_ERROR(fres); - } - return nullptr; -} - -// Will overwrite if destination exists -const char * sdMoveFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir) -{ - const char *result; - result = sdCopyFile(srcFilename, srcDir, destFilename, destDir); - if(result != 0) { - return result; - } - - char srcPath[2*CLIPBOARD_PATH_LEN+1]; - char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); - *tmp++ = '/'; - strAppend(tmp, srcFilename, CLIPBOARD_PATH_LEN); - FRESULT fres = f_unlink(srcPath); - if(fres != FR_OK) { - return SDCARD_ERROR(fres); - } - return nullptr; -} - -#endif // defined(SDCARD) - #if !defined(SIMU) || defined(SIMU_DISKIO) -uint32_t sdGetNoSectors() -{ - static DWORD noSectors = 0; - if (noSectors == 0 ) { - disk_ioctl(0, GET_SECTOR_COUNT, &noSectors); - } - return noSectors; -} - uint32_t sdGetSize() { return (sdGetNoSectors() / 1000000) * BLOCK_SIZE; @@ -499,13 +95,14 @@ FIL g_telemetryFile = {}; FIL g_bluetoothFile = {}; #endif -#include "audio.h" +/*#include "audio.h" #include "sdcard.h" #include "disk_cache.h" - +*/ /*-----------------------------------------------------------------------*/ /* Lock / unlock functions */ /*-----------------------------------------------------------------------*/ +/* static RTOS_MUTEX_HANDLE ioMutex; uint32_t ioMutexReq = 0, ioMutexRel = 0; int ff_cre_syncobj (BYTE vol, FF_SYNC_t * mutex) @@ -613,3 +210,4 @@ uint32_t sdGetSpeed() { return 330000; } +*/ diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index 4a83cfe7948..2146df0dd36 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -24,110 +24,9 @@ #include "ff.h" -extern FIL g_oLogFile; - #include "translations.h" -#define FILE_COPY_PREFIX "cp_" - -#define PATH_SEPARATOR "/" -#define ROOT_PATH PATH_SEPARATOR -#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important -#define DELETED_MODELS_PATH MODELS_PATH PATH_SEPARATOR "DELETED" -#define UNUSED_MODELS_PATH MODELS_PATH PATH_SEPARATOR "UNUSED" -#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important -#define TEMPLATES_PATH ROOT_PATH "TEMPLATES" -#define PERS_TEMPL_PATH TEMPLATES_PATH "/PERSONAL" -#define LOGS_PATH ROOT_PATH "LOGS" -#define SCREENSHOTS_PATH ROOT_PATH "SCREENSHOTS" -#define SOUNDS_PATH ROOT_PATH "SOUNDS/en" -#define SOUNDS_PATH_LNG_OFS (sizeof(SOUNDS_PATH)-3) -#define SYSTEM_SUBDIR "SYSTEM" -#define BITMAPS_PATH ROOT_PATH "IMAGES" -#define FIRMWARES_PATH ROOT_PATH "FIRMWARE" -#define AUTOUPDATE_FILENAME FIRMWARES_PATH PATH_SEPARATOR "autoupdate.frsk" -#define EEPROMS_PATH ROOT_PATH "EEPROM" -#define BACKUP_PATH ROOT_PATH "BACKUP" -#define SCRIPTS_PATH ROOT_PATH "SCRIPTS" -#define WIZARD_PATH SCRIPTS_PATH PATH_SEPARATOR "WIZARD" -#define THEMES_PATH ROOT_PATH "THEMES" -#define LAYOUTS_PATH ROOT_PATH "LAYOUTS" -#define WIDGETS_PATH ROOT_PATH "WIDGETS" -#define WIZARD_NAME "wizard.lua" -#define SCRIPTS_MIXES_PATH SCRIPTS_PATH PATH_SEPARATOR "MIXES" -#define SCRIPTS_FUNCS_PATH SCRIPTS_PATH PATH_SEPARATOR "FUNCTIONS" -#define SCRIPTS_TELEM_PATH SCRIPTS_PATH PATH_SEPARATOR "TELEMETRY" -#define SCRIPTS_TOOLS_PATH SCRIPTS_PATH PATH_SEPARATOR "TOOLS" - -#define LEN_FILE_PATH_MAX (sizeof(SCRIPTS_TELEM_PATH)+1) // longest + "/" - -#if defined(SDCARD_YAML) || defined(SDCARD_RAW) -#define RADIO_FILENAME "radio.bin" -const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt"; -const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME; -#if defined(SDCARD_YAML) -#define LABELS_FILENAME "labels.yml" -#define MODELS_FILENAME "models.yml" -const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR MODELS_FILENAME; -const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR MODELS_FILENAME; -const char LABELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR LABELS_FILENAME; -const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml"; -const char RADIO_SETTINGS_TMPFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_new.yml"; -const char RADIO_SETTINGS_ERRORFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_error.yml"; - -const char YAMLFILE_CHECKSUM_TAG_NAME[] = "checksum"; -#endif -#define SPLASH_FILE "splash.png" -#endif - -#define MODELS_EXT ".bin" -#define LOGS_EXT ".csv" -#define SOUNDS_EXT ".wav" -#define BMP_EXT ".bmp" -#define PNG_EXT ".png" -#define JPG_EXT ".jpg" -#define SCRIPT_EXT ".lua" -#define SCRIPT_BIN_EXT ".luac" -#define TEXT_EXT ".txt" -#define FIRMWARE_EXT ".bin" -#define EEPROM_EXT ".bin" -#define SPORT_FIRMWARE_EXT ".frk" -#define FRSKY_FIRMWARE_EXT ".frsk" -#define MULTI_FIRMWARE_EXT ".bin" -#define ELRS_FIRMWARE_EXT ".elrs" -#define YAML_EXT ".yml" - -#if defined(COLORLCD) -#define BITMAPS_EXT BMP_EXT JPG_EXT PNG_EXT -#define LEN_BITMAPS_EXT 4 -#else -#define BITMAPS_EXT BMP_EXT -#endif - -#ifdef LUA_COMPILER - #define SCRIPTS_EXT SCRIPT_BIN_EXT SCRIPT_EXT -#else - #define SCRIPTS_EXT SCRIPT_EXT -#endif - -#define GET_FILENAME(filename, path, var, ext) \ - char filename[sizeof(path) + sizeof(var) + sizeof(ext)]; \ - memcpy(filename, path, sizeof(path) - 1); \ - filename[sizeof(path) - 1] = '/'; \ - memcpy(&filename[sizeof(path)], var, sizeof(var)); \ - filename[sizeof(path)+sizeof(var)] = '\0'; \ - strcat(&filename[sizeof(path)], ext) - -extern uint8_t logDelay100ms; -void logsInit(); -void logsClose(); -void logsWrite(); - -void sdInit(); -void sdMount(); -void sdDone(); -uint32_t sdMounted(); - +bool sdCardFormat(); uint32_t sdGetNoSectors(); uint32_t sdGetSize(); uint32_t sdGetFreeSectors(); @@ -138,42 +37,4 @@ bool sdIsFull(); void sdPoll10ms(); #endif -#if !defined(SIMU) || defined(SIMU_DISKIO) - uint32_t sdIsHC(); - uint32_t sdGetSpeed(); - #define SD_IS_HC() (sdIsHC()) - #define SD_GET_SPEED() (sdGetSpeed()) - #define SD_GET_FREE_BLOCKNR() (sdGetFreeSectors()) -#else - #define SD_IS_HC() (0) - #define SD_GET_SPEED() (0) -#endif - -const char * sdCheckAndCreateDirectory(const char * path); - -#if !defined(BOOT) -inline const char * SDCARD_ERROR(FRESULT result) -{ - if (result == FR_NOT_READY) - return STR_NO_SDCARD; - else - return STR_SDCARD_ERROR; -} -#endif - -// NOTE: 'size' must = 0 or be a valid character position within 'filename' array -- it is NOT validated -const char * getBasename(const char * path); - -bool isFileAvailable(const char * filename, bool exclDir = false); -unsigned int findNextFileIndex(char * filename, uint8_t size, const char * directory); - -const char * sdCopyFile(const char * src, const char * dest); -const char * sdCopyFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir); -const char * sdMoveFile(const char * src, const char * dest); -const char * sdMoveFile(const char * srcFilename, const char * srcDir, const char * destFilename, const char * destDir); - -#define LIST_NONE_SD_FILE 1 -#define LIST_SD_FILE_EXT 2 -bool sdListFiles(const char * path, const char * extension, const uint8_t maxlen, const char * selection, uint8_t flags=0); - #endif // _SDCARD_H_ diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index feb3cd06011..a31133bd8d8 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -281,8 +281,18 @@ long OpenTxSim::onMouseDown(FXObject *, FXSelector, void * v) simTouchState.tapCount = 0; simTouchState.event = TE_DOWN; - simTouchState.startX = simTouchState.x = evt->win_x; - simTouchState.startY = simTouchState.y = evt->win_y; + +#if !defined(PCBPL18) + auto evtX = evt->win_x; + auto evtY = evt->win_y; +#else + auto evtX = evt->win_y; + auto evtY = LCD_W - evt->win_x; +#endif + + simTouchState.startX = simTouchState.x = evtX; + simTouchState.startY = simTouchState.y = evtY; + downTime = now; simTouchOccured = true; #endif @@ -331,12 +341,23 @@ long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v) TRACE_WINDOWS("[Mouse Move] %d %d", evt->win_x, evt->win_y); #if defined(HARDWARE_TOUCH) - simTouchState.deltaX += evt->win_x - simTouchState.x; - simTouchState.deltaY += evt->win_y - simTouchState.y; +#if !defined(PCBPL18) + auto evtX = evt->win_x; + auto evtY = evt->win_y; +#else + auto evtX = evt->win_y; + auto evtY = LCD_W - evt->win_x; +#endif + + simTouchState.deltaX += evtX - simTouchState.x; + simTouchState.deltaY += evtY - simTouchState.y; if (simTouchState.event == TE_SLIDE || abs(simTouchState.deltaX) >= SLIDE_RANGE || abs(simTouchState.deltaY) >= SLIDE_RANGE) { simTouchState.event = TE_SLIDE; simTouchState.x = evt->win_x; simTouchState.y = evt->win_y; + simTouchState.x = evtX; + simTouchState.y = evtY; + } simTouchOccured = true; #endif @@ -348,7 +369,7 @@ long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v) void OpenTxSim::updateKeysAndSwitches(bool start) { static int keys[] = { -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) // no keys #elif defined(PCBHORUS) KEY_Page_Up, KEY_PAGEUP, @@ -559,10 +580,10 @@ void OpenTxSim::refreshDisplay() FXColor onColor = FXRGB(0, 0, 0); #endif #endif - for (int x = 0; x < LCD_W; x++) { - for (int y = 0; y < LCD_H; y++) { + for (int x=0; x> 8) + ((z & 0xE000) >> 13), ((z & 0x07E0) >> 3) + ((z & 0x0600) >> 9), @@ -570,7 +591,7 @@ void OpenTxSim::refreshDisplay() setPixel(x, y, color); #else #if LCD_DEPTH == 4 - pixel_t *p = &simuLcdBuf[y / 2 * LCD_W + x]; + pixel_t * p = &simuLcdBuf[y / 2 * LCD_W + x]; uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F); if (z) { FXColor color; @@ -583,7 +604,7 @@ void OpenTxSim::refreshDisplay() setPixel(x, y, color); } #else // LCD_DEPTH == 1 - if (simuLcdBuf[x + (y / 8) * LCD_W] & (1 << (y % 8))) { + if (simuLcdBuf[x+(y/8)*LCD_W] & (1<<(y%8))) { setPixel(x, y, onColor); } #endif diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 7d2ecab02b4..2f789585f4d 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -901,9 +901,9 @@ bool ModelsList::loadTxt() char line[LEN_MODELS_IDX_LINE + 1]; ModelCell *model = nullptr; - FRESULT result = - f_open(&file, RADIO_MODELSLIST_PATH, FA_OPEN_EXISTING | FA_READ); - if (result == FR_OK) { + VfsError result = + VirtualFS::instance().openFile(file, RADIO_MODELSLIST_PATH, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result == VfsError::OK) { // TXT reader while (readNextLine(line, LEN_MODELS_IDX_LINE)) { int len = strlen(line); // TODO could be returned by readNextLine @@ -918,7 +918,7 @@ bool ModelsList::loadTxt() } } - f_close(&file); + file.close(); return true; } @@ -969,11 +969,15 @@ void ModelMap::updateModelCell(ModelCell *cell) * @return char* Pointer to buffer supplied */ -char *FILInfoToHexStr(char buffer[17], FILINFO *finfo) +char *fileInfoToHexStr(char buffer[17], VfsFileInfo& fInfo) { char *str = buffer; + FInfoH info; + info.fsize = fInfo.getSize(); + info.fdate = fInfo.getDate(); + info.ftime = fInfo.getTime(); for (unsigned int i = 0; i < sizeof(FInfoH); i++) { - sprintf(str, "%02x", *((uint8_t *)finfo + i)); + sprintf(str, "%02x", *((uint8_t *)(&info) + i)); str += 2; } return buffer; @@ -996,20 +1000,22 @@ bool ModelsList::loadYaml() DEBUG_TIMER_START(debugTimerYamlScan); // Scan all models in folder - DIR moddir; - FILINFO finfo; - if (f_opendir(&moddir, MODELS_PATH) == FR_OK) { + VirtualFS& vfs = VirtualFS::instance(); + VfsDir moddir; + VfsFileInfo finfo; + if (vfs.openDirectory(moddir, MODELS_PATH) == VfsError::OK) { for (;;) { - FRESULT res = f_readdir(&moddir, &finfo); - if (res != FR_OK || finfo.fname[0] == 0) break; - if (finfo.fattrib & AM_DIR) continue; - unsigned int len = strlen(finfo.fname); + VfsError res = moddir.read(finfo); + const char* fName = finfo.getName(); + if (res != VfsError::OK || fName[0] == 0) break; + if (finfo.getType() == VfsType::DIR) continue; + unsigned int len = strlen(fName); // Only open model###.yml files bool modelNameInvalid = false; - if (strncasecmp(finfo.fname, MODEL_FILENAME_PREFIX, sizeof(MODEL_FILENAME_PREFIX) - 1) == 0) { + if (strncasecmp(fName, MODEL_FILENAME_PREFIX, sizeof(MODEL_FILENAME_PREFIX) - 1) == 0) { for (unsigned int i = sizeof(MODEL_FILENAME_PREFIX) - 1; i < len - 4; i++) { - if(finfo.fname[i] < '0' || finfo.fname[i] > '9') { + if(fName[i] < '0' || fName[i] > '9') { modelNameInvalid = true; break; } @@ -1019,17 +1025,17 @@ bool ModelsList::loadYaml() } if (modelNameInvalid || - strcasecmp(finfo.fname + len - 4, YAML_EXT) || // Skip non .yml files - (finfo.fattrib & AM_DIR)) { // Skip sub dirs + strcasecmp(fName + len - 4, YAML_EXT) || // Skip non .yml files + (finfo.getType() == VfsType::DIR)) { // Skip sub dirs continue; } // Store hash & filename filedat cf; - FILInfoToHexStr(cf.hash, &finfo); - cf.name = finfo.fname; + fileInfoToHexStr(cf.hash, finfo); + cf.name = fName; cf.celladded = false; - if (!strncmp(finfo.fname, g_eeGeneral.currModelFilename, + if (!strncmp(fName, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME)) cf.curmodel = true; else @@ -1038,48 +1044,48 @@ bool ModelsList::loadYaml() TRACE_LABELS("File - %s \r\n HASH - %s - CM: %s", finfo.fname, cf.hash, cf.curmodel ? "Y" : "N"); } - f_closedir(&moddir); + moddir.close(); } // Check if models.yml exists // Any files found above that are not listed in the file will be moved into // /MDOELS/UNUSED and removed from the discovered file hash list char line[LEN_MODELS_IDX_LINE + 1]; - FILINFO fno; - FRESULT result; - bool foundInModels = f_stat(MODELSLIST_YAML_PATH, &fno) == FR_OK; - bool foundInRadio = f_stat(FALLBACK_MODELSLIST_YAML_PATH, &fno) == FR_OK; + VfsFileInfo fno; + VfsError result; + bool foundInModels = vfs.fstat(MODELSLIST_YAML_PATH, fno) == VfsError::OK; + bool foundInRadio = vfs.fstat(FALLBACK_MODELSLIST_YAML_PATH, fno) == VfsError::OK; if(foundInModels) { // Default to /Models copy - result = f_open(&file, MODELSLIST_YAML_PATH, FA_OPEN_EXISTING | FA_READ); + result = vfs.openFile(file, MODELSLIST_YAML_PATH, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); } else if (foundInRadio) { - result = f_open(&file, FALLBACK_MODELSLIST_YAML_PATH, FA_OPEN_EXISTING | FA_READ); + result = vfs.openFile(file, FALLBACK_MODELSLIST_YAML_PATH, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); } - if((foundInModels || foundInRadio) && result == FR_OK) { + if((foundInModels || foundInRadio) && result == VfsError::OK) { // Create /Models/Unused if it doesn't exist bool moveRequired = false; - DIR unusedFolder; - FRESULT result = f_opendir(&unusedFolder, UNUSED_MODELS_PATH); - if (result != FR_OK) { - if (result == FR_NO_PATH) result = f_mkdir(UNUSED_MODELS_PATH); - if (result != FR_OK) { + VfsDir unusedFolder; + VfsError result = vfs.openDirectory(unusedFolder, UNUSED_MODELS_PATH); + if (result != VfsError::OK) { + if (result == VfsError::NOENT) result = vfs.makeDirectory(UNUSED_MODELS_PATH); + if (result != VfsError::OK) { TRACE("Unable to create unused models folder"); - f_close(&file); + file.close(); return false; } - } else f_closedir(&unusedFolder); + } else unusedFolder.close(); YamlParser ymp; std::vector modfiles; void *ctx = get_modelslist_iter(&modfiles); ymp.init(get_modelslist_parser_calls(), ctx); - UINT bytes_read = 0; - while (f_read(&file, line, sizeof(line), &bytes_read) == FR_OK) { + size_t bytes_read = 0; + while (file.read(line, sizeof(line), bytes_read) == VfsError::OK) { if (bytes_read == 0) break; - if (f_eof(&file)) ymp.set_eof(); + if (file.eof()) ymp.set_eof(); if (ymp.parse(line, bytes_read) != YamlParser::CONTINUE_PARSING) break; } - f_close(&file); + file.close(); // Loop through file hases, move any files found that don't exists to /unused std::vector newFileHash; @@ -1096,7 +1102,7 @@ bool ModelsList::loadYaml() moveRequired = true; TRACE_LABELS("Model %s not in models.yml, moving to /UNUSED", fhas.name.c_str()); // Move model into unused folder. - const char *warning = sdMoveFile(fhas.name.c_str(), MODELS_PATH, fhas.name.c_str(), UNUSED_MODELS_PATH); + const char *warning = vfs.moveFile(fhas.name.c_str(), MODELS_PATH, fhas.name.c_str(), UNUSED_MODELS_PATH); if(warning) POPUP_WARNING(warning); } else { @@ -1105,12 +1111,12 @@ bool ModelsList::loadYaml() } if(foundInRadio) { - const char *warning = sdMoveFile(MODELS_FILENAME, RADIO_PATH, MODELS_FILENAME ".old", UNUSED_MODELS_PATH); + const char *warning = vfs.moveFile(MODELS_FILENAME, RADIO_PATH, MODELS_FILENAME ".old", UNUSED_MODELS_PATH); if(warning) POPUP_WARNING(warning); } if(foundInModels) { // Will overwrite the copy from /radio if both existed, do last - const char *warning = sdMoveFile(MODELS_FILENAME, MODELS_PATH, MODELS_FILENAME ".old", UNUSED_MODELS_PATH); + const char *warning = vfs.moveFile(MODELS_FILENAME, MODELS_PATH, MODELS_FILENAME ".old", UNUSED_MODELS_PATH); if(warning) POPUP_WARNING(warning); } @@ -1127,18 +1133,18 @@ bool ModelsList::loadYaml() #endif // Scan labels.yml - result = f_open(&file, LABELSLIST_YAML_PATH, FA_OPEN_EXISTING | FA_READ); - if (result == FR_OK) { + result = vfs.openFile(file, LABELSLIST_YAML_PATH, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result == VfsError::OK) { YamlParser yp; void *ctx = get_labelslist_iter(); yp.init(get_labelslist_parser_calls(), ctx); - UINT bytes_read = 0; - while (f_read(&file, line, sizeof(line), &bytes_read) == FR_OK) { + size_t bytes_read = 0; + while (file.read(line, sizeof(line), bytes_read) == VfsError::OK) { if (bytes_read == 0) break; - if (f_eof(&file)) yp.set_eof(); + if (file.eof()) yp.set_eof(); if (yp.parse(line, bytes_read) != YamlParser::CONTINUE_PARSING) break; } - f_close(&file); + file.close(); } #if defined(DEBUG_TIMERS) @@ -1206,15 +1212,16 @@ bool ModelsList::load(Format fmt) { if (loaded) return true; + VirtualFS& vfs = VirtualFS::instance(); bool res = false; #if !defined(SDCARD_YAML) (void)fmt; res = loadTxt(); #else - FILINFO fno; + VfsFileInfo fno; if (fmt == Format::txt || - (fmt == Format::yaml_txt && f_stat(MODELSLIST_YAML_PATH, &fno) != FR_OK && - f_stat(FALLBACK_MODELSLIST_YAML_PATH, &fno) != FR_OK)) { + (fmt == Format::yaml_txt && vfs.fstat(MODELSLIST_YAML_PATH, fno) != VfsError::OK && + vfs.fstat(FALLBACK_MODELSLIST_YAML_PATH, fno) != VfsError::OK)) { res = loadTxt(); } else { res = loadYaml(); @@ -1252,70 +1259,71 @@ bool ModelsList::load(Format fmt) const char *ModelsList::save(LabelsVector newOrder) { + VirtualFS& vfs = VirtualFS::instance(); #if !defined(SDCARD_YAML) - FRESULT result = - f_open(&file, RADIO_MODELSLIST_PATH, FA_CREATE_ALWAYS | FA_WRITE); + VfsError result = + vfs.openFile(file, RADIO_MODELSLIST_PATH, VfsOpenFlags::CREATE_ALWAYS | VfsOpenFlags::WRITE); #else - FRESULT result = - f_open(&file, LABELSLIST_YAML_PATH, FA_CREATE_ALWAYS | FA_WRITE); + VfsError result = + vfs.openFile(file, LABELSLIST_YAML_PATH, VfsOpenFlags::CREATE_ALWAYS | VfsOpenFlags::WRITE); #endif - if (result != FR_OK) return "Couldn't open labels.yml for writing"; + if (result != VfsError::OK) return "Couldn't open labels.yml for writing"; // Save current selection - f_puts("Labels:\r\n", &file); + file.puts("Labels:\r\n"); std::string cursel = modelslabels.getCurrentLabel(); if(newOrder.empty()) newOrder = modelslabels.getLabels(); for (auto &lbl : newOrder) { - f_printf(&file, " \"%s\":\r\n", lbl.c_str()); + file.fprintf(" \"%s\":\r\n", lbl.c_str()); if (modelslabels.isLabelFiltered(lbl)) - f_printf(&file, " selected: true\r\n", lbl.c_str()); + file.fprintf(" selected: true\r\n", lbl.c_str()); } // Save current sort order - f_printf( &file, "Sort: %d\r\n", modelslabels.sortOrder()); + file.fprintf("Sort: %d\r\n", modelslabels.sortOrder()); - f_puts("Models:\r\n", &file); + file.puts("Models:\r\n"); for (auto &model : modelslist) { - f_puts(" ", &file); - f_puts(model->modelFilename, &file); - f_puts(":\r\n", &file); + file.puts(" "); + file.puts(model->modelFilename); + file.puts(":\r\n"); - f_puts(" hash: \"", &file); - f_puts(model->modelFinfoHash, &file); - f_puts("\"\r\n", &file); + file.puts(" hash: \""); + file.puts(model->modelFinfoHash); + file.puts("\"\r\n"); - f_puts(" name: \"", &file); - f_puts(model->modelName, &file); - f_puts("\"\r\n", &file); + file.puts(" name: \""); + file.puts(model->modelName); + file.puts("\"\r\n"); for (int i = 0; i < NUM_MODULES; i++) { if (model->modelId[i]) - f_printf(&file, " " MODULE_ID_STR ": %u\r\n", i, + file.fprintf(" " MODULE_ID_STR ": %u\r\n", i, (unsigned int)model->modelId[i]); if (model->moduleData[i].type) - f_printf(&file, " " MODULE_TYPE_STR ": %u\r\n", i, + file.fprintf(" " MODULE_TYPE_STR ": %u\r\n", i, (unsigned int)model->moduleData[i].type); if (model->moduleData[i].subType) - f_printf(&file, " " MODULE_RFPROTOCOL_STR ": %u\r\n", i, + file.fprintf(" " MODULE_RFPROTOCOL_STR ": %u\r\n", i, (unsigned int)model->moduleData[i].subType); } - f_printf(&file, " labels: \"%s\"\r\n", ModelMap::toCSV(modelslabels.getLabelsByModel(model)).c_str()); + file.fprintf(" labels: \"%s\"\r\n", ModelMap::toCSV(modelslabels.getLabelsByModel(model)).c_str()); #if LEN_BITMAP_NAME > 0 - f_puts(" bitmap: \"", &file); - f_puts(model->modelBitmap, &file); - f_puts("\"\r\n", &file); + file.puts(" bitmap: \""); + file.puts(model->modelBitmap); + file.puts("\"\r\n"); #endif - f_puts(" lastopen: ", &file); - f_puts(std::to_string(model->lastOpened).c_str(), &file); - f_puts("\r\n", &file); + file.puts(" lastopen: "); + file.puts(std::to_string(model->lastOpened).c_str()); + file.puts("\r\n"); } - f_puts("\r\n", &file); - f_close(&file); + file.puts("\r\n"); + file.close(); modelslabels._isDirty = false; return NULL; @@ -1373,7 +1381,7 @@ void ModelsList::updateCurrentModelCell() bool ModelsList::readNextLine(char *line, int maxlen) { - if (f_gets(line, maxlen, &file) != NULL) { + if (file.gets(line, maxlen) != NULL) { int curlen = strlen(line) - 1; if (line[curlen] == '\n') { // remove unwanted chars if file was edited using windows @@ -1433,20 +1441,21 @@ bool ModelsList::removeModel(ModelCell *model) modelslabels.removeModels(model); // Create deleted folder if it doesn't exist - DIR deletedFolder; - FRESULT result = f_opendir(&deletedFolder, DELETED_MODELS_PATH); - if (result != FR_OK) { - if (result == FR_NO_PATH) result = f_mkdir(DELETED_MODELS_PATH); - if (result != FR_OK) { + VirtualFS& vfs = VirtualFS::instance(); + VfsDir deletedFolder; + VfsError result = vfs.openDirectory(deletedFolder, DELETED_MODELS_PATH); + if (result != VfsError::OK) { + if (result == VfsError::NOENT) result = vfs.makeDirectory(DELETED_MODELS_PATH); + if (result != VfsError::OK) { TRACE("Unable to create deleted models folder"); return true; } - } else f_closedir(&deletedFolder); + } else deletedFolder.close(); // Move model into deleted folder. If not moved will be re-added on next // reboot TRACE_LABELS("Deleting Model %s", model->modelFilename); - const char *warning = sdMoveFile(model->modelFilename, MODELS_PATH, model->modelFilename, DELETED_MODELS_PATH); + const char *warning = vfs.moveFile(model->modelFilename, MODELS_PATH, model->modelFilename, DELETED_MODELS_PATH); if (warning) { TRACE("Labels: Unable to move file"); return true; diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index 34235d4055a..4501d5b2a0d 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -33,7 +33,7 @@ #include #include -#include "sdcard.h" +#include "VirtualFS.h" #if !defined(SDCARD_YAML) #include "sdcard_raw.h" @@ -267,7 +267,7 @@ class ModelsList : public ModelsVector std::vector fileHashInfo; protected: - FIL file; + VfsFile file; bool loadTxt(); #if defined(SDCARD_YAML) diff --git a/radio/src/storage/rlc.cpp b/radio/src/storage/rlc.cpp index f63a57efcea..a05b6d8c690 100644 --- a/radio/src/storage/rlc.cpp +++ b/radio/src/storage/rlc.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include "edgetx_assert.h" #include "debug.h" #include "rlc.h" diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index 4695dfd15ac..75f8c67ed76 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -63,8 +63,9 @@ void storageEraseAll(bool warn) void storageFormat() { - sdCheckAndCreateDirectory(RADIO_PATH); - sdCheckAndCreateDirectory(MODELS_PATH); + VirtualFS& vfs = VirtualFS::instance(); + vfs.checkAndCreateDirectory(RADIO_PATH); + vfs.checkAndCreateDirectory(MODELS_PATH); generalDefault(); setModelDefaults(); } @@ -113,7 +114,7 @@ const char * createModel() memset(filename, 0, sizeof(filename)); strcpy(filename, MODEL_FILENAME_PATTERN); - int index = findNextFileIndex(filename, LEN_MODEL_FILENAME, MODELS_PATH); + int index = VirtualFS::instance().findNextFileIndex(filename, LEN_MODEL_FILENAME, MODELS_PATH); if (index > 0) { setModelDefaults(index); memcpy(g_eeGeneral.currModelFilename, filename, sizeof(g_eeGeneral.currModelFilename)); diff --git a/radio/src/storage/sdcard_common.h b/radio/src/storage/sdcard_common.h index 5b7c171308b..c92d21b07cb 100644 --- a/radio/src/storage/sdcard_common.h +++ b/radio/src/storage/sdcard_common.h @@ -22,7 +22,7 @@ #ifndef _SDCARD_COMMON_H_ #define _SDCARD_COMMON_H_ -#include "ff.h" +#include "VirtualFS.h" #include "translations.h" #define MODEL_FILENAME_PREFIX "model" @@ -39,7 +39,7 @@ extern ModelHeader modelHeaders[MAX_MODELS]; #endif // opens radio.bin or model file -const char* openFileBin(const char* fullpath, FIL* file, uint16_t* size, +const char* openFileBin(const char* fullpath, VfsFile& file, uint16_t* size, uint8_t* version); const char* writeFileBin(const char* fullpath, const uint8_t* data, diff --git a/radio/src/storage/sdcard_raw.h b/radio/src/storage/sdcard_raw.h index 78a13ed2a1c..338eaa48568 100644 --- a/radio/src/storage/sdcard_raw.h +++ b/radio/src/storage/sdcard_raw.h @@ -37,7 +37,7 @@ const char* readModel(const char* filename, uint8_t* buffer, uint32_t size); const char* loadRadioSettingsBin(); const char * writeGeneralSettingsBin(); -const char* openFileBin(const char* fullpath, FIL* file, uint16_t* size, +const char* openFileBin(const char* fullpath, VfsFile& file, uint16_t& size, uint8_t* version); const char* readModelBin(const char* filename, uint8_t* buffer, uint32_t size, diff --git a/radio/src/storage/sdcard_yaml.cpp b/radio/src/storage/sdcard_yaml.cpp index e690153e619..81fcc072f3b 100644 --- a/radio/src/storage/sdcard_yaml.cpp +++ b/radio/src/storage/sdcard_yaml.cpp @@ -26,6 +26,7 @@ #include "sdcard_raw.h" #include "sdcard_yaml.h" #include "modelslist.h" +#include "VirtualFS.h" #include "yaml/yaml_tree_walker.h" #include "yaml/yaml_parser.h" @@ -40,13 +41,14 @@ const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, void* parser_ctx, ChecksumResult* checksum_result) { - FIL file; - UINT bytes_read; - UINT total_bytes = 0; - - FRESULT result = f_open(&file, fullpath, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { - return SDCARD_ERROR(result); + VfsFile file; + size_t bytes_read; + uint32_t total_bytes = 0; + + VirtualFS& vfs = VirtualFS::instance(); + VfsError result = vfs.openFile(file, fullpath, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + if (result != VfsError::OK) { + return STORAGE_ERROR(result); } YamlParser yp; //TODO: move to re-usable buffer @@ -57,7 +59,7 @@ const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, vo bool first_block = true; char buffer[32]; - while (f_read(&file, buffer, sizeof(buffer)-1, &bytes_read) == FR_OK) { + while (file.read(buffer, sizeof(buffer)-1, bytes_read) == VfsError::OK) { if (bytes_read == 0) // EOF break; total_bytes += bytes_read; @@ -75,7 +77,7 @@ const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, vo // Advance through the value while((*endPos != '\r') && (*endPos != '\n')) { if (endPos > buffer + bytes_read) { - return SDCARD_ERROR( FR_INT_ERR ); + return STORAGE_ERROR( VfsError::INVAL ); } endPos++; } @@ -95,11 +97,11 @@ const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, vo calculated_checksum = crc16(0, (const uint8_t *)buffer + skip, bytes_read - skip, calculated_checksum); } - if (f_eof(&file)) yp.set_eof(); + if (file.eof()) yp.set_eof(); if (yp.parse(buffer + skip, bytes_read - skip) != YamlParser::CONTINUE_PARSING) break; } - f_close(&file); + file.close(); if (checksum_result != NULL) { // Special case to handle "old" files with no checksum field @@ -143,8 +145,9 @@ const char * loadRadioSettingsYaml(bool checks) return p; if((p != NULL) || (checksum_status != ChecksumResult::Success) ) { + VirtualFS &vfs = VirtualFS::instance(); // Read failed or checksum check failed - FRESULT result = FR_OK; + VfsError result = VfsError::OK; TRACE("radio settings: Reading failed"); if ( (p == NULL) && g_eeGeneral.manuallyEdited) { // Read sussessfull, checksum failed, manuallyEdited set @@ -153,15 +156,15 @@ const char * loadRadioSettingsYaml(bool checks) storageDirty(EE_GENERAL); // Trigger a save on sucessfull recovery } else { TRACE("File is corrupted, attempting alternative file"); - f_unlink(RADIO_SETTINGS_ERRORFILE_YAML_PATH); - result = f_rename(RADIO_SETTINGS_YAML_PATH, RADIO_SETTINGS_ERRORFILE_YAML_PATH); // Save corrupted file for later analysis + vfs.unlink(RADIO_SETTINGS_ERRORFILE_YAML_PATH); + result = vfs.rename(RADIO_SETTINGS_YAML_PATH, RADIO_SETTINGS_ERRORFILE_YAML_PATH); // Save corrupted file for later analysis p = attemptLoad(RADIO_SETTINGS_TMPFILE_YAML_PATH, &checksum_status); if (p == NULL && (checksum_status == ChecksumResult::Success)) { - f_unlink(RADIO_SETTINGS_YAML_PATH); - result = f_rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH); // Rename previously saved file to active file - if (result != FR_OK) { + vfs.unlink(RADIO_SETTINGS_YAML_PATH); + result = vfs.rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH); // Rename previously saved file to active file + if (result != VfsError::OK) { ALERT(STR_STORAGE_WARNING, TR_RADIO_DATA_UNRECOVERABLE, AU_BAD_RADIODATA); - return SDCARD_ERROR(result); + return STORAGE_ERROR(result); } } TRACE("Unable to recover radio data"); @@ -173,9 +176,9 @@ const char * loadRadioSettingsYaml(bool checks) const char * loadRadioSettings() { - FILINFO fno; - - if ( (f_stat(RADIO_SETTINGS_YAML_PATH, &fno) != FR_OK) && ((f_stat(RADIO_SETTINGS_TMPFILE_YAML_PATH, &fno) != FR_OK)) ) { + VfsFileInfo fno; + VirtualFS& vfs = VirtualFS::instance(); + if ( (vfs.fstat(RADIO_SETTINGS_YAML_PATH, fno) != VfsError::OK) && ((vfs.fstat(RADIO_SETTINGS_TMPFILE_YAML_PATH, fno) != VfsError::OK)) ) { // If neither the radio configuraion YAML file or the temporary file generated on write exist, this must be a first run with YAML support. // - thus requiring a conversion from binary to YAML. return "no radio settings"; @@ -197,7 +200,7 @@ const char * loadRadioSettings() struct yaml_checksummer_ctx { - FRESULT result; + VfsError result; uint16_t checksum; bool checksum_invalid; }; @@ -216,12 +219,12 @@ bool YamlFileChecksum(const YamlNode* root_node, uint8_t* data, uint16_t* checks tree.reset(root_node, data); yaml_checksummer_ctx ctx; - ctx.result = FR_OK; + ctx.result = VfsError::OK; ctx.checksum = 0xFFFF; ctx.checksum_invalid = false; if (!tree.generate(yaml_checksummer, &ctx)) { - if (ctx.result != FR_OK) { + if (ctx.result != VfsError::OK) { ctx.checksum_invalid = true; return false; } @@ -236,63 +239,65 @@ bool YamlFileChecksum(const YamlNode* root_node, uint8_t* data, uint16_t* checks struct yaml_writer_ctx { - FIL* file; - FRESULT result; + VfsFile* file; + VfsError result; }; static bool yaml_writer(void* opaque, const char* str, size_t len) { - UINT bytes_written; + size_t bytes_written; yaml_writer_ctx* ctx = (yaml_writer_ctx*)opaque; #if defined(DEBUG_YAML) TRACE_NOCRLF("%.*s",len,str); #endif - ctx->result = f_write(ctx->file, str, len, &bytes_written); - return (ctx->result == FR_OK) && (bytes_written == len); + ctx->result = ctx->file->write(str, len, bytes_written); + return (ctx->result == VfsError::OK) && (bytes_written == len); } const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data, uint16_t checksum) { - FIL file; + VfsFile file; - FRESULT result = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE); - if (result != FR_OK) { - return SDCARD_ERROR(result); + VirtualFS::instance().unlink(path); + VfsError result = VirtualFS::instance().openFile(file, path, VfsOpenFlags::CREATE_ALWAYS | VfsOpenFlags::WRITE); + if (result != VfsError::OK) { + return STORAGE_ERROR(result); } YamlTreeWalker tree; tree.reset(root_node, data); yaml_writer_ctx ctx; ctx.file = &file; - ctx.result = FR_OK; + ctx.result = VfsError::OK; // Try to add CRC if (checksum != 0) { if (!yaml_writer(&ctx, YAMLFILE_CHECKSUM_TAG_NAME, strlen(YAMLFILE_CHECKSUM_TAG_NAME))) return NULL; - if (!yaml_writer(&ctx, ": ", 2)) return SDCARD_ERROR(FR_INVALID_PARAMETER); + if (!yaml_writer(&ctx, ": ", 2)) return STORAGE_ERROR(VfsError::INVAL); const char* p_out = NULL; p_out = yaml_unsigned2str((int)checksum); - if (p_out && !yaml_writer(&ctx, p_out, strlen(p_out))) return SDCARD_ERROR(FR_INVALID_PARAMETER); + if (p_out && !yaml_writer(&ctx, p_out, strlen(p_out))) return STORAGE_ERROR(VfsError::INVAL); yaml_writer(&ctx, "\r\n", 2); } if (!tree.generate(yaml_writer, &ctx)) { - if (ctx.result != FR_OK) { - f_close(&file); - return SDCARD_ERROR(ctx.result); + if (ctx.result != VfsError::OK) { + file.close(); + return STORAGE_ERROR(ctx.result); } } - f_close(&file); + file.close(); return NULL; } const char * writeGeneralSettings() { TRACE("YAML radio settings writer"); + VirtualFS &vfs = VirtualFS::instance(); uint16_t file_checksum = 0; YamlFileChecksum(get_radiodata_nodes(), (uint8_t*)&g_eeGeneral, &file_checksum); @@ -305,11 +310,11 @@ const char * writeGeneralSettings() if (p != NULL) { return p; } - f_unlink(RADIO_SETTINGS_YAML_PATH); + vfs.unlink(RADIO_SETTINGS_YAML_PATH); - FRESULT result = f_rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH); - if(result != FR_OK) - return SDCARD_ERROR(result); + VfsError result = vfs.rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH); + if(result != VfsError::OK) + return STORAGE_ERROR(result); return nullptr; } @@ -441,8 +446,8 @@ bool modelExists(uint8_t idx) getModelNumberStr(idx, model_idx); GET_FILENAME(fname, MODELS_PATH, model_idx, YAML_EXT); - FILINFO fno; - return f_stat(fname, &fno) == FR_OK; + VfsFileInfo fno; + return VirtualFS::instance().fstat(fname, fno) == VfsError::OK; } bool copyModel(uint8_t dst, uint8_t src) @@ -456,7 +461,7 @@ bool copyModel(uint8_t dst, uint8_t src) GET_FILENAME(fname_src, MODELS_PATH, model_idx_src, YAML_EXT); GET_FILENAME(fname_dst, MODELS_PATH, model_idx_dst, YAML_EXT); - return sdCopyFile(fname_src, fname_dst); + return VirtualFS::instance().copyFile(fname_src, fname_dst) == VfsError::OK; } static void swapModelHeaders(uint8_t id1, uint8_t id2) @@ -478,34 +483,35 @@ void swapModels(uint8_t id1, uint8_t id2) GET_FILENAME(fname1_tmp, MODELS_PATH, model_idx_1, ".tmp"); GET_FILENAME(fname2, MODELS_PATH, model_idx_2, YAML_EXT); - FILINFO fno; - if (f_stat(fname2,&fno) != FR_OK) { - if (f_stat(fname1,&fno) == FR_OK) { - if (f_rename(fname1, fname2) == FR_OK) + VirtualFS &vfs = VirtualFS::instance(); + VfsFileInfo fno; + if (vfs.fstat(fname2,fno) != VfsError::OK) { + if (vfs.fstat(fname1,fno) == VfsError::OK) { + if (vfs.rename(fname1, fname2) == VfsError::OK) swapModelHeaders(id1,id2); } return; } - if (f_stat(fname1,&fno) != FR_OK) { - f_rename(fname2, fname1); + if (vfs.fstat(fname1,fno) != VfsError::OK) { + vfs.rename(fname2, fname1); return; } // just in case... - f_unlink(fname1_tmp); + vfs.unlink(fname1_tmp); - if (f_rename(fname1, fname1_tmp) != FR_OK) { + if (vfs.rename(fname1, fname1_tmp) != VfsError::OK) { TRACE("Error renaming 1"); return; } - if (f_rename(fname2, fname1) != FR_OK) { + if (vfs.rename(fname2, fname1) != VfsError::OK) { TRACE("Error renaming 2"); return; } - if (f_rename(fname1_tmp, fname2) != FR_OK) { + if (vfs.rename(fname1_tmp, fname2) != VfsError::OK) { TRACE("Error renaming 1 tmp"); return; } @@ -519,7 +525,7 @@ int8_t deleteModel(uint8_t idx) getModelNumberStr(idx, model_idx); GET_FILENAME(fname, MODELS_PATH, model_idx, YAML_EXT); - if (f_unlink(fname) != FR_OK) { + if (VirtualFS::instance().unlink(fname) != VfsError::OK) { return -1; } @@ -532,7 +538,7 @@ const char * backupModel(uint8_t idx) char * buf = reusableBuffer.modelsel.mainname; // check and create folder here - const char * error = sdCheckAndCreateDirectory(STR_BACKUP_PATH); + const char * error = VirtualFS::instance().checkAndCreateDirectory(STR_BACKUP_PATH); if (error) { return error; } @@ -575,7 +581,7 @@ const char * backupModel(uint8_t idx) getModelNumberStr(idx, model_idx); strcat(model_idx, STR_YAML_EXT); - return sdCopyFile(model_idx, STR_MODELS_PATH, buf, STR_BACKUP_PATH); + return STORAGE_ERROR(VirtualFS::instance().copyFile(model_idx, STR_MODELS_PATH, buf, STR_BACKUP_PATH)); } const char * restoreModel(uint8_t idx, char *model_name) @@ -588,7 +594,7 @@ const char * restoreModel(uint8_t idx, char *model_name) getModelNumberStr(idx, model_idx); strcat(model_idx, STR_YAML_EXT); - const char* error = sdCopyFile(buf, STR_BACKUP_PATH, model_idx, STR_MODELS_PATH); + const char* error = STORAGE_ERROR(VirtualFS::instance().copyFile(buf, STR_BACKUP_PATH, model_idx, STR_MODELS_PATH)); if (!error) { loadModelHeader(idx, &modelHeaders[idx]); } @@ -600,6 +606,5 @@ const char * restoreModel(uint8_t idx, char *model_name) bool storageReadRadioSettings(bool checks) { - if (!sdMounted()) sdInit(); return loadRadioSettingsYaml(checks) == nullptr; } diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index d10c244db10..a00e2ffd715 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -22,6 +22,7 @@ #include "opentx.h" #include "timers_driver.h" #include "tasks/mixer_task.h" +#include "logs.h" #if defined(USBJ_EX) #include "usb_joystick.h" @@ -242,9 +243,7 @@ if(g_model.rssiSource) { pulsesStart(); } -#if defined(SDCARD) referenceModelAudioFiles(); -#endif #if defined(COLORLCD) loadCustomScreens(); 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 35b47e53f61..a756869d0a3 100644 --- a/radio/src/storage/yaml/yaml_datastructs_funcs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_funcs.cpp @@ -36,7 +36,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..dd7e6fda55b --- /dev/null +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -0,0 +1,992 @@ +// generated by generate_yaml.py + +// +// Enums first +// + +const struct YamlIdStr enum_BacklightMode[] = { + { e_backlight_mode_off, "backlight_mode_off" }, + { e_backlight_mode_keys, "backlight_mode_keys" }, + { e_backlight_mode_sticks, "backlight_mode_sticks" }, + { e_backlight_mode_all, "backlight_mode_all" }, + { e_backlight_mode_on, "backlight_mode_on" }, + { 0, NULL } +}; +const struct YamlIdStr enum_AntennaModes[] = { + { ANTENNA_MODE_INTERNAL, "MODE_INTERNAL" }, + { ANTENNA_MODE_ASK, "MODE_ASK" }, + { ANTENNA_MODE_PER_MODEL, "MODE_PER_MODEL" }, + { ANTENNA_MODE_EXTERNAL, "MODE_EXTERNAL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModuleType[] = { + { MODULE_TYPE_NONE, "TYPE_NONE" }, + { MODULE_TYPE_PPM, "TYPE_PPM" }, + { MODULE_TYPE_XJT_PXX1, "TYPE_XJT_PXX1" }, + { MODULE_TYPE_ISRM_PXX2, "TYPE_ISRM_PXX2" }, + { MODULE_TYPE_DSM2, "TYPE_DSM2" }, + { MODULE_TYPE_CROSSFIRE, "TYPE_CROSSFIRE" }, + { MODULE_TYPE_MULTIMODULE, "TYPE_MULTIMODULE" }, + { MODULE_TYPE_R9M_PXX1, "TYPE_R9M_PXX1" }, + { MODULE_TYPE_R9M_PXX2, "TYPE_R9M_PXX2" }, + { MODULE_TYPE_R9M_LITE_PXX1, "TYPE_R9M_LITE_PXX1" }, + { MODULE_TYPE_R9M_LITE_PXX2, "TYPE_R9M_LITE_PXX2" }, + { MODULE_TYPE_GHOST, "TYPE_GHOST" }, + { MODULE_TYPE_R9M_LITE_PRO_PXX2, "TYPE_R9M_LITE_PRO_PXX2" }, + { MODULE_TYPE_SBUS, "TYPE_SBUS" }, + { MODULE_TYPE_XJT_LITE_PXX2, "TYPE_XJT_LITE_PXX2" }, + { MODULE_TYPE_FLYSKY, "TYPE_FLYSKY" }, + { 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_RESERVE4, "RESERVE4" }, + { FUNC_PLAY_SCRIPT, "PLAY_SCRIPT" }, + { FUNC_RESERVE5, "RESERVE5" }, + { 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" }, + { 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_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_Rud, "Rud" }, + { MIXSRC_Ele, "Ele" }, + { MIXSRC_Thr, "Thr" }, + { MIXSRC_Ail, "Ail" }, + { MIXSRC_POT1, "POT1" }, + { MIXSRC_POT2, "POT2" }, + { MIXSRC_POT3, "POT3" }, + { MIXSRC_LS, "LS" }, + { MIXSRC_RS, "RS" }, + { MIXSRC_MAX, "MAX" }, + { MIXSRC_CYC1, "CYC1" }, + { MIXSRC_CYC2, "CYC2" }, + { MIXSRC_CYC3, "CYC3" }, + { 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_SA, "SA" }, + { MIXSRC_SB, "SB" }, + { MIXSRC_SC, "SC" }, + { MIXSRC_SD, "SD" }, + { MIXSRC_SE, "SE" }, + { MIXSRC_SF, "SF" }, + { MIXSRC_SG, "SG" }, + { MIXSRC_SH, "SH" }, + { MIXSRC_SW1, "SW1" }, + { MIXSRC_CH1, "CH1" }, + { MIXSRC_CH2, "CH2" }, + { MIXSRC_CH3, "CH3" }, + { MIXSRC_CH4, "CH4" }, + { MIXSRC_CH5, "CH5" }, + { MIXSRC_CH6, "CH6" }, + { MIXSRC_CH7, "CH7" }, + { MIXSRC_CH8, "CH8" }, + { MIXSRC_CH9, "CH9" }, + { MIXSRC_CH10, "CH10" }, + { MIXSRC_CH11, "CH11" }, + { MIXSRC_CH12, "CH12" }, + { MIXSRC_CH13, "CH13" }, + { MIXSRC_CH14, "CH14" }, + { MIXSRC_CH15, "CH15" }, + { MIXSRC_CH16, "CH16" }, + { MIXSRC_GVAR1, "GVAR1" }, + { MIXSRC_TX_VOLTAGE, "TX_VOLTAGE" }, + { MIXSRC_TX_TIME, "TX_TIME" }, + { MIXSRC_TX_GPS, "TX_GPS" }, + { MIXSRC_TIMER1, "TIMER1" }, + { MIXSRC_TIMER2, "TIMER2" }, + { MIXSRC_TIMER3, "TIMER3" }, + { 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_RANGE, "FUNC_RANGE" }, + { 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_SA0, "SA0" }, + { SWSRC_SA1, "SA1" }, + { SWSRC_SA2, "SA2" }, + { SWSRC_SB0, "SB0" }, + { SWSRC_SB1, "SB1" }, + { SWSRC_SB2, "SB2" }, + { SWSRC_SC0, "SC0" }, + { SWSRC_SC1, "SC1" }, + { SWSRC_SC2, "SC2" }, + { SWSRC_SD0, "SD0" }, + { SWSRC_SD1, "SD1" }, + { SWSRC_SD2, "SD2" }, + { SWSRC_SE0, "SE0" }, + { SWSRC_SE1, "SE1" }, + { SWSRC_SE2, "SE2" }, + { SWSRC_SF0, "SF0" }, + { SWSRC_SF1, "SF1" }, + { SWSRC_SF2, "SF2" }, + { SWSRC_SG0, "SG0" }, + { SWSRC_SG1, "SG1" }, + { SWSRC_SG2, "SG2" }, + { SWSRC_SH0, "SH0" }, + { SWSRC_SH1, "SH1" }, + { SWSRC_SH2, "SH2" }, + { SWSRC_TrimRudLeft, "TrimRudLeft" }, + { SWSRC_TrimRudRight, "TrimRudRight" }, + { SWSRC_TrimEleDown, "TrimEleDown" }, + { SWSRC_TrimEleUp, "TrimEleUp" }, + { SWSRC_TrimThrDown, "TrimThrDown" }, + { SWSRC_TrimThrUp, "TrimThrUp" }, + { SWSRC_TrimAilLeft, "TrimAilLeft" }, + { SWSRC_TrimAilRight, "TrimAilRight" }, + { SWSRC_TrimT5Down, "TrimT5Down" }, + { SWSRC_TrimT5Up, "TrimT5Up" }, + { SWSRC_TrimT6Down, "TrimT6Down" }, + { SWSRC_TrimT6Up, "TrimT6Up" }, + { SWSRC_TrimT7Down, "TrimT7Down" }, + { SWSRC_TrimT7Up, "TrimT7Up" }, + { SWSRC_TrimT8Down, "TrimT8Down" }, + { SWSRC_TrimT8Up, "TrimT8Up" }, + { SWSRC_SW1, "SW1" }, + { SWSRC_SW2, "SW2" }, + { SWSRC_ON, "ON" }, + { SWSRC_ONE, "ONE" }, + { SWSRC_TELEMETRY_STREAMING, "TELEMETRY_STREAMING" }, + { SWSRC_RADIO_ACTIVITY, "RADIO_ACTIVITY" }, + { SWSRC_OFF, "OFF" }, + { 0, NULL } +}; +const struct YamlIdStr enum_PotsWarnMode[] = { + { POTS_WARN_OFF, "WARN_OFF" }, + { POTS_WARN_MANUAL, "WARN_MANUAL" }, + { POTS_WARN_AUTO, "WARN_AUTO" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModelOverridableEnable[] = { + { OVERRIDE_GLOBAL, "GLOBAL" }, + { OVERRIDE_OFF, "OFF" }, + { OVERRIDE_ON, "ON" }, + { 0, NULL } +}; +const struct YamlIdStr enum_FailsafeModes[] = { + { FAILSAFE_NOT_SET, "NOT_SET" }, + { FAILSAFE_HOLD, "HOLD" }, + { FAILSAFE_CUSTOM, "CUSTOM" }, + { FAILSAFE_NOPULSES, "NOPULSES" }, + { FAILSAFE_RECEIVER, "RECEIVER" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorFormula[] = { + { TELEM_FORMULA_ADD, "FORMULA_ADD" }, + { TELEM_FORMULA_AVERAGE, "FORMULA_AVERAGE" }, + { TELEM_FORMULA_MIN, "FORMULA_MIN" }, + { TELEM_FORMULA_MAX, "FORMULA_MAX" }, + { TELEM_FORMULA_MULTIPLY, "FORMULA_MULTIPLY" }, + { TELEM_FORMULA_TOTALIZE, "FORMULA_TOTALIZE" }, + { TELEM_FORMULA_CELL, "FORMULA_CELL" }, + { TELEM_FORMULA_CONSUMPTION, "FORMULA_CONSUMPTION" }, + { TELEM_FORMULA_DIST, "FORMULA_DIST" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorType[] = { + { TELEM_TYPE_CUSTOM, "TYPE_CUSTOM" }, + { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; + +// +// Structs last +// + +static const struct YamlNode struct_CalibData[] = { + YAML_IDX_CUST("calib",r_calib,w_calib), + YAML_SIGNED( "mid", 16 ), + YAML_SIGNED( "spanNeg", 16 ), + YAML_SIGNED( "spanPos", 16 ), + YAML_END +}; +static const struct YamlNode struct_signed_16[] = { + YAML_IDX, + YAML_SIGNED( "val", 16 ), + YAML_END +}; +static const struct YamlNode struct_TrainerMix[] = { + YAML_IDX, + YAML_UNSIGNED( "srcChn", 6 ), + YAML_ENUM("mode", 2, enum_TrainerMultiplex), + 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", 9, r_swtchSrc, w_swtchSrc ), + YAML_ENUM("func", 7, enum_Functions), + YAML_CUSTOM("def",r_customFn,w_customFn), + YAML_PADDING( 48 ), + YAML_PADDING( 8 ), + YAML_END +}; +static const struct YamlNode struct_string_24[] = { + YAML_IDX, + YAML_STRING("val", 3), + 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_OpenTxTheme__PersistentData[] = { + YAML_ARRAY("options", 96, 5, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_RadioData[] = { + YAML_UNSIGNED( "manuallyEdited", 1 ), + YAML_PADDING( 7 ), + YAML_CUSTOM("semver",nullptr,w_semver), + YAML_CUSTOM("board",nullptr,w_board), + YAML_ARRAY("calib", 48, 9, 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_PADDING( 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_PADDING( 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, 4, struct_sticksConfig, stick_name_valid), + YAML_ARRAY("switchConfig", 2, 16, struct_switchConfig, nullptr), + YAML_ARRAY("potsConfig", 2, 8, struct_potConfig, nullptr), + YAML_ARRAY("slidersConfig", 1, 8, struct_sliderConfig, nullptr), + YAML_PADDING( 192 ), + YAML_PADDING( 216 ), + YAML_STRING("currModelFilename", 17), + YAML_UNSIGNED( "modelQuickSelect", 1 ), + YAML_UNSIGNED( "blOffBright", 7 ), + YAML_STRING("bluetoothName", 10), + YAML_STRING("themeName", 8), + YAML_STRUCT("themeData", 480, struct_OpenTxTheme__PersistentData, NULL), + YAML_STRING("ownerRegistrationID", 8), + YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), + YAML_UNSIGNED( "rotEncMode", 2 ), + YAML_SIGNED( "uartSampleMode", 2 ), + YAML_UNSIGNED( "stickDeadZone", 3 ), + YAML_PADDING( 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", 14, in_read_weight, in_write_weight ), + YAML_SIGNED_CUST( "swtch", 9, 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", 9, 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_PADDING( 1 ), + 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", 9, r_swtchSrc, w_swtchSrc ), + YAML_PADDING( 1 ), + YAML_PADDING( 2 ), + 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", 9, r_swtchSrc, w_swtchSrc ), + YAML_PADDING( 7 ), + 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_ENUM("type", 8, enum_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 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, 4, 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_PADDING( 5 ), + 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( 32 ), + 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, 5, struct_signed_8, NULL), + YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), + YAML_ARRAY("screenData", 6816, 10, struct_CustomScreenData, NULL), + YAML_STRUCT("topbarData", 2400, 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/strhelpers.h b/radio/src/strhelpers.h index 161da6e5d07..e21e0de7650 100644 --- a/radio/src/strhelpers.h +++ b/radio/src/strhelpers.h @@ -24,9 +24,8 @@ #include "definitions.h" #include "opentx_types.h" - -#include - +#include "strings.h" +#include #include #include @@ -174,6 +173,12 @@ int strncasecmp(char (&s1)[L1], const char *const s2) std::string getValueWithUnit(int val, uint8_t unit, LcdFlags flags); std::string getGVarValue(uint8_t gvar, gvar_t value, LcdFlags flags); +// comparison, not case sensitive. +inline bool compare_nocase(const std::string & first, const std::string & second) +{ + return strcasecmp(first.c_str(), second.c_str()) < 0; +} + // Timezone handling extern int8_t minTimezone(); extern int8_t maxTimezone(); diff --git a/radio/src/syscalls.c b/radio/src/syscalls.c index d57597bbac8..a81f75f3b2a 100644 --- a/radio/src/syscalls.c +++ b/radio/src/syscalls.c @@ -1,7 +1,8 @@ /* - * Copyright (C) OpenTX + * 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 @@ -32,17 +33,20 @@ extern int _heap_end; unsigned char * heap = (unsigned char *)&_end; +static int set_errno(int errval) +{ + errno = errval; + return -1; +} + extern caddr_t _sbrk(int nbytes) { - if (heap + nbytes < (unsigned char *)&_heap_end) { - unsigned char * prev_heap = heap; - heap += nbytes; - return (caddr_t)prev_heap; - } - else { - errno = ENOMEM; - return ((void *)-1); - } + if (heap + nbytes >= (unsigned char *)&_heap_end) + return (void*)set_errno(ENOMEM); + + unsigned char * prev_heap = heap; + heap += nbytes; + return (caddr_t)prev_heap; } #if defined(THREADSAFE_MALLOC) && !defined(BOOT) @@ -73,63 +77,67 @@ extern int _gettimeofday(void *p1, void *p2) extern int _link(char *old, char *nw) { - return -1; + (void)old; + (void)nw; + return set_errno(ENOSYS); } extern int _unlink(const char *path) { - return -1; + (void)path; + return set_errno(ENOSYS); } -extern int _open(const char *name, int flags, int mode) +extern int _isatty(int file) { - return -1; + (void)file; + return 0; } -extern int _close(int file) +extern int _getpid() { return -1; } +#endif -extern int _fstat(int file, struct stat * st) +extern void _exit(int status) { - st->st_mode = S_IFCHR; - return 0; + TRACE("_exit(%d)", status); + for (;;); } -extern int _isatty(int file) +extern void _kill(int pid, int sig) { - return 1; + return; } -extern int _lseek(int file, int ptr, int dir) +int _open(const char *name, int flags, ...) { return 0; } -extern int _read(int file, char *ptr, int len) +int _close(int fd) { return 0; } -extern int _write(int file, char *ptr, int len) +int _fstat(int fd, struct stat * st) { return 0; } -extern int _getpid() +int _lseek(int fd, int ptr, int dir) { - return -1; + return 0; } -#endif -extern void _exit(int status) +int _read(int fd, char *ptr, int len) { - TRACE("_exit(%d)", status); - for (;;); + return 0; } -extern void _kill(int pid, int sig) +int _write(int fd, char *ptr, int len) { - return; + return 0; } + diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 449bbcd2789..8bc906b6431 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -236,7 +236,11 @@ endif() if(AFHDS2 AND INTERNAL_MODULE_AFHDS2A) add_definitions(-DAFHDS2) - set(SRC ${SRC} telemetry/flysky_nv14.cpp) + if(PCB STREQUAL PL18) + set(SRC ${SRC} telemetry/flysky_pl18.cpp) + else() + set(SRC ${SRC} telemetry/flysky_nv14.cpp) + endif() set(PULSES_SRC ${PULSES_SRC} flysky.cpp) endif() diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 77df75fa3f9..69fd467d1b4 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -44,9 +44,31 @@ set(BOOTLOADER_SRC ../stm32_dma.cpp init.c boot.cpp - bin_files.cpp ) +if(SPI_FLASH) +set(BOOTLOADER_SRC + ${BOOTLOADER_SRC} + ../../../../../targets/common/arm/stm32/flash_spi_driver.cpp + ../../../../../frftl.cpp + ../../../../../crc.cpp + ) +endif() + +if(SDCARD OR (SPI_FLASH AND NOT LITTLEFS)) + if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14 OR PCB STREQUAL PL18) + set(BOOTLOADER_SRC + ${BOOTLOADER_SRC} + ../../../../../targets/common/arm/stm32/diskio_sdio_spiflash.cpp + ) + else() + set(BOOTLOADER_SRC + ${BOOTLOADER_SRC} + ../../../../../targets/${TARGET_DIR}/diskio.cpp + ) + endif() +endif() + if(ROTARY_ENCODER) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} @@ -68,8 +90,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 @@ -78,10 +99,11 @@ 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/sdio_sd.cpp - ../../../../../targets/common/arm/stm32/diskio_sdio.cpp ../../../../../targets/common/arm/stm32/rtc_driver.cpp ../../../../../targets/${TARGET_DIR}/sdram_driver.c ../../../../../targets/${TARGET_DIR}/haptic_driver.cpp + ../../../../../VirtualFS.cpp + bin_files_vfs.cpp ) # Add LVGL sources @@ -120,7 +142,7 @@ else() ../../../../../gui/common/stdlcd/fonts.cpp ../../../../../gui/common/stdlcd/utf8.cpp ../../../../../targets/${TARGET_DIR}/eeprom_driver.cpp - ../../../../../targets/${TARGET_DIR}/diskio.cpp + bin_files.cpp ) remove_definitions(-DDEBUG) endif() diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp b/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp index f909d2f8b2f..32b483fe40d 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp @@ -19,15 +19,12 @@ * GNU General Public License for more details. */ -#include -#include - #include "boot.h" #include "board.h" -#include "sdcard.h" #include "bin_files.h" #include "fw_version.h" #include "strhelpers.h" +#include "VirtualFS.h" // 'private' static DIR dir; @@ -49,10 +46,7 @@ void sdInit(void) FRESULT openBinDir(MemoryType mt) { - FRESULT fr = f_chdir(getBinaryPath(mt)); - if (fr != FR_OK) return fr; - - return f_opendir(&dir, "."); + return f_opendir(&dir, getBinaryPath(mt)); } static FRESULT findNextBinFile(FILINFO* fno) @@ -137,7 +131,7 @@ FRESULT openBinFile(MemoryType mt, unsigned int index) return fr; // skip bootloader in firmware - if (mt == MEM_FLASH && + if ((mt == MEM_SDCARD || mt == MEM_INTERNAL) && ((fr = f_lseek(&FlashFile, BOOTLOADER_SIZE)) != FR_OK)) return fr; diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files.h b/radio/src/targets/common/arm/stm32/bootloader/bin_files.h index 9681059be01..0d29e1b94d2 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/bin_files.h +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files.h @@ -22,14 +22,12 @@ #ifndef _bin_files_h_ #define _bin_files_h_ -#include -#include "sdcard.h" -#if defined(COLORLCD) -#include "bitmapbuffer.h" -#endif +#include "FatFs/ff.h" +#include "VirtualFS.h" enum MemoryType { - MEM_FLASH, + MEM_INTERNAL, + MEM_SDCARD, MEM_EEPROM }; diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.cpp b/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.cpp new file mode 100644 index 00000000000..93ff2d995f2 --- /dev/null +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "boot.h" +#include "board.h" +#include "VirtualFS.h" +#include "bin_files_vfs.h" +#include "fw_version.h" +#include "strhelpers.h" + +// 'private' +static VfsDir dir; +static VfsFile FlashFile; + +// 'public' variables +BinFileInfo binFiles[MAX_BIN_FILES]; +uint8_t Block_buffer[BLOCK_LEN]; +size_t BlockCount; + +const char *getBinaryPath(MemoryType mt) +{ + switch(mt) + { + case MEM_EEPROM: return EEPROMS_PATH; +#if defined(SPI_FLASH) && defined(SDCARD) + case MEM_INTERNAL: return INTERNAL_ST_FIRMWARES_PATH; + case MEM_SDCARD: return SDCARD_FIRMWARES_PATH; +#else + case MEM_INTERNAL: + case MEM_SDCARD: return FIRMWARES_PATH; +#endif + } + return ""; +} + + +VfsError openBinDir(MemoryType mt) +{ + VirtualFS& vfs = VirtualFS::instance(); + VfsError fr = vfs.changeDirectory(getBinaryPath(mt)); + if (fr != VfsError::OK) return fr; + + return vfs.openDirectory(dir, "."); +} + +static VfsError findNextBinFile(VfsFileInfo* fno) +{ + VfsError fr; + + do { + fr = dir.read(*fno); + + if (fr != VfsError::OK || strlen(fno->getName()) == 0) + break; + + int32_t len = strlen(fno->getName()) - 4; + if (len < 0) + continue; + + const char* fname = fno->getName(); + if (fname[len] != '.') + continue; + + if ((fname[len + 1] != 'b') && (fname[len + 1] != 'B')) + continue; + + if ((fname[len + 2] != 'i') && (fname[len + 2] != 'I')) + continue; + + if ((fname[len + 3] != 'n') && (fname[len + 3] != 'N')) + continue; + + // match! + break; + + } while (1); + + return fr; +} + +unsigned int fetchBinFiles(unsigned int index) +{ + VfsFileInfo file_info; + + // rewind + if (dir.rewind() != VfsError::OK) + return 0; + + // skip 'index' .bin files + for (unsigned int i = 0; i <= index; i++) { + + if (findNextBinFile(&file_info) != VfsError::OK /*|| file_info.fname[0] == 0*/) + return 0; + } + + strAppend(binFiles[0].name, file_info.getName()); + binFiles[0].size = file_info.getSize(); + + unsigned int i = 1; + for (; i < MAX_NAMES_ON_SCREEN+1; i++) { + + if (findNextBinFile(&file_info) != VfsError::OK || strlen(file_info.getName()) == 0) + return i; + + strAppend(binFiles[i].name, file_info.getName()); + binFiles[i].size = file_info.getSize(); + } + + return i; +} + +VfsError openBinFile(MemoryType mt, unsigned int index) +{ + TCHAR full_path[FF_MAX_LFN+1]; + VfsError fr; + + // build full_path: [bin path]/[filename] + char* s = strAppend(full_path, getBinaryPath(mt)); + s = strAppend(s, "/"); + strAppend(s, binFiles[index].name); + + BlockCount = 0; + + // open the file + if ((fr = VirtualFS::instance().openFile(FlashFile, full_path, VfsOpenFlags::READ)) != VfsError::OK) + return fr; + + // skip bootloader in firmware + if (mt != MEM_EEPROM && + ((fr = FlashFile.lseek(BOOTLOADER_SIZE)) != VfsError::OK)) + return fr; + + // ... and fetch BLOCK_LEN bytes + fr = FlashFile.read(Block_buffer, BLOCK_LEN, BlockCount); + + if (BlockCount == BLOCK_LEN) + return fr; + + return VfsError::INVAL; +} + +void extractFirmwareVersion(VersionTag* tag) +{ + const char * vers = getFirmwareVersion((const char *)Block_buffer); + if (!vers || (vers[0] == 'n' && vers[1] == 'o')) { // "no version found" + memcpy(tag->flavour, "unknown", sizeof("unknown")); + tag->version = "unknown"; + return; + } + + tag->fork = vers; + + // skip 'edgetx-' / 'opentx-' + vers += sizeof("edgetx-") - 1; + + char* fl = tag->flavour; + while(*vers != '-') + *(fl++) = *(vers++); + + // skip '-' + tag->version = ++vers; +} + +VfsError readBinFile() +{ + BlockCount = 0; + return FlashFile.read(Block_buffer, sizeof(Block_buffer), BlockCount); +} + +VfsError closeBinFile() +{ + return FlashFile.close(); +} diff --git a/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.h b/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.h new file mode 100644 index 00000000000..0b1e8c05229 --- /dev/null +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.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 _bin_files_h_ +#define _bin_files_h_ + +#include "VirtualFS.h" + +enum MemoryType { + MEM_INTERNAL, + MEM_SDCARD, + MEM_EEPROM +}; + +const char *getBinaryPath(MemoryType mt); + +#if LCD_H == 480 +#define MAX_NAMES_ON_SCREEN 13 +#else +#define MAX_NAMES_ON_SCREEN 6 +#endif + +#define MAX_BIN_FILES (MAX_NAMES_ON_SCREEN+1) + +// Size of the block read when checking / writing BIN files +#define BLOCK_LEN 4096 + +// File info struct while browsing files on SD card +struct BinFileInfo { + TCHAR name[FF_MAX_LFN + 1]; + unsigned int size; +}; + +// File info storage while browsing files on SD card +extern BinFileInfo binFiles[MAX_BIN_FILES]; + +// Block buffer used when checking / writing BIN files +extern uint8_t Block_buffer[BLOCK_LEN]; + +// Bytes read into the Block_buffer +extern size_t BlockCount; + +// Open directory for EEPROM / firmware files +VfsError openBinDir(MemoryType mt); + +// Fetch file names and sizes into binFiles, +// starting at the provided index. +// Only files ending with ".bin" (case-insensitive) +// will be considered. +unsigned int fetchBinFiles(unsigned int index); + +// Open file indexed in binFiles and read the first BLOCK_LEN bytes +// Bootloader is skipped in firmware files +VfsError openBinFile(MemoryType mt, unsigned int index); + +struct VersionTag +{ + char flavour[8]; + const char* version; + const char* fork; +}; + +// Can be called right after openBinFile() to extract the version information +// from a firmware file +void extractFirmwareVersion(VersionTag* tag); + +// Read the next BLOCK_LEN bytes into 'Block_buffer' +// Check 'BlockCount' for # of bytes read +VfsError readBinFile(); + +// Close the previously opened file +VfsError closeBinFile(); + +#endif diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index 0839e8d7f4b..8aae189c626 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -55,10 +55,26 @@ #define APP_START_ADDRESS (uint32_t)(FIRMWARE_ADDRESS + BOOTLOADER_SIZE) #if defined(EEPROM) +#if defined(SPI_FLASH) + #define MAIN_MENU_LEN 4 +#else + #define MAIN_MENU_LEN 3 +#endif +#else +#if defined(SPI_FLASH) #define MAIN_MENU_LEN 3 #else #define MAIN_MENU_LEN 2 #endif +#endif + +#if defined(SPI_FLASH) && defined(SDCARD) + #define SEL_STORAGE_MENU_LEN 2 +#endif + +#if defined(SPI_FLASH) + #define SEL_CLEAR_FLASH_STORAGE_MENU_LEN 2 +#endif typedef void (*voidFunction)(void); @@ -147,7 +163,7 @@ uint32_t isValidBufferStart(const uint8_t * buffer) { #if !defined(SIMU) #if defined(EEPROM) - if (memoryType == MEM_FLASH) + if (memoryType != MEM_EEPROM) return isFirmwareStart(buffer); else return isEepromStart(buffer); @@ -228,16 +244,31 @@ void bootloaderInitApp() { RCC_AHB1PeriphClockCmd(PWR_RCC_AHB1Periph | LCD_RCC_AHB1Periph | BACKLIGHT_RCC_AHB1Periph | - KEYS_BACKLIGHT_RCC_AHB1Periph | SD_RCC_AHB1Periph, + KEYS_BACKLIGHT_RCC_AHB1Periph | +#if defined(FLASH_RCC_AHB1Periph) + FLASH_RCC_AHB1Periph | +#endif + SD_RCC_AHB1Periph, ENABLE); - RCC_APB1PeriphClockCmd(ROTARY_ENCODER_RCC_APB1Periph | LCD_RCC_APB1Periph | - BACKLIGHT_RCC_APB1Periph | - INTERRUPT_xMS_RCC_APB1Periph | SD_RCC_APB1Periph, - ENABLE); + RCC_APB1PeriphClockCmd( + ROTARY_ENCODER_RCC_APB1Periph | + LCD_RCC_APB1Periph | + BACKLIGHT_RCC_APB1Periph | + INTERRUPT_xMS_RCC_APB1Periph | +#if defined(FLASH_RCC_APB1Periph) + FLASH_RCC_APB1Periph | +#endif + SD_RCC_APB1Periph, + ENABLE); RCC_APB2PeriphClockCmd( - LCD_RCC_APB2Periph | BACKLIGHT_RCC_APB2Periph | RCC_APB2Periph_SYSCFG, + LCD_RCC_APB2Periph | + BACKLIGHT_RCC_APB2Periph | +#if defined(FLASH_RCC_APB2Periph) + FLASH_RCC_APB2Periph | +#endif + RCC_APB2Periph_SYSCFG, ENABLE); #if defined(HAVE_BOARD_BOOTLOADER_INIT) @@ -265,9 +296,11 @@ void bootloaderInitApp() // wait a bit for the inputs to stabilize... if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) { +#if !defined(PCBHORUS) && !defined(PCBNV14) && !defined(PCBPL18) for (uint32_t i = 0; i < 150000; i++) { __ASM volatile ("nop"); } +#endif } #if (defined(RADIO_T8) || defined(RADIO_COMMANDO8)) && !defined(RADIOMASTER_RELEASE) @@ -313,11 +346,14 @@ void bootloaderInitApp() init10msTimer(); - // SD card detect pin - sdInit(); +#if defined(SPI_FLASH) + flashInit(); +#endif usbInit(); } +void flashSpiEraseAll(); + int main() #else // SIMU void bootloaderInitApp() {} @@ -385,7 +421,6 @@ int bootloaderMain() if (state == ST_START) { bootloaderDrawScreen(state, vpos); - if (IS_NEXT_EVENT(event)) { if (vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN) - 1) { vpos++; } continue; @@ -397,15 +432,30 @@ int bootloaderMain() else if (event == EVT_KEY_BREAK(KEY_ENTER)) { switch (vpos) { case 0: - memoryType = MEM_FLASH; +#if defined(SPI_FLASH) && defined(SDCARD) + state = ST_SELECT_STORAGE; +#else + memoryType = MEM_INTERNAL; state = ST_DIR_CHECK; +#endif break; #if defined(EEPROM) case 1: memoryType = MEM_EEPROM; state = ST_DIR_CHECK; break; +#if defined(SPI_FLASH) + case 2: + state = ST_CLEAR_FLASH_CHECK; + break; +#endif +#elif defined(SPI_FLASH) + case 1: + state = ST_CLEAR_FLASH_CHECK; + vpos = 1; + break; #endif + default: if(vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN-1)) { @@ -421,6 +471,43 @@ int bootloaderMain() continue; } } +#if defined(SPI_FLASH) && defined(SDCARD) + else if (state == ST_SELECT_STORAGE) { + + bootloaderDrawScreen(state, vpos); + if (event == EVT_KEY_FIRST(KEY_DOWN)) { + if (vpos < SEL_STORAGE_MENU_LEN - 1) { vpos++; } + continue; + } + else if (event == EVT_KEY_FIRST(KEY_UP)) { + if (vpos > 0) { vpos--; } + continue; + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + switch (vpos) { + case 0: + memoryType = MEM_INTERNAL; + state = ST_DIR_CHECK; + break; + case 1: + memoryType = MEM_SDCARD; + state = ST_DIR_CHECK; + break; + default: + break; + } + + // next loop + continue; + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + state = ST_START; + vpos = 0; + continue; + } + + } +#endif else if (state == ST_DIR_CHECK) { fr = openBinDir(memoryType); @@ -431,7 +518,7 @@ int bootloaderMain() continue; } else { - bootloaderDrawScreen(state, fr); + bootloaderDrawScreen(state, (int)fr); if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { vpos = 0; @@ -446,7 +533,6 @@ int bootloaderMain() if (nameCount < limit) { limit = nameCount; } - if (IS_NEXT_EVENT(event)) { if (vpos < limit - 1) { vpos += 1; @@ -459,7 +545,7 @@ int bootloaderMain() } } else if (IS_PREVIOUS_EVENT(event)) { - if (vpos > 0) { + if (vpos > 0) { vpos -= 1; } else { @@ -483,7 +569,11 @@ int bootloaderMain() continue; } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { +#if defined(SPI_FLASH) && defined(SDCARD) + state = ST_SELECT_STORAGE; +#else state = ST_START; +#endif vpos = 0; continue; } @@ -499,7 +589,7 @@ int bootloaderMain() else if (result == 1) { // confirmed - if (memoryType == MEM_FLASH) { + if (memoryType != MEM_EEPROM) { firmwareSize = binFiles[vpos].size - BOOTLOADER_SIZE; firmwareAddress = FIRMWARE_ADDRESS + BOOTLOADER_SIZE; firmwareWritten = 0; @@ -515,13 +605,13 @@ int bootloaderMain() } else if (state == ST_FLASHING) { // commit to flashing - if (!unlocked && (memoryType == MEM_FLASH)) { + if (!unlocked && (memoryType != MEM_EEPROM)) { unlocked = 1; unlockFlash(); } int progress = 0; - if (memoryType == MEM_FLASH) { + if (memoryType != MEM_EEPROM) { flashWriteBlock(); firmwareWritten += sizeof(Block_buffer); progress = (100 * firmwareWritten) / firmwareSize; @@ -540,13 +630,42 @@ int bootloaderMain() if (BlockCount == 0) { state = ST_FLASH_DONE; // EOF } - else if (memoryType == MEM_FLASH && firmwareWritten >= FLASHSIZE - BOOTLOADER_SIZE) { + else if (memoryType != MEM_EEPROM && firmwareWritten >= FLASHSIZE - BOOTLOADER_SIZE) { state = ST_FLASH_DONE; // Backstop } #if defined(EEPROM) 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)) @@ -557,6 +676,7 @@ int bootloaderMain() } } + if (state == ST_FLASH_DONE) { if (unlocked) { lockFlash(); @@ -613,6 +733,6 @@ int bootloaderMain() return 0; } -#if !defined(SIMU) && (defined(PCBHORUS) || defined(PCBNV14)) +#if !defined(SIMU) 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..dccf2a210c0 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.h +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.h @@ -46,6 +46,7 @@ enum BootloaderState { ST_START, ST_FLASH_MENU, + ST_SELECT_STORAGE, ST_DIR_CHECK, ST_OPEN_DIR, ST_FILE_LIST, @@ -54,6 +55,8 @@ enum BootloaderState { ST_FLASH_DONE, ST_RESTORE_MENU, ST_USB, + ST_CLEAR_FLASH_CHECK, + ST_CLEAR_FLASH, ST_RADIO_MENU, ST_REBOOT, }; diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp similarity index 65% rename from radio/src/targets/common/arm/stm32/diskio_sdio.cpp rename to radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index a156849a13e..e87888486f5 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -43,16 +43,134 @@ // Disk status extern volatile uint32_t WriteStatus; extern volatile uint32_t ReadStatus; +/*-----------------------------------------------------------------------*/ +/* Lock / unlock functions */ +/*-----------------------------------------------------------------------*/ +#if !defined(BOOT) +static RTOS_MUTEX_HANDLE ioMutex; +static bool initialized = false; +uint32_t ioMutexReq = 0, ioMutexRel = 0; +int ff_cre_syncobj (BYTE vol, FF_SYNC_t *mutex) +{ + if(!initialized) + { + RTOS_CREATE_MUTEX(ioMutex); + initialized = true; + } + *mutex = ioMutex; + return 1; +} +int ff_req_grant (FF_SYNC_t mutex) +{ + ioMutexReq += 1; + RTOS_LOCK_MUTEX(mutex); + return 1; +} + +void ff_rel_grant (FF_SYNC_t mutex) +{ + ioMutexRel += 1; + RTOS_UNLOCK_MUTEX(mutex); +} + +int ff_del_syncobj (FF_SYNC_t mutex) +{ + return 1; +} +#endif +#if defined(SPI_FLASH) +#include "frftl.h" + +size_t flashSpiRead(size_t address, uint8_t* data, size_t size); +size_t flashSpiWrite(size_t address, const uint8_t* data, size_t size); +uint16_t flashSpiGetPageSize(); +size_t flashSpiGetSize(); +//uint32_t flashSpiGetBlockCount(); + +int flashSpiErase(size_t address); +bool flashSpiIsErased(size_t address); +int flashSpiBlockErase(size_t address); +void flashSpiEraseAll(); + +void flashSpiSync(); + +static FrFTL* frftl = nullptr; +extern "C" { +static bool flashRead(uint32_t addr, uint8_t* buf, uint32_t len) +{ + flashSpiRead(addr, buf, len); + return true; +} + +static bool flashWrite(uint32_t addr, const uint8_t *buf, uint32_t len) +{ + size_t pageSize = flashSpiGetPageSize(); + if(len%pageSize != 0) + return false; + while(len > 0) + { + flashSpiWrite(addr, buf, pageSize); + len -= pageSize; + buf += pageSize; + addr += pageSize; + } + if(len != 0) + return false; + return true; +} + +static bool flashErase(uint32_t addr) +{ + flashSpiErase(addr); + return true; +} + +static bool isFlashErased(uint32_t addr) +{ + return flashSpiIsErased(addr); +} + +void flushFTL() +{ + ftlSync(frftl); +} + +} +#endif /*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ DSTATUS disk_initialize ( - BYTE drv /* Physical drive nmuber (0..) */ + BYTE drv /* Physical drive number (0..) */ ) { DSTATUS stat = 0; - +#if defined(SPI_FLASH) + if(drv == 1) + { + if(frftl != nullptr) + return stat; +// if(!tjftl_detect(flashRead, nullptr)) +// flashSpiEraseAll(); + + int flashSize = flashSpiGetSize(); + int flashSizeMB = flashSize / 1024 / 1024; + // int blockCount = flashSpiGetBlockCount(); + + // tjftl requires 1/64 overhead and some blocks for GC (at least 10) + // However, if give it more GC blocks, it can help to reduce wearing level and improve performance + // Simulation is done to give a balanace between wearing and overhead +// int overheadBlockCount = blockCount / 64 + (flashSizeMB >= 32 ? flashSizeMB * 2 : 32); + +// tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize - overheadBlockCount * 32768)/512, 0); + frftl = ftlInit(flashRead, flashWrite, flashErase, isFlashErased, flashSizeMB); + + if(frftl == nullptr) + stat |= STA_NOINIT; + return stat; + } +#endif /* Supports only single drive */ if (drv) { @@ -60,12 +178,17 @@ DSTATUS disk_initialize ( } /*-------------------------- SD Init ----------------------------- */ + static bool initialized = false; + if(initialized) + return stat; + SD_Error res = SD_Init(); if (res != SD_OK) { TRACE("SD_Init() failed: %d", res); stat |= STA_NOINIT; } + initialized = true; TRACE("SD card info:"); TRACE("type: %u", (uint32_t)(SD_GetCardType())); @@ -87,7 +210,14 @@ DSTATUS disk_status ( ) { DSTATUS stat = 0; - +#if defined(SPI_FLASH) + if(drv == 1) + { + if(frftl == nullptr) + stat |= STA_NODISK; + return stat; + } +#endif if (SD_Detect() != SD_PRESENT) stat |= STA_NODISK; @@ -154,6 +284,27 @@ DRESULT disk_read_dma(BYTE drv, BYTE * buff, DWORD sector, UINT count) DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) { + DRESULT res = RES_OK; +#if defined(SPI_FLASH) + if(drv == 1) + { + if(frftl == nullptr) + { + res = RES_ERROR; + return res; + } + while(count) + { + if(!ftlRead(frftl, sector, (uint8_t*)buff)) + return RES_ERROR; + buff += 512; + sector++; + count --; + } + return res; + } +#endif + // If unaligned, do the single block reads with a scratch buffer. // If aligned and single sector, do a single block read. // If aligned and multiple sectors, try multi block read. @@ -170,7 +321,6 @@ DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) return RES_ERROR; } - DRESULT res = RES_OK; if (count == 0) return res; if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) { @@ -211,7 +361,19 @@ DRESULT __disk_write( ) { DRESULT res = RES_OK; - +#if defined(SPI_FLASH) + if(drv == 1) + { + if(frftl == nullptr) + { + res = RES_ERROR; + return res; + } + if (!ftlWrite(frftl, sector, count, (uint8_t*)buff)) + return RES_ERROR; + return res; + } +#endif // TRACE("disk_write %d %p %10d %d", drv, buff, sector, count); if (SD_Detect() != SD_PRESENT) @@ -282,11 +444,59 @@ DRESULT disk_ioctl ( { DRESULT res; uint32_t tmp; +#if defined(SPI_FLASH) + if(drv == 1) + { + disk_initialize(1); + if(frftl == nullptr) + { + res = RES_ERROR; + return res; + } + res = RES_ERROR; + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + { + *(DWORD*)buff = frftl->usableSectorCount; + res = RES_OK; + break; + } + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ + // TODO verify that this is the correct value + *(DWORD*)buff = 4096; + res = RES_OK; + break; + + case CTRL_SYNC: + if (ftlSync(frftl)) + res = RES_OK; + break; + + case CTRL_TRIM: + if (ftlTrim(frftl, *(DWORD*)buff, 1 + *((DWORD*)buff + 1) - *(DWORD*)buff)) + res = RES_OK; + break; + + default: + res = RES_OK; + break; + } + + return res; + } +#endif if (drv) return RES_PARERR; res = RES_ERROR; + disk_initialize(0); switch (ctrl) { case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ tmp = SD_GetSectorCount(); diff --git a/radio/src/targets/common/arm/stm32/f4/system_stm32f4xx.c b/radio/src/targets/common/arm/stm32/f4/system_stm32f4xx.c index ec73f6d838f..50ef054bffd 100644 --- a/radio/src/targets/common/arm/stm32/f4/system_stm32f4xx.c +++ b/radio/src/targets/common/arm/stm32/f4/system_stm32f4xx.c @@ -228,7 +228,7 @@ static void SetSysClock(void); */ void SystemInit(void) { -#if defined(PCBFLYSKY) +#if defined(PCBNV14) backlightLowInit(); #endif /* FPU settings ------------------------------------------------------------*/ diff --git a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp new file mode 100644 index 00000000000..417b85b8129 --- /dev/null +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "board.h" + +#if !defined(SIMU) + +#define SPI_MODE 0x00 +#define SPI_STATUS 0x01 +#define SPI_BASS 0x02 +#define SPI_CLOCKF 0x03 +#define SPI_DECODE_TIME 0x04 +#define SPI_AUDATA 0x05 +#define SPI_WRAM 0x06 +#define SPI_WRAMADDR 0x07 +#define SPI_HDAT0 0x08 +#define SPI_HDAT1 0x09 +#define SPI_AIADDR 0x0a +#define SPI_VOL 0x0b +#define SPI_AICTRL0 0x0c +#define SPI_AICTRL1 0x0d +#define SPI_AICTRL2 0x0e +#define SPI_AICTRL3 0x0f + +#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 CS_HIGH() do { FLASH_SPI_CS_GPIO->BSRRL = FLASH_SPI_CS_GPIO_PIN; } while (0) +#define CS_LOW() do { FLASH_SPI_CS_GPIO->BSRRH = FLASH_SPI_CS_GPIO_PIN; } while (0) + + +struct SpiFlashDescriptor +{ + uint16_t id; + uint32_t pageSize; + uint32_t sectorSize; + uint32_t blockSize; + uint32_t blockCount; + + uint8_t readStatusCmd; + uint8_t readCmd; + uint8_t writeCmd; + uint8_t writeEnableCmd; + uint8_t eraseSectorCmd; + uint8_t eraseBlockCmd; + uint8_t eraseChipCmd; + bool use4BytesAddress; +}; + +// * RadioMaster/Eachine TX16S, RadioKing TX18S and Jumper T18 use GD25Q127C (16 MByte) +// * FlySky PL18, Paladin EV and NV14 use WinBond W25Q64JV (8 MByte) + +static const SpiFlashDescriptor spiFlashDescriptors[] = +{ + { // MX25L25645G + .id = 0xC218, + .pageSize = 256, + .sectorSize = 4096, + .blockSize = 32768, + .blockCount = 1024, + + .readStatusCmd = 0x05, + .readCmd = 0x13, // 4 bytes address command + .writeCmd = 0x12, // 4 bytes address command + .writeEnableCmd = 0x06, + .eraseSectorCmd = 0x21, // 4 bytes address 4k block erase command + .eraseBlockCmd = 0x5C, // 4 bytes address 32k block erase command + .eraseChipCmd = 0x60, + .use4BytesAddress = true + }, + { // GD25Q127C + .id = 0xC817, + .pageSize = 256, + .sectorSize = 4096, + .blockSize = 32768, + .blockCount = 512, + + .readStatusCmd = 0x05, + .readCmd = 0x03, + .writeCmd = 0x02, + .writeEnableCmd = 0x06, + .eraseSectorCmd = 0x20, // 4k block erase command + .eraseBlockCmd = 0x52, // 32k block erase command + .eraseChipCmd = 0x60, + .use4BytesAddress = false + }, + { // W25Q64JV + .id = 0xEF16, + .pageSize = 256, + .sectorSize = 4096, + .blockSize = 32768, + .blockCount = 256, + + .readStatusCmd = 0x05, + .readCmd = 0x03, + .writeCmd = 0x02, + .writeEnableCmd = 0x06, + .eraseSectorCmd = 0x20, // 4k block erase command + .eraseBlockCmd = 0x52, // 32k block erase command + .eraseChipCmd = 0xC7, + .use4BytesAddress = false + } +}; + +static const SpiFlashDescriptor* flashDescriptor = nullptr; +#if !defined(BOOT) && 0 +static DMA_InitTypeDef dmaTxInfo = {0}; +static DMA_InitTypeDef dmaRxInfo = {0}; + +static RTOS_SEMAPHORE_HANDLE irqSem; +static uint8_t *dmaReadBuf = nullptr; +static uint8_t *dmaWriteBuf = nullptr; +static volatile bool reading = false; +#endif + +void flashSpiInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + SPI_InitTypeDef SPI_InitStructure; + + GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(FLASH_SPI_MISO_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_GPIO_PIN; + GPIO_Init(FLASH_SPI_SCK_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_GPIO_PIN; + GPIO_Init(FLASH_SPI_MOSI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(FLASH_SPI_CS_GPIO, &GPIO_InitStructure); + CS_HIGH(); + + GPIO_PinAFConfig(FLASH_SPI_SCK_GPIO, FLASH_SPI_SCK_GPIO_PinSource, FLASH_SPI_GPIO_AF); + GPIO_PinAFConfig(FLASH_SPI_MISO_GPIO, FLASH_SPI_MISO_GPIO_PinSource, FLASH_SPI_GPIO_AF); + GPIO_PinAFConfig(FLASH_SPI_MOSI_GPIO, FLASH_SPI_MOSI_GPIO_PinSource, FLASH_SPI_GPIO_AF); + + RCC_ClocksTypeDef RCC_Clocks; + RCC_GetClocksFreq(&RCC_Clocks); + + SPI_I2S_DeInit(FLASH_SPI); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_Init(FLASH_SPI, &SPI_InitStructure); + SPI_Cmd(FLASH_SPI, ENABLE); + + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_RXNE); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_TXE); +} + +void flashSpiSetSpeed(uint8_t speed) +{ + FLASH_SPI->CR1 &= 0xFFC7; // Fsck=Fcpu/256 + switch (speed) { + case SPI_SPEED_2: + FLASH_SPI->CR1 |= 0x00 << 3; // Fsck=Fpclk/2=36Mhz + break; + case SPI_SPEED_4: + FLASH_SPI->CR1 |= 0x01 << 3; // Fsck=Fpclk/4=18Mhz + break; + case SPI_SPEED_8: + FLASH_SPI->CR1 |= 0x02 << 3; // Fsck=Fpclk/8=9Mhz + break; + case SPI_SPEED_16: + FLASH_SPI->CR1 |= 0x03 << 3; // Fsck=Fpclk/16=4.5Mhz + break; + case SPI_SPEED_32: + FLASH_SPI->CR1 |= 0x04 << 3; // Fsck=Fpclk/32=2.25Mhz + break; + case SPI_SPEED_64: + FLASH_SPI->CR1 |= 0x05 << 3; // Fsck=Fpclk/16=1.125Mhz + break; + case SPI_SPEED_128: + FLASH_SPI->CR1 |= 0x06 << 3; // Fsck=Fpclk/16=562.5Khz + break; + case SPI_SPEED_256: + FLASH_SPI->CR1 |= 0x07 << 3; // Fsck=Fpclk/16=281.25Khz + break; + } + FLASH_SPI->CR1 |= 0x01 << 6; +} + +uint8_t flashSpiReadWriteByte(uint8_t value) +{ + uint16_t time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(FLASH_SPI, DISABLE); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(FLASH_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(FLASH_SPI, ENABLE); + break; + } + } + SPI_I2S_SendData(FLASH_SPI, value); + + time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(FLASH_SPI, DISABLE); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(FLASH_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(FLASH_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(FLASH_SPI, ENABLE); + break; + } + } + return SPI_I2S_ReceiveData(FLASH_SPI); +} + +void flashSpiSync() +{ + delay_01us(100); + CS_LOW(); + while(true) + { + uint8_t status = flashSpiReadWriteByte(flashDescriptor->readStatusCmd); + if((status & 0x01) == 0) + break; + } + CS_HIGH(); + delay_01us(100); +} + +uint16_t flashSpiReadID() +{ + CS_LOW(); + flashSpiReadWriteByte(0x90); + flashSpiReadWriteByte(0x00); + flashSpiReadWriteByte(0x00); + flashSpiReadWriteByte(0x00); + uint16_t result = flashSpiReadWriteByte(0xff) << 8; + result += flashSpiReadWriteByte(0xff); + delay_01us(100); // 10us + CS_HIGH(); + + return result; +} + +size_t flashSpiGetSize() +{ + return flashDescriptor->blockSize * flashDescriptor->blockCount; +} + +/*uint32_t flashSpiGetBlockCount() +{ + return flashDescriptor->blockCount; +}*/ + +bool flashSpiIsErased(size_t address) +{ + if(address%flashDescriptor->sectorSize != 0) + return false; + + flashSpiSync(); + + CS_LOW(); + + flashSpiReadWriteByte(flashDescriptor->readCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } + flashSpiReadWriteByte((address>>16)&0xFF); + flashSpiReadWriteByte((address>>8)&0xFF); + flashSpiReadWriteByte(address&0xFF); + + bool ret = true; + for(size_t i = 0; i < flashDescriptor->sectorSize; i++) + { + uint8_t byte = flashSpiReadWriteByte(0xFF); + if (byte != 0xff) + { + ret = false; + break; + } + } + + delay_01us(100); // 10us + CS_HIGH(); + + return ret; +} + +size_t flashSpiRead(size_t address, uint8_t* data, size_t size) +{ +#if !defined(BOOT) && 0 + static char buf __DMA = 0; +#endif + flashSpiSync(); + + size = std::min(size, (size_t)(flashSpiGetSize() - address)); + + DMA_DeInit(FLASH_SPI_RX_DMA_STREAM); + + CS_LOW(); + + flashSpiReadWriteByte(flashDescriptor->readCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } + flashSpiReadWriteByte((address>>16)&0xFF); + flashSpiReadWriteByte((address>>8)&0xFF); + flashSpiReadWriteByte(address&0xFF); + +// reading = true; +// +// dmaRxInfo.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(data); +// dmaRxInfo.DMA_BufferSize = size; +// DMA_Init(FLASH_SPI_RX_DMA_STREAM, &dmaRxInfo); +// DMA_Cmd(FLASH_SPI_RX_DMA_STREAM, ENABLE); +// +// dmaTxInfo.DMA_MemoryInc = DMA_MemoryInc_Disable; +// dmaTxInfo.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(buf); +// dmaTxInfo.DMA_BufferSize = size; +// DMA_Init(FLASH_SPI_TX_DMA_STREAM, &dmaTxInfo); +// DMA_Cmd(FLASH_SPI_TX_DMA_STREAM, ENABLE); +// DMA_ITConfig(FLASH_SPI_RX_DMA_STREAM, DMA_IT_TC, ENABLE); +// DMA_ITConfig(FLASH_SPI_TX_DMA_STREAM, DMA_IT_TC, ENABLE); +// SPI_I2S_DMACmd(FLASH_SPI, SPI_I2S_DMAReq_Rx|SPI_I2S_DMAReq_Tx, ENABLE); +// +// if(SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) +// { +// int32_t i; +// xSemaphoreTakeFromISR(irqSem.rtos_handle, &i); +// } else { +// RTOS_TAKE_SEMAPHORE(irqSem); +// } + + for(size_t i=0; i < size; i++) + *data++ = flashSpiReadWriteByte(0xFF); + + delay_01us(100); // 10us + CS_HIGH(); +#if !defined(BOOT) && 0 + reading = false; +#endif + return size; +} + +size_t flashSpiWrite(size_t address, const uint8_t* data, size_t size) +{ + size = std::min(size, (size_t)(flashSpiGetSize() - address)); + if(size != flashDescriptor->pageSize || address%flashDescriptor->pageSize != 0) + return -1; + + flashSpiSync(); + +// DMA_DeInit(FLASH_SPI_TX_DMA_STREAM); + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + CS_LOW(); + + flashSpiReadWriteByte(flashDescriptor->writeCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } + flashSpiReadWriteByte((address>>16)&0xFF); + flashSpiReadWriteByte((address>>8)&0xFF); + flashSpiReadWriteByte(address&0xFF); + +// dmaTxInfo.DMA_MemoryInc = DMA_MemoryInc_Enable; +// dmaTxInfo.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(data); +// dmaTxInfo.DMA_BufferSize = flashDescriptor->pageSize; +// DMA_Init(FLASH_SPI_TX_DMA_STREAM, &dmaTxInfo); +// DMA_Cmd(FLASH_SPI_TX_DMA_STREAM, ENABLE); +// SPI_I2S_DMACmd(FLASH_SPI, SPI_I2S_DMAReq_Tx, ENABLE); +// DMA_ITConfig(FLASH_SPI_TX_DMA_STREAM, DMA_IT_TC, ENABLE); +// +// RTOS_TAKE_SEMAPHORE(irqSem); + for(size_t i=0; i < size; i++) + flashSpiReadWriteByte(*data++); + + delay_01us(100); // 10us + CS_HIGH(); + + flashSpiSync(); + + return size; +} + +int flashSpiErase(size_t address) +{ + if(address%flashDescriptor->sectorSize != 0) + return -1; + + flashSpiSync(); + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->eraseSectorCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } + flashSpiReadWriteByte((address>>16)&0xFF); + flashSpiReadWriteByte((address>>8)&0xFF); + flashSpiReadWriteByte(address&0xFF); + delay_01us(100); // 10us + CS_HIGH(); + + flashSpiSync(); + + return 0; +} + +int flashSpiBlockErase(size_t address) +{ + if(address%flashDescriptor->blockSize != 0) + return -1; + + flashSpiSync(); + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->eraseBlockCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } + flashSpiReadWriteByte((address>>16)&0xFF); + flashSpiReadWriteByte((address>>8)&0xFF); + flashSpiReadWriteByte(address&0xFF); + delay_01us(100); // 10us + CS_HIGH(); + + flashSpiSync(); + + return 0; +} + +void flashSpiEraseAll() +{ + flashSpiSync(); + + delay_01us(100); // 10us// + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->eraseChipCmd); + delay_01us(100); // 10us + CS_HIGH(); + + flashSpiSync(); +} + +uint16_t flashSpiGetPageSize() +{ + return flashDescriptor->pageSize; +} +uint16_t flashSpiGetSectorSize() +{ + return flashDescriptor->sectorSize; +} +uint16_t flashSpiGetSectorCount() +{ + return flashDescriptor->blockCount * (flashDescriptor->blockSize / flashDescriptor->sectorSize); +} + +#if !defined(BOOT) && 0 +extern "C" void FLASH_SPI_TX_DMA_IRQHandler(void) +{ + if (DMA_GetITStatus(FLASH_SPI_TX_DMA_STREAM, FLASH_SPI_TX_DMA_FLAG_TC)) + { + DMA_ClearITPendingBit(FLASH_SPI_TX_DMA_STREAM, FLASH_SPI_TX_DMA_FLAG_TC); + if(!reading) + RTOS_GIVE_SEMAPHORE_ISR(irqSem); + } +} +extern "C" void FLASH_SPI_RX_DMA_IRQHandler(void) +{ + if (DMA_GetITStatus(FLASH_SPI_RX_DMA_STREAM, FLASH_SPI_RX_DMA_FLAG_TC)) + { + DMA_ClearITPendingBit(FLASH_SPI_RX_DMA_STREAM, FLASH_SPI_RX_DMA_FLAG_TC); + RTOS_GIVE_SEMAPHORE_ISR(irqSem); + } +} + +static void flashSpiInitDMA() +{ +#if 0 + RTOS_CREATE_SEAPHORE(irqSem); +// dmaReadBuf = (uint8_t*)aligned_alloc(4, flashDescriptor->pageSize); +// dmaWriteBuf = (uint8_t*)aligned_alloc(4, flashDescriptor->pageSize); + DMA_DeInit(FLASH_SPI_TX_DMA_STREAM); + dmaTxInfo.DMA_Channel = FLASH_SPI_TX_DMA_CHANNEL; + dmaTxInfo.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&FLASH_SPI->DR); + dmaTxInfo.DMA_DIR = DMA_DIR_MemoryToPeripheral; + dmaTxInfo.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(dmaWriteBuf); + dmaTxInfo.DMA_BufferSize = flashDescriptor->pageSize; + dmaTxInfo.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + dmaTxInfo.DMA_MemoryInc = DMA_MemoryInc_Enable; + dmaTxInfo.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + dmaTxInfo.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + dmaTxInfo.DMA_Mode = DMA_Mode_Normal; + dmaTxInfo.DMA_Priority = DMA_Priority_Low; + dmaTxInfo.DMA_FIFOMode = DMA_FIFOMode_Disable; + dmaTxInfo.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + dmaTxInfo.DMA_MemoryBurst = DMA_MemoryBurst_Single; + dmaTxInfo.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + /* enable interrupt and set it's priority */ + NVIC_EnableIRQ(FLASH_SPI_TX_DMA_IRQn); + NVIC_SetPriority(FLASH_SPI_TX_DMA_IRQn, 5); + + DMA_DeInit(FLASH_SPI_RX_DMA_STREAM); + dmaRxInfo.DMA_Channel = FLASH_SPI_RX_DMA_CHANNEL; + dmaRxInfo.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&FLASH_SPI->DR); + dmaRxInfo.DMA_DIR = DMA_DIR_PeripheralToMemory; + dmaRxInfo.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(dmaReadBuf); + dmaRxInfo.DMA_BufferSize = flashDescriptor->pageSize; + dmaRxInfo.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + dmaRxInfo.DMA_MemoryInc = DMA_MemoryInc_Enable; + dmaRxInfo.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + dmaRxInfo.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + dmaRxInfo.DMA_Mode = DMA_Mode_Normal; + dmaRxInfo.DMA_Priority = DMA_Priority_Low; + dmaRxInfo.DMA_FIFOMode = DMA_FIFOMode_Disable; + dmaRxInfo.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + dmaRxInfo.DMA_MemoryBurst = DMA_MemoryBurst_Single; + dmaRxInfo.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + /* enable interrupt and set it's priority */ + NVIC_EnableIRQ(FLASH_SPI_RX_DMA_IRQn); + NVIC_SetPriority(FLASH_SPI_RX_DMA_IRQn, 5); +#endif +} +#endif +void flashInit() +{ + flashSpiInit(); + flashSpiSetSpeed(SPI_SPEED_2); + delay_ms(1); + + uint16_t id = flashSpiReadID(); + for(size_t i = 0; i < sizeof(spiFlashDescriptors)/sizeof(SpiFlashDescriptor); i++) + { + if(spiFlashDescriptors[i].id == id) + { + flashDescriptor = &spiFlashDescriptors[i]; + break; + } + } +#if !defined(BOOT) +/* if(flashDescriptor != nullptr) + flashSpiInitDMA();*/ +#endif +} + +#endif diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index 59c34778a5e..9f589f16665 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/sdio_sd.h b/radio/src/targets/common/arm/stm32/sdio_sd.h index 7e87224efc1..74c545f745a 100644 --- a/radio/src/targets/common/arm/stm32/sdio_sd.h +++ b/radio/src/targets/common/arm/stm32/sdio_sd.h @@ -52,6 +52,7 @@ SD_Error SD_Init(void); SDTransferState SD_GetStatus(void); int SD_CheckStatusWithTimeout(uint32_t timeout); uint8_t SD_Detect(void); +uint8_t SD_isHC(void); SD_Error SD_PowerOFF(void); SD_Error SD_ReadBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks); SD_Error SD_WriteBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks); diff --git a/radio/src/targets/common/arm/stm32/usb_conf.h b/radio/src/targets/common/arm/stm32/usb_conf.h index 22f3b2656db..38e581971e8 100644 --- a/radio/src/targets/common/arm/stm32/usb_conf.h +++ b/radio/src/targets/common/arm/stm32/usb_conf.h @@ -97,7 +97,9 @@ #endif /****************** USB OTG MISC CONFIGURATION ********************************/ +#if !defined(PCBPL18) #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..6bca69fe824 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(PCBPL18) 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/common/arm/stm32/usbd_storage_msd.cpp b/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp index 8d7152fc431..b05554c63ed 100644 --- a/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp +++ b/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp @@ -51,22 +51,23 @@ extern "C" { enum MassstorageLuns { STORAGE_SDCARD_LUN, -#if defined(FWDRIVE) +#if defined(FWDRIVE) STORAGE_EEPROM_LUN, #endif + STORAGE_SPI_FLASH_LUN, STORAGE_LUN_NBR }; /* USB Mass storage Standard Inquiry Data */ const unsigned char STORAGE_Inquirydata[] = { /* LUN 0 */ - 0x00, - 0x80, - 0x02, + 0x00, + 0x80, + 0x02, 0x02, (USBD_STD_INQUIRY_LENGTH - 5), 0x00, - 0x00, + 0x00, 0x00, USB_MANUFACTURER, /* Manufacturer : 8 bytes */ USB_PRODUCT, /* Product : 16 Bytes */ @@ -74,6 +75,20 @@ const unsigned char STORAGE_Inquirydata[] = { '1', '.', '0', '0', /* Version : 4 Bytes */ #if defined(FWDRIVE) /* LUN 1 */ + 0x00, + 0x80, + 0x02, + 0x02, + (USBD_STD_INQUIRY_LENGTH - 5), + 0x00, + 0x00, + 0x00, + USB_MANUFACTURER, /* Manufacturer : 8 bytes */ + USB_PRODUCT, /* Product : 16 Bytes */ + 'R', 'a', 'd', 'i', 'o', ' ', ' ', ' ', + '1', '.', '0' ,'0', /* Version : 4 Bytes */ +#endif + /* LUN 2 */ 0x00, 0x80, 0x02, @@ -86,33 +101,32 @@ const unsigned char STORAGE_Inquirydata[] = { USB_PRODUCT, /* Product : 16 Bytes */ 'R', 'a', 'd', 'i', 'o', ' ', ' ', ' ', '1', '.', '0' ,'0', /* Version : 4 Bytes */ -#endif }; #if defined(FWDRIVE) - #define RESERVED_SECTORS (1 /*Boot*/ + 2 /*Fat table */ + 1 /*Root dir*/ + 8 /* one cluster for firmware.txt */) +#define RESERVED_SECTORS (1 /*Boot*/ + 2 /*Fat table */ + 1 /*Root dir*/ + 8 /* one cluster for firmware.txt */) - int32_t fat12Write(const uint8_t * buffer, uint16_t sector, uint16_t count); - int32_t fat12Read(uint8_t * buffer, uint16_t sector, uint16_t count ); +int32_t fat12Write(const uint8_t * buffer, uint16_t sector, uint16_t count); +int32_t fat12Read(uint8_t * buffer, uint16_t sector, uint16_t count ); #endif int8_t STORAGE_Init (uint8_t lun); -int8_t STORAGE_GetCapacity (uint8_t lun, - uint32_t *block_num, +int8_t STORAGE_GetCapacity (uint8_t lun, + uint32_t *block_num, uint32_t *block_size); -int8_t STORAGE_IsReady (uint8_t lun); +int8_t STORAGE_IsReady (uint8_t lun); -int8_t STORAGE_IsWriteProtected (uint8_t lun); +int8_t STORAGE_IsWriteProtected (uint8_t lun); -int8_t STORAGE_Read (uint8_t lun, - uint8_t *buf, +int8_t STORAGE_Read (uint8_t lun, + uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); -int8_t STORAGE_Write (uint8_t lun, - uint8_t *buf, +int8_t STORAGE_Write (uint8_t lun, + uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); @@ -136,21 +150,40 @@ const USBD_STORAGE_cb_TypeDef * const USBD_STORAGE_fops = &USBD_MICRO_SDIO_fops } #endif + int8_t STORAGE_Init (uint8_t lun) { - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - -/* TODO if no SD ... if( SD_Init() != 0) +#if defined(SDCARD) + if(lun == STORAGE_SDCARD_LUN) + { + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* TODO if no SD ... if( SD_Init() != 0) + { + return (-1); + } + */ + return (0); + } +#endif +#if defined(SPI_FLASH) + if(lun == STORAGE_SPI_FLASH_LUN) { - return (-1); + disk_initialize(1); + return (0); + } +#endif +#if defined(FWDRIVE) + if (lun == STORAGE_EEPROM_LUN) { + return 0; } -*/ - return (0); +#endif + return -1; } /** @@ -165,18 +198,42 @@ int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_si #if defined(FWDRIVE) if (lun == STORAGE_EEPROM_LUN) { *block_size = BLOCK_SIZE; - #if defined(EEPROM) +#if defined(EEPROM) *block_num = RESERVED_SECTORS + EEPROM_SIZE/BLOCK_SIZE + FLASHSIZE/BLOCK_SIZE; - #else +#else *block_num = RESERVED_SECTORS + FLASHSIZE/BLOCK_SIZE; - #endif +#endif // EEPROM return 0; } -#endif +#endif // FWDRIVE - if (!SD_CARD_PRESENT()) + if (lun == STORAGE_SPI_FLASH_LUN) { +#if !defined(SPI_FLASH) return -1; +#else + DWORD secCount = 0; + if (disk_ioctl(1, GET_SECTOR_COUNT, &secCount) != RES_OK) { + secCount = 0; + return -1; + } + DWORD secSize = 0; + if (disk_ioctl(1, GET_SECTOR_SIZE, &secSize) != RES_OK) { + secSize = 0; + return -1; + } + *block_num = secCount; + *block_size = secSize; + return 0; +#endif // SPI_FLASH + } +#if !defined(SDCARD) + *block_num = 0; + return -1; +#else + if (!SD_CARD_PRESENT()) + return -1; + *block_size = BLOCK_SIZE; static DWORD sector_count = 0; @@ -190,16 +247,26 @@ int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_si *block_num = sector_count; return 0; +#endif } uint8_t lunReady[STORAGE_LUN_NBR]; void usbInitLUNs() { +#if defined(SDCARD) lunReady[STORAGE_SDCARD_LUN] = 1; +#else + lunReady[STORAGE_SDCARD_LUN] = 0; +#endif #if defined(FWDRIVE) lunReady[STORAGE_EEPROM_LUN] = 1; #endif +#if defined(SPI_FLASH) + lunReady[STORAGE_SPI_FLASH_LUN] = 1; +#else + lunReady[STORAGE_SPI_FLASH_LUN] = 0; +#endif } /** @@ -208,12 +275,16 @@ void usbInitLUNs() * @retval Status */ int8_t STORAGE_IsReady (uint8_t lun) -{ +{ #if defined(FWDRIVE) && defined(EEPROM) if (lun == STORAGE_EEPROM_LUN) { return (lunReady[STORAGE_EEPROM_LUN] != 0) ? 0 : -1; } #endif + if (lun == STORAGE_SPI_FLASH_LUN) { + return (lunReady[STORAGE_SPI_FLASH_LUN] != 0) ? 0 : -1; + } + return (lunReady[STORAGE_SDCARD_LUN] != 0 && SD_CARD_PRESENT()) ? 0 : -1; } @@ -236,21 +307,32 @@ int8_t STORAGE_IsWriteProtected (uint8_t lun) * @retval Status */ -int8_t STORAGE_Read (uint8_t lun, - uint8_t *buf, - uint32_t blk_addr, +int8_t STORAGE_Read (uint8_t lun, + uint8_t *buf, + uint32_t blk_addr, uint16_t blk_len) { WATCHDOG_SUSPEND(100/*1s*/); - + #if defined(FWDRIVE) if (lun == STORAGE_EEPROM_LUN) { return (fat12Read(buf, blk_addr, blk_len) == 0) ? 0 : -1; } #endif + if (lun == STORAGE_SPI_FLASH_LUN) { +#if defined(SPI_FLASH) + return (__disk_read(1, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1; +#else + return -1; +#endif + } +#if defined(SDCARD) // read without cache return (__disk_read(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1; +#else + return -1; +#endif } /** * @brief Write data to the medium @@ -261,21 +343,31 @@ int8_t STORAGE_Read (uint8_t lun, * @retval Status */ -int8_t STORAGE_Write (uint8_t lun, - uint8_t *buf, +int8_t STORAGE_Write (uint8_t lun, + uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { WATCHDOG_SUSPEND(100/*1s*/); - + #if defined(FWDRIVE) if (lun == STORAGE_EEPROM_LUN) { return (fat12Write(buf, blk_addr, blk_len) == 0) ? 0 : -1; } +#endif + if (lun == STORAGE_SPI_FLASH_LUN) { +#if defined(SPI_FLASH) + return (__disk_write(1, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1; +#else + return -1; #endif - + } +#if !defined(SDCARD) + return -1; +#else // write without cache return (__disk_write(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1; +#endif } /** @@ -292,36 +384,36 @@ int8_t STORAGE_GetMaxLun (void) #if defined(FWDRIVE) /* Firmware.txt */ const char firmware_txt[] = - #if defined(BOOT) +#if defined(BOOT) "EdgeTX Bootloader" - #else +#else "EdgeTX Firmware" - #endif +#endif " for " FLAVOUR "\r\n\r\n" - #if defined(BOOT) +#if defined(BOOT) "BOOTVER " - #else +#else "FWVERSION " - #endif +#endif "edgetx-" FLAVOUR "-" VERSION " (" GIT_STR ")\r\n" "DATE " DATE "\r\n" "TIME " TIME "\r\n" - #if !defined(BOOT) +#if !defined(BOOT) "BOOTVER " - #else +#else "FWVERSION " - #endif +#endif ; //------------------------------------------------------------------------------ /** * FAT12 boot sector partition. */ - #if defined(EEPROM) - #define TOTALSECTORS (RESERVED_SECTORS + (EEPROM_SIZE/BLOCK_SIZE) + (FLASHSIZE/BLOCK_SIZE)) - #else - #define TOTALSECTORS (RESERVED_SECTORS + (FLASHSIZE/BLOCK_SIZE)) - #endif +#if defined(EEPROM) +#define TOTALSECTORS (RESERVED_SECTORS + (EEPROM_SIZE/BLOCK_SIZE) + (FLASHSIZE/BLOCK_SIZE)) +#else +#define TOTALSECTORS (RESERVED_SECTORS + (FLASHSIZE/BLOCK_SIZE)) +#endif const char g_FATboot[BLOCK_SIZE] = { 0xeb, 0x3c, 0x90, // Jump instruction. @@ -444,11 +536,11 @@ const FATDirEntry_t g_DIRroot[] = { { 'F', 'I', 'R', 'M', 'W', 'A', 'R', 'E'}, { 'B', 'I', 'N'}, - #if defined(BOOT) +#if defined(BOOT) 0x20, // Archive - #else +#else 0x21, // Readonly+Archive - #endif +#endif 0x00, 0x3E, 0xA301, @@ -460,7 +552,7 @@ const FATDirEntry_t g_DIRroot[] = 0x0003, FLASHSIZE }, - #if defined(EEPROM) +#if defined(EEPROM) { { 'E', 'E', 'P', 'R', 'O', 'M', ' ', ' '}, { 'B', 'I', 'N'}, @@ -476,7 +568,7 @@ const FATDirEntry_t g_DIRroot[] = 0x0003 + (FLASHSIZE/BLOCK_SIZE)/8, EEPROM_SIZE }, - #endif +#endif // Emty entries are 0x00, omitted here. Up to 16 entries can be defined here }; @@ -528,12 +620,12 @@ int32_t fat12Read(uint8_t * buffer, uint16_t sector, uint16_t count) pushCluster (buffer, sector, cluster, rest, cluster+1); pushCluster (buffer, sector, cluster, rest, (uint16_t) 0xFFF); - #if defined(EEPROM) +#if defined(EEPROM) // Entry for eeprom.bin for (int i=0;i= 13 + FLASH_RCC_APB2Periph | +#endif HAPTIC_RCC_APB2Periph | TELEMETRY_RCC_APB2Periph | BT_RCC_APB2Periph | @@ -155,6 +162,8 @@ void boardInit() TRACE("\nHorus board started :)"); TRACE("RCC->CSR = %08x", RCC->CSR); + flashInit(); + audioInit(); keysInit(); @@ -206,6 +215,10 @@ extern void rtcDisableBackupReg(); void boardOff() { +#if defined(SPI_FLASH) + flushFTL(); +#endif + ledOff(); backlightEnable(0); diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 42f7e1c2098..603b2f7e9d2 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -298,6 +298,11 @@ void usbJoystickUpdate(); } #endif +// SPI Flash driver + +void flashInit(); + + // Audio driver void audioInit(); void audioConsumeCurrentBuffer(); diff --git a/radio/src/targets/horus/bootloader/boot_menu.cpp b/radio/src/targets/horus/bootloader/boot_menu.cpp index 765a7d609f5..b6a26a368a8 100644 --- a/radio/src/targets/horus/bootloader/boot_menu.cpp +++ b/radio/src/targets/horus/bootloader/boot_menu.cpp @@ -114,11 +114,20 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) coord_t pos = lcd->drawText(124, 75, TR_BL_WRITE_FW, BL_FOREGROUND); pos += 8; +#if defined(SPI_FLASH) + lcd->drawText(100, 110, LV_SYMBOL_WARNING, BL_FOREGROUND); + pos = lcd->drawText(124, 110, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(100, 145, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(124, 145, "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, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + lcd->drawSolidRect(92, 72 + (opt*35), pos, 26, 2, BL_SELECTED); lcd->drawBitmap(60, 166, (const BitmapBuffer*)&BMP_PLUG_USB); lcd->drawText(195, 175, TR_BL_USB_PLUGIN, BL_FOREGROUND); @@ -127,6 +136,55 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) bootloaderDrawFooter(); lcd->drawText(LCD_W / 2, 242, getFirmwareVersion(), CENTERED | BL_FOREGROUND); } +#if defined(SPI_FLASH) && defined(SDCARD) + else if (st == ST_SELECT_STORAGE) { + + bootloaderDrawTitle(88, LV_SYMBOL_DIRECTORY " select storage"); + + lcd->drawText(102, 75, LV_SYMBOL_DIRECTORY, BL_FOREGROUND); + coord_t pos = lcd->drawText(124, 75, "Internal", BL_FOREGROUND); + + pos += 8; + + lcd->drawText(100, 110, LV_SYMBOL_SD_CARD, BL_FOREGROUND); + lcd->drawText(124, 110, "SD Card", BL_FOREGROUND); + + pos -= 92; + lcd->drawSolidRect(92, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(56, 244, "[ENT] to select storage", BL_FOREGROUND); + lcd->drawText(305, 244, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(335, 244, "[RTN] to exit", BL_FOREGROUND); + } +#endif +#if defined(SPI_FLASH) + else if (st == ST_CLEAR_FLASH_CHECK) { + + bootloaderDrawTitle(88, "erase internal flash storage"); + + lcd->drawText(100, 75, LV_SYMBOL_DRIVE, BL_FOREGROUND); + coord_t pos = lcd->drawText(124, 75, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(102, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(124, 110, "Exit", BL_FOREGROUND); + + pos -= 92; + lcd->drawSolidRect(92, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(56, 244, "Hold [ENT] long to erase storage", BL_FOREGROUND); + lcd->drawText(305, 244, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(335, 244, "[RTN] to exit", BL_FOREGROUND); + } + else if (st == ST_CLEAR_FLASH) { + bootloaderDrawTitle(88, "erasing internal flash storage"); + + lcd->drawText(102, 75, "This may take up to 150s", BL_FOREGROUND); + bootloaderDrawFooter(); + } +#endif else if (st == ST_USB) { lcd->drawBitmap(136, 98, (const BitmapBuffer*)&BMP_USB_PLUGGED); diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index c62e2eb7738..abd6dc7bef2 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -630,35 +630,65 @@ #define SD_SDIO_INIT_CLK_DIV SD_SDIO_CLK_DIV(400000) #define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) -// EEPROM +// SPI NOR Flash #if defined(PCBX12S) && PCBREV >= 13 - #define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOA - #define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI1 - #define EEPROM_SPI_CS_GPIO GPIOA - #define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_15 // PA.15 - #define EEPROM_SPI_SCK_GPIO GPIOA - #define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_5 // PA.05 - #define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource5 - #define EEPROM_SPI_MISO_GPIO GPIOA - #define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_6 // PA.06 - #define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource6 - #define EEPROM_SPI_MOSI_GPIO GPIOA - #define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_7 // PA.07 - #define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource7 + #define FLASH_RCC_AHB1Periph RCC_AHB1Periph_GPIOA + #define FLASH_RCC_APB2Periph RCC_APB2Periph_SPI1 + #define FLASH_SPI SPI1 + #define FLASH_SPI_GPIO_AF GPIO_AF_SPI1 + #define FLASH_SPI_CS_GPIO GPIOA + #define FLASH_SPI_CS_GPIO_PIN GPIO_Pin_15 // PA.15 + #define FLASH_SPI_CS_GPIO_PinSource GPIO_PinSource15 + #define FLASH_SPI_SCK_GPIO GPIOA + #define FLASH_SPI_SCK_GPIO_PIN GPIO_Pin_5 // PA.05 + #define FLASH_SPI_SCK_GPIO_PinSource GPIO_PinSource5 + #define FLASH_SPI_MISO_GPIO GPIOA + #define FLASH_SPI_MISO_GPIO_PIN GPIO_Pin_6 // PA.06 + #define FLASH_SPI_MISO_GPIO_PinSource GPIO_PinSource6 + #define FLASH_SPI_MOSI_GPIO GPIOA + #define FLASH_SPI_MOSI_GPIO_PIN GPIO_Pin_7 // PA.07 + #define FLASH_SPI_MOSI_GPIO_PinSource GPIO_PinSource7 + #define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_3 + #define FLASH_SPI_TX_DMA_STREAM DMA2_Stream3 + #define FLASH_SPI_TX_DMA_IRQn DMA2_Stream3_IRQn + #define FLASH_SPI_TX_DMA_IRQHandler DMA2_Stream3_IRQHandler + #define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF3 + #define FLASH_SPI_TX_DMA_STATUS_REG HISR + #define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_3 + #define FLASH_SPI_RX_DMA_STREAM DMA2_Stream5 + #define FLASH_SPI_RX_DMA_IRQn DMA2_Stream5_IRQn + #define FLASH_SPI_RX_DMA_IRQHandler DMA2_Stream5_IRQHandler + #define FLASH_SPI_RX_DMA_STATUS_REG HISR + #define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF5 #elif defined(PCBX10) - #define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOI - #define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI2 - #define EEPROM_SPI_CS_GPIO GPIOI - #define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_0 // PI.00 - #define EEPROM_SPI_SCK_GPIO GPIOI - #define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_1 // PI.01 - #define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource1 - #define EEPROM_SPI_MISO_GPIO GPIOI - #define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_2 // PI.02 - #define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource2 - #define EEPROM_SPI_MOSI_GPIO GPIOI - #define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_3 // PI.03 - #define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource3 + #define FLASH_RCC_AHB1Periph RCC_AHB1Periph_GPIOI + #define FLASH_RCC_APB1Periph RCC_APB1Periph_SPI2 + #define FLASH_SPI SPI2 + #define FLASH_SPI_GPIO_AF GPIO_AF_SPI2 + #define FLASH_SPI_CS_GPIO GPIOI + #define FLASH_SPI_CS_GPIO_PIN GPIO_Pin_0 // PI.00 + #define FLASH_SPI_CS_GPIO_PinSource GPIO_PinSource0 + #define FLASH_SPI_SCK_GPIO GPIOI + #define FLASH_SPI_SCK_GPIO_PIN GPIO_Pin_1 // PI.01 + #define FLASH_SPI_SCK_GPIO_PinSource GPIO_PinSource1 + #define FLASH_SPI_MISO_GPIO GPIOI + #define FLASH_SPI_MISO_GPIO_PIN GPIO_Pin_2 // PI.02 + #define FLASH_SPI_MISO_GPIO_PinSource GPIO_PinSource2 + #define FLASH_SPI_MOSI_GPIO GPIOI + #define FLASH_SPI_MOSI_GPIO_PIN GPIO_Pin_3 // PI.03 + #define FLASH_SPI_MOSI_GPIO_PinSource GPIO_PinSource3 +// #define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_0 +// #define FLASH_SPI_TX_DMA_STREAM DMA1_Stream4 +// #define FLASH_SPI_TX_DMA_IRQn DMA1_Stream4_IRQn +// #define FLASH_SPI_TX_DMA_IRQHandler DMA1_Stream4_IRQHandler +// #define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF4 +// #define FLASH_SPI_TX_DMA_STATUS_REG HISR + #define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_0 + #define FLASH_SPI_RX_DMA_STREAM DMA1_Stream3 + #define FLASH_SPI_RX_DMA_IRQn DMA1_Stream3_IRQn + #define FLASH_SPI_RX_DMA_IRQHandler DMA1_Stream3_IRQHandler + #define FLASH_SPI_RX_DMA_STATUS_REG LISR + #define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF3 #endif // Audio diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 74f4c28b060..7cef2a4ae3b 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -7,12 +7,15 @@ option(GHOST "Ghost TX Module" ON) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) option(MODULE_SIZE_STD "Standard size TX Module" ON) +option(SPI_FLASH "enable internal spi flash as additional storage" NO) +set(DEFAULT_STORAGE "SDCARD" CACHE STRING "storage to use for settings, model data, ... (INTERNAL/SDCARD)") 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(SPI_FLASH YES) set(HAPTIC YES) set(GUI_DIR colorlcd) set(BITMAPS_DIR 480x272) @@ -21,8 +24,48 @@ set(HARDWARE_EXTERNAL_MODULE YES) set(TARGET_DIR nv14) add_definitions(-DHARDWARE_TRAINER_JACK) +if(NOT SDCARD AND NOT SPI_FLASH) + message(FATAL_ERROR "No storage type enabled") +endif() +if(NOT SDCARD AND DEFAULT_STORAGE STREQUAL "SDCARD") + message(FATAL_ERROR "SD card support disabled, but 'SDCARD' selected as default storage") +endif() +if(NOT SPI_FLASH AND DEFAULT_STORAGE STREQUAL "INTERNAL") + message(FATAL_ERROR "SPI flash support disabled, but 'INTERNAL' selected as default storage") +endif() + +add_definitions(-DDEFAULT_STORAGE=${DEFAULT_STORAGE}) + +if(SPI_FLASH) + set(TARGET_SRC ${TARGET_SRC} + ../common/arm/stm32/flash_spi_driver.cpp + ) + + if(LITTLEFS) + set(TARGET_SRC ${TARGET_SRC} + ../../thirdparty/littlefs_v2.4.1/lfs_util.c + ../../thirdparty/littlefs_v2.4.1/lfs.c + ) + add_definitions(-DUSE_LITTLEFS) + else() + set(TARGET_SRC ${TARGET_SRC} + ../../frftl.cpp + ../../crc.cpp + ) + endif() +endif() + if(BOOTLOADER) set(LINKER_SCRIPT targets/nv14/stm32f4_flash_bootloader.ld) + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} + ../common/arm/stm32/diskio_sdio_spiflash.cpp + ../common/arm/stm32/flash_spi_driver.cpp + ../../frftl.cpp + ../../crc.cpp + ) + add_definitions(-DSPI_FLASH) + add_definitions(-DUSE_FATFS) + else() set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) endif() @@ -148,7 +191,7 @@ set(FIRMWARE_SRC targets/common/arm/stm32/trainer_driver.cpp targets/common/arm/stm32/pwr_driver.cpp targets/common/arm/stm32/sdio_sd.cpp - targets/common/arm/stm32/diskio_sdio.cpp + targets/common/arm/stm32/diskio_sdio_spiflash.cpp ) # Make malloc() thread-safe diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index c3db55d84c1..783abc011ef 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -29,7 +29,9 @@ #include "hal/switch_driver.h" #include "globals.h" +#if defined(SDCARD) #include "sdcard.h" +#endif #include "touch.h" #include "debug.h" @@ -102,6 +104,10 @@ void watchdogInit(unsigned int duration) 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--) @@ -112,7 +118,8 @@ void delay_self(int count) #define RCC_AHB1PeriphMinimum (PWR_RCC_AHB1Periph |\ LCD_RCC_AHB1Periph |\ BACKLIGHT_RCC_AHB1Periph |\ - SDRAM_RCC_AHB1Periph \ + SDRAM_RCC_AHB1Periph |\ + FLASH_RCC_AHB1Periph \ ) #define RCC_AHB1PeriphOther (SD_RCC_AHB1Periph |\ AUDIO_RCC_AHB1Periph |\ @@ -134,7 +141,9 @@ void delay_self(int count) #define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ MIXER_SCHEDULER_TIMER_RCC_APB1Periph \ ) -#define RCC_APB2PeriphMinimum (LCD_RCC_APB2Periph) +#define RCC_APB2PeriphMinimum (LCD_RCC_APB2Periph |\ + FLASH_RCC_APB2Periph \ + ) #define RCC_APB2PeriphOther (HAPTIC_RCC_APB2Periph |\ AUDIO_RCC_APB2Periph \ @@ -232,6 +241,7 @@ void boardInit() init1msTimer(); TouchInit(); usbInit(); + flashInit(); uint32_t press_start = 0; uint32_t press_end = 0; @@ -298,6 +308,10 @@ extern void rtcDisableBackupReg(); void boardOff() { +#if defined(SPI_FLASH) + flushFTL(); +#endif + lcdOff(); while (pwrPressed()) { diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index 971d6819a27..2571dd95a89 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -242,6 +242,10 @@ void usbJoystickUpdate(); } #endif +// SPI Flash driver + +void flashInit(); + // Audio driver void audioInit(); void audioConsumeCurrentBuffer(); diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp index 492538563c5..a43e6b4f970 100644 --- a/radio/src/targets/nv14/bootloader/boot_menu.cpp +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -132,11 +132,20 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) yOffset = 35; } +#if defined(SPI_FLASH) + lcd->drawText(60, 110, LV_SYMBOL_WARNING, BL_FOREGROUND); + pos = lcd->drawText(84, 110, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(60, 145, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(84, 145, "Exit", BL_FOREGROUND); +#else lcd->drawText(60, 110 + yOffset, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); lcd->drawText(84, 110 + yOffset, TR_BL_EXIT, BL_FOREGROUND); +#endif pos -= 79; - lcd->drawSolidRect(79, 72 + (opt * 35), pos, 26, 2, BL_SELECTED); + lcd->drawSolidRect(79, 72 + (opt*35), pos, 26, 2, BL_SELECTED); lcd->drawBitmap(center - 55, 165, (const BitmapBuffer*)&BMP_PLUG_USB); lcd->drawText(center, 250, TR_BL_USB_PLUGIN, CENTERED | BL_FOREGROUND); @@ -147,7 +156,57 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) TR_BL_CURRENT_FW, CENTERED | BL_FOREGROUND); lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); - } else if (st == ST_USB) { + } +#if defined(SPI_FLASH) && defined(SDCARD) + else if (st == ST_SELECT_STORAGE) { + + bootloaderDrawTitle(LV_SYMBOL_DIRECTORY " select storage"); + + lcd->drawText(62, 75, LV_SYMBOL_DIRECTORY, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Internal", BL_FOREGROUND); + + pos += 8; + + lcd->drawText(60, 110, LV_SYMBOL_SD_CARD, BL_FOREGROUND); + lcd->drawText(84, 110, "SD Card", BL_FOREGROUND); + + pos -= 79; + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "[R TRIM] to select storage", BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE " [L TRIM] to exit", BL_FOREGROUND); + } +#endif +#if defined(SPI_FLASH) + else if (st == ST_CLEAR_FLASH_CHECK) { + + bootloaderDrawTitle("erase internal flash storage"); + + lcd->drawText(62, 75, LV_SYMBOL_DRIVE, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(60, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(84, 110, "Exit", BL_FOREGROUND); + + pos -= 79; + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to erase storage", BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, LV_SYMBOL_NEW_LINE "[L TRIM] to exit", BL_FOREGROUND); + } + else if (st == ST_CLEAR_FLASH) { + bootloaderDrawTitle("erasing internal flash storage"); + + lcd->drawText(62, 75, "This may take up to 150s", 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 || diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index 8b50df5a51d..ff24ee4987f 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -311,19 +311,33 @@ #define SDRAM_RCC_AHB3Periph RCC_AHB3Periph_FMC // SPI FLASH -#define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOG -#define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI6 -#define EEPROM_SPI_CS_GPIO GPIOG -#define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_6 // PG.06 -#define EEPROM_SPI_SCK_GPIO GPIOG -#define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_13 // PG.13 -#define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource13 -#define EEPROM_SPI_MISO_GPIO GPIOG -#define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_12 // PG.12 -#define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource12 -#define EEPROM_SPI_MOSI_GPIO GPIOG -#define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_14 // PG.14 -#define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 +#define FLASH_RCC_AHB1Periph RCC_AHB1Periph_GPIOG +#define FLASH_RCC_APB2Periph RCC_APB2Periph_SPI6 +#define FLASH_SPI SPI6 +#define FLASH_SPI_GPIO_AF GPIO_AF_SPI6 +#define FLASH_SPI_CS_GPIO GPIOG +#define FLASH_SPI_CS_GPIO_PIN GPIO_Pin_6 // PG.06 +#define FLASH_SPI_SCK_GPIO GPIOG +#define FLASH_SPI_SCK_GPIO_PIN GPIO_Pin_13 // PG.13 +#define FLASH_SPI_SCK_GPIO_PinSource GPIO_PinSource13 +#define FLASH_SPI_MISO_GPIO GPIOG +#define FLASH_SPI_MISO_GPIO_PIN GPIO_Pin_12 // PG.12 +#define FLASH_SPI_MISO_GPIO_PinSource GPIO_PinSource12 +#define FLASH_SPI_MOSI_GPIO GPIOG +#define FLASH_SPI_MOSI_GPIO_PIN GPIO_Pin_14 // PG.14 +#define FLASH_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 +#define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_1 +#define FLASH_SPI_TX_DMA_STREAM DMA2_Stream5 +#define FLASH_SPI_TX_DMA_IRQn DMA2_Stream5_IRQn +#define FLASH_SPI_TX_DMA_IRQHandler DMA2_Stream5_IRQHandler +#define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF5 +#define FLASH_SPI_TX_DMA_STATUS_REG HISR +#define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_1 +#define FLASH_SPI_RX_DMA_STREAM DMA2_Stream6 +#define FLASH_SPI_RX_DMA_IRQn DMA2_Stream6_IRQn +#define FLASH_SPI_RX_DMA_IRQHandler DMA2_Stream6_IRQHandler +#define FLASH_SPI_RX_DMA_STATUS_REG HISR +#define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF6 // Audio #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt new file mode 100644 index 00000000000..0b202965a87 --- /dev/null +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -0,0 +1,226 @@ +option(DISK_CACHE "Enable SD card disk cache" ON) +option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) +option(STICKS_DEAD_ZONE "Enable sticks dead zone" YES) +option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) +option(AFHDS2 "Support for AFHDS2" OFF) +option(GHOST "Ghost TX Module" ON) +option(PXX1 "PXX1 protocol support" ON) +option(PXX2 "PXX2 protocol support" OFF) +option(SPI_FLASH "enable internal spi flash as additional storage" NO) + +set(DEFAULT_STORAGE "INTERNAL" CACHE STRING "storage to use for settings, model data, ... (INTERNAL/SDCARD)") +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") +set(CPU_TYPE STM32F4) +set(HSE_VALUE 12000000) +set(SDCARD NO) +#set(STORAGE SDCARD) +#set(STORAGE_FORMAT RAW) +set(STORAGE_MODELSLIST YES) +set(SPI_FLASH YES) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +set(BITMAPS_DIR 480x272) +set(HARDWARE_EXTERNAL_MODULE YES) +set(WIRELESS_CHARGER YES) +#set(AUX_SERIAL ON) +# set(NAVIGATION_TYPE touch) +set(TARGET_DIR pl18) +add_definitions(-DHARDWARE_TRAINER_JACK) + +if(NOT SDCARD AND NOT SPI_FLASH) + message(FATAL_ERROR "No storage type enabled") +endif() +if(NOT SDCARD AND DEFAULT_STORAGE STREQUAL "SDCARD") + message(FATAL_ERROR "SD card support disabled, but 'SDCARD' selected as default storage") +endif() +if(NOT SPI_FLASH AND DEFAULT_STORAGE STREQUAL "INTERNAL") + message(FATAL_ERROR "SPI flash support disabled, but 'INTERNAL' selected as default storage") +endif() + +add_definitions(-DDEFAULT_STORAGE=${DEFAULT_STORAGE}) + +if(SPI_FLASH) + set(TARGET_SRC ${TARGET_SRC} + ../common/arm/stm32/flash_spi_driver.cpp + ) + + if(LITTLEFS) + set(TARGET_SRC ${TARGET_SRC} + ../../thirdparty/littlefs_v2.4.1/lfs_util.c + ../../thirdparty/littlefs_v2.4.1/lfs.c + ) + add_definitions(-DUSE_LITTLEFS) + else() + set(TARGET_SRC ${TARGET_SRC} + ../../frftl.cpp + ../../crc.cpp + ) + endif() +endif() + +if(BOOTLOADER) + set(LINKER_SCRIPT targets/pl18/stm32f4_flash_bootloader.ld) + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} + ../common/arm/stm32/diskio_sdio_spiflash.cpp + ../common/arm/stm32/flash_spi_driver.cpp + ../../frftl.cpp + ../../crc.cpp + ) + add_definitions(-DSPI_FLASH) + add_definitions(-DUSE_FATFS) + +else() + set(LINKER_SCRIPT targets/pl18/stm32f4_flash.ld) +endif() + +if(YAML_STORAGE) + set(STORAGE_CONVERT RAW) + set(STORAGE_FORMAT YAML) +endif() + +set(RTC_BACKUP_RAM YES) +set(PPM_LIMITS_SYMETRICAL YES) +# for size report script +set(CPU_TYPE_FULL STM32F429xI) +set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM2=8192") +option(USB_SERIAL "Enable USB serial (CDC)" ON) + +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}) + +set(FLAVOUR pl18) +add_definitions(-DPCBPL18 -DPCBFLYSKY) +add_definitions(-DBATTERY_CHARGE) +add_definitions(-DSOFTWARE_VOLUME) + +# defines existing internal modules +set(INTERNAL_MODULES MULTI CACHE STRING "Internal modules") +set(DEFAULT_INTERNAL_MODULE MULTIMODULE CACHE STRING "Default internal module") + +set(BITMAPS_TARGET pl18_bitmaps) +set(FONTS_TARGET x12_fonts) +set(LCD_DRIVER lcd_driver.cpp) +set(LUA_EXPORT lua_export_pl18) +set(TOUCH_DRIVER tp_cst340.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 -DCOLORLCD -DLIBOPENUI + -DHARDWARE_TOUCH -DHARDWARE_KEYS + -DSOFTWARE_KEYBOARD) + +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) +add_definitions(-DWIRELESS_CHARGER) +#add_definitions(-DAUX_SERIAL) +#add_definitions(-DFLYSKY_AUTO_POWER_DOWN) + +if(STICKS_DEAD_ZONE) + add_definitions(-DSTICK_DEAD_ZONE) +endif() + +if(NOT UNEXPECTED_SHUTDOWN) + add_definitions(-DNO_UNEXPECTED_SHUTDOWN) +endif() + +if(STICKS_DEAD_ZONE) + add_definitions(-DSTICK_DEAD_ZONE) +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) + +if(DISK_CACHE) + set(SRC ${SRC} disk_cache.cpp) + add_definitions(-DDISK_CACHE) +endif() + +#set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.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} + board.cpp + ${LCD_DRIVER} + ${TOUCH_DRIVER} + sdram_driver.c + battery_driver.cpp + backlight_driver.cpp + ../common/arm/stm32/diskio_sdio_spiflash.cpp + ) + +set(FIRMWARE_SRC + ${FIRMWARE_SRC} + hal/adc_driver.cpp + targets/common/arm/stm32/audio_dac_driver.cpp + boards/generic_stm32/module_ports.cpp + boards/generic_stm32/aux_ports.cpp + targets/common/arm/stm32/stm32_hal_adc.cpp + targets/common/arm/stm32/timers_driver.cpp + targets/common/arm/stm32/mixer_scheduler_driver.cpp + targets/common/arm/stm32/extmodule_driver.cpp + targets/common/arm/stm32/stm32_pulse_driver.cpp + targets/common/arm/stm32/stm32_usart_driver.cpp + targets/common/arm/stm32/trainer_driver.cpp + targets/common/arm/stm32/pwr_driver.cpp + targets/common/arm/stm32/sdio_sd.c + ) + +# 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 + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_syscfg.c + ) + diff --git a/radio/src/targets/pl18/audio_spi_driver.cpp b/radio/src/targets/pl18/audio_spi_driver.cpp new file mode 100644 index 00000000000..8c058701a3a --- /dev/null +++ b/radio/src/targets/pl18/audio_spi_driver.cpp @@ -0,0 +1,495 @@ +/* + * 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" + +#if !defined(SIMU) + +#define VS_WRITE_COMMAND 0x02 +#define VS_READ_COMMAND 0x03 + +#define SPI_MODE 0x00 +#define SPI_STATUS 0x01 +#define SPI_BASS 0x02 +#define SPI_CLOCKF 0x03 +#define SPI_DECODE_TIME 0x04 +#define SPI_AUDATA 0x05 +#define SPI_WRAM 0x06 +#define SPI_WRAMADDR 0x07 +#define SPI_HDAT0 0x08 +#define SPI_HDAT1 0x09 +#define SPI_AIADDR 0x0a +#define SPI_VOL 0x0b +#define SPI_AICTRL0 0x0c +#define SPI_AICTRL1 0x0d +#define SPI_AICTRL2 0x0e +#define SPI_AICTRL3 0x0f + +#define SM_DIFF 0x01 +#define SM_LAYER12 0x02 +#define SM_RESET 0x04 +#define SM_CANCEL 0x08 +#define SM_EARSPEAKER_LO 0x10 +#define SM_TESTS 0x20 +#define SM_STREAM 0x40 +#define SM_EARSPEAKER_HI 0x80 +#define SM_DACT 0x100 +#define SM_SDIORD 0x200 +#define SM_SDISHARE 0x400 +#define SM_SDINEW 0x800 +#define SM_ADPCM 0x1000 +#define SM_LINE1 0x4000 +#define SM_CLK_RANGE 0x8000 + +#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 MP3_BUFFER_SIZE 32 + +#define CS_HIGH() do { AUDIO_CS_GPIO->BSRRL = AUDIO_CS_GPIO_PIN; } while (0) +#define CS_LOW() do { AUDIO_CS_GPIO->BSRRH = AUDIO_CS_GPIO_PIN; } while (0) +#define XDCS_HIGH() do { AUDIO_XDCS_GPIO->BSRRL = AUDIO_XDCS_GPIO_PIN; } while (0) +#define XDCS_LOW() do { AUDIO_XDCS_GPIO->BSRRH = AUDIO_XDCS_GPIO_PIN; } while (0) +#define RST_HIGH() do { AUDIO_RST_GPIO->BSRRL = AUDIO_RST_GPIO_PIN; } while (0) +#define RST_LOW() do { AUDIO_RST_GPIO->BSRRH = AUDIO_RST_GPIO_PIN; } while (0) + +#define READ_DREQ() (GPIO_ReadInputDataBit(AUDIO_DREQ_GPIO, AUDIO_DREQ_GPIO_PIN)) + +void audioSpiInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + SPI_InitTypeDef SPI_InitStructure; + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MISO_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(AUDIO_SPI_MISO_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_SCK_GPIO_PIN; + GPIO_Init(AUDIO_SPI_SCK_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MOSI_GPIO_PIN; + GPIO_Init(AUDIO_SPI_MOSI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_CS_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(AUDIO_CS_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_XDCS_GPIO_PIN; + GPIO_Init(AUDIO_XDCS_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_RST_GPIO_PIN; + GPIO_Init(AUDIO_RST_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_DREQ_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_Init(AUDIO_DREQ_GPIO, &GPIO_InitStructure); + + GPIO_PinAFConfig(AUDIO_SPI_SCK_GPIO, AUDIO_SPI_SCK_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + GPIO_PinAFConfig(AUDIO_SPI_MISO_GPIO, AUDIO_SPI_MISO_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + GPIO_PinAFConfig(AUDIO_SPI_MOSI_GPIO, AUDIO_SPI_MOSI_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + + RCC_ClocksTypeDef RCC_Clocks; + RCC_GetClocksFreq(&RCC_Clocks); + + SPI_I2S_DeInit(AUDIO_SPI); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_Init(AUDIO_SPI, &SPI_InitStructure); + SPI_Cmd(AUDIO_SPI, ENABLE); + + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_RXNE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TXE); +} + +void audioSpiSetSpeed(uint8_t speed) +{ + AUDIO_SPI->CR1 &= 0xFFC7; // Fsck=Fcpu/256 + switch (speed) { + case SPI_SPEED_2: + AUDIO_SPI->CR1 |= 0x00 << 3; // Fsck=Fpclk/2=36Mhz + break; + case SPI_SPEED_4: + AUDIO_SPI->CR1 |= 0x01 << 3; // Fsck=Fpclk/4=18Mhz + break; + case SPI_SPEED_8: + AUDIO_SPI->CR1 |= 0x02 << 3; // Fsck=Fpclk/8=9Mhz + break; + case SPI_SPEED_16: + AUDIO_SPI->CR1 |= 0x03 << 3; // Fsck=Fpclk/16=4.5Mhz + break; + case SPI_SPEED_32: + AUDIO_SPI->CR1 |= 0x04 << 3; // Fsck=Fpclk/32=2.25Mhz + break; + case SPI_SPEED_64: + AUDIO_SPI->CR1 |= 0x05 << 3; // Fsck=Fpclk/16=1.125Mhz + break; + case SPI_SPEED_128: + AUDIO_SPI->CR1 |= 0x06 << 3; // Fsck=Fpclk/16=562.5Khz + break; + case SPI_SPEED_256: + AUDIO_SPI->CR1 |= 0x07 << 3; // Fsck=Fpclk/16=281.25Khz + break; + } + AUDIO_SPI->CR1 |= 0x01 << 6; +} + +uint8_t audioSpiReadWriteByte(uint8_t value) +{ + uint16_t time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_TXE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(AUDIO_SPI, DISABLE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(AUDIO_SPI, ENABLE); + break; + } + } + SPI_I2S_SendData(AUDIO_SPI, value); + + time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_RXNE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(AUDIO_SPI, DISABLE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(AUDIO_SPI, ENABLE); + break; + } + } + return SPI_I2S_ReceiveData(AUDIO_SPI); +} + +uint8_t audioWaitDreq(int32_t delay_us) +{ + while (READ_DREQ() == 0) { + if (delay_us-- == 0) return 0; + delay_01us(10); + } + return 1; +} + +uint16_t audioSpiReadReg(uint8_t address) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_READ_COMMAND); + audioSpiReadWriteByte(address); + uint16_t result = audioSpiReadWriteByte(0xff) << 8; + result += audioSpiReadWriteByte(0xff); + delay_01us(100); // 10us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return result; +} + +uint16_t audioSpiReadCmd(uint8_t address) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_READ_COMMAND); + audioSpiReadWriteByte(address); + uint16_t result = audioSpiReadWriteByte(0) << 8; + result |= audioSpiReadWriteByte(0); + delay_01us(50); // 5us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return result; +} + +uint8_t audioSpiWriteCmd(uint8_t address, uint16_t data) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_WRITE_COMMAND); + audioSpiReadWriteByte(address); + audioSpiReadWriteByte(data >> 8); + audioSpiReadWriteByte(data); + delay_01us(50); // 5us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return 1; +} + +void audioResetDecodeTime(void) +{ + audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000); + audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000); +} + +uint8_t audioHardReset(void) +{ + uint8_t retry=0; + RST_LOW(); + delay_ms(20); + XDCS_HIGH(); + CS_HIGH(); + RST_HIGH(); + while (READ_DREQ() == 0 && retry < 200) + { + retry++; + delay_us(50); + } + delay_ms(20); // 20ms + return retry < 200; +} + +uint8_t audioSoftReset(void) +{ + audioSpiSetSpeed(SPI_SPEED_64); + if (!audioWaitDreq(100)) + { + TRACE("audioSoftReset !audioWaitDreq"); + return 0; + } + + audioSpiReadWriteByte(0Xff); + + uint8_t retry = 0; + while (audioSpiReadReg(SPI_MODE) != 0x0800 && retry < 100) { + retry++; + audioSpiWriteCmd(SPI_MODE, 0x0804); + } + // wait for set up successful + retry = 0; + while (audioSpiReadReg(SPI_CLOCKF) != 0x9800 && retry < 100) { + retry++; + audioSpiWriteCmd(SPI_CLOCKF, 0x9800); + } + audioResetDecodeTime(); // reset the decoding time + audioSpiSetSpeed(SPI_SPEED_8); + XDCS_LOW(); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + delay_01us(100); // 10us + XDCS_HIGH(); + return 1; +} + +uint32_t audioSpiWriteData(const uint8_t * buffer, uint32_t size) +{ + XDCS_LOW(); + + uint32_t index = 0; + while (index < size && READ_DREQ() != 0) { + for (int i=0; i 0) { + uint32_t written = audioSpiWriteData(p, size); + p += written; + size -= written; + } +} + +const uint8_t RiffHeader[] = { + 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, + 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +void audioSendRiffHeader() +{ + audioSpiWriteBuffer(RiffHeader, sizeof(RiffHeader)); +} + +void audioOn() +{ + GPIO_SetBits(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN); +} + +void audioOff() +{ + GPIO_ResetBits(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN); +} + +void audioAmpInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = AUDIO_SHUTDOWN_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(AUDIO_SHUTDOWN_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN); +} + +bool hardResetDone = false; +bool softResetDone = false; + +bool isAudioReady() +{ + return hardResetDone && softResetDone; +} + +void audioWaitReady() +{ + while (!isAudioReady()) { + audioChipReset(); + RTOS_WAIT_MS(1000); + } +} + +bool audioChipReset() +{ + audioSpiSetSpeed(SPI_SPEED_64); + if (!hardResetDone) { + hardResetDone = audioHardReset() > 0; + softResetDone = false; + } + if (!softResetDone) { + softResetDone = audioSoftReset() > 0; + } + audioSpiSetSpeed(SPI_SPEED_8); + if (hardResetDone && softResetDone) { + audioSendRiffHeader(); + audioOn(); + } + return hardResetDone && softResetDone; +} + +void audioInit() +{ + audioAmpInit(); + audioSpiInit(); +} + +uint8_t * currentBuffer = nullptr; +uint32_t currentSize = 0; +int16_t newVolume = -1; + +void audioSetCurrentBuffer(const AudioBuffer * buffer) +{ + if (buffer) { + currentBuffer = (uint8_t *)buffer->data; + currentSize = buffer->size * 2; + } + else { + currentBuffer = nullptr; + currentSize = 0; + } +} + +void audioConsumeCurrentBuffer() +{ + if (!hardResetDone || !softResetDone) { + return; + } + + if (newVolume >= 0) { + uint8_t value = newVolume; + audioSpiWriteCmd(SPI_VOL, (value << 8) + value); + audioSendRiffHeader(); + newVolume = -1; + } + + if (!currentBuffer) { + audioSetCurrentBuffer(audioQueue.buffersFifo.getNextFilledBuffer()); + } + + if (currentBuffer) { + uint32_t written = audioSpiWriteData(currentBuffer, currentSize); + currentBuffer += written; + currentSize -= written; + if (currentSize == 0) { + audioQueue.buffersFifo.freeNextFilledBuffer(); + currentBuffer = nullptr; + currentSize = 0; + } + } +} + +// adjust this value for a volume level just above the silence +// values is attenuation in dB, higher value - less volume +// max value is 126 +#define VOLUME_MIN_DB 40 + +void setScaledVolume(uint8_t volume) +{ + if (volume > VOLUME_LEVEL_MAX) { + volume = VOLUME_LEVEL_MAX; + } + // maximum volume is 0x00 and total silence is 0xFE + if (volume == 0) { + setVolume(0xFE); // silence + } + else { + uint32_t vol = (VOLUME_MIN_DB * 2) - ((uint32_t)volume * (VOLUME_MIN_DB * 2)) / VOLUME_LEVEL_MAX; + setVolume(vol); + } +} + +void setVolume(uint8_t volume) +{ + newVolume = volume; +} + +int32_t getVolume() +{ + return -1; // TODO +} + +#endif diff --git a/radio/src/targets/pl18/backlight_driver.cpp b/radio/src/targets/pl18/backlight_driver.cpp new file mode 100644 index 00000000000..7a324fd43d6 --- /dev/null +++ b/radio/src/targets/pl18/backlight_driver.cpp @@ -0,0 +1,76 @@ +/* + * 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" + +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 isBacklightEnabled() { + return lastDutyCycle != 0; +} diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp new file mode 100644 index 00000000000..612b81756a1 --- /dev/null +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -0,0 +1,437 @@ +/* + * 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" + +#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 +void TouchInit(); + +//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; + TouchInit(); + } + 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..795a20dccbc --- /dev/null +++ b/radio/src/targets/pl18/board.cpp @@ -0,0 +1,336 @@ +/* + * 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/module_ports.h" + +#include "hal/adc_driver.h" +#include "hal/trainer_driver.h" +#include "tp_cst340.h" + +#include "globals.h" +#if defined(SDCARD) +#include "sdcard.h" +#endif +#include "touch.h" +#include "debug.h" + +#include "stm32_hal_adc.h" +#include "flysky_gimbal_driver.h" +#include "timers_driver.h" +#include "../../debounce.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 + +HardwareOptions hardwareOptions; + +void watchdogInit(unsigned int duration) +{ + IWDG->KR = 0x5555; // Unlock registers + IWDG->PR = 3; // Divide by 32 => 1kHz clock + IWDG->KR = 0x5555; // Unlock registers + IWDG->RLR = duration; // 1.5 seconds nominal + IWDG->KR = 0xAAAA; // reload + IWDG->KR = 0xCCCC; // start +} + +#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 |\ + FLASH_RCC_AHB1Periph \ + ) +#define RCC_AHB1PeriphOther (SD_RCC_AHB1Periph |\ + AUDIO_RCC_AHB1Periph |\ + MONITOR_RCC_AHB1Periph |\ + KEYS_RCC_AHB1Periph |\ + ADC_RCC_AHB1Periph |\ + AUX_SERIAL_RCC_AHB1Periph |\ + TELEMETRY_RCC_AHB1Periph |\ + TRAINER_RCC_AHB1Periph |\ + HAPTIC_RCC_AHB1Periph |\ + FLYSKY_HALL_RCC_AHB1Periph |\ + EXTMODULE_RCC_AHB1Periph\ + ) +#define RCC_AHB3PeriphMinimum (SDRAM_RCC_AHB3Periph) + +#define RCC_APB1PeriphMinimum (INTERRUPT_xMS_RCC_APB1Periph |\ + TIMER_2MHz_RCC_APB1Periph |\ + BACKLIGHT_RCC_APB1Periph \ + ) + +#define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ + AUDIO_RCC_APB1Periph |\ + FLYSKY_HALL_RCC_APB1Periph |\ + MIXER_SCHEDULER_TIMER_RCC_APB1Periph \ + ) +#define RCC_APB2PeriphMinimum (LCD_RCC_APB2Periph |\ + FLASH_RCC_APB2Periph \ + ) +#define RCC_APB2PeriphOther (ADC_RCC_APB2Periph |\ + 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) + serialInit(SP_AUX1, UART_MODE_DEBUG); +#endif + + TRACE("\nPL18 board started :)"); + delay_ms(10); + TRACE("RCC->CSR = %08x", RCC->CSR); + + pwrInit(); + boardInitModulePorts(); + + init_trainer(); + battery_charge_init(); + globalData.flyskygimbals = flysky_gimbal_init(); + init2MhzTimer(); + init1msTimer(); + TouchInit(); + usbInit(); + flashInit(); + + uint32_t press_start = 0; + uint32_t press_end = 0; + +/* if (UNEXPECTED_SHUTDOWN()) { + pwrOn(); + } else { + // prime debounce state... + uint8_t usb_state = usbPlugged(); + usb_state |= usbPlugged(); + while (usb_state) { + 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 (!usbPlugged()){ + delay_ms(20); + if(!usbPlugged()){ + 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(20); + 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; + } + } + } + + +/* if (UNEXPECTED_SHUTDOWN()) { + pwrOn(); + } else { + while (isChargerActive()) { + uint32_t now = get_tmr10ms(); + if (pwrPressed()) { + press_end = now; + if (press_start == 0) press_start = now; + if ((now - press_start) > POWER_ON_DELAY) { + pwrOn(); + break; + } + } else { + uint32_t press_end_touch = press_end; + if (touchPanelEventOccured()) { + TouchState ts = touchPanelRead(); + if(ts.event == TE_UP) + press_end_touch = get_tmr10ms(); + } + press_start = 0; + handle_battery_charge(press_end_touch); + delay_ms(10); + press_end = 0; + } + } + }*/ + + keysInit(); + audioInit(); + monitorInit(); + adcInit(&stm32_hal_adc_driver); + hapticInit(); + + + #if defined(RTCLOCK) + rtcInit(); // RTC must be initialized before rambackupRestore() is called +#endif + + +#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 +} + +void boardOff() +{ +// lcd->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_THEME_FOCUS); +#if defined(SPI_FLASH) + flushFTL(); +#endif + + lcdOff(); + + while (pwrPressed()) { + WDG_RESET(); + } + + SysTick->CTRL = 0; // turn off systick + + // Shutdown the Haptic + hapticDone(); + +#if defined(RTC_BACKUP_RAM) + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, DISABLE); + PWR_BackupRegulatorCmd(DISABLE); +#endif + +#if !defined(BOOT) + if (isChargerActive()) +// if (usbPlugged()) + { + 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 PinDebounce debounce; + return debounce.debounce(UCHARGER_GPIO, UCHARGER_GPIO_PIN); +} + diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h new file mode 100644 index 00000000000..acac5d0d872 --- /dev/null +++ b/radio/src/targets/pl18/board.h @@ -0,0 +1,531 @@ +/* + * 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" + +#if !defined(LUA_EXPORT_GENERATION) +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_dma2d.h" +#include "stm32f4xx_ltdc.h" +#include "stm32f4xx_fmc.h" +#endif + +#include "tp_cst340.h" +#include "lcd_driver.h" +#include "battery_driver.h" +#include "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) +#define TRAINER_CONNECTED() (true) + +PACK(typedef struct { + uint8_t pxx2Enabled:1; +}) HardwareOptions; + +extern HardwareOptions hardwareOptions; + +// Board driver +void boardInit(); +void boardOff(); + +// Timers driver +void init2MhzTimer(); +void init1msTimer(); + +// CPU Unique ID +#define LEN_CPU_UID (3*8+2) +void getCPUUniqueID(char * s); + +// SD driver +#define BLOCK_SIZE 512 /* Block Size in Bytes */ +#if !defined(SIMU) || defined(SIMU_DISKIO) +uint32_t sdIsHC(); +uint32_t sdGetSpeed(); +#define SD_IS_HC() (sdIsHC()) +#define SD_GET_SPEED() (sdGetSpeed()) +#define SD_GET_FREE_BLOCKNR() (sdGetFreeSectors()) +#if defined(SDCARD) +#define SD_CARD_PRESENT() true +#else +#define SD_CARD_PRESENT() false +#endif +void sdInit(); +void sdMount(); +void sdDone(); +#define sdPoll10ms() +uint32_t sdMounted(); +#else +#define SD_IS_HC() (0) +#define SD_GET_SPEED() (0) +#define sdInit() +#define sdMount() +#define sdDone() +#define SD_CARD_PRESENT() true +#endif + +// 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) +#define IS_PXX2_INTERNAL_ENABLED() (false) + +#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) + +void init_intmodule_heartbeat(); +void check_intmodule_heartbeat(); + +//void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +void extmoduleSerialStart(); +void extmoduleSendNextFrame(); +void extmoduleSendInvertedByte(uint8_t byte); + +// Trainer driver +void init_trainer_ppm(); +void stop_trainer_ppm(); +void init_trainer_capture(); +void stop_trainer_capture(); + +// Keys driver +enum EnumKeys +{ + KEY_ENTER, + KEY_EXIT, + KEY_PGUP, + KEY_PGDN, + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_MODEL, + KEY_RADIO, + KEY_TELEM, + TRM_BASE, + TRM_LH_DWN = TRM_BASE, + TRM_LH_UP, + TRM_LV_DWN, + TRM_LV_UP, + TRM_RV_DWN, + TRM_RV_UP, + TRM_RH_DWN, + TRM_RH_UP, + TRM_LS_DWN, + TRM_LS_UP, + TRM_RS_DWN, + TRM_RS_UP, + TRM_EX1_DWN, + TRM_EX1_UP, + TRM_EX2_DWN, + TRM_EX2_UP, + TRM_LAST = TRM_EX2_UP, + NUM_KEYS +}; + +#define IS_SHIFT_KEY(index) (false) +#define IS_SHIFT_PRESSED() (false) +enum VirtualKeys { + VKEY_MIN, + VKEY_MAX, + VKEY_INC, + VKEY_DEC, + VKEY_INC_LARGE, + VKEY_DEC_LARGE, + VKEY_DEFAULT, +}; + +enum LUATouchEvent { + TOUCH_DOWN = 1, + TOUCH_UP, + TOUCH_SLIDE_UP, + TOUCH_SLIDE_DOWN, + TOUCH_SLIDE_LEFT, + TOUCH_SLIDE_RIGHT, +}; + +enum EnumSwitches +{ + SW_SA, + SW_SB, + SW_SC, + SW_SD, + SW_SE, + SW_SF, + SW_SG, + SW_SH, + NUM_SWITCHES +}; + +#define STORAGE_NUM_SWITCHES NUM_SWITCHES +#define DEFAULT_SWITCH_CONFIG (SWITCH_3POS << 14) + (SWITCH_3POS << 12) + (SWITCH_2POS << 10) + (SWITCH_3POS << 8) + (SWITCH_3POS << 6) + (SWITCH_2POS << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); /* SWH ... SWA */ + +enum EnumSwitchesPositions +{ + SW_SA0, + SW_SA1, + SW_SA2, + SW_SB0, + SW_SB1, + SW_SB2, + SW_SC0, + SW_SC1, + SW_SC2, + SW_SD0, + SW_SD1, + SW_SD2, + SW_SE0, + SW_SE1, + SW_SE2, + SW_SF0, + SW_SF1, + SW_SF2, + SW_SG0, + SW_SG1, + SW_SG2, + SW_SH0, + SW_SH1, + SW_SH2, + NUM_SWITCHES_POSITIONS +}; + +#define STORAGE_NUM_SWITCHES_POSITIONS (STORAGE_NUM_SWITCHES * 3) + +#if !defined(NUM_FUNCTIONS_SWITCHES) +#define NUM_FUNCTIONS_SWITCHES 0 +#endif + +void monitorInit(); +void keysInit(); +uint8_t keyState(uint8_t index); +uint32_t switchState(uint8_t index); +uint32_t readKeys(); +uint32_t readTrims(); +#define NUM_TRIMS 8 +#define NUM_TRIMS_KEYS (NUM_TRIMS * 2) +#define TRIMS_PRESSED() (readTrims()) +#define KEYS_PRESSED() (readKeys()) +#define DBLKEYS_PRESSED_RGT_LFT(in) (false) +#define DBLKEYS_PRESSED_UP_DWN(in) (false) +#define DBLKEYS_PRESSED_RGT_UP(in) (false) +#define DBLKEYS_PRESSED_LFT_DWN(in) (false) + +// ADC driver +#define NUM_POTS 3 +#define NUM_XPOTS 0 // NUM_POTS +#define NUM_SLIDERS 2 +#define NUM_PWMSTICKS 0 +#define NUM_MOUSE_ANALOGS 0 +#define STORAGE_NUM_POTS 3 +#define STORAGE_NUM_SLIDERS 2 +#define STORAGE_NUM_MOUSE_ANALOGS 0 + +enum Analogs { + STICK1, + STICK2, + STICK3, + STICK4, + POT_FIRST, + POT1 = POT_FIRST, + POT2, + POT3, + POT_LAST = POT_FIRST + NUM_POTS - 1, + SLIDER_FIRST, + SLIDER_FRONT_LEFT = SLIDER_FIRST, + SLIDER_FRONT_RIGHT, + SLIDER_LAST = SLIDER_FIRST + NUM_SLIDERS - 1, + SWITCH_FIRST, + SWB = SWITCH_FIRST, + SWD, + SWE, + SWF, + SWG, + SWH, + SUB_ANALOG_POS = SWH, + SWITCH_END = SWH, + TX_VOLTAGE, + TX_VBAT, + NUM_ANALOGS +}; + +#define HARDWARE_POT3 + +#define SLIDER1 SLIDER_FRONT_LEFT +#define SLIDER2 SLIDER_FRONT_RIGHT + +#define DEFAULT_STICK_DEADZONE 2 + +#define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 4) + (POT_WITHOUT_DETENT << 2) + (POT_WITH_DETENT << 0) // VRA and VRC pots with detent, VRB without +#define DEFAULT_SLIDERS_CONFIG (SLIDER_WITH_DETENT << 1) + (SLIDER_WITH_DETENT << 0) + +enum CalibratedAnalogs { + CALIBRATED_STICK1, + CALIBRATED_STICK2, + CALIBRATED_STICK3, + CALIBRATED_STICK4, + CALIBRATED_POT1, + CALIBRATED_POT2, + CALIBRATED_POT3, + CALIBRATED_SLIDER_REAR_LEFT, + CALIBRATED_SLIDER_REAR_RIGHT, + CALIBRATED_SWB, + CALIBRATED_SWD, + CALIBRATED_SWE, + CALIBRATED_SWF, + CALIBRATED_SWG, + CALIBRATED_SWH, + NUM_CALIBRATED_ANALOGS +}; + +#define IS_POT(x) ((x)>=POT_FIRST && (x)<=POT_LAST) +#define IS_SLIDER(x) ((x)>=SLIDER_FIRST && (x)<=SLIDER_LAST) + +extern uint16_t adcValues[NUM_ANALOGS]; + + +#define BATTERY_WARN 37 // 3.7V +#define BATTERY_MIN 35 // 3.4V +#define BATTERY_MAX 43 // 4.3V +#define BATTERY_DIVIDER 962 + +enum EnumPowerupState +{ + BOARD_POWER_OFF = 0xCAFEDEAD, + BOARD_POWER_ON = 0xDEADBEEF, + BOARD_STARTED = 0xBAADF00D, + BOARD_REBOOT = 0xC00010FF, +}; + + +#if defined(__cplusplus) +enum PowerReason { + SHUTDOWN_REQUEST = 0xDEADBEEF, + SOFTRESET_REQUEST = 0xCAFEDEAD, +}; + +constexpr uint32_t POWER_REASON_SIGNATURE = 0x0178746F; + +inline bool UNEXPECTED_SHUTDOWN() +{ +#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) + return false; +#else + if (WAS_RESET_BY_WATCHDOG()) + return true; + else if (WAS_RESET_BY_SOFTWARE()) + return RTC->BKP0R != SOFTRESET_REQUEST; + else + return RTC->BKP1R == POWER_REASON_SIGNATURE && RTC->BKP0R != SHUTDOWN_REQUEST; +#endif +} + +inline void SET_POWER_REASON(uint32_t value) +{ + RTC->BKP0R = value; + RTC->BKP1R = POWER_REASON_SIGNATURE; +} +#endif + +#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(); +#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() + +// LCD driver +#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 +void lcdInit(); +void lcdRefresh(); +void lcdCopy(void * dest, void * src); +void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); +void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format); +void lcdStoreBackupBuffer(); +int lcdRestoreBackupBuffer(); +void lcdSetContrast(); +void lcdOff(); +void lcdOn(); +#define lcdSetRefVolt(...) +#define lcdRefreshWait(...) + +// Backlight driver +void backlightInit(); +#if defined(SIMU) || !defined(__cplusplus) +#define backlightEnable(...) +#define isBacklightEnabled() (true) +#else +void backlightEnable(uint8_t dutyCycle = 0); +bool isBacklightEnabled(); +#endif + +#define BACKLIGHT_LEVEL_MAX 100 +#define BACKLIGHT_FORCED_ON BACKLIGHT_LEVEL_MAX + 1 +#define BACKLIGHT_LEVEL_MIN 1 + +#define BACKLIGHT_ENABLE() backlightEnable(globalData.unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : BACKLIGHT_LEVEL_MAX - currentBacklightBright) +#define BACKLIGHT_DISABLE() backlightEnable(globalData.unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : ((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && (g_eeGeneral.backlightMode != e_backlight_mode_off)) ? 0 : g_eeGeneral.blOffBright) + + +#if !defined(SIMU) +void usbJoystickUpdate(); +#endif +#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 */ + +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +// SPI Flash driver +void flashInit(); + +// 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..002145deab6 --- /dev/null +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -0,0 +1,313 @@ +/* + * 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); + +const uint8_t __bmp_background[] { +#include "bmp_background.lbm" +}; +LZ4Bitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background); + +#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() +{ + backlightEnable(BACKLIGHT_LEVEL_MAX); + lcdInitDisplayDriver(); +} + +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 - (DOUBLE_PADDING + 4), LCD_W - DOUBLE_PADDING, 2, BL_FOREGROUND); +} + +static void bootloaderDrawBackground() +{ + // we have plenty of memory, let's cache that background + static BitmapBuffer* _background = nullptr; + + if (!_background) { + _background = new BitmapBuffer(BMP_RGB565, LCD_W, LCD_H); + + for (int i=0; idrawBitmap(i, j, bg_bmp); + } + } + _background->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, + COLOR2FLAGS(BLACK), OPACITY(4)); + } + + if (_background) { + lcd->drawBitmap(0, 0, _background); + } + else { + 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(62, 75, LV_SYMBOL_CHARGE, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Write Firmware", BL_FOREGROUND); + pos += 8; + +#if defined(SPI_FLASH) + lcd->drawText(60, 110, LV_SYMBOL_WARNING, BL_FOREGROUND); + pos = lcd->drawText(84, 110, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(60, 145, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(84, 145, "Exit", BL_FOREGROUND); +#else + lcd->drawText(60, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(84, 110, "Exit", BL_FOREGROUND); +#endif + + pos -= 79; + lcd->drawSolidRect(79, 72 + (opt*35), pos, 26, 2, BL_SELECTED); + + lcd->drawBitmap(center - 55, 165, (const BitmapBuffer*)&BMP_PLUG_USB); + lcd->drawText(center, 250, "Or plug in a USB cable", CENTERED | BL_FOREGROUND); + lcd->drawText(center, 275, "for mass storage", CENTERED | BL_FOREGROUND); + + bootloaderDrawFooter(); + lcd->drawText(center, LCD_H - DOUBLE_PADDING, + "Current Firmware:", CENTERED | BL_FOREGROUND); + lcd->drawText(center, LCD_H - DEFAULT_PADDING, + getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); + } +#if defined(SPI_FLASH) && defined(SDCARD) + else if (st == ST_SELECT_STORAGE) { + + bootloaderDrawTitle(LV_SYMBOL_DIRECTORY " select storage"); + + lcd->drawText(62, 75, LV_SYMBOL_DIRECTORY, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Internal", BL_FOREGROUND); + + pos += 8; + + lcd->drawText(60, 110, LV_SYMBOL_SD_CARD, BL_FOREGROUND); + lcd->drawText(84, 110, "SD Card", BL_FOREGROUND); + + pos -= 79; + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "[R TRIM] to select storage", BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE " [L TRIM] to exit", BL_FOREGROUND); + + } +#endif +#if defined(SPI_FLASH) + else if (st == ST_CLEAR_FLASH_CHECK) { + + bootloaderDrawTitle("erase internal flash storage"); + + lcd->drawText(62, 75, LV_SYMBOL_DRIVE, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Erase Flash Storage", BL_FOREGROUND); + pos += 8; + + lcd->drawText(60, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); + lcd->drawText(84, 110, "Exit", BL_FOREGROUND); + + pos -= 79; + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); + + bootloaderDrawFooter(); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to erase storage", BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, LV_SYMBOL_NEW_LINE "[L TRIM] to exit", BL_FOREGROUND); + } + else if (st == ST_CLEAR_FLASH) { + bootloaderDrawTitle("erasing internal flash storage"); + + lcd->drawText(62, 75, "This may take up to 150s", BL_FOREGROUND); + bootloaderDrawFooter(); + } +#endif + + else if (st == ST_USB) { + lcd->drawBitmap(center - 26, 98, (const BitmapBuffer*)&BMP_USB_PLUGGED); + lcd->drawText(center, 168, "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 " Directory is missing", BL_FOREGROUND); + } else { + lcd->drawText(20, MESSAGE_TOP, LV_SYMBOL_CLOSE " Directory is 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, + "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, + "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, + "Radio:", RIGHT | BL_FOREGROUND); + lcd->drawText(LCD_W / 4 + 6 + DEFAULT_PADDING, + MESSAGE_TOP + DEFAULT_PADDING, tag.flavour, + BL_FOREGROUND); + + lcd->drawText(LCD_W - DOUBLE_PADDING, MESSAGE_TOP - 10, + LV_SYMBOL_OK, BL_GREEN); + } + } + + bootloaderDrawFooter(); + + if (st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { + + lcd->drawText(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, + LV_SYMBOL_CHARGE, BL_FOREGROUND); + + if (st == ST_FILE_LIST) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "[R TRIM] to select file", BL_FOREGROUND); + } else if (st == ST_FLASH_CHECK && opt == FC_OK) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "Hold [R TRIM] long to flash", BL_FOREGROUND); + } else if (st == ST_FLASHING) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "Writing Firmware ...", BL_FOREGROUND); + } else if (st == ST_FLASH_DONE) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, + "Writing Completed", BL_FOREGROUND); + } + } + + if (st != ST_FLASHING) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE " [L TRIM] to exit", 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/diskio.cpp b/radio/src/targets/pl18/diskio.cpp new file mode 100644 index 00000000000..8de94e47895 --- /dev/null +++ b/radio/src/targets/pl18/diskio.cpp @@ -0,0 +1,392 @@ +/* + * 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. + */ + +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" +#include "debug.h" +#include "targets/common/arm/stm32/sdio_sd.h" + +#include + +// TODO share this with Horus (and perhaps other STM32) + +/*-----------------------------------------------------------------------*/ +/* Lock / unlock functions */ +/*-----------------------------------------------------------------------*/ +#if !defined(BOOT) +static RTOS_MUTEX_HANDLE ioMutex; +uint32_t ioMutexReq = 0, ioMutexRel = 0; +int ff_cre_syncobj (BYTE vol, FF_SYNC_t *mutex) +{ + *mutex = ioMutex; + return 1; +} + +int ff_req_grant (FF_SYNC_t mutex) +{ + ioMutexReq += 1; + RTOS_LOCK_MUTEX(mutex); + return 1; +} + +void ff_rel_grant (FF_SYNC_t mutex) +{ + ioMutexRel += 1; + RTOS_UNLOCK_MUTEX(mutex); +} + +int ff_del_syncobj (FF_SYNC_t mutex) +{ + return 1; +} +#endif + + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat = 0; + + /* Supports only single drive */ + if (drv) + { + stat |= STA_NOINIT; + } + + /*-------------------------- SD Init ----------------------------- */ + SD_Error res = SD_Init(); + if (res != SD_OK) + { + TRACE("SD_Init() failed: %d", res); + stat |= STA_NOINIT; + } + + TRACE("SD card info:"); + TRACE("sectors: %u", (uint32_t)(SDCardInfo.CardCapacity / 512)); + TRACE("type: %u", (uint32_t)(SDCardInfo.CardType)); + TRACE("EraseGrSize: %u", (uint32_t)(SDCardInfo.SD_csd.EraseGrSize)); + TRACE("EraseGrMul: %u", (uint32_t)(SDCardInfo.SD_csd.EraseGrMul)); + TRACE("ManufacturerID: %u", (uint32_t)(SDCardInfo.SD_cid.ManufacturerID)); + + return(stat); +} + +DWORD scratch[BLOCK_SIZE / 4] __DMA; + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat = 0; + + if (SD_Detect() != SD_PRESENT) + stat |= STA_NODISK; + + // STA_NOTINIT - Subsystem not initailized + // STA_PROTECTED - Write protected, MMC/SD switch if available + + return(stat); +} + +uint32_t sdReadRetries = 0; + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ + + +DRESULT disk_read_dma(BYTE drv, BYTE * buff, DWORD sector, UINT count) +{ + // this functions assumes that buff is properly aligned and in the right RAM segment for DMA + DRESULT res; + SD_Error Status; + SDTransferState State; + for (int retry=0; retry<3; retry++) { + res = RES_OK; + if (count == 1) { + Status = SD_ReadBlock(buff, sector, BLOCK_SIZE); // 4GB Compliant + } + else { + Status = SD_ReadMultiBlocks(buff, sector, BLOCK_SIZE, count); // 4GB Compliant + } + if (Status == SD_OK) { + Status = SD_WaitReadOperation(200*count); // Check if the Transfer is finished + while ((State = SD_GetStatus()) == SD_TRANSFER_BUSY); // BUSY, OK (DONE), ERROR (FAIL) + if (State == SD_TRANSFER_ERROR) { + TRACE("State=SD_TRANSFER_ERROR, c: %u", sector, (uint32_t)count); + res = RES_ERROR; + } + else if (Status != SD_OK) { + TRACE("Status(WaitRead)=%d, s:%u c: %u", Status, sector, (uint32_t)count); + res = RES_ERROR; + } + } + else { + TRACE("Status(ReadBlock)=%d, s:%u c: %u", Status, sector, (uint32_t)count); + res = RES_ERROR; + } + if (res == RES_OK) break; + sdReadRetries += 1; + } + return res; +} + +DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) +{ + // If unaligned, do the single block reads with a scratch buffer. + // If aligned and single sector, do a single block read. + // If aligned and multiple sectors, try multi block read. + // If multi block read fails, try single block reads without + // an intermediate buffer (move trough the provided buffer) + + // TRACE("disk_read %d %p %10d %d", drv, buff, sector, count); + if (SD_Detect() != SD_PRESENT) { + TRACE("SD_Detect() != SD_PRESENT"); + return RES_NOTRDY; + } + + DRESULT res = RES_OK; + if (count == 0) return res; + + if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) { + // buffer is not aligned, use scratch buffer that is aligned + TRACE("disk_read bad alignment (%p)", buff); + while (count--) { + res = disk_read_dma(drv, (BYTE *)scratch, sector++, 1); + if (res != RES_OK) break; + memcpy(buff, scratch, BLOCK_SIZE); + buff += BLOCK_SIZE; + } + return res; + } + + res = disk_read_dma(drv, buff, sector, count); + if (res != RES_OK && count > 1) { + // multi-read failed, try reading same sectors, one by one + TRACE("disk_read() multi-block failed, trying single block reads..."); + while (count--) { + res = disk_read_dma(drv, buff, sector++, 1); + if (res != RES_OK) break; + buff += BLOCK_SIZE; + } + } + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ + +#if _READONLY == 0 +DRESULT __disk_write( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + UINT count /* Number of sectors to write (1..255) */ +) +{ + SD_Error Status; + DRESULT res = RES_OK; + + // TRACE("disk_write %d %p %10d %d", drv, buff, sector, count); + + if (SD_Detect() != SD_PRESENT) + return(RES_NOTRDY); + + if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) { + TRACE("disk_write bad alignment (%p)", buff); + while(count--) { + memcpy(scratch, buff, BLOCK_SIZE); + + res = __disk_write(drv, (BYTE *)scratch, sector++, 1); + + if (res != RES_OK) + break; + + buff += BLOCK_SIZE; + } + return(res); + } + + if (count == 1) { + Status = SD_WriteBlock((uint8_t *)buff, sector, BLOCK_SIZE); // 4GB Compliant + } + else { + Status = SD_WriteMultiBlocks((uint8_t *)buff, sector, BLOCK_SIZE, count); // 4GB Compliant + } + + if (Status == SD_OK) { + SDTransferState State; + + Status = SD_WaitWriteOperation(500*count); // Check if the Transfer is finished + + while((State = SD_GetStatus()) == SD_TRANSFER_BUSY); // BUSY, OK (DONE), ERROR (FAIL) + + if ((State == SD_TRANSFER_ERROR) || (Status != SD_OK)) { + TRACE("__disk_write() err, st:%d,%d, s:%u c: %u", Status, State, sector, (uint32_t)count); + res = RES_ERROR; + } + } + else { + res = RES_ERROR; + } + + // TRACE("result=%d", res); + return res; +} +#endif /* _READONLY */ + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + // use 512 for sector size, SDCardInfo.CardBlockSize is not sector size and can be 1024 for 2G SD cards!!!! + *(DWORD*)buff = SDCardInfo.CardCapacity / BLOCK_SIZE; + res = RES_OK; + break; + + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = BLOCK_SIZE; // force sector size. SDCardInfo.CardBlockSize is not sector size and can be 1024 for 2G SD cards!!!! + res = RES_OK; + break; + + case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ + // TODO verify that this is the correct value + *(DWORD*)buff = (uint32_t)SDCardInfo.SD_csd.EraseGrSize * (uint32_t)SDCardInfo.SD_csd.EraseGrMul; + res = RES_OK; + break; + + case CTRL_SYNC: + while (SD_GetStatus() == SD_TRANSFER_BUSY); /* Complete pending write process (needed at _FS_READONLY == 0) */ + res = RES_OK; + break; + + default: + res = RES_OK; + break; + + } + + return res; +} + +// TODO everything here should not be in the driver layer ... + +FATFS g_FATFS_Obj __DMA; // initialized in boardInit() +#if defined(LOG_TELEMETRY) +FIL g_telemetryFile = {}; +#endif + +#if defined(BOOT) +void sdInit(void) +{ + if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { + f_chdir("/"); + } +} +#else + +#include "audio.h" +#include "sdcard.h" +#include "disk_cache.h" + +void sdInit() +{ + TRACE("sdInit"); + RTOS_CREATE_MUTEX(ioMutex); + sdMount(); +} + +void sdMount() +{ + TRACE("sdMount"); + +#if defined(DISK_CACHE) + diskCache.clear(); +#endif + + if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { + // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called + sdGetFreeSectors(); + +#if defined(LOG_TELEMETRY) + f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE); + if (f_size(&g_telemetryFile) > 0) { + f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append + } +#endif + } + else { + TRACE("f_mount() failed"); + } +} + +void sdDone() +{ + TRACE("sdDone"); + + if (sdMounted()) { + audioQueue.stopSD(); +#if defined(LOG_TELEMETRY) + f_close(&g_telemetryFile); +#endif + f_mount(NULL, "", 0); // unmount SD + } +} +#endif + +uint32_t sdMounted() +{ + return g_FATFS_Obj.fs_type != 0; +} + +uint32_t sdIsHC() +{ + return true; // TODO (CardType & CT_BLOCK); +} + +uint32_t sdGetSpeed() +{ + return 330000; +} 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..6aed6cd1b0d --- /dev/null +++ b/radio/src/targets/pl18/hal.h @@ -0,0 +1,614 @@ +/* + * 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_TR1U GPIOH->IDR +#define TRIMS_GPIO_PIN_TR1U GPIO_Pin_8 // PH.08 +#define TRIMS_GPIO_REG_TR1D GPIOH->IDR +#define TRIMS_GPIO_PIN_TR1D GPIO_Pin_9 // PH.09 +#define TRIMS_GPIO_REG_TR2U GPIOH->IDR +#define TRIMS_GPIO_PIN_TR2U GPIO_Pin_10 // PH.10 +#define TRIMS_GPIO_REG_TR2D GPIOH->IDR +#define TRIMS_GPIO_PIN_TR2D 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 GPIO_Pin_2 // PG.02 +#define TRIMS_GPIO_OUT2 GPIOG +#define TRIMS_GPIO_OUT2_PIN GPIO_Pin_10 // PG.10 +#define TRIMS_GPIO_OUT3 GPIOG +#define TRIMS_GPIO_OUT3_PIN 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 GPIO_Pin_7 // PH.07 + +#define TRIMS_GPIO_REG_IN1 GPIOB->IDR +#define TRIMS_GPIO_PIN_IN1 GPIO_Pin_15 // PB.15 +#define TRIMS_GPIO_REG_IN2 GPIOC->IDR +#define TRIMS_GPIO_PIN_IN2 GPIO_Pin_13 // PC.13 +#define TRIMS_GPIO_REG_IN3 GPIOD->IDR +#define TRIMS_GPIO_PIN_IN3 GPIO_Pin_7 // PD.07 +#define TRIMS_GPIO_REG_IN4 GPIOJ->IDR +#define TRIMS_GPIO_PIN_IN4 GPIO_Pin_12 // PJ.12 + +// Monitor pin +#define MONITOR_RCC_AHB1Periph (RCC_AHB1Periph_GPIOJ) +#define VBUS_MONITOR_GPIO (GPIOJ) +#define VBUS_MONITOR_PIN (GPIO_Pin_14) + +// Switches +#define HARDWARE_SWITCH_A +#define STORAGE_SWITCH_A +// 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->IDR +//#define SWITCHES_GPIO_PIN_A_H GPIO_Pin_8 // PC.08 +#define SWITCHES_GPIO_REG_A_L GPIOC->IDR +#define SWITCHES_GPIO_PIN_A_L GPIO_Pin_9 // PC.09 +#define HARDWARE_SWITCH_B +#define STORAGE_SWITCH_B +#define HARDWARE_SWITCH_C +#define STORAGE_SWITCH_C +// High rail of Switch C is not required and thus PC10 is free to use for customizations. +//#define SWITCHES_GPIO_REG_C_H GPIOC->IDR +//#define SWITCHES_GPIO_PIN_C_H GPIO_Pin_10 // PC.10 +#define SWITCHES_GPIO_REG_C_L GPIOC->IDR +#define SWITCHES_GPIO_PIN_C_L GPIO_Pin_11 // PC.11 +#define HARDWARE_SWITCH_D +#define STORAGE_SWITCH_D +#define HARDWARE_SWITCH_E +#define STORAGE_SWITCH_E +#define HARDWARE_SWITCH_F +#define STORAGE_SWITCH_F +#define HARDWARE_SWITCH_G +#define STORAGE_SWITCH_G +#define HARDWARE_SWITCH_H +#define STORAGE_SWITCH_H + +// Index of all switches / trims +#define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOJ) +#define KEYS_GPIOB_PINS (GPIO_Pin_15) +#define KEYS_GPIOC_PINS (GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_13 ) // PC8 allocated to SDIO D0, is not required to sample SWA ! +#define KEYS_GPIOD_PINS (GPIO_Pin_7) +#define KEYS_GPIOH_PINS (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11) +#define KEYS_GPIOJ_PINS (GPIO_Pin_12) +#define KEYS_OUT_GPIOG_PINS (GPIO_Pin_2 | GPIO_Pin_10 | GPIO_Pin_11) +#define KEYS_OUT_GPIOH_PINS (GPIO_Pin_7) + +// ADC +#define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_DMA2) +#define ADC_RCC_APB2Periph (RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC3) +#define ADC_GPIO_PIN_POT1 GPIO_Pin_6 // PA.06 VRA +#define ADC_GPIO_PIN_POT2 GPIO_Pin_4 // PC.04 VRB +#define ADC_GPIO_PIN_POT3 GPIO_Pin_8 // PF.08 VRC +#define ADC_GPIO_PIN_EXT1 GPIO_Pin_2 // PA.02 +#define ADC_GPIO_PIN_EXT2 GPIO_Pin_6 // PF.06 +#define ADC_GPIO_PIN_SLIDER1 GPIO_Pin_9 // PF.09 VRD/LS +#define ADC_GPIO_PIN_SLIDER2 GPIO_Pin_7 // PA.07 VRE/RS +#define ADC_GPIO_PIN_SWB GPIO_Pin_1 // PC.01 +#define ADC_GPIO_PIN_SWD GPIO_Pin_0 // PC.00 +#define ADC_GPIO_PIN_SWE GPIO_Pin_2 // PC.02 +#define ADC_GPIO_PIN_SWF GPIO_Pin_0 // PB.00 +#define ADC_GPIO_PIN_SWG GPIO_Pin_1 // PB.01 +#define ADC_GPIO_PIN_SWH GPIO_Pin_10 // PF.10 +#define ADC_GPIO_PIN_SWI GPIO_Pin_3 // PA.03 +#define ADC_GPIO_PIN_SWJ GPIO_Pin_5 // PA.05 +#define ADC_GPIO_PIN_BATT GPIO_Pin_5 // PC.05 + +#define ADC_GPIOA_PINS_FS (GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7) +#define ADC_GPIOA_PINS ADC_GPIOA_PINS_FS +#define ADC_GPIOB_PINS (GPIO_Pin_0 | GPIO_Pin_1) +#define ADC_GPIOC_PINS \ + (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_5) +#define ADC_GPIOF_PINS (GPIO_Pin_6 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10) + +#define ADC_CHANNEL_STICK_LH 0 +#define ADC_CHANNEL_STICK_LV 0 +#define ADC_CHANNEL_STICK_RH 0 +#define ADC_CHANNEL_STICK_RV 0 + +#define ADC_CHANNEL_POT1 ADC_Channel_6 // ADC12_IN6 -> ADC1_IN6 +#define ADC_CHANNEL_POT2 ADC_Channel_14 // ADC12_IN14 -> ADC1_IN14 +#define ADC_CHANNEL_POT3 ADC_Channel_6 // ADC3_IN6 -> ADC3_IN6 +#define ADC_CHANNEL_EXT1 ADC_Channel_2 // ADC123_IN2 -> ADC3_IN2 (Right stick end pot on PL18EV) +#define ADC_CHANNEL_EXT2 ADC_Channel_4 // ADC3_IN4 -> ADC3_IN4 (Left stick end pot on PL18EV) +#define ADC_CHANNEL_SLIDER1 ADC_Channel_7 // ADC3_IN7 -> ADC3_IN7 +#define ADC_CHANNEL_SLIDER2 ADC_Channel_7 // ADC12_IN7 -> ADC1_IN7 +#define ADC_CHANNEL_SWB ADC_Channel_11 // ADC123_IN11 -> ADC3_IN11 +#define ADC_CHANNEL_SWD ADC_Channel_10 // ADC123_IN10 -> ADC3_IN10 +#define ADC_CHANNEL_SWE ADC_Channel_12 // ADC123_IN12 -> ADC3_IN12 +#define ADC_CHANNEL_SWF ADC_Channel_8 // ADC12_IN8 -> ADC1_IN8 +#define ADC_CHANNEL_SWG ADC_Channel_9 // ADC12_IN9 -> ADC1_IN9 +#define ADC_CHANNEL_SWH ADC_Channel_8 // ADC3_IN8 -> ADC3_IN8 +#define ADC_CHANNEL_SWI ADC_Channel_3 // ADC123_IN3 -> ADC3_IN3 (Right stick end buttons on PL18EV) +#define ADC_CHANNEL_SWJ ADC_Channel_5 // ADC12_IN5 -> ADC1_IN5 (Left stick end buttons on PL18EV) + +#define ADC_CHANNEL_BATT ADC_Channel_15 // ADC12_IN15 -> ADC1_IN15 +#define ADC_MAIN ADC1 +#define ADC_EXT ADC3 +#define ADC_SAMPTIME 3 +#define ADC_DMA DMA2 +#define ADC_DMA_Channel DMA_Channel_0 +#define ADC_DMA_Stream DMA2_Stream4 +//#define ADC_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF4) +#define ADC_DMA_TC_Flag DMA_FLAG_TCIF4 + +#define ADC_EXT_DMA_Channel DMA_Channel_2 +#define ADC_EXT_DMA_Stream DMA2_Stream0 +#define ADC_EXT_TC_Flag DMA_FLAG_TCIF0 + +#define ADC_VREF_PREC2 330 + +// 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) + +// Led +// #define STATUS_LEDS +#define LED_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define LED_GPIO GPIOI +#define LED_GPIO_PIN GPIO_Pin_5 // PI.05 + +// 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 +// 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_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 GPIO_Pin_9 // PG.09 +#define LCD_SPI_GPIO GPIOE +#define LCD_SPI_CS_GPIO_PIN GPIO_Pin_4 // PE.04 +#define LCD_SPI_SCK_GPIO_PIN GPIO_Pin_2 // PE.02 +#define LCD_SPI_MISO_GPIO_PIN GPIO_Pin_5 // PE.05 +#define LCD_SPI_MOSI_GPIO_PIN 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 + +// SD card +#define SD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA2) +#define SD_RCC_APB1Periph 0 +/*#define SD_PRESENT_GPIO GPIOH +#define SD_PRESENT_GPIO_PIN GPIO_Pin_10 // PH.10 +*/ +#define SD_SDIO_DMA_STREAM DMA2_Stream3 +#define SD_SDIO_DMA_CHANNEL DMA_Channel_4 +#define SD_SDIO_DMA_FLAG_FEIF DMA_FLAG_FEIF3 +#define SD_SDIO_DMA_FLAG_DMEIF DMA_FLAG_DMEIF3 +#define SD_SDIO_DMA_FLAG_TEIF DMA_FLAG_TEIF3 +#define SD_SDIO_DMA_FLAG_HTIF DMA_FLAG_HTIF3 +#define SD_SDIO_DMA_FLAG_TCIF DMA_FLAG_TCIF3 +#define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn +#define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler +#define SD_SDIO_FIFO_ADDRESS ((uint32_t)0x40012C80) +#define SD_SDIO_CLK_DIV(fq) ((48000000 / (fq)) - 2) +#define SD_SDIO_INIT_CLK_DIV SD_SDIO_CLK_DIV(400000) +#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) + +// 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 + +// SPI FLASH +#define FLASH_RCC_AHB1Periph RCC_AHB1Periph_GPIOG +#define FLASH_RCC_APB2Periph RCC_APB2Periph_SPI6 +#define FLASH_SPI SPI6 +#define FLASH_SPI_GPIO_AF GPIO_AF_SPI6 +#define FLASH_SPI_CS_GPIO GPIOG +#define FLASH_SPI_CS_GPIO_PIN GPIO_Pin_6 // PG.06 +#define FLASH_SPI_SCK_GPIO GPIOG +#define FLASH_SPI_SCK_GPIO_PIN GPIO_Pin_13 // PG.13 +#define FLASH_SPI_SCK_GPIO_PinSource GPIO_PinSource13 +#define FLASH_SPI_MISO_GPIO GPIOG +#define FLASH_SPI_MISO_GPIO_PIN GPIO_Pin_12 // PG.12 +#define FLASH_SPI_MISO_GPIO_PinSource GPIO_PinSource12 +#define FLASH_SPI_MOSI_GPIO GPIOG +#define FLASH_SPI_MOSI_GPIO_PIN GPIO_Pin_14 // PG.14 +#define FLASH_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 +#define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_1 +#define FLASH_SPI_TX_DMA_STREAM DMA2_Stream5 +#define FLASH_SPI_TX_DMA_IRQn DMA2_Stream5_IRQn +#define FLASH_SPI_TX_DMA_IRQHandler DMA2_Stream5_IRQHandler +#define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF5 +#define FLASH_SPI_TX_DMA_STATUS_REG HISR +#define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_1 +#define FLASH_SPI_RX_DMA_STREAM DMA2_Stream6 +#define FLASH_SPI_RX_DMA_IRQn DMA2_Stream6_IRQn +#define FLASH_SPI_RX_DMA_IRQHandler DMA2_Stream6_IRQHandler +#define FLASH_SPI_RX_DMA_STATUS_REG HISR +#define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF6 + +// 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 - Touch +#define I2C_B1_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define I2C_B1_RCC_APB1Periph RCC_APB1Periph_I2C1 +#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 GPIO_AF4_I2C1 +//#define I2C_B1_SDA_GPIO_PinSource GPIO_PinSource7 +//#define I2C_B1_SCL_GPIO_PinSource GPIO_PinSource8 +//#define I2C_B1_CLK_RATE 100000 + +#define TOUCH_I2C_BUS I2C_Bus_1 +#define TOUCH_I2C_CLK_RATE 400000 + + +#define TOUCH_RST_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_RST_GPIO GPIOB +#define TOUCH_RST_GPIO_PIN LL_GPIO_PIN_12 // PB.12 + +#define TOUCH_INT_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_INT_GPIO GPIOB +#define TOUCH_INT_GPIO_PIN LL_GPIO_PIN_9 // PB.09 +#define TOUCH_INT_EXTI_LINE1 LL_EXTI_LINE_9 + +#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 +#define TOUCH_INT_EXTI_IRQn EXTI9_5_IRQn +#define TOUCH_INT_EXTI_IRQHandler EXTI9_5_IRQHandler + +// 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 + +// Internal Module +#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | 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 GPIO_AF_UART7 +#define INTMODULE_GPIO_AF_LL 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_IT_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) + +// External Module +#define EXTMODULE +#define EXTMODULE_PULSES +#define EXTMODULE_PWR_GPIO GPIOD +#define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_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) + + +//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 + +// X ms Interrupt +#define INTERRUPT_xMS_RCC_APB1Periph RCC_APB1Periph_TIM14 +#define INTERRUPT_xMS_TIMER TIM14 +#define INTERRUPT_xMS_IRQn TIM8_TRG_COM_TIM14_IRQn +#define INTERRUPT_xMS_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler + +// 2MHz Timer +#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 +#define TIMER_2MHz_TIMER TIM7 + +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM12 +#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 + +#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/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp new file mode 100644 index 00000000000..ead1b7c39a9 --- /dev/null +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -0,0 +1,335 @@ +/* + * 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 "keys.h" + +#include "hal/adc_driver.h" + +#if defined(LUA) +#include "lua/lua_api.h" +#endif + +enum PhysicalTrims +{ + TR3D, + TR3U, + TR4D, + TR4U, + TR5D, + TR5U, + TR6D, + TR6U, + TR7L, + TR7R, + TR8L, + TR8R +}; + +bool trimsAsButtons = false; + +void setTrimsAsButtons(bool val) { trimsAsButtons = val; } + +bool getTrimsAsButtons() +{ + bool lua = false; +#if defined(LUA) + lua = isLuaStandaloneRunning(); +#endif + return (trimsAsButtons || lua); +} + +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 + GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + GPIO_SetBits(TRIMS_GPIO_OUT4, TRIMS_GPIO_OUT4_PIN); + GPIO_ResetBits(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_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; + + GPIO_SetBits(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_PIN); + GPIO_ResetBits(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; + + GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + GPIO_ResetBits(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; + GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + syncelem.oldResult = result; + syncelem.ui8ReadInProgress = 0; + return result; +} + +uint32_t readKeys() +{ + uint32_t result = 0; + uint32_t mkeys = readKeyMatrix(); + +#ifndef BOOT + if (getTrimsAsButtons()) { +#endif + + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) + result |= 1 << KEY_RADIO; + if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) + result |= 1 << KEY_MODEL; + if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) + result |= 1 << KEY_TELEM; + + if (mkeys & (1 << TR3U)) result |= 1 << KEY_UP; + if (mkeys & (1 << TR3D)) result |= 1 << KEY_DOWN; + if (mkeys & (1 << TR5U)) result |= 1 << KEY_PGUP; + if (mkeys & (1 << TR5D)) result |= 1 << KEY_PGDN; + if (mkeys & (1 << TR7L)) result |= 1 << KEY_LEFT; + if (mkeys & (1 << TR7R)) result |= 1 << KEY_RIGHT; +#ifndef BOOT + } +#endif + + // Enter and Exit are always supported + 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; + if(getTrimsAsButtons()) return result; + + /* 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 + */ + + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) + result |= 1 << (TRM_LS_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) + result |= 1 << (TRM_LS_DWN - TRM_BASE); + + if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) + result |= 1 << (TRM_RS_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) + result |= 1 << (TRM_RS_DWN - TRM_BASE); + + uint32_t mkeys = readKeyMatrix(); + if (mkeys & (1 << TR3D)) result |= (1 << (TRM_EX1_DWN - TRM_BASE)); + if (mkeys & (1 << TR3U)) result |= (1 << (TRM_EX1_UP - TRM_BASE)); + if (mkeys & (1 << TR4D)) result |= (1 << (TRM_EX2_DWN - TRM_BASE)); + if (mkeys & (1 << TR4U)) result |= (1 << (TRM_EX2_UP - TRM_BASE)); + if (mkeys & (1 << TR5D)) result |= (1 << (TRM_LV_DWN - TRM_BASE)); + if (mkeys & (1 << TR5U)) result |= (1 << (TRM_LV_UP - TRM_BASE)); + if (mkeys & (1 << TR6D)) result |= (1 << (TRM_RV_DWN - TRM_BASE)); + if (mkeys & (1 << TR6U)) result |= (1 << (TRM_RV_UP - TRM_BASE)); + if (mkeys & (1 << TR7L)) result |= (1 << (TRM_LH_DWN - TRM_BASE)); + if (mkeys & (1 << TR7R)) result |= (1 << (TRM_LH_UP - TRM_BASE)); + if (mkeys & (1 << TR8L)) result |= (1 << (TRM_RH_DWN - TRM_BASE)); + if (mkeys & (1 << TR8R)) result |= (1 << (TRM_RH_UP - TRM_BASE)); + return result; +} + +bool trimDown(uint8_t idx) +{ + return readTrims() & (1 << idx); +} + +bool keyDown() +{ + return readKeys() || readTrims(); +} + +/* TODO common to ARM */ +/*void readKeysAndTrims() +{ + int i; + + uint8_t index = 0; + uint32_t in = readKeys(); + uint32_t trims = readTrims(); + + for (i = 0; i < TRM_BASE; i++) { + keys[index++].input(in & (1 << i)); + } + + for (i = 1; i <= 1 << (TRM_LAST-TRM_BASE); i <<= 1) { + keys[index++].input(trims & i); + }*/ +/* + if ((in || trims) && (g_eeGeneral.backlightMode & e_backlight_mode_keys)) { + // on keypress turn the light on + resetBacklightTimeout(); + } + */ +//} + +#if !defined(BOOT) +uint32_t switchState(uint8_t index) +{ + uint8_t analogIdx = 0; + // Switches A and C are wired to digital inputs, other switches are sampled via analog inputs. + switch (index) + { + // Switch A: + case SW_SA0: return (SWITCHES_GPIO_REG_A_L & SWITCHES_GPIO_PIN_A_L); break; + case SW_SA2: return (~SWITCHES_GPIO_REG_A_L & SWITCHES_GPIO_PIN_A_L); break; + // Switch B: + case SW_SB0: + case SW_SB1: + case SW_SB2: analogIdx = 9; break; + // Switch C: + case SW_SC0: return (SWITCHES_GPIO_REG_C_L & SWITCHES_GPIO_PIN_C_L); break; + case SW_SC2: return (~SWITCHES_GPIO_REG_C_L & SWITCHES_GPIO_PIN_C_L); break; + default: + // all further cases are analog switches (SWD, SWE, SWF, SWG, SWH) and follow a pattern + analogIdx = ((index - SW_SD0)/3) + 10; // SWD is analog 10, SWE 11 and so on + break; + } + + uint16_t value = getAnalogValue(analogIdx); + uint8_t position; + + if (value < 1024) + position = 0; + else if (value > 3 * 1024) + position = 2; + else + position = 1; + return position == (index % 3); +} +#endif + +void monitorInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + 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_DOWN; + + GPIO_InitStructure.GPIO_Pin = VBUS_MONITOR_PIN; + GPIO_Init(GPIOJ, &GPIO_InitStructure); +} + +void keysInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + 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_DOWN; + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOB_PINS; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOC_PINS; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOD_PINS; + GPIO_Init(GPIOD, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOH_PINS; + GPIO_Init(GPIOH, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOJ_PINS; + GPIO_Init(GPIOJ, &GPIO_InitStructure); + + // Matrix outputs + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + + GPIO_InitStructure.GPIO_Pin = KEYS_OUT_GPIOG_PINS; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_OUT_GPIOH_PINS; + GPIO_Init(GPIOH, &GPIO_InitStructure); +} diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp new file mode 100644 index 00000000000..2cd0bd5848b --- /dev/null +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -0,0 +1,3079 @@ +/* + * 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 "libopenui_config.h" +#include "lcd.h" + +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 = (uint32_t)buffer; + + // reload shadow registers on vertical blank + _frame_addr_reloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + // 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 < 100; 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 +}; + + +void GPIO_SetDirection( GPIO_TypeDef *GPIOx, unsigned char Pin, unsigned char IsInput ) +{ + unsigned int Mask; + unsigned int Position; + unsigned int Register; + + + Position = Pin << 1; + Mask = ~( 0x03UL << Position ); + + //EnterCritical(); + Register = GPIOx->OSPEEDR & Mask; + Register |= IO_SPEED_HIGH << Position; + GPIOx->OSPEEDR = Register; + //ExitCritical(); + + //EnterCritical(); + Register = GPIOx->MODER & Mask; + if( !IsInput ) + { + Register |= IO_MODE_OUTPUT << Position; + } + + GPIOx->MODER = Register; + //ExitCritical(); +} +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 | + */ + + // GPIOG configuration + GPIO_PinAFConfig(GPIOG, GPIO_PinSource7, GPIO_AF_LTDC); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + // GPIOI configuration + GPIO_PinAFConfig(GPIOI, GPIO_PinSource12, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOI, GPIO_PinSource13, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; + GPIO_Init(GPIOI, &GPIO_InitStructure); + + // GPIOJ configuration + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource2, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource3, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource4, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource5, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource6, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource9, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource10, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource11, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource15, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_15; + GPIO_Init(GPIOJ, &GPIO_InitStructure); + + // GPIOK configuration + GPIO_PinAFConfig(GPIOK, GPIO_PinSource0, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource1, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource2, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource3, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource4, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource5, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource6, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource7, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; + GPIO_Init(GPIOK, &GPIO_InitStructure); +} + +static void lcdSpiConfig(void) { + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_InitStructure.GPIO_Pin = LCD_SPI_SCK_GPIO_PIN | LCD_SPI_MOSI_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = LCD_SPI_CS_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = LCD_NRST_GPIO_PIN; + GPIO_Init(LCD_NRST_GPIO, &GPIO_InitStructure); + + /* Set the chip select pin aways low */ + SET_LCD_CS(); +} + +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; + + SET_LCD_DATA(); + SET_LCD_DATA_INPUT(); + + for (i = 0; i < 8; i++) { + LCD_DELAY(); + SET_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + + CLR_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + if (READ_LCD_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + + SET_LCD_DATA_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; + + SET_LCD_DATA(); + SET_LCD_DATA_INPUT(); + for (i = 0; i < 8; i++) { + CLR_LCD_CLK(); + lcdDelay(); + ReceiveData <<= 1; + SET_LCD_CLK(); + lcdDelay(); + if (READ_LCD_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + CLR_LCD_CLK(); + SET_LCD_DATA_OUTPUT(); + + return (ReceiveData); +} + +unsigned char LCD_ReadRegister(unsigned char Register) { + unsigned char ReadData = 0; + + CLR_LCD_CS(); + lcdWriteByte(0, Register); + lcdDelay(); + lcdDelay(); + ReadData = LCD_ReadByte(); + SET_LCD_CS(); + return (ReadData); +} + +void lcdWriteCommand(uint8_t command) { + CLR_LCD_CS(); + lcdWriteByte(0, command); + SET_LCD_CS(); +} + +void lcdWriteData(uint8_t data) { + CLR_LCD_CS(); + lcdWriteByte(1, data); + SET_LCD_CS(); +} + +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) { + lcdWriteCommand(0x29); + lcdWriteCommand(0x23); //all pixels on +} + +void LCD_ILI9488_Init(void) { + lcdWriteCommand(0XFB); + lcdWriteData(0x00); + + lcdWriteCommand(0XF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + lcdWriteCommand(0xC0); + lcdWriteData(0x11); + lcdWriteData(0x09); + + lcdWriteCommand(0xC1); + lcdWriteData(0x41); + + lcdWriteCommand(0XC5); + lcdWriteData(0x00); + lcdWriteData(0x0A); + lcdWriteData(0x80); + + lcdWriteCommand(0xB1); + lcdWriteData(0xB0); + lcdWriteData(0x11); + + lcdWriteCommand(0xB4); + lcdWriteData(0x02); + + lcdWriteCommand(0xB6); + lcdWriteData(0x30); + lcdWriteData(0x02); + + lcdWriteCommand(0xB7); + lcdWriteData(0xc6); + + lcdWriteCommand(0xBE); + lcdWriteData(0x00); + lcdWriteData(0x04); + + lcdWriteCommand(0xE9); + lcdWriteData(0x00); + + lcdWriteCommand(0x36); + lcdWriteData(0x08); + + lcdWriteCommand(0x3A); + lcdWriteData(0x65); + + 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); + + lcdWriteCommand(0x11); + delay_ms(120); + lcdWriteCommand(0x28); + + LCD_ILI9488_On(); +} + +void LCD_ILI9488_Off(void) { + lcdWriteCommand(0x22); //all pixels off + lcdWriteCommand(0x28); +} + +void LCD_ILI9488_ReadDevice(void) { + int Index = 0; + int Parameter = 0x80; + +#if 1 + +#if 1 + lcdWriteCommand(0XF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + lcdWriteCommand(0XB0); + lcdWriteData(0X80); + +#endif + 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); +#endif + +#if 0 + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x00 ); //Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + lcdWriteData( Parameter|0x01 );//Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x02 );//Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x03 );//Parameter4=0X88 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); +#else + //lcdWriteCommand( 0xd0 ); + //lcdWriteData( Parameter|0x03 ); //Parameter4=0X88 + //LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd0 ); +#endif +} + +unsigned int LCD_ILI9488_ReadID(void) { + int ID = 0; + + lcdWriteCommand(0XF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + lcdWriteCommand(0XB0); + lcdWriteData(0X80); + + 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); + + 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 ); + + SET_LCD_CLK_OUTPUT(); + SET_LCD_DATA_INPUT(); + CLR_LCD_CLK(); + lcdDelay(); + lcdDelay(); + SET_LCD_CLK(); + 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() { + LTDC_InitTypeDef LTDC_InitStruct; + + /* 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_PLLSAIConfig(clock, 6, 4); + RCC_LTDCCLKDivConfig (RCC_PLLSAIDivR_Div4); + + /* Enable PLLSAI Clock */ + RCC_PLLSAICmd(ENABLE); + + /* Wait for PLLSAI activation */ + while (RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET); + + /* LTDC Configuration *********************************************************/ + /* Polarity configuration */ + /* Initialize the horizontal synchronization polarity as active low */ + LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL; + /* Initialize the vertical synchronization polarity as active low */ + LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL; + /* Initialize the data enable polarity as active low */ + LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL; + /* Initialize the pixel clock polarity as input pixel clock */ +// LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC; + LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IIPC; + + /* Configure R,G,B component values for LCD background color */ + LTDC_InitStruct.LTDC_BackgroundRedValue = 0; + LTDC_InitStruct.LTDC_BackgroundGreenValue = 0; + LTDC_InitStruct.LTDC_BackgroundBlueValue = 0; + + /* Configure horizontal synchronization width */ + LTDC_InitStruct.LTDC_HorizontalSync = HSW; + /* Configure vertical synchronization height */ + LTDC_InitStruct.LTDC_VerticalSync = VSH; + /* Configure accumulated horizontal back porch */ + LTDC_InitStruct.LTDC_AccumulatedHBP = HBP; + /* Configure accumulated vertical back porch */ + LTDC_InitStruct.LTDC_AccumulatedVBP = VBP; + /* Configure accumulated active width */ + LTDC_InitStruct.LTDC_AccumulatedActiveW = LCD_PHYS_W + HBP; + /* Configure accumulated active height */ + LTDC_InitStruct.LTDC_AccumulatedActiveH = LCD_PHYS_H + VBP; + /* Configure total width */ + LTDC_InitStruct.LTDC_TotalWidth = LCD_PHYS_W + HBP + HFP; + /* Configure total height */ + LTDC_InitStruct.LTDC_TotalHeigh = LCD_PHYS_H + VBP + VFP; + + LTDC_Init(<DC_InitStruct); + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); + + // Configure IRQ (line) + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + // Trigger on last line + LTDC_LIPConfig(LCD_PHYS_H); + +#if 0 + DMA2D_ITConfig(DMA2D_CR_TCIE, ENABLE); + NVIC_InitStructure.NVIC_IRQChannel = DMA2D_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA_SCREEN_IRQ_PRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init( &NVIC_InitStructure ); + DMA2D->IFCR = (unsigned long)DMA2D_IFSR_CTCIF; +#endif +} + +void LCD_LayerInit() { + LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct; + + /* Windowing configuration */ + /* In this case all the active display area is used to display a picture then : + Horizontal start = horizontal synchronization + Horizontal back porch = 30 + Horizontal stop = Horizontal start + window width -1 = 30 + 240 -1 + Vertical start = vertical synchronization + vertical back porch = 4 + Vertical stop = Vertical start + window height -1 = 4 + 320 -1 */ + LTDC_Layer_InitStruct.LTDC_HorizontalStart = HBP + 1; + LTDC_Layer_InitStruct.LTDC_HorizontalStop = (LCD_PHYS_W + HBP); + LTDC_Layer_InitStruct.LTDC_VerticalStart = VBP + 1; + LTDC_Layer_InitStruct.LTDC_VerticalStop = (LCD_PHYS_H + VBP); + + /* Pixel Format configuration*/ + LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565; + /* Alpha constant (255 totally opaque) */ + LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 255; + /* Default Color configuration (configure A,R,G,B component values) */ + LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0; + + /* Configure blending factors */ + LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_CA; + LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_CA; + + /* the length of one line of pixels in bytes + 3 then : + Line Lenth = Active high width x number of bytes per pixel + 3 + Active high width = LCD_W + number of bytes per pixel = 2 (pixel_format : RGB565) + */ + LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((LCD_PHYS_W * 2) + 3); + /* the pitch is the increment from the start of one line of pixels to the + start of the next line in bytes, then : + Pitch = Active high width x number of bytes per pixel */ + LTDC_Layer_InitStruct.LTDC_CFBPitch = (LCD_PHYS_W * 2); + + /* Configure the number of lines */ + LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_PHYS_H; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + uint32_t layer_address = (uint32_t)lcdFront->getData(); + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = layer_address; + + /* Initialize LTDC layer 1 */ + LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); + + /* LTDC configuration reload */ + LTDC_ReloadConfig(LTDC_IMReload); + + LTDC_LayerCmd(LTDC_Layer1, ENABLE); + LTDC_LayerAlpha(LTDC_Layer1, 255); + + LTDC_ReloadConfig(LTDC_IMReload); + + /* dithering activation */ + LTDC_DitherCmd(ENABLE); +} + +const char* boardLcdType = ""; + +void lcdInit(void) +{ + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + // TODO: init lVGL + + /* 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; + } 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 || 1) { + TRACE("LCD INIT (default): ST7796S"); + boardLcdType = "ST7796S"; + lcdInitFunction = LCD_ST7796S_Init; + lcdOffFunction = LCD_ST7796S_Off; + lcdOnFunction = LCD_ST7796S_On; + lcdPixelClock = 14500000; + /*} else { // NT35310 can not be detected + TRACE("LCD INIT (default): NT35310"); + boardLcdType = "NT35310"; + lcdInitFunction = LCD_NT35310_Init; + lcdOffFunction = LCD_NT35310_Off; + lcdOnFunction = LCD_NT35310_On; + lcdPixelClock = 12500000;*/ +/* } else { + TRACE("LCD INIT: unknown LCD controller"); + boardLcdType = "unknown";*/ + } + + lcdInitFunction(); + LCD_Init_LTDC(); + LCD_LayerInit(); + LTDC_Cmd(ENABLE); + LTDC_ReloadConfig(LTDC_IMReload); + + lcdSetFlushCb(startLcdRefresh); +} + +void DMAWait() +{ + while(DMA2D->CR & DMA2D_CR_START); +} + +void DMACopyBitmap(uint16_t *dest, uint16_t destw, uint16_t desth, uint16_t x, + uint16_t y, const uint16_t *src, uint16_t srcw, + uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, + uint16_t h) +{ + DMAWait(); + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_RGB565; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); +} + +void DMACopyAlphaBitmap(uint16_t *dest, uint16_t destw, uint16_t desth, + uint16_t x, uint16_t y, const uint16_t *src, + uint16_t srcw, uint16_t srch, uint16_t srcx, + uint16_t srcy, uint16_t w, uint16_t h) +{ + DMAWait(); + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_BLEND; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_ARGB4444; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + DMA2D_BG_InitTypeDef DMA2D_BG_InitStruct; + DMA2D_BG_StructInit(&DMA2D_BG_InitStruct); + DMA2D_BG_InitStruct.DMA2D_BGMA = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_BG_InitStruct.DMA2D_BGO = destw - w; + DMA2D_BG_InitStruct.DMA2D_BGCM = CM_RGB565; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_VALUE = 0; + DMA2D_BGConfig(&DMA2D_BG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); +} + +// same as DMACopyAlphaBitmap(), but with an 8 bit mask for each pixel (used by fonts) +void DMACopyAlphaMask(uint16_t *dest, uint16_t destw, uint16_t desth, + uint16_t x, uint16_t y, const uint8_t *src, uint16_t srcw, + uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, + uint16_t h, uint16_t bg_color) +{ + DMAWait(); + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_BLEND; + DMA2D_InitStruct.DMA2D_CMode = CM_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_A8; // 8 bit inputs every time + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FG_InitStruct.DMA2D_FGC_RED = GET_RED(bg_color); // 8 bit red + DMA2D_FG_InitStruct.DMA2D_FGC_GREEN = GET_GREEN(bg_color); // 8 bit green + DMA2D_FG_InitStruct.DMA2D_FGC_BLUE = GET_BLUE(bg_color); // 8 bit blue + + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + DMA2D_BG_InitTypeDef DMA2D_BG_InitStruct; + DMA2D_BG_StructInit(&DMA2D_BG_InitStruct); + DMA2D_BG_InitStruct.DMA2D_BGMA = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_BG_InitStruct.DMA2D_BGO = destw - w; + DMA2D_BG_InitStruct.DMA2D_BGCM = CM_RGB565; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_VALUE = 0; + DMA2D_BGConfig(&DMA2D_BG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); +} + +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) +{ + DMAWait(); + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_PFC; + DMA2D_InitStruct.DMA2D_CMode = format; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = 0; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src); + DMA2D_FG_InitStruct.DMA2D_FGO = 0; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_ARGB8888; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = REPLACE_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); +} + +extern "C" void LTDC_IRQHandler(void) +{ + // clear interrupt flag + LTDC->ICR = LTDC_ICR_CLIF; + + _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..27519c68607 --- /dev/null +++ b/radio/src/targets/pl18/lcd_driver.h @@ -0,0 +1,146 @@ +/* + * 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__ + +#include "board.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 PORT_LCD_CS ( GPIOE ) +#define LCD_CS_PIN ( GPIO_Pin_4 ) +#define PIN_LCD_CS ( 4 ) + +#define PORT_LCD_CLK ( GPIOE ) +#define LCD_CLK_PIN ( GPIO_Pin_2 ) +#define PIN_LCD_CLK ( 2 ) + +#define PORT_LCD_MOSI ( GPIOE ) +#define LCD_MOSI_PIN ( GPIO_Pin_6 ) +#define PIN_LCD_MOSI ( 6 ) + +#define PORT_LCD_MISO ( GPIOE ) +#define LCD_MISO_PIN ( GPIO_Pin_5 ) +#define PIN_LCD_MISO ( 5 ) + +#define PORT_LCD_DE ( GPIOK ) +#define LCD_DE_PIN ( GPIO_Pin_7 ) +#define PIN_LCD_DE ( 7 ) + +#define PORT_LCD_RESET ( GPIOG ) +#define LCD_RESET_PIN ( GPIO_Pin_9 ) +#define PIN_LCD_RESET ( 9 ) + +#define PORT_LCD_HSYNC ( GPIOI ) +#define LCD_HSYNC_PIN ( GPIO_Pin_12 ) +#define PIN_LCD_HSYNC ( 12 ) + +#define PORT_LCD_VSYNC ( GPIOI ) +#define LCD_VSYNC_PIN ( GPIO_Pin_13 ) +#define PIN_LCD_VSYNC ( 13 ) + +#define PORT_LCD_DOTCLK ( GPIOG ) +#define LCD_DOTCLK_PIN ( GPIO_Pin_7 ) +#define PIN_LCD_DOTCLK ( 7 ) + +#define SUPPORTED_LCD_CNT ( 5 ) + +#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 void GPIO_SetDirection( GPIO_TypeDef *GPIOx, unsigned char Pin, unsigned char IsInput ); + +extern lcdSpiInitFucPtr lcdInitFunction; +extern lcdSpiInitFucPtr lcdOffFunction; +extern lcdSpiInitFucPtr lcdOnFunction; + +#define SET_IO_INPUT( PORT, PIN ) GPIO_SetDirection( PORT, PIN, 1 ) +#define SET_IO_OUTPUT( PORT, PIN ) GPIO_SetDirection( PORT, PIN, 0 ) + +#define LCD_NRST_HIGH() GPIO_WriteBit(LCD_NRST_GPIO, LCD_NRST_GPIO_PIN, Bit_SET) +#define LCD_NRST_LOW() GPIO_WriteBit(LCD_NRST_GPIO, LCD_NRST_GPIO_PIN, Bit_RESET) + +#define LCD_CS_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN, Bit_SET) +#define LCD_CS_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN, Bit_RESET) + +#define LCD_SCK_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN, Bit_SET) +#define LCD_SCK_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN, Bit_RESET) + +#define LCD_MOSI_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN, Bit_SET) +#define LCD_MOSI_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN, Bit_RESET) + +#define SET_LCD_CS() GPIO_WriteBit(PORT_LCD_CS, LCD_CS_PIN, Bit_SET) +#define CLR_LCD_CS() GPIO_WriteBit(PORT_LCD_CS, LCD_CS_PIN, Bit_RESET) +#define SET_LCD_CS_OUTPUT() SET_IO_OUTPUT( PORT_LCD_CS, PIN_LCD_CS ) + +#define SET_LCD_CLK() GPIO_WriteBit( PORT_LCD_CLK, LCD_CLK_PIN, Bit_SET ) +#define CLR_LCD_CLK() GPIO_WriteBit( PORT_LCD_CLK, LCD_CLK_PIN, Bit_RESET ) +#define SET_LCD_CLK_OUTPUT() SET_IO_OUTPUT( PORT_LCD_CLK, PIN_LCD_CLK ) + +#define SET_LCD_DATA() GPIO_WriteBit( PORT_LCD_MOSI, LCD_MOSI_PIN, Bit_SET ) +#define CLR_LCD_DATA() GPIO_WriteBit( PORT_LCD_MOSI, LCD_MOSI_PIN, Bit_RESET ) +#define SET_LCD_DATA_INPUT() SET_IO_INPUT( PORT_LCD_MOSI, PIN_LCD_MOSI ) +#define SET_LCD_DATA_OUTPUT() SET_IO_OUTPUT( PORT_LCD_MOSI, PIN_LCD_MOSI ) + +#define READ_LCD_DATA_PIN() GPIO_ReadInputDataBit(PORT_LCD_MOSI, LCD_MOSI_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/libopenui_config.h b/radio/src/targets/pl18/libopenui_config.h new file mode 100644 index 00000000000..2c216664d13 --- /dev/null +++ b/radio/src/targets/pl18/libopenui_config.h @@ -0,0 +1,147 @@ +/* + * 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 "debug.h" +#include "libopenui_defines.h" + +#include "colors.h" +#include "board.h" +#include "keys.h" + +enum FontIndex +{ + FONT_STD_INDEX, +#if !defined(BOLD) + FONT_BOLD_INDEX, + FONT_XXS_INDEX, + FONT_XS_INDEX, + FONT_L_INDEX, + FONT_XL_INDEX, + FONT_XXL_INDEX, +#endif + + // this one MUST be last + FONTS_COUNT +}; + +typedef uint16_t pixel_t; + +constexpr bool WRAP_FORM_FIELDS_WITHIN_PAGE = true; +constexpr short SLIDE_SPEED_REDUCTION = 5; + +constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33; +constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47; + +constexpr uint32_t MENU_HEADER_HEIGHT = 45; +constexpr uint32_t MENU_TITLE_TOP = 48; +constexpr uint32_t MENU_TITLE_HEIGHT = 21; +constexpr uint32_t MENU_BODY_TOP = MENU_TITLE_TOP + MENU_TITLE_HEIGHT; +constexpr uint32_t MENU_CONTENT_TOP = MENU_BODY_TOP + 1; +constexpr uint32_t MENU_FOOTER_HEIGHT = 0; +constexpr uint32_t MENU_FOOTER_TOP = LCD_H - MENU_FOOTER_HEIGHT; +constexpr uint32_t MENU_BODY_HEIGHT = MENU_FOOTER_TOP - MENU_BODY_TOP; +constexpr uint32_t MENUS_MARGIN_LEFT = 6; +constexpr coord_t MENUS_SEPARATOR_HEIGHT = 15; + +constexpr uint32_t MENU_HEADER_BACK_BUTTON_WIDTH = MENU_HEADER_HEIGHT; +constexpr uint32_t MENU_HEADER_BACK_BUTTON_HEIGHT = MENU_HEADER_HEIGHT; + +constexpr coord_t PAGE_PADDING = 6; +constexpr uint32_t PAGE_LINE_HEIGHT = 20; +constexpr uint32_t PAGE_LINE_SPACING = 2; +constexpr uint32_t PAGE_INDENT_WIDTH = 10; +constexpr uint32_t PAGE_LABEL_WIDTH = 140; +constexpr uint32_t FH = PAGE_LINE_HEIGHT; +constexpr uint32_t NUM_BODY_LINES = MENU_BODY_HEIGHT / PAGE_LINE_HEIGHT; +constexpr uint32_t TEXT_VIEWER_LINES = (MENU_FOOTER_TOP - MENU_HEADER_HEIGHT) / FH; + +constexpr uint32_t FIELD_PADDING_LEFT = 3; +constexpr uint32_t FIELD_PADDING_TOP = 2; + +constexpr uint32_t CURVE_SIDE_WIDTH = 100; +constexpr uint32_t CURVE_CENTER_X = LCD_W - CURVE_SIDE_WIDTH - 7; +constexpr uint32_t CURVE_CENTER_Y = 151; +constexpr uint32_t CURVE_COORD_WIDTH = 36; +constexpr uint32_t CURVE_COORD_HEIGHT = 17; + +constexpr uint32_t DATETIME_SEPARATOR_X = LCD_W - 53; +constexpr uint32_t DATETIME_LINE1 = 7; +constexpr uint32_t DATETIME_LINE2 = 22; +constexpr uint32_t DATETIME_MIDDLE = (LCD_W + DATETIME_SEPARATOR_X + 1) / 2; +constexpr coord_t LINE_HEIGHT = PAGE_LINE_HEIGHT; + +constexpr uint32_t RSSI_X = LCD_W - 80; +constexpr uint32_t AUDIO_X = LCD_W - 115; +constexpr uint32_t USB_X = LCD_W - 82; +constexpr uint32_t LOG_X = LCD_W - 82; +constexpr uint32_t GPS_X = LCD_W - 130; + +constexpr uint32_t MENUS_TOOLBAR_BUTTON_WIDTH = 30; +constexpr uint32_t MENUS_TOOLBAR_BUTTON_PADDING = 3; + +constexpr uint32_t ALERT_FRAME_TOP = 70; +constexpr uint32_t ALERT_FRAME_HEIGHT = (LCD_H - 2 * ALERT_FRAME_TOP); +constexpr uint32_t ALERT_BITMAP_TOP = ALERT_FRAME_TOP + 15; +constexpr uint32_t ALERT_BITMAP_LEFT = 15; +constexpr uint32_t ALERT_TITLE_TOP = ALERT_FRAME_TOP + 10; +constexpr uint32_t ALERT_TITLE_LEFT = 140; +constexpr uint32_t ALERT_TITLE_LINE_HEIGHT = 30; +constexpr uint32_t ALERT_MESSAGE_TOP = ALERT_TITLE_TOP + 130; +constexpr uint32_t ALERT_MESSAGE_LEFT = 15; +constexpr uint32_t ALERT_ACTION_TOP = 240; +constexpr uint32_t ALERT_BUTTON_TOP = 300; + +constexpr uint32_t PAGE_TITLE_TOP = 2; +constexpr uint32_t PAGE_TITLE_LEFT = 50; + +constexpr uint32_t INPUT_EDIT_LABELS_WIDTH = 120; +constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 132; +constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH; +constexpr coord_t INPUT_EDIT_CURVE_LEFT = PAGE_PADDING; +constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT + PAGE_PADDING; +constexpr coord_t MENUS_LINE_HEIGHT = 40; +constexpr coord_t MENUS_WIDTH = 200; +constexpr coord_t MENUS_OFFSET_TOP = 20; +constexpr coord_t POPUP_HEADER_HEIGHT = 30; +constexpr coord_t MENUS_MIN_HEIGHT = 2 * MENUS_LINE_HEIGHT - 1; +constexpr coord_t MENUS_MAX_HEIGHT = 7 * MENUS_LINE_HEIGHT - 1; + +constexpr rect_t MENUS_TOOLBAR_RECT = { 35, (LCD_H - MENUS_MAX_HEIGHT) / 2, 50, MENUS_MAX_HEIGHT }; + +constexpr coord_t MODEL_SELECT_FOOTER_HEIGHT = 24; +constexpr coord_t SCROLLBAR_WIDTH = 3; +constexpr coord_t TABLE_LINE_HEIGHT = 50; +constexpr coord_t TABLE_HEADER_HEIGHT = 48; + +constexpr coord_t ROLLER_LINE_HEIGHT = 40; + +constexpr LcdFlags MENU_HEADER_FONT = FONT(BOLD); +constexpr LcdFlags MENU_FONT = FONT(STD); +constexpr LcdFlags TABLE_HEADER_FONT = FONT(STD); +constexpr LcdFlags TABLE_BODY_FONT = FONT(STD); + +constexpr int CJK_FIRST_LETTER_INDEX = 128 - 32 + 21; +constexpr coord_t CHAR_SPACING = 0; + +// Disable rotary encoder, as the PL18 does not have one +#define ROTARY_ENCODER_SPEED() 0 diff --git a/radio/src/targets/pl18/pulses_driver.cpp b/radio/src/targets/pl18/pulses_driver.cpp new file mode 100644 index 00000000000..b99cea241b2 --- /dev/null +++ b/radio/src/targets/pl18/pulses_driver.cpp @@ -0,0 +1,130 @@ +/* + * 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" + +void intmoduleStop(void); +void extmoduleStop(void); + +void intmoduleNoneStart(void); +void intmodulePxxStart(void); + +void extmoduleNoneStart(void); +void extmodulePpmStart(void); +void extmodulePxxStart(void); +#if defined(DSM2) +void extmoduleDsm2Start(void); +#endif +void extmoduleCrossfireStart(void); + +void init_no_pulses(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleNoneStart(); + else + extmoduleNoneStart(); +} + +void disable_no_pulses(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleStop(); + else + extmoduleStop(); +} + +void init_ppm(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmodulePpmStart(); + } +} + +void disable_ppm(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} + +void init_pxx(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmodulePxxStart(); + else + extmodulePxxStart(); +} + +void disable_pxx(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleStop(); + else + extmoduleStop(); +} + +#if defined(DSM2) +void init_dsm2(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleDsm2Start(); + } +} + +void disable_dsm2(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} +#endif + +void init_sbusOut(uint32_t port) +{ + init_dsm2(port); +} + +void disable_sbusOut(uint32_t port) +{ + disable_dsm2(port); +} + +void init_crossfire(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleCrossfireStart(); + } +} + +void disable_crossfire(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} + +void init_serial(uint32_t module_index, uint32_t baudrate, uint32_t period_half_us) +{ +} + +void disable_serial(uint32_t port) +{ +} \ No newline at end of file diff --git a/radio/src/targets/pl18/sdram_driver.c b/radio/src/targets/pl18/sdram_driver.c new file mode 100644 index 00000000000..3d933c6b306 --- /dev/null +++ b/radio/src/targets/pl18/sdram_driver.c @@ -0,0 +1,256 @@ +/* + * 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" + +#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/startup_stm32f42_43xxx.s b/radio/src/targets/pl18/startup_stm32f42_43xxx.s new file mode 100644 index 00000000000..68671dc34cf --- /dev/null +++ b/radio/src/targets/pl18/startup_stm32f42_43xxx.s @@ -0,0 +1,565 @@ +/** + ****************************************************************************** + * @file startup_stm32f40_41xxx.s + * @author MCD Application Team + * @version V1.3.0 + * @date 08-November-2013 + * @brief STM32F40xxx/41xxx Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM324xG-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2013 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + + bl pwrResetHandler /*jump to WDT reset handler where soft power control pin is turned on as soon as possible */ + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/*Paint Main Stack */ + ldr r2, = _main_stack_start +PaintMainStack: + movs r3, #0x55555555 + str r3, [r2], #4 +LoopPaintMainStack: + ldr r3, = _estack + cmp r2, r3 + bcc PaintMainStack + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call C++ constructors for static objects */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FSMC_IRQHandler /* FSMC */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word CRYP_IRQHandler /* CRYP crypto */ + .word HASH_RNG_IRQHandler /* Hash and Rng */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word LTDC_IRQHandler /* LTDC */ + .word LTDC_ER_IRQHandler /* LTDC error */ + .word DMA2D_IRQHandler /* DMA2D */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak CRYP_IRQHandler + .thumb_set CRYP_IRQHandler,Default_Handler + + .weak HASH_RNG_IRQHandler + .thumb_set HASH_RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak LTDC_IRQHandler + .thumb_set LTDC_IRQHandler,Default_Handler + + .weak LTDC_ER_IRQHandler + .thumb_set LTDC_ER_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/radio/src/targets/pl18/stm32_ramboot.ld b/radio/src/targets/pl18/stm32_ramboot.ld new file mode 100644 index 00000000000..b15c1aa04c4 --- /dev/null +++ b/radio/src/targets/pl18/stm32_ramboot.ld @@ -0,0 +1,188 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 2MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x2002FFF0; /* end of 192K RAM */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Main_Stack_Size = 1024; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 65520 + NO_INIT (xrw) : ORIGIN = 0x1000FFF0, LENGTH = 16 + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.version)) + KEEP(*(.bootversiondata)) + . = ALIGN(4); /* Align the start of the text part */ + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >CCM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >CCM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >NO_INIT + + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Main_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + . = ALIGN(4); + _eram = .; + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/pl18/stm32f4_flash.ld b/radio/src/targets/pl18/stm32f4_flash.ld new file mode 100644 index 00000000000..ac5c7ef74d5 --- /dev/null +++ b/radio/src/targets/pl18/stm32f4_flash.ld @@ -0,0 +1,197 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 2MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Main_Stack_Size = 1024; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 65520 + NO_INIT (xrw) : ORIGIN = 0x1000FFF0, LENGTH = 16 + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Main stack end */ +_estack = 0x10000000 + LENGTH(CCM); +_main_stack_start = _estack - _Main_Stack_Size; + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + FILL(0xFFFF) + + CREATE_OBJECT_SYMBOLS + + + + + _stext = .; /* Provide the name for the start of this section */ + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.fwversiondata)) + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >CCM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >CCM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >NO_INIT + + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Main_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + . = ALIGN(4); + _eram = .; + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/pl18/stm32f4_flash_bootloader.ld b/radio/src/targets/pl18/stm32f4_flash_bootloader.ld new file mode 100644 index 00000000000..5160bfd9e73 --- /dev/null +++ b/radio/src/targets/pl18/stm32f4_flash_bootloader.ld @@ -0,0 +1,195 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 2MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x10010000; /* end of 64K CCM */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 4096K; /* required amount of heap */ +_Main_Stack_Size = 8192; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + FILL(0xFFFF) + + CREATE_OBJECT_SYMBOLS + + KEEP(*(.bootloader)) /* Bootloader code */ + + . = 0x20000; /* Set the start of the main program */ + _stext = .; /* Provide the name for the start of this section */ + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.fwversiondata)) + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM AT> FLASH + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >RAM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >RAM + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + .ccm (NOLOAD) : + { + . = ALIGN(4); + _sccm = .; + *(.ccm) + . = ALIGN(4); + . = . + _Main_Stack_Size; + _eccm = .; + } >CCM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + _eram = .; + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = ALIGN(4); + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/pl18/telemetry_driver.cpp b/radio/src/targets/pl18/telemetry_driver.cpp new file mode 100644 index 00000000000..007c23c99bd --- /dev/null +++ b/radio/src/targets/pl18/telemetry_driver.cpp @@ -0,0 +1,472 @@ +/* + * 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_exti_driver.h" +#include "aux_serial_driver.h" + +#include "opentx.h" + +#if defined(GHOST) + #include "telemetry/ghost.h" +#endif + +Fifo telemetryNoDMAFifo; +uint32_t telemetryErrors = 0; + +#if defined(PCBX12S) || defined (PCBNV14) || defined (PCBPL18) +#include "dmafifo.h" + +DMAFifo telemetryDMAFifo __DMA (TELEMETRY_DMA_Stream_RX); +uint8_t telemetryFifoMode; +#endif + +static void telemetryInitDirPin() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Pin = TELEMETRY_DIR_GPIO_PIN; + GPIO_Init(TELEMETRY_DIR_GPIO, &GPIO_InitStructure); + TELEMETRY_DIR_INPUT(); +} + +void telemetryPortInitCommon(uint32_t baudrate, uint8_t mode, uint8_t noinv = 0) +{ + if (baudrate == 0) { + USART_DeInit(TELEMETRY_USART); + return; + } + //deinit inverted mode +#if !(defined(PCBNV14) || defined(PCBPL18)) + telemetryPortInvertedInit(0); +#endif + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = TELEMETRY_DMA_TX_Stream_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pre-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + USART_InitTypeDef USART_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_PinAFConfig(TELEMETRY_GPIO, TELEMETRY_GPIO_PinSource_RX, TELEMETRY_GPIO_AF); + GPIO_PinAFConfig(TELEMETRY_GPIO, TELEMETRY_GPIO_PinSource_TX, TELEMETRY_GPIO_AF); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_GPIO_PIN | TELEMETRY_RX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = baudrate <= 400000 ? GPIO_Speed_2MHz : GPIO_Speed_25MHz; + GPIO_Init(TELEMETRY_GPIO, &GPIO_InitStructure); + + telemetryInitDirPin(); + +#if defined(PCBNV14) || defined(PCBPL18) + GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_INV_GPIO_PIN | TELEMETRY_RX_INV_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(TELEMETRY_INV_GPIO, &GPIO_InitStructure); + + if (noinv != 0) { + TELEMETRY_TX_NORM(); + TELEMETRY_RX_NORM(); + } else { + TELEMETRY_TX_INV(); + TELEMETRY_RX_INV(); + } +#endif + USART_DeInit(TELEMETRY_USART); + + USART_OverSampling8Cmd(TELEMETRY_USART, baudrate <= 400000 ? DISABLE : ENABLE); + + USART_InitStructure.USART_BaudRate = baudrate; + if (mode & TELEMETRY_SERIAL_8E2) { + USART_InitStructure.USART_WordLength = USART_WordLength_9b; + USART_InitStructure.USART_StopBits = USART_StopBits_2; + USART_InitStructure.USART_Parity = USART_Parity_Even; + } + else { + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + } + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + if (g_eeGeneral.uartSampleMode == UART_SAMPLE_MODE_ONEBIT) { + USART_OneBitMethodCmd(TELEMETRY_USART, ENABLE); + } + USART_Init(TELEMETRY_USART, &USART_InitStructure); + +#if defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) + telemetryFifoMode = mode; + + DMA_Cmd(TELEMETRY_DMA_Stream_RX, DISABLE); + USART_DMACmd(TELEMETRY_USART, USART_DMAReq_Rx, DISABLE); + DMA_DeInit(TELEMETRY_DMA_Stream_RX); + + if (mode & TELEMETRY_SERIAL_WITHOUT_DMA) { + USART_Cmd(TELEMETRY_USART, ENABLE); + USART_ITConfig(TELEMETRY_USART, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(TELEMETRY_USART_IRQn, 6); + NVIC_EnableIRQ(TELEMETRY_USART_IRQn); + } + else { + DMA_InitTypeDef DMA_InitStructure; + telemetryDMAFifo.clear(); + + USART_ITConfig(TELEMETRY_USART, USART_IT_RXNE, DISABLE); + USART_ITConfig(TELEMETRY_USART, USART_IT_TXE, DISABLE); + NVIC_SetPriority(TELEMETRY_USART_IRQn, 6); + NVIC_EnableIRQ(TELEMETRY_USART_IRQn); + + DMA_InitStructure.DMA_Channel = TELEMETRY_DMA_Channel_RX; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&TELEMETRY_USART->DR); + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(telemetryDMAFifo.buffer()); + DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; + DMA_InitStructure.DMA_BufferSize = telemetryDMAFifo.size(); + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_Low; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(TELEMETRY_DMA_Stream_RX, &DMA_InitStructure); + USART_DMACmd(TELEMETRY_USART, USART_DMAReq_Rx, ENABLE); + USART_Cmd(TELEMETRY_USART, ENABLE); + DMA_Cmd(TELEMETRY_DMA_Stream_RX, ENABLE); + } +#else + USART_Cmd(TELEMETRY_USART, ENABLE); + USART_ITConfig(TELEMETRY_USART, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(TELEMETRY_USART_IRQn, 6); + NVIC_EnableIRQ(TELEMETRY_USART_IRQn); +#endif +} + +void telemetryPortInit(uint32_t baudrate, uint8_t mode) +{ + telemetryPortInitCommon(baudrate, mode, 0); +} +// soft serial vars +static uint8_t rxBitCount; +static uint8_t rxByte; +// single bit length expresses in half us +static uint16_t bitLength; +static uint16_t probeTimeFromStartBit; + +#if !(defined(PCBNV14) || defined(PCBPL18)) +static void do_telemetry_exti() +{ + if (rxBitCount == 0) { + + TELEMETRY_TIMER->ARR = probeTimeFromStartBit; // 1,5 cycle from start at 57600bps + TELEMETRY_TIMER->CR1 |= TIM_CR1_CEN; + + // disable start bit interrupt + EXTI->IMR &= ~EXTI_IMR_MR6; + } +} +#endif + +void telemetryPortInvertedInit(uint32_t baudrate) +{ +#if defined(PCBNV14) || defined(PCBPL18) + telemetryPortInitCommon(baudrate, TELEMETRY_SERIAL_DEFAULT, 1); +#else + if (baudrate == 0) { + + stm32_exti_disable(TELEMETRY_EXTI_LINE); + NVIC_DisableIRQ(TELEMETRY_TIMER_IRQn); + return; + } + + rxBitCount = 0; + + switch(baudrate) { + case 115200: + bitLength = 17; + probeTimeFromStartBit = 23; //because pin is not probed immediately +// probeTimeFromStartBit = 25; // TODO: Tarnis used 25, how to merge? + break; + case 57600: + bitLength = 35; //34 was used before - I prefer to use use 35 because of lower error + probeTimeFromStartBit = 48; // 48 used in original implementation + break; + default: + bitLength = 2000000/baudrate; //because of 0,5 us tick + probeTimeFromStartBit = 3000000/baudrate; + } + +#if defined(PCBX12S) + telemetryFifoMode = TELEMETRY_SERIAL_WITHOUT_DMA; +#endif + + // configure bit sample timer + TELEMETRY_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 2000000 - 1; // 0.5uS + TELEMETRY_TIMER->CCER = 0; + TELEMETRY_TIMER->CCMR1 = 0; + TELEMETRY_TIMER->CR1 = TIM_CR1_CEN; + TELEMETRY_TIMER->DIER = TIM_DIER_UIE; + + NVIC_SetPriority(TELEMETRY_TIMER_IRQn, 0); + NVIC_EnableIRQ(TELEMETRY_TIMER_IRQn); + + // init TELEMETRY_RX_GPIO_PIN + GPIO_InitTypeDef GPIO_InitStructure; + 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_DOWN; + GPIO_InitStructure.GPIO_Pin = TELEMETRY_RX_GPIO_PIN; + GPIO_Init(TELEMETRY_GPIO, &GPIO_InitStructure); + + telemetryInitDirPin(); + + // Connect EXTI line to TELEMETRY RX pin + LL_SYSCFG_SetEXTISource(TELEMETRY_EXTI_PORT, TELEMETRY_EXTI_SYS_LINE); + + // Configure EXTI for raising edge (start bit) + stm32_exti_enable(TELEMETRY_EXTI_LINE, TELEMETRY_EXTI_TRIGGER, do_telemetry_exti); +#endif // defined(PCBNV14) +} + +#if !(defined(PCBNV14) || defined(PCBPL18)) +void telemetryPortInvertedRxBit() +{ + if (rxBitCount < 8) { + if (rxBitCount == 0) { + TELEMETRY_TIMER->ARR = bitLength; + rxByte = 0; + } + else { + rxByte >>= 1; + } + + if (GPIO_ReadInputDataBit(TELEMETRY_GPIO, TELEMETRY_RX_GPIO_PIN) == Bit_RESET) + rxByte |= 0x80; + + ++rxBitCount; + } + else if (rxBitCount == 8) { + telemetryNoDMAFifo.push(rxByte); + rxBitCount = 0; + + // disable timer + TELEMETRY_TIMER->CR1 &= ~TIM_CR1_CEN; + + // re-enable start bit interrupt + EXTI->IMR |= EXTI_IMR_MR6; + } +} +#endif + +void telemetryPortSetDirectionOutput() +{ +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE)) { + TELEMETRY_USART->BRR = BRR_400K; + } +#endif + TELEMETRY_DIR_OUTPUT(); + TELEMETRY_USART->CR1 &= ~USART_CR1_RE; // turn off receiver +} + +void sportWaitTransmissionComplete() +{ + while (!(TELEMETRY_USART->SR & USART_SR_TC)); +} + +void telemetryPortSetDirectionInput() +{ + sportWaitTransmissionComplete(); +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE) && g_model.moduleData[EXTERNAL_MODULE].ghost.telemetryBaudrate == GHST_TELEMETRY_RATE_115K) { + TELEMETRY_USART->BRR = BRR_115K; + } +#endif + TELEMETRY_DIR_INPUT(); + TELEMETRY_USART->CR1 |= USART_CR1_RE; // turn on receiver +} + +void sportSendByte(uint8_t byte) +{ + telemetryPortSetDirectionOutput(); + + while (!(TELEMETRY_USART->SR & USART_SR_TXE)); + USART_SendData(TELEMETRY_USART, byte); +} + +void sportStopSendByteLoop() +{ + DMA_Cmd(TELEMETRY_DMA_Stream_TX, DISABLE); + DMA_DeInit(TELEMETRY_DMA_Stream_TX); +} +void sportSendByteLoop(uint8_t byte) +{ + telemetryPortSetDirectionOutput(); + + outputTelemetryBuffer.data[0] = byte; + + DMA_InitTypeDef DMA_InitStructure; + DMA_DeInit(TELEMETRY_DMA_Stream_TX); + DMA_InitStructure.DMA_Channel = TELEMETRY_DMA_Channel_TX; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&TELEMETRY_USART->DR); + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(outputTelemetryBuffer.data); + DMA_InitStructure.DMA_BufferSize = 1; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(TELEMETRY_DMA_Stream_TX, &DMA_InitStructure); + DMA_Cmd(TELEMETRY_DMA_Stream_TX, ENABLE); + USART_DMACmd(TELEMETRY_USART, USART_DMAReq_Tx, ENABLE); +} + +void sportSendBuffer(const uint8_t * buffer, uint32_t count) +{ + telemetryPortSetDirectionOutput(); + + DMA_InitTypeDef DMA_InitStructure; + DMA_DeInit(TELEMETRY_DMA_Stream_TX); + DMA_InitStructure.DMA_Channel = TELEMETRY_DMA_Channel_TX; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&TELEMETRY_USART->DR); + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(buffer); + DMA_InitStructure.DMA_BufferSize = count; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; + DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(TELEMETRY_DMA_Stream_TX, &DMA_InitStructure); + DMA_Cmd(TELEMETRY_DMA_Stream_TX, ENABLE); + USART_DMACmd(TELEMETRY_USART, USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(TELEMETRY_DMA_Stream_TX, DMA_IT_TC, ENABLE); +// USART_ClearITPendingBit(TELEMETRY_USART, USART_IT_TC); // TODO: This requires? Only appear in tarnis driver + + // enable interrupt and set it's priority + NVIC_EnableIRQ(TELEMETRY_DMA_TX_Stream_IRQ) ; + NVIC_SetPriority(TELEMETRY_DMA_TX_Stream_IRQ, 7); +} + +extern "C" void TELEMETRY_DMA_TX_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_DMA); + if (DMA_GetITStatus(TELEMETRY_DMA_Stream_TX, TELEMETRY_DMA_TX_FLAG_TC)) { + DMA_ClearITPendingBit(TELEMETRY_DMA_Stream_TX, TELEMETRY_DMA_TX_FLAG_TC); + + // clear TC flag before enabling interrupt + TELEMETRY_USART->SR &= ~USART_SR_TC; + TELEMETRY_USART->CR1 |= USART_CR1_TCIE; + + if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) { + outputTelemetryBuffer.reset(); + } + } +} + +#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) +extern "C" void TELEMETRY_USART_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_USART); + uint32_t status = TELEMETRY_USART->SR; + + if ((status & USART_SR_TC) && (TELEMETRY_USART->CR1 & USART_CR1_TCIE)) { + TELEMETRY_USART->CR1 &= ~USART_CR1_TCIE; + telemetryPortSetDirectionInput(); + while (status & (USART_FLAG_RXNE)) { + status = TELEMETRY_USART->DR; + status = TELEMETRY_USART->SR; + } + } + + while (status & (USART_FLAG_RXNE | USART_FLAG_ERRORS)) { + uint8_t data = TELEMETRY_USART->DR; + if (status & USART_FLAG_ERRORS) { + telemetryErrors++; + } + else { + telemetryNoDMAFifo.push(data); +#if defined(LUA) + if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) { + static uint8_t prevdata; + if (prevdata == 0x7E && outputTelemetryBuffer.size > 0 && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && data == outputTelemetryBuffer.sport.physicalId) { + sportSendBuffer(outputTelemetryBuffer.data + 1, outputTelemetryBuffer.size - 1); + } + prevdata = data; + } +#endif + } + status = TELEMETRY_USART->SR; + } +} + +#if !(defined(PCBNV14) || defined(PCBPL18)) +extern "C" void TELEMETRY_TIMER_IRQHandler() +{ + TELEMETRY_TIMER->SR &= ~TIM_SR_UIF; + telemetryPortInvertedRxBit(); +} +#endif + +// TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp +bool sportGetByte(uint8_t * byte) +{ +#if defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) + if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) + return telemetryNoDMAFifo.pop(*byte); + else + return telemetryDMAFifo.pop(*byte); +#else + return telemetryNoDMAFifo.pop(*byte); +#endif +} + +void telemetryClearFifo() +{ +#if defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) + telemetryDMAFifo.clear(); +#endif + + telemetryNoDMAFifo.clear(); +} + diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp new file mode 100644 index 00000000000..39dcaf97c0e --- /dev/null +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -0,0 +1,606 @@ +/* + * 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 "hal.h" + +#include "stm32_i2c_driver.h" +#include "timers_driver.h" +#include "delays_driver.h" +#include "tp_cst340.h" + +#include "debug.h" + +volatile static bool touchEventOccured; + +#define TOUCH_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_RCC_APB1Periph RCC_APB1Periph_I2C1 +#define TOUCH_GPIO I2C_B1_GPIO +#define TOUCH_SCL_GPIO_PIN I2C_B1_SCL_GPIO_PIN // PB.08 +#define TOUCH_SDA_GPIO_PIN I2C_B1_SDA_GPIO_PIN // PB.09 + +#define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) +#define TOUCH_CST340_I2C_ADDRESS 0x1A + +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 = {}; + +void I2C_FreeBus() +{ + LL_GPIO_InitTypeDef gpioInit; + LL_GPIO_StructInit(&gpioInit); + + // reset i2c bus by setting clk as output and sending manual clock pulses + gpioInit.Pin = TOUCH_SCL_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_OUTPUT; + gpioInit.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + gpioInit.Pull = LL_GPIO_PULL_NO; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + LL_GPIO_Init(TOUCH_GPIO, &gpioInit); + + gpioInit.Pin = TOUCH_SDA_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_INPUT; + gpioInit.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; + gpioInit.Pull = LL_GPIO_PULL_UP; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + LL_GPIO_Init(TOUCH_GPIO, &gpioInit); + + //send 100khz clock train for some 100ms + tmr10ms_t until = get_tmr10ms() + 11; + while (get_tmr10ms() < until) { + if (LL_GPIO_IsInputPinSet(TOUCH_GPIO, TOUCH_SDA_GPIO_PIN)) { + TRACE("touch: i2c free again\n"); + break; + } + TRACE("FREEEEE"); + LL_GPIO_ResetOutputPin(TOUCH_GPIO, TOUCH_SCL_GPIO_PIN); + delay_us(10); + LL_GPIO_SetOutputPin(TOUCH_GPIO, TOUCH_SCL_GPIO_PIN); + delay_us(10); + } + + //send stop condition: + gpioInit.Pin = TOUCH_SDA_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_OUTPUT; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + LL_GPIO_Init(TOUCH_GPIO, &gpioInit); + + //clock is low + LL_GPIO_ResetOutputPin(TOUCH_GPIO, TOUCH_SCL_GPIO_PIN); + delay_us(10); + //sda = lo + LL_GPIO_SetOutputPin(TOUCH_GPIO, TOUCH_SDA_GPIO_PIN); + delay_us(10); + //clock goes high + LL_GPIO_ResetOutputPin(TOUCH_GPIO, TOUCH_SCL_GPIO_PIN); + delay_us(10); + //sda = hi + LL_GPIO_SetOutputPin(TOUCH_GPIO, TOUCH_SDA_GPIO_PIN); + delay_us(10); + TRACE("FREE BUS"); +} + +// void Touch_DeInit() +// { +// I2C_DeInit(I2C_B1); +// __HAL_RCC_I2C1_FORCE_RESET(); +// delay_ms(150); +// __HAL_RCC_I2C1_RELEASE_RESET(); +// } + +static int _enable_gpio_clock(GPIO_TypeDef *GPIOx) +{ + if (GPIOx == GPIOB) + __HAL_RCC_GPIOB_CLK_ENABLE(); + else + return -1; + + return 0; +} + +void I2C_Init() +{ + stm32_i2c_deinit(TOUCH_I2C_BUS); + + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + __HAL_RCC_I2C1_CLK_ENABLE(); + __HAL_RCC_I2C1_CLK_DISABLE(); + + I2C_FreeBus(); + + stm32_i2c_init(TOUCH_I2C_BUS, TOUCH_I2C_CLK_RATE); + + LL_GPIO_InitTypeDef gpioInit; + LL_GPIO_StructInit(&gpioInit); + + _enable_gpio_clock(TOUCH_RST_GPIO); + _enable_gpio_clock(TOUCH_INT_GPIO); + + gpioInit.Pin = TOUCH_RST_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_OUTPUT; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_LOW; + gpioInit.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + gpioInit.Pull = LL_GPIO_PULL_UP; + LL_GPIO_Init(TOUCH_RST_GPIO, &gpioInit); + + //ext interupt + gpioInit.Pin = TOUCH_INT_GPIO_PIN; + gpioInit.Mode = LL_GPIO_MODE_INPUT; + gpioInit.Pull = LL_GPIO_PULL_UP; + gpioInit.Speed = LL_GPIO_SPEED_FREQ_HIGH; + gpioInit.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; + LL_GPIO_Init(TOUCH_INT_GPIO, &gpioInit); + + LL_SYSCFG_SetEXTISource(TOUCH_INT_EXTI_Port, TOUCH_INT_EXTI_SysCfgLine); + + LL_EXTI_InitTypeDef extiInit; + LL_EXTI_StructInit(&extiInit); + + extiInit.Line_0_31 = TOUCH_INT_EXTI_Line; + extiInit.Mode = LL_EXTI_MODE_IT; + extiInit.Trigger = LL_EXTI_TRIGGER_FALLING; + extiInit.LineCommand = ENABLE; + LL_EXTI_Init(&extiInit); + + NVIC_SetPriority(TOUCH_INT_EXTI_IRQn, 8); + NVIC_EnableIRQ(TOUCH_INT_EXTI_IRQn); +} + +#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 retult; + uint8_t tryCount = 3; + while (!touch_i2c_read(addr, reg, &retult, 1)) { + if (--tryCount == 0) break; + I2C_Init(); + } + return retult; +} + +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(); + } + 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) + { + touchController = TC_CST340; + tc = &CST340; + } else { + touchController = TC_FT6236; + tc = &FT6236; + } +} + +void TouchInit() +{ + I2C_Init(); + TouchReset(); + detectTouchController(); + tc->printDebugInfo(); +} + +void handleTouch() +{ + unsigned short touchX; + unsigned short touchY; + uint32_t tEvent = 0; + tc->read(&touchX, &touchY, &tEvent); +#if 0 + unsigned short tmp = /*(480 - 1) - */touchY; + touchY = 319 - touchX; + touchX = tmp; +#else + touchX = 319 - touchX; + touchY = 479 - touchY; +#endif + 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; + } + + } +} + +#if !defined(TOUCH_INT_EXTI_IRQHandler) + #error "TOUCH_INT_EXTI_IRQHandler is not defined" +#endif +extern "C" void TOUCH_INT_EXTI_IRQHandler(void) +{ + if (LL_EXTI_IsEnabledIT_0_31(TOUCH_INT_EXTI_Line) && + LL_EXTI_IsActiveFlag_0_31(TOUCH_INT_EXTI_Line)) { + touchEventOccured = true; + LL_EXTI_ClearFlag_0_31(TOUCH_INT_EXTI_Line); + } +} + +bool touchPanelEventOccured() +{ + return touchEventOccured; +} + +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; + 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/pl18/trainer_driver.cpp b/radio/src/targets/pl18/trainer_driver.cpp new file mode 100644 index 00000000000..55282d5003d --- /dev/null +++ b/radio/src/targets/pl18/trainer_driver.cpp @@ -0,0 +1,142 @@ +/* + * 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" + +void trainerSendNextFrame(); + +void init_trainer_ppm() +{ + GPIO_PinAFConfig(TRAINER_GPIO, TRAINER_OUT_GPIO_PinSource, TRAINER_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = TRAINER_OUT_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(TRAINER_GPIO, &GPIO_InitStructure); + + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; + TRAINER_TIMER->PSC = TRAINER_TIMER_FREQ / 2000000 - 1; // 0.5uS + TRAINER_TIMER->ARR = 45000; + TRAINER_TIMER->CCR2 = GET_TRAINER_PPM_DELAY()*2; + TRAINER_TIMER->CCER = TIM_CCER_CC2E | (GET_TRAINER_PPM_POLARITY() ? 0 : TIM_CCER_CC2P); + TRAINER_TIMER->CCMR1 = TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_0; // Force O/P high + TRAINER_TIMER->BDTR = TIM_BDTR_MOE; + TRAINER_TIMER->EGR = 1; + TRAINER_TIMER->DIER |= TIM_DIER_UDE; + TRAINER_TIMER->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE; // PWM mode 1 + TRAINER_TIMER->CR1 |= TIM_CR1_CEN; + + trainerSendNextFrame(); + + NVIC_EnableIRQ(TRAINER_TIMER_IRQn); + NVIC_SetPriority(TRAINER_TIMER_IRQn, 7); +} + +void stop_trainer_ppm() +{ + TRAINER_TIMER->DIER = 0; // Stop Interrupt + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; // Stop counter +} + +void init_trainer_capture() +{ + GPIO_PinAFConfig(TRAINER_GPIO, TRAINER_IN_GPIO_PinSource, TRAINER_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = TRAINER_IN_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TRAINER_GPIO, &GPIO_InitStructure); + + TRAINER_TIMER->ARR = 0xFFFF; + TRAINER_TIMER->PSC = TRAINER_TIMER_FREQ / 2000000 - 1; // 0.5uS + TRAINER_TIMER->CR2 = 0; + TRAINER_TIMER->CCMR1 = TRAINER_IN_CCMR1; + TRAINER_TIMER->CCER = TRAINER_IN_CCER; + TRAINER_TIMER->SR &= ~TIM_SR_CC3IF & ~TIM_SR_CC2IF & ~TIM_SR_UIF; // Clear flags + TRAINER_TIMER->DIER |= TIM_DIER_CC1IE; + TRAINER_TIMER->CR1 = TIM_CR1_CEN; + + NVIC_EnableIRQ(TRAINER_TIMER_IRQn); + NVIC_SetPriority(TRAINER_TIMER_IRQn, 7); +} + +void stop_trainer_capture() +{ + NVIC_DisableIRQ(TRAINER_TIMER_IRQn); // Stop Interrupt + + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; // Stop counter + TRAINER_TIMER->DIER = 0; // Stop Interrupt +} +short unsigned int* lastPulse = 0; + +void trainerSendNextFrame() +{ + setupPulsesPPMTrainer(); + TRAINER_TIMER->CCR2 = GET_TRAINER_PPM_DELAY() * 2; + TRAINER_TIMER->CCER = TIM_CCER_CC2E | (GET_TRAINER_PPM_POLARITY() ? 0 : TIM_CCER_CC2P); + lastPulse = trainerPulsesData.ppm.ptr; + TRAINER_TIMER->CCR3 = *(trainerPulsesData.ppm.ptr - 1) - 4000; // 2mS in advance + + trainerPulsesData.ppm.ptr = trainerPulsesData.ppm.pulses; + TRAINER_TIMER->DIER |= TIM_DIER_UDE; + TRAINER_TIMER->SR &= ~TIM_SR_UIF; // Clear this flag + TRAINER_TIMER->DIER |= TIM_DIER_UIE; // Enable this interrupt +} + +extern "C" void TRAINER_TIMER_IRQHandler() +{ + DEBUG_INTERRUPT(INT_TRAINER); + uint16_t capture = 0; + bool doCapture = false; + + if ((TRAINER_TIMER->DIER & TIM_DIER_CC1IE) && (TRAINER_TIMER->SR & TIM_SR_CC1IF)) { + // capture mode on trainer jack + capture = TRAINER_IN_COUNTER_REGISTER; + if (TRAINER_CONNECTED() && currentTrainerMode == TRAINER_MODE_MASTER_TRAINER_JACK) { + doCapture = true; + } + } + if (doCapture) { + captureTrainerPulses(capture); + } + // PPM out compare interrupt + if ((TRAINER_TIMER->DIER & TIM_DIER_CC3IE) && (TRAINER_TIMER->SR & TIM_SR_CC3IF)) { + // compare interrupt + TRAINER_TIMER->DIER &= ~TIM_DIER_CC3IE; // stop this interrupt + TRAINER_TIMER->SR &= ~TIM_SR_CC3IF; // Clear flag + trainerSendNextFrame(); + } + + if ((TRAINER_TIMER->DIER & TIM_DIER_UIE) && (TRAINER_TIMER->SR & TIM_SR_UIF)) { + TRAINER_TIMER->SR &= ~TIM_SR_UIF; // Clear flag + TRAINER_TIMER->ARR = *trainerPulsesData.ppm.ptr++; + if (trainerPulsesData.ppm.ptr == lastPulse) { + TRAINER_TIMER->SR &= ~TIM_SR_CC3IF; // Clear flag + TRAINER_TIMER->DIER |= TIM_DIER_CC3IE; // Enable this interrupt + } + } +} diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index d8e62917f53..ca22372631c 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.h b/radio/src/targets/simu/simpgmspace.h index 7793948bd0f..8e8a6b2a506 100644 --- a/radio/src/targets/simu/simpgmspace.h +++ b/radio/src/targets/simu/simpgmspace.h @@ -76,11 +76,11 @@ void simuMain(); #define configure_pins(...) -#if defined(SDCARD) && !defined(SKIP_FATFS_DECLARATION) && !defined(SIMU_DISKIO) - #define SIMU_USE_SDCARD +#if (defined(SDCARD) || (defined(SPI_FLASH) && !defined(LITTLEFS))) && !defined(SKIP_FATFS_DECLARATION) && !defined(SIMU_DISKIO) + #define SIMU_USE_FAT_EMU #endif -#if defined(SIMU_USE_SDCARD) +#if defined(SIMU_USE_FAT_EMU) void simuFatfsSetPaths(const char * sdPath, const char * settingsPath); #else #define simuFatfsSetPaths(...) diff --git a/radio/src/targets/simu/simudisk.cpp b/radio/src/targets/simu/simudisk.cpp index d3a1d3fc097..d9ed8b8575b 100644 --- a/radio/src/targets/simu/simudisk.cpp +++ b/radio/src/targets/simu/simudisk.cpp @@ -29,8 +29,6 @@ FILE * diskImage = 0; -bool _g_FATFS_init = false; - RTOS_MUTEX_HANDLE ioMutex; int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj) /* Create a sync object */ @@ -198,86 +196,6 @@ DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) return RES_OK; } -void sdInit(void) -{ - // ioMutex = CoCreateMutex(); - // if (ioMutex >= CFG_MAX_MUTEX ) { - // // sd error - // return; - // } - - if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { - // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called - sdGetFreeSectors(); - -#if defined(LOG_TELEMETRY) - f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE); - if (f_size(&g_telemetryFile) > 0) { - f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append - } -#endif - -#if defined(LOG_BLUETOOTH) - f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE); - if (f_size(&g_bluetoothFile) > 0) { - f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append - } -#endif - } - else { - TRACE_SIMPGMSPACE("f_mount() failed"); - } -} - -void sdDone() -{ - if (sdMounted()) { - audioQueue.stopSD(); -#if defined(LOG_TELEMETRY) - f_close(&g_telemetryFile); -#endif -#if defined(LOG_BLUETOOTH) - f_close(&g_bluetoothFile); -#endif - f_mount(NULL, "", 0); // unmount SD - } -} - -void sdMount() -{ - TRACE("sdMount"); - - diskCache.clear(); - - if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { - // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called - _g_FATFS_init = true; - sdGetFreeSectors(); - -#if defined(LOG_TELEMETRY) - f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE); - if (f_size(&g_telemetryFile) > 0) { - f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append - } -#endif - -#if defined(LOG_BLUETOOTH) - f_open(&g_bluetoothFile, LOGS_PATH "/bluetooth.log", FA_OPEN_ALWAYS | FA_WRITE); - if (f_size(&g_bluetoothFile) > 0) { - f_lseek(&g_bluetoothFile, f_size(&g_bluetoothFile)); // append - } -#endif - } - else { - TRACE("f_mount() failed"); - } -} - -uint32_t sdMounted() -{ - return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0); -} - uint32_t sdIsHC() { return sdGetSize() > 2000000; diff --git a/radio/src/targets/simu/simufatfs.cpp b/radio/src/targets/simu/simufatfs.cpp index e3ed95f47aa..64390023eea 100644 --- a/radio/src/targets/simu/simufatfs.cpp +++ b/radio/src/targets/simu/simufatfs.cpp @@ -25,7 +25,7 @@ #include #include "opentx.h" -#if defined(SIMU_USE_SDCARD) // rest of file is excluded otherwise +#if defined(SIMU_USE_FAT_EMU) // rest of file is excluded otherwise #if defined(_MSC_VER) || !defined (__GNUC__) #define MSVC_BUILD 1 @@ -39,6 +39,7 @@ #include #include #include +#include #if MSVC_BUILD #include @@ -67,6 +68,15 @@ bool isPathDelimiter(char delimiter) return delimiter == '/'; } +bool pathHasStorageSelector(const char* path) +{ + if(strlen(path)<3) + return false; + if(isdigit(path[0]) && path[1] == ':' && path[2] == '/') + return true; + return false; +} + std::string removeTrailingPathDelimiter(const std::string & path) { std::string result = path; @@ -145,6 +155,8 @@ bool redirectToSettingsDirectory(const std::string & path) std::string convertToSimuPath(const char * path) { std::string result; + if(pathHasStorageSelector(path)) + path +=2; if (isPathDelimiter(path[0])) { if (redirectToSettingsDirectory(path)) { // TRACE("REDIRECT ********************************************"); diff --git a/radio/src/targets/simu/simulcd.cpp b/radio/src/targets/simu/simulcd.cpp index 0fc69390ce5..57e1f76823b 100644 --- a/radio/src/targets/simu/simulcd.cpp +++ b/radio/src/targets/simu/simulcd.cpp @@ -61,13 +61,13 @@ pixel_t* simuLcdBackBuf = _LCD_BUF2; // Copy 2 pixels at once to speed up a little static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_area) { - coord_t x1 = LCD_W - copy_area.w - copy_area.x; - coord_t y1 = LCD_H - copy_area.h - copy_area.y; + coord_t x1 = LCD_PHYS_W - copy_area.w - copy_area.x; + coord_t y1 = LCD_PHYS_H - copy_area.h - copy_area.y; auto total = copy_area.w * copy_area.h; uint16_t* px_src = src + total - 2; - auto px_dst = dst + y1 * LCD_W + x1; + auto px_dst = dst + y1 * LCD_PHYS_W + x1; for (auto line = 0; line < copy_area.h; line++) { auto line_end = px_dst + (copy_area.w & ~1); @@ -87,7 +87,7 @@ static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_ar px_src--; } - px_dst += LCD_W - copy_area.w; + px_dst += LCD_PHYS_W - copy_area.w; } } @@ -95,11 +95,11 @@ static void _rotate_area_180(lv_area_t& area) { lv_coord_t tmp_coord; tmp_coord = area.y2; - area.y2 = LCD_H - area.y1 - 1; - area.y1 = LCD_H - tmp_coord - 1; + area.y2 = LCD_PHYS_H - area.y1 - 1; + area.y1 = LCD_PHYS_H - tmp_coord - 1; tmp_coord = area.x2; - area.x2 = LCD_W - area.x1 - 1; - area.x1 = LCD_W - tmp_coord - 1; + area.x2 = LCD_PHYS_W - area.x1 - 1; + area.x1 = LCD_PHYS_W - tmp_coord - 1; } #endif static void _copy_screen_area(uint16_t* dst, uint16_t* src, const lv_area_t& copy_area) @@ -108,14 +108,14 @@ static void _copy_screen_area(uint16_t* dst, uint16_t* src, const lv_area_t& cop lv_coord_t y1 = copy_area.y1; lv_coord_t area_w = copy_area.x2 - copy_area.x1 + 1; - auto offset = y1 * LCD_W + x1; + auto offset = y1 * LCD_PHYS_W + x1; auto px_src = src + offset; auto px_dst = dst + offset; for (auto line = copy_area.y1; line <= copy_area.y2; line++) { memcpy(px_dst, px_src, area_w * sizeof(uint16_t)); - px_dst += LCD_W; - px_src += LCD_W; + px_dst += LCD_PHYS_W; + px_src += LCD_PHYS_W; } } @@ -124,13 +124,13 @@ static void _copy_area(uint16_t* dst, uint16_t* src, const rect_t& copy_area) lv_coord_t x1 = copy_area.x; lv_coord_t y1 = copy_area.y; - auto offset = y1 * LCD_W + x1; + auto offset = y1 * LCD_PHYS_W + x1; auto px_src = src; auto px_dst = dst + offset; for (auto line = 0; line < copy_area.h; line++) { memcpy(px_dst, px_src, copy_area.w * sizeof(uint16_t)); - px_dst += LCD_W; + px_dst += LCD_PHYS_W; px_src += copy_area.w; } } diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 5105f269d94..fc794a754e1 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -13,6 +13,7 @@ set(USE_RTC_CLOCK YES) set(HARDWARE_EXTERNAL_MODULE YES) add_definitions(-DPCBFRSKY) +add_definitions(-DDEFAULT_STORAGE=SDCARD) if(PCB STREQUAL X9E) set(DEFAULT_INTERNAL_MODULE XJT_PXX1 CACHE STRING "Default internal module") @@ -566,6 +567,13 @@ if(STATUS_LEDS) set(LED_DRIVER led_driver.cpp) endif() +if(SDCARD) + set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + diskio.cpp + ) +endif() + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} ${LED_DRIVER} diff --git a/radio/src/targets/taranis/bootloader/boot_menu.cpp b/radio/src/targets/taranis/bootloader/boot_menu.cpp index d23eb5b89bb..3591658f38c 100644 --- a/radio/src/targets/taranis/bootloader/boot_menu.cpp +++ b/radio/src/targets/taranis/bootloader/boot_menu.cpp @@ -32,7 +32,7 @@ #include "../../common/arm/stm32/bootloader/bin_files.h" extern MemoryType memoryType; - +static FATFS sdFatFs __DMA = {0}; void bootloaderInitScreen() { lcdInit(); @@ -40,6 +40,7 @@ void bootloaderInitScreen() backlightInit(); backlightFullOn(); + f_mount(&sdFatFs, "", 1); } static void bootloaderDrawMsg(unsigned int x, const char *str, uint8_t line, bool inverted) @@ -78,6 +79,12 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char *str) lcdDrawCenteredText(7 * FH, vers); lcdInvertLine(7); } +#if defined(SPI_FLASH) && defined(SDCARD) + else if (st == ST_SELECT_STORAGE) { + lcdDrawText(3*FW, 2*FH, "Internal", opt == 0 ? INVERS : 0); + lcdDrawText(3*FW, 2*FH, "SD Card", opt == 1 ? INVERS : 0); + } +#endif else if (st == ST_USB) { lcdDrawCenteredText(4 * FH, TR_BL_USB_CONNECTED); } @@ -92,13 +99,13 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char *str) } else if (st == ST_FLASH_CHECK) { if (opt == FC_ERROR) { - if (memoryType == MEM_FLASH) + if (memoryType == MEM_INTERNAL) bootloaderDrawMsg(0, TR_BL_INVALID_FIRMWARE, 2, false); else bootloaderDrawMsg(0, TR_BL_INVALID_EEPROM, 2, false); } else if (opt == FC_OK) { - if (memoryType == MEM_FLASH) { + if (memoryType == MEM_INTERNAL) { const char * vers = getFirmwareVersion((const char *)Block_buffer); #if LCD_W < 212 // Remove "edgetx-" from string diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index ed779acc8ee..22adcf23fd5 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -58,7 +58,7 @@ TASK_FUNCTION(menusTask) } #endif -#if defined(HARDWARE_TOUCH) && !defined(PCBFLYSKY) && !defined(SIMU) +#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) && !defined(PCBPL18) && !defined(SIMU) touchPanelInit(); #endif diff --git a/radio/src/telemetry/flysky_pl18.cpp b/radio/src/telemetry/flysky_pl18.cpp new file mode 100644 index 00000000000..e12e1d740bd --- /dev/null +++ b/radio/src/telemetry/flysky_pl18.cpp @@ -0,0 +1,252 @@ +/* + * 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 "flysky_pl18.h" +#include "flysky_ibus.h" +#include "dataconstants.h" + + + +#define FLYSKY_FIXED_RX_VOLTAGE (uint8_t)(FLYSKY_SENSOR_RX_VOLTAGE + (uint8_t)0xA0) + +#define MIN_SNR 8 +#define MAX_SNR 45 + +#define FIXED_PRECISION 15 +#define FIXED(val) (val << FIXED_PRECISION) +#define DECIMAL(val) (val >> FIXED_PRECISION) +#define R_DIV_G_MUL_10_Q15 UINT64_C(9591506) +#define INV_LOG2_E_Q1DOT31 UINT64_C(0x58b90bfc) // Inverse log base 2 of e +#define PRESSURE_MASK 0x7FFFF + +struct FlyskyPl18Sensor { + const uint16_t id; + const uint8_t subId; + const char * name; + const TelemetryUnit unit; + const uint8_t precision; + const uint8_t offset; + const uint8_t bytes; + const bool issigned; +}; + +union pl18SensorData { + uint8_t UINT8; + uint16_t UINT16; + int16_t INT16; + uint32_t UINT32; +}; + +FlyskyPl18Sensor defaultPl18Sensor = {0, 0, "UNKNOWN", UNIT_RAW, 0, 0, 2, false}; + +const FlyskyPl18Sensor Pl18Sensor[]= +{ + {FLYSKY_FIXED_RX_VOLTAGE, 0, STR_SENSOR_A1, UNIT_VOLTS, 2, 0, 2, false}, + {FLYSKY_SENSOR_RX_SIGNAL, 0, STR_SENSOR_RX_SIGNAL, UNIT_RAW, 0, 0, 2, false}, + {FLYSKY_SENSOR_RX_RSSI, 0, STR_SENSOR_RSSI, UNIT_DB, 0, 0, 2, true,}, + {FLYSKY_SENSOR_RX_NOISE, 0, STR_SENSOR_RX_NOISE, UNIT_DB, 0, 0, 2, true}, + {FLYSKY_SENSOR_RX_SNR, 0, STR_SENSOR_RX_SNR, UNIT_DB, 0, 0, 2, false}, + {FLYSKY_SENSOR_RX_SNR, 1, STR_SENSOR_RX_QUALITY, UNIT_PERCENT, 0, 0, 2, false}, + {FLYSKY_SENSOR_TEMP, 0, STR_SENSOR_TEMP1, UNIT_CELSIUS, 1, 0, 2, true}, + {FLYSKY_SENSOR_EXT_VOLTAGE,0, STR_SENSOR_A3, UNIT_VOLTS, 2, 0, 2, false}, + {FLYSKY_SENSOR_MOTO_RPM, 0, STR_SENSOR_RPM, UNIT_RPMS, 0, 0, 2, false}, + {FLYSKY_SENSOR_PRESSURE, 0, STR_SENSOR_PRES, UNIT_RAW, 1, 0, 2, false}, + {FLYSKY_SENSOR_PRESSURE, 1, STR_SENSOR_ALT, UNIT_METERS, 2, 0, 2, true}, +// {FLYSKY_SENSOR_PRESSURE, 2, STR_SENSOR_TEMP2, UNIT_CELSIUS, 1, 0, 4, true}, + {FLYSKY_SENSOR_GPS, 1, STR_SENSOR_SATELLITES, UNIT_RAW, 0, 0, 1, false}, + {FLYSKY_SENSOR_GPS, 2, STR_SENSOR_GPS, UNIT_GPS_LATITUDE, 0, 1, 4, true}, + {FLYSKY_SENSOR_GPS, 3, STR_SENSOR_GPS, UNIT_GPS_LONGITUDE, 0, 5, 4, true}, + {FLYSKY_SENSOR_GPS, 4, STR_SENSOR_ALT, UNIT_METERS, 0, 8, 2, true}, + {FLYSKY_SENSOR_GPS, 5, STR_SENSOR_GSPD, UNIT_KMH, 1, 10, 2, false}, + {FLYSKY_SENSOR_GPS, 6, STR_SENSOR_HDG, UNIT_DEGREE, 3, 12, 2, false}, +// {FLYSKY_SENSOR_SYNC, 0, "Sync", UNIT_RAW, 0, 0, 2, false}, + defaultPl18Sensor +}; + + +extern int32_t getALT(uint32_t value); + +signed short CalculateAltitude(unsigned int pressure) +{ + int32_t alt = getALT(pressure); + return alt; +} + +const FlyskyPl18Sensor* getFlyskyPl18Sensor(uint16_t id, uint8_t subId) +{ + for (const FlyskyPl18Sensor* sensor = Pl18Sensor; sensor->id; sensor++) { + if (sensor->id == id && sensor->subId == subId) { + return sensor; + } + } + return &defaultPl18Sensor; +} + +void flySkyPl18SetDefault(int index, uint8_t id, uint8_t subId, + uint8_t instance) +{ + TelemetrySensor& telemetrySensor = g_model.telemetrySensors[index]; + telemetrySensor.id = id; + telemetrySensor.subId = subId; + telemetrySensor.instance = instance; + const FlyskyPl18Sensor* sensor = getFlyskyPl18Sensor(id, subId); + telemetrySensor.init(sensor->name, sensor->unit, sensor->precision); + if (sensor->unit == UNIT_RPMS) { + telemetrySensor.custom.ratio = 1; + telemetrySensor.custom.offset = 1; + } + storageDirty(EE_MODEL); +} + +int32_t GetSensorValueFlySkyPl18(const FlyskyPl18Sensor* sensor, + const uint8_t* data) +{ + int32_t value = 0; + const pl18SensorData* sensorData = + reinterpret_cast(data + sensor->offset); + if (sensor->bytes == 1) + value = sensorData->UINT8; + else if (sensor->bytes == 2) + value = sensor->issigned ? sensorData->INT16 : sensorData->UINT16; + else if (sensor->bytes == 4) + value = sensorData->UINT32; + + // For older RF module FW Sgml is in [0, 10] range + // and we need to use RSSI for alarm +/* + if (PL18internalModuleFwVersion < 0x1000E) { + if (sensor->id == FLYSKY_SENSOR_RX_RSSI) { + if (value < -200) value = -200; + // if g_model.rssiAlarms.flysky_telemetry == 1 + // RSSI will be kept within native FlySky range [-90, -60] + if (!g_model.rssiAlarms.flysky_telemetry) { + value += 200; + value /= 2; + } + telemetryData.rssi.set(value); + } + } else if (sensor->id == FLYSKY_SENSOR_RX_SIGNAL) { + telemetryData.rssi.set(value); + } +*/ + + if (sensor->id == FLYSKY_SENSOR_PRESSURE) { + switch(sensor->subId) + { + case 0: + value = value & PRESSURE_MASK; + break; + case 1: + value = CalculateAltitude(value); + break; + case 2: + // TO DO: fix temperature calculation + value = (int16_t)(value >> 19) + 150;// - 400; + break; + } + } + return value; +} + +// Module pulse synchronization +#define SAFE_SYNC_LAG 800 /* us */ +#define SYNC_UPDATE_TIMEOUT 200 /* *10ms */ +#define AFHDS2_SYNC_SAMPLES 8 +#define AFHDS2_NEGATIVE_SYNC_LIMIT (AFHDS2_PERIOD - SAFE_SYNC_LAG) + +int16_t syncAfhds2min = 0; +int16_t syncAfhds2max = 0; +unsigned currentSyncIndex; + +void flySkyPl18Sync(int16_t delayValue) +{ + if (delayValue > AFHDS2_NEGATIVE_SYNC_LIMIT) { + delayValue -= AFHDS2_PERIOD; + } + if (currentSyncIndex == 0) { + syncAfhds2min = AFHDS2_PERIOD; + syncAfhds2max = -SAFE_SYNC_LAG; + } + + if (delayValue > syncAfhds2max) { + syncAfhds2max = delayValue; + } + if (delayValue < syncAfhds2min) { + syncAfhds2min = delayValue; + } + if (currentSyncIndex++ == AFHDS2_SYNC_SAMPLES) { + currentSyncIndex = 0; + // check against to late delivered frames up to 800us, some frames still in + // range + if (syncAfhds2min < 0 && syncAfhds2max < SAFE_SYNC_LAG) { + getModuleSyncStatus(INTERNAL_MODULE) + .update(AFHDS2_PERIOD, (syncAfhds2min - 100) + SAFE_SYNC_LAG); + } else if (syncAfhds2max > SAFE_SYNC_LAG + 100) { // > 900us + if (syncAfhds2min > 100) { // never sync if last registred value is below + // 100us - we are to close to perfect time + getModuleSyncStatus(INTERNAL_MODULE) + .update(AFHDS2_PERIOD, (syncAfhds2min - 100) + SAFE_SYNC_LAG); + } else if (syncAfhds2min < 0) { + getModuleSyncStatus(INTERNAL_MODULE) + .update(AFHDS2_PERIOD, (syncAfhds2max - 900) + SAFE_SYNC_LAG); + } + } + } +} + +void flySkyPl18ProcessTelemetryPacket(const uint8_t* ptr, uint8_t size) +{ + uint8_t sensorID = ptr[0]; + uint8_t instnace = ptr[1]; + int sensorCount = 0; + if (sensorID != FLYSKY_SENSOR_SYNC) sensorCount++; + + // native telemetry for 1.1.2 +/* + if (PL18internalModuleFwVersion >= 0x010102) { + if (sensorID == FLYSKY_SENSOR_SYNC) + flySkyPl18Sync((int16_t)(ptr[3] << 8 | ptr[2])); + uint8_t frameType = 0xAA; + if (size > 4) { + frameType = 0xAC; + } else if (size != 4) { + return; + } + processFlySkySensor(ptr, frameType); + } else { + if (sensorID == FLYSKY_SENSOR_RX_VOLTAGE) + sensorID = FLYSKY_FIXED_RX_VOLTAGE; + for (const FlyskyPl18Sensor* sensor = Pl18Sensor; sensor->id; sensor++) { + if (sensor->id == sensorID) { + int32_t value = GetSensorValueFlySkyPl18(sensor, ptr + 2); + setTelemetryValue(PROTOCOL_TELEMETRY_FLYSKY_PL18, sensor->id, + sensor->subId, instnace, value, sensor->unit, + sensor->precision); + if (sensor->id == FLYSKY_SENSOR_SYNC) flySkyPl18Sync(value); + } + } + } +*/ + if (sensorCount) { + telemetryStreaming = TELEMETRY_TIMEOUT10ms; + } +} diff --git a/radio/src/telemetry/flysky_pl18.h b/radio/src/telemetry/flysky_pl18.h new file mode 100644 index 00000000000..bfa62818e9b --- /dev/null +++ b/radio/src/telemetry/flysky_pl18.h @@ -0,0 +1,71 @@ +/* + * 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 __FLYSKY_PL18_H__ +#define __FLYSKY_PL18_H__ + +enum FlySkySensorType_E { + FLYSKY_SENSOR_RX_VOLTAGE, + FLYSKY_SENSOR_RX_SIGNAL, + FLYSKY_SENSOR_RX_RSSI, + FLYSKY_SENSOR_RX_NOISE, + FLYSKY_SENSOR_RX_SNR, + FLYSKY_SENSOR_TEMP, + FLYSKY_SENSOR_EXT_VOLTAGE, + FLYSKY_SENSOR_MOTO_RPM, + FLYSKY_SENSOR_PRESSURE, + FLYSKY_SENSOR_GPS, + FLYSKY_SENSOR_SYNC = 0xEE, +}; + + +typedef struct FLYSKY_GPS_INFO_S { + uint8_t position_fix; + uint8_t satell_cnt; + uint8_t latitude[4]; + uint8_t longtitude[4]; + uint8_t altitude[4]; + uint8_t g_speed[2]; + uint8_t direction[2]; +} gps_info_t; +typedef struct FLYSKY_SENSOR_DATA_S { + uint8_t sensor_type; + uint8_t sensor_id; + uint8_t voltage[2]; + uint8_t signal; + uint8_t rssi[2]; + uint8_t noise[2]; + uint8_t snr[2]; + uint8_t temp[2]; + uint8_t ext_voltage[2]; + uint8_t moto_rpm[2]; + uint8_t pressure_value[2]; + gps_info_t gps_info; +} rx_sensor_t; + +void flySkyPl18SetDefault(int index, uint8_t id, uint8_t subId, uint8_t instance); +void flySkyPl18ProcessTelemetryPacket(const uint8_t * ptr, uint8_t SensorType ); +void processInternalFlySkyTelemetryData(uint8_t byte); +uint8_t intmoduleGetByte(uint8_t * byte); + +extern bool syncAfhds2Module; + +#endif diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 4bad6091dd7..692a280f83b 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -475,7 +475,7 @@ void telemetryInit(uint8_t protocol) } #if defined(LOG_TELEMETRY) && !defined(SIMU) -extern FIL g_telemetryFile; +extern VfsFile g_telemetryFile; void logTelemetryWriteStart() { static tmr10ms_t lastTime = 0; @@ -483,7 +483,7 @@ void logTelemetryWriteStart() if (lastTime != newTime) { struct gtm utm; gettime(&utm); - f_printf(&g_telemetryFile, "\r\n%4d-%02d-%02d,%02d:%02d:%02d.%02d0:", + g_telemetryFile.fprintf("\r\n%4d-%02d-%02d,%02d:%02d:%02d.%02d0:", utm.tm_year + TM_YEAR_BASE, utm.tm_mon + 1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); lastTime = newTime; @@ -492,7 +492,7 @@ void logTelemetryWriteStart() void logTelemetryWriteByte(uint8_t data) { - f_printf(&g_telemetryFile, " %02X", data); + g_telemetryFile.fprintf" %02X", data); } #endif diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 6fc6d01dfea..236e4557660 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -179,7 +179,11 @@ inline uint8_t modelTelemetryProtocol() // TODO: Check if that is really necessary... #if defined(AFHDS2) && defined(HARDWARE_INTERNAL_MODULE) if (isModuleAFHDS2A(INTERNAL_MODULE)) { +#if defined(PCBPL18) + return PROTOCOL_TELEMETRY_FLYSKY_PL18; +#else return PROTOCOL_TELEMETRY_FLYSKY_NV14; +#endif } #endif diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index d9f6a5ceccf..3f50a0ea4f3 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -37,8 +37,10 @@ #include "ghost.h" #endif -#if defined(PCBNV14) - #include "telemetry/flysky_nv14.h" +#if defined(PCBPL18) +#include "telemetry/flysky_pl18.h" +#else +#include "telemetry/flysky_nv14.h" #endif #if defined(MULTIMODULE) @@ -550,9 +552,15 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, #endif #if defined(AFHDS2) && defined(PCBNV14) - case PROTOCOL_TELEMETRY_FLYSKY_NV14: - flySkyNv14SetDefault(index, id, subId, instance); +#if defined(PCBPL18) + case PROTOCOL_TELEMETRY_FLYSKY_PL18: + flySkyPl18SetDefault(index, id, subId, instance); break; +#else + case PROTOCOL_TELEMETRY_FLYSKY_NV14: + flySkyNv14SetDefault(index, id, subId, instance); + break; +#endif #endif case PROTOCOL_TELEMETRY_SPEKTRUM: diff --git a/radio/src/tests/gtests.cpp b/radio/src/tests/gtests.cpp index 11a1933370a..ffdd0054496 100644 --- a/radio/src/tests/gtests.cpp +++ b/radio/src/tests/gtests.cpp @@ -24,8 +24,10 @@ #include #include #include + #include "gtests.h" #include "hal/adc_driver.h" +#include "location.h" using ::testing::TestEventListener; using ::testing::EmptyTestEventListener; @@ -156,6 +158,8 @@ int main(int argc, char **argv) simuInit(); adcInit(&simu_adc_driver); + simuFatfsSetPaths(TESTS_PATH, TESTS_PATH); + #if !defined(COLORLCD) menuLevel = 0; #endif diff --git a/radio/src/tests/lcd.cpp b/radio/src/tests/lcd.cpp index 2def7b670f2..b56f5eb9c62 100644 --- a/radio/src/tests/lcd.cpp +++ b/radio/src/tests/lcd.cpp @@ -32,6 +32,10 @@ #include "location.h" #include "targets/simu/simulcd.h" +#if !defined(ROOT_PATH) + #define ROOT_PATH "/DEFAULT" +#endif + void doPaint(QPainter & p) { QRgb rgb = qRgb(161, 161, 161); @@ -324,7 +328,7 @@ TEST(Lcd, BMPWrapping) { lcdClear(); uint8_t bitmap[2+40*40/2]; - lcdLoadBitmap(bitmap, TESTS_PATH "/plane.bmp", 40, 40); + lcdLoadBitmap(bitmap, ROOT_PATH "/plane.bmp", 40, 40); lcdDrawBitmap(200, 0, bitmap); lcdDrawBitmap(200, 60, bitmap); lcdDrawBitmap(240, 60, bitmap); // x too big @@ -393,31 +397,31 @@ TEST(Lcd, lcdDrawBitmapLoadAndDisplay) // Test proper BMP files, they should display correctly { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(7, 32)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/4b_7x32.bmp", 7, 32) != NULL); + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/4b_7x32.bmp", 7, 32) != NULL); bitmap.leakCheck(); lcdDrawBitmap(10, 2, bitmap.buffer()); } { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(6, 32)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/1b_6x32.bmp", 6, 32) != NULL); + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/1b_6x32.bmp", 6, 32) != NULL); bitmap.leakCheck(); lcdDrawBitmap(20, 2, bitmap.buffer()); } { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(31, 31)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/4b_31x31.bmp", 31, 31) != NULL); + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/4b_31x31.bmp", 31, 31) != NULL); bitmap.leakCheck(); lcdDrawBitmap(30, 2, bitmap.buffer()); } { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(39, 32)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/1b_39x32.bmp", 39, 32) != NULL); + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/1b_39x32.bmp", 39, 32) != NULL); bitmap.leakCheck(); lcdDrawBitmap(70, 2, bitmap.buffer()); } { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(20, 20)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/4b_20x20.bmp", 20, 20) != NULL); + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/4b_20x20.bmp", 20, 20) != NULL); bitmap.leakCheck(); lcdDrawBitmap(120, 2, bitmap.buffer()); } @@ -431,7 +435,7 @@ TEST(Lcd, lcdDrawBitmapLoadAndDisplay) } { TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(10, 10)); - EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), TESTS_PATH "/1b_39x32.bmp", 10, 10) == NULL) << "to small buffer"; + EXPECT_TRUE(lcdLoadBitmap(bitmap.buffer(), ROOT_PATH "/1b_39x32.bmp", 10, 10) == NULL) << "to small buffer"; bitmap.leakCheck(); } } diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 589a80d48af..b60c36df5c3 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -70,7 +70,7 @@ bool checkScreenshot_colorlcd(const BitmapBuffer* dc, const char* test) filename += 'x' + std::to_string(LCD_H); filename += ".png"; - std::string fullpath = TESTS_PATH "/" + filename; + std::string fullpath = ROOT_PATH "/" + filename; std::unique_ptr testPict(BitmapBuffer::loadBitmap(fullpath.c_str())); if (!testPict || testPict->width() != LCD_W || testPict->height() != LCD_H) { @@ -254,7 +254,7 @@ TEST(Lcd_colorlcd, bitmap) dc.clear(COLOR_THEME_SECONDARY3); dc.setClippingRect(100, 400, 50, 200); - std::unique_ptr bmp(BitmapBuffer::loadBitmap(TESTS_PATH "/opentx.png")); + std::unique_ptr bmp(BitmapBuffer::loadBitmap(ROOT_PATH "/opentx.png")); dc.drawBitmap( 0, 0, bmp.get()); dc.drawBitmap(320, 0, bmp.get()); dc.drawBitmap( 0, 150, bmp.get()); @@ -269,7 +269,7 @@ TEST(Lcd_colorlcd, masks) dc.clear(COLOR_THEME_SECONDARY3); - BitmapBuffer* mask = BitmapBuffer::loadMask(TESTS_PATH "/mask_menu_radio.png"); + BitmapBuffer* mask = BitmapBuffer::loadMask(ROOT_PATH "/mask_menu_radio.png"); for (int i=0; iwidth()) { for (int j=0; jheight()) { dc.drawMask(i, j, mask, COLOR2FLAGS(BLACK)); diff --git a/radio/src/tests/switches.cpp b/radio/src/tests/switches.cpp index 8e864db347a..b4a639c2c2a 100644 --- a/radio/src/tests/switches.cpp +++ b/radio/src/tests/switches.cpp @@ -121,13 +121,13 @@ TEST(evalLogicalSwitches, playFile) sdAvailableLogicalSwitchAudioFiles.setBit(INDEX_LOGICAL_SWITCH_AUDIO_FILE(31,AUDIO_EVENT_ON)); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (0 << 16) + AUDIO_EVENT_OFF, filename); - EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L1-off.wav"), 0); + EXPECT_EQ(strcmp(filename, "/DEFAULT/SOUNDS/en/" MODELNAME "/L1-off.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (0 << 16) + AUDIO_EVENT_ON, filename); - EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L1-on.wav"), 0); + EXPECT_EQ(strcmp(filename, "/DEFAULT/SOUNDS/en/" MODELNAME "/L1-on.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_OFF, filename); - EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L32-off.wav"), 0); + EXPECT_EQ(strcmp(filename, "/DEFAULT/SOUNDS/en/" MODELNAME "/L32-off.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_ON, filename); - EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L32-on.wav"), 0); + EXPECT_EQ(strcmp(filename, "/DEFAULT/SOUNDS/en/" MODELNAME "/L32-on.wav"), 0); EXPECT_EQ(isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_ON, filename), true); EXPECT_EQ(isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (32 << 16) + AUDIO_EVENT_ON, filename), false); diff --git a/radio/src/thirdparty/FatFs/ffconf.h b/radio/src/thirdparty/FatFs/ffconf.h index fda61e9edcf..5fd25c451c4 100644 --- a/radio/src/thirdparty/FatFs/ffconf.h +++ b/radio/src/thirdparty/FatFs/ffconf.h @@ -174,7 +174,7 @@ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define FF_VOLUMES 1 +#define FF_VOLUMES 2 /* Number of volumes (logical drives) to be used. (1-10) */ @@ -211,7 +211,7 @@ / GET_SECTOR_SIZE command. */ -#define FF_USE_TRIM 0 +#define FF_USE_TRIM 1 /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) / To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ diff --git a/radio/src/thirdparty/Lua/src/lauxlib.c b/radio/src/thirdparty/Lua/src/lauxlib.c index b3ef28d4a91..8a1f4da52fc 100644 --- a/radio/src/thirdparty/Lua/src/lauxlib.c +++ b/radio/src/thirdparty/Lua/src/lauxlib.c @@ -34,17 +34,6 @@ #define LEVELS2 10 /* size of the second part of the stack */ -#if defined(USE_FATFS) -int lua__getc(FIL *f) -{ - char c; - UINT result; - if (f_read(f, &c, 1, &result) == FR_OK && result == 1) - return c; - else - return -1; -} -#endif /* @@ -584,11 +573,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { typedef struct LoadF { int n; /* number of pre-read characters */ -#if defined(USE_FATFS) - FIL f; -#else - FILE *f; /* file being read */ -#endif + lua_FILE *f; /* file being read */ char buff[LUAL_BUFFERSIZE]; /* area for reading file */ } LoadF; @@ -601,18 +586,11 @@ static const char *getF (lua_State *L, void *ud, size_t *size) { lf->n = 0; /* no more pre-read characters */ } else { /* read a block from file */ - /* 'fread' can return > 0 *and* set the EOF flag. If next call to - 'getF' called 'fread', it might still wait for user input. + /* 'lua_fread' can return > 0 *and* set the EOF flag. If next call to + 'getF' called 'lua_fread', it might still wait for user input. The next check avoids this problem. */ -#if defined(USE_FATFS) - UINT f_read_size; - FRESULT result = f_read(&lf->f, lf->buff, sizeof(lf->buff), &f_read_size); - if (result != FR_OK || f_read_size == 0) return NULL; - *size = f_read_size; -#else - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ -#endif + if (lua_feof(lf->f)) return NULL; + *size = lua_fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ } return lf->buff; } @@ -623,7 +601,7 @@ static int errfile (lua_State *L, const char *what, int fnameindex) { const char *filename = lua_tostring(L, fnameindex) + 1; lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); lua_remove(L, fnameindex); - return LUA_ERRFILE; + return LUA_ERR_FILE; } @@ -632,12 +610,12 @@ static int skipBOM (LoadF *lf) { int c; lf->n = 0; do { - c = lua_getc(lf->f); + c = lua_fgetc(lf->f); if (c == EOF || c != *(const unsigned char *)p++) return c; lf->buff[lf->n++] = c; /* to be read by the parser */ } while (*p != '\0'); lf->n = 0; /* prefix matched; discard it */ - return lua_getc(lf->f); /* return next character */ + return lua_fgetc(lf->f); /* return next character */ } @@ -652,9 +630,9 @@ static int skipcomment (LoadF *lf, int *cp) { int c = *cp = skipBOM(lf); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ - c = lua_getc(lf->f); + c = lua_fgetc(lf->f); } while (c != EOF && c != '\n') ; - *cp = lua_getc(lf->f); /* skip end-of-line, if present */ + *cp = lua_fgetc(lf->f); /* skip end-of-line, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ @@ -668,42 +646,27 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, int c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ if (filename == NULL) { -#if defined(USE_FATFS) return errfile(L, "open", fnameindex); -#else - lua_pushliteral(L, "=stdin"); - lf.f = stdin; -#endif +// lua_pushliteral(L, "=stdin"); +// lf.f = stdin; } else { lua_pushfstring(L, "@%s", filename); -#if defined(USE_FATFS) - FRESULT result = f_open(&lf.f, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) return errfile(L, "open", fnameindex); -#else - lf.f = fopen(filename, "r"); + lf.f = lua_fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); -#endif } if (skipcomment(&lf, &c)) /* read initial portion */ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ -#if !defined(USE_FATFS) if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + lf.f = lua_freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); skipcomment(&lf, &c); /* re-read initial portion */ } -#endif if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); -#if defined(USE_FATFS) - readstatus = 0; - if (filename) f_close(&lf.f); -#else - readstatus = ferror(lf.f); - if (filename) fclose(lf.f); /* close file (even in case of errors) */ -#endif + readstatus = lua_ferror(lf.f); + if (filename) lua_fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from `lua_load' */ return errfile(L, "read", fnameindex); diff --git a/radio/src/thirdparty/Lua/src/lauxlib.h b/radio/src/thirdparty/Lua/src/lauxlib.h index a9f9e8e4393..3c80ac6d8dd 100644 --- a/radio/src/thirdparty/Lua/src/lauxlib.h +++ b/radio/src/thirdparty/Lua/src/lauxlib.h @@ -19,7 +19,7 @@ /* extra error code for `luaL_load' */ -#define LUA_ERRFILE (LUA_ERRERR+1) +#define LUA_ERR_FILE (LUA_ERRERR+1) typedef struct luaL_Reg { @@ -202,12 +202,8 @@ LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); #define LUA_FILEHANDLE "FILE*" typedef struct luaL_Stream { -#if defined(USE_FATFS) - FIL f; -#else - FILE *f; /* stream (NULL for incompletely created streams) */ + lua_FILE *f; /* stream (NULL for incompletely created streams) */ lua_CFunction closef; /* to close stream (NULL for closed streams) */ -#endif } luaL_Stream; /* }====================================================== */ diff --git a/radio/src/thirdparty/Lua/src/ldblib.c b/radio/src/thirdparty/Lua/src/ldblib.c index 596d10d303f..977b82cf499 100644 --- a/radio/src/thirdparty/Lua/src/ldblib.c +++ b/radio/src/thirdparty/Lua/src/ldblib.c @@ -343,7 +343,7 @@ static int db_debug (lua_State *L) { for (;;) { char buffer[250]; luai_writestringerror("%s", "lua_debug> "); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || + if (lua_fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || diff --git a/radio/src/thirdparty/Lua/src/liolib.c b/radio/src/thirdparty/Lua/src/liolib.c index 4d4a9a17ffc..60c6740c4f3 100644 --- a/radio/src/thirdparty/Lua/src/liolib.c +++ b/radio/src/thirdparty/Lua/src/liolib.c @@ -26,15 +26,12 @@ #include "lauxlib.h" #include "lualib.h" -#if defined(USE_FATFS) - #define FILE FIL -#endif #if !defined(lua_checkmode) /* ** Check whether 'mode' matches '[rwa]%+?b?'. -** Change this macro to accept other modes for 'fopen' besides +** Change this macro to accept other modes for 'lua_fopen' besides ** the standard ones. */ #define lua_checkmode(mode) \ @@ -103,8 +100,8 @@ #else -#define l_fseek(f,o,w) fseek(f,o,w) -#define l_ftell(f) ftell(f) +#define l_fseek(f,o,w) lua_fseek(f,o,w) +#define l_ftell(f) lua_ftell(f) #define l_seeknum long #endif @@ -124,7 +121,8 @@ typedef luaL_Stream LStream; #define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) -#if !defined(USE_FATFS) + +#if defined(LUA_DISABLED_IO) #define isclosed(p) ((p)->closef == NULL) @@ -150,13 +148,13 @@ static int f_tostring (lua_State *L) { lua_pushfstring(L, "file (%p)", p->f); return 1; } +#endif // LUA_DISABLED_IO -#endif -static FILE *tofile (lua_State *L) { +static lua_FILE *tofile (lua_State *L) { LStream *p = tolstream(L); -#if defined(USE_FATFS) - return &p->f; +#if !defined(LUA_DISABLED_IO) + return p->f; #else if (isclosed(p)) luaL_error(L, "attempt to use a closed file"); @@ -173,14 +171,12 @@ static FILE *tofile (lua_State *L) { */ static LStream *newprefile (lua_State *L) { LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); -#if !defined(USE_FATFS) p->closef = NULL; /* mark file handle as 'closed' */ -#endif luaL_setmetatable(L, LUA_FILEHANDLE); return p; } -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) static int aux_close (lua_State *L) { LStream *p = tolstream(L); lua_CFunction cf = p->closef; @@ -190,51 +186,52 @@ static int aux_close (lua_State *L) { #endif static int io_close (lua_State *L) { -#if defined(USE_FATFS) - f_close(tofile(L)); - return 0; -#else +#if defined(LUA_DISABLED_IO) if (lua_isnone(L, 1)) /* no argument? */ lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ tofile(L); /* make sure argument is an open stream */ return aux_close(L); +#else + lua_fclose(tofile(L)); + return 0; #endif } static int f_gc (lua_State *L) { -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) LStream *p = tolstream(L); if (!isclosed(p) && p->f != NULL) aux_close(L); /* ignore closed and incompletely open files */ +#else + lua_fclose(tofile(L)); #endif - f_close(tofile(L)); // no need to check if file was already closed (fatfs will not close it if p->f->fs is 0) return 0; } -#if !defined(USE_FATFS) + +#if defined(LUA_DISABLED_IO) + /* ** function to close regular files */ static int io_fclose (lua_State *L) { LStream *p = tolstream(L); - int res = fclose(p->f); + int res = lua_fclose(p->f); return luaL_fileresult(L, (res == 0), NULL); } #endif static LStream *newfile (lua_State *L) { LStream *p = newprefile(L); -#if !defined(USE_FATFS) p->f = NULL; - p->closef = &io_fclose; -#endif + p->closef = &io_close; return p; } -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) static void opencheck (lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); - p->f = fopen(fname, mode); + p->f = lua_fopen(fname, mode); if (p->f == NULL) luaL_error(L, "cannot open file " LUA_QS " (%s)", fname, strerror(errno)); } @@ -244,31 +241,14 @@ static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *md = luaL_optstring(L, 2, "r"); LStream *p = newfile(L); -#if defined(USE_FATFS) - BYTE mode = FA_READ; - if (*md == 'w') - mode = FA_WRITE | FA_CREATE_ALWAYS; // always create file and truncate it - else if (*md == 'a') - mode = FA_WRITE | FA_OPEN_ALWAYS; // always open file (create it if necessary) - FRESULT result = f_open(&p->f, filename, mode); - if (result == FR_OK) { - if (*md == 'a') - f_lseek(&p->f, f_size(&p->f)); // seek to the end of the file - return 1; - } - else { - return luaL_fileresult(L, 0, filename); - } -#else const char *mode = md; /* to traverse/check mode */ luaL_argcheck(L, lua_checkmode(mode), 2, "invalid mode"); - p->f = fopen(filename, md); + p->f = lua_fopen(filename, md); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; -#endif } -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) /* ** function to close 'popen' files */ @@ -286,15 +266,13 @@ static int io_popen (lua_State *L) { return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } - static int io_tmpfile (lua_State *L) { LStream *p = newfile(L); p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; } - -static FILE *getiofile (lua_State *L, const char *findex) { +static lua_FILE *getiofile (lua_State *L, const char *findex) { LStream *p; lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); @@ -303,7 +281,6 @@ static FILE *getiofile (lua_State *L, const char *findex) { return p->f; } - static int g_iofile (lua_State *L, const char *f, const char *mode) { if (!lua_isnoneornil(L, 1)) { const char *filename = lua_tostring(L, 1); @@ -320,12 +297,10 @@ static int g_iofile (lua_State *L, const char *f, const char *mode) { return 1; } - static int io_input (lua_State *L) { return g_iofile(L, IO_INPUT, "r"); } - static int io_output (lua_State *L) { return g_iofile(L, IO_OUTPUT, "w"); } @@ -380,7 +355,7 @@ static int io_lines (lua_State *L) { */ -static int read_number (lua_State *L, FILE *f) { +static int read_number (lua_State *L, lua_FILE *f) { lua_Number d; if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { lua_pushnumber(L, d); @@ -392,7 +367,7 @@ static int read_number (lua_State *L, FILE *f) { } } -static int test_eof (lua_State *L, FILE *f) { +static int test_eof (lua_State *L, lua_FILE *f) { int c = lua_getc(f); ungetc(c, f); lua_pushlstring(L, NULL, 0); @@ -400,13 +375,13 @@ static int test_eof (lua_State *L, FILE *f) { } -static int read_line (lua_State *L, FILE *f, int chop) { +static int read_line (lua_State *L, lua_FILE *f, int chop) { luaL_Buffer b; luaL_buffinit(L, &b); for (;;) { size_t l; char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + if (lua_fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ luaL_pushresult(&b); /* close buffer */ return (lua_rawlen(L, -1) > 0); /* check whether read something */ } @@ -423,13 +398,13 @@ static int read_line (lua_State *L, FILE *f, int chop) { #define MAX_SIZE_T (~(size_t)0) -static void read_all (lua_State *L, FILE *f) { +static void read_all (lua_State *L, lua_FILE *f) { size_t rlen = LUAL_BUFFERSIZE; /* how much to read in each cycle */ luaL_Buffer b; luaL_buffinit(L, &b); for (;;) { char *p = luaL_prepbuffsize(&b, rlen); - size_t nr = fread(p, sizeof(char), rlen, f); + size_t nr = lua_fread(p, sizeof(char), rlen, f); luaL_addsize(&b, nr); if (nr < rlen) break; /* eof? */ else if (rlen <= (MAX_SIZE_T / 4)) /* avoid buffers too large */ @@ -439,20 +414,20 @@ static void read_all (lua_State *L, FILE *f) { } #endif -static int read_chars (lua_State *L, FILE *f, size_t n) { - unsigned int nr; /* number of chars actually read */ +static int read_chars (lua_State *L, lua_FILE *f, size_t n) { + size_t nr; /* number of chars actually read */ char *p; luaL_Buffer b; luaL_buffinit(L, &b); p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ - FRESULT result = f_read(f, p, n, &nr); /* try to read 'n' chars */ + nr = lua_fread(p, sizeof(char), n, f); /* try to read 'n' chars */ luaL_addsize(&b, nr); luaL_pushresult(&b); /* close buffer */ - return (result == FR_OK && nr > 0); /* true iff read something */ + return (nr > 0); /* true iff read something */ } -#if !defined(USE_FATFS) -static int g_read (lua_State *L, FILE *f, int first) { +#if defined(LUA_DISABLED_IO) +static int g_read (lua_State *L, lua_FILE *f, int first) { int nargs = lua_gettop(L) - 1; int success; int n; @@ -492,7 +467,7 @@ static int g_read (lua_State *L, FILE *f, int first) { } } } - if (ferror(f)) + if (lua_ferror(f)) return luaL_fileresult(L, 0, NULL); if (!success) { lua_pop(L, 1); /* remove last result */ @@ -506,23 +481,20 @@ static int io_read (lua_State *L) { return g_read(L, getiofile(L, IO_INPUT), 1); } - static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } - #else static int io_read (lua_State *L) { LStream *p = tolstream(L); size_t l = (size_t)lua_tointeger(L, 2); - read_chars(L, &p->f, l); + read_chars(L, p->f, l); return 1; } - #endif -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) static int io_readline (lua_State *L) { LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); int i; @@ -549,12 +521,11 @@ static int io_readline (lua_State *L) { return 0; } } +#endif // LUA_DISABLED_IO /* }====================================================== */ -#endif - -static int g_write (lua_State *L, FILE *f, int arg) { +static int g_write (lua_State *L, lua_FILE *f, int arg) { int nargs = lua_gettop(L) - arg; int status = 1; for (; nargs--; arg++) { @@ -562,42 +533,41 @@ static int g_write (lua_State *L, FILE *f, int arg) { /* optimization: could be done exactly as for strings */ char s[LUAI_MAXNUMBER2STR]; sprintf(s, LUA_NUMBER_FMT, lua_tonumber(L, arg)); - status = status && f_puts(s, f) > 0; + status = status && lua_fputs(s, f) > 0; } else { size_t l; - UINT count; const char *s = luaL_checklstring(L, arg, &l); - status = status && (f_write(f, s, l, &count) == FR_OK && count == l); + status = status && (lua_fwrite(s, sizeof(char), l, f) == l); } } if (status) return 1; /* file handle already on stack top */ else return luaL_fileresult(L, status, NULL); } -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) static int io_write (lua_State *L) { return g_write(L, getiofile(L, IO_OUTPUT), 1); } static int f_write (lua_State *L) { - FILE *f = tofile(L); + lua_FILE *f = tofile(L); lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ return g_write(L, f, 2); } #else static int io_write (lua_State *L) { - FILE *f = tofile(L); + lua_FILE *f = tofile(L); lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ return g_write(L, f, 2); } #endif -#if !defined(USE_FATFS) +#if defined(LUA_DISABLED_IO) static int f_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L); + lua_FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); lua_Number p3 = luaL_optnumber(L, 3, 0); l_seeknum offset = (l_seeknum)p3; @@ -616,7 +586,7 @@ static int f_seek (lua_State *L) { static int f_setvbuf (lua_State *L) { static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; static const char *const modenames[] = {"no", "full", "line", NULL}; - FILE *f = tofile(L); + lua_FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); int res = setvbuf(f, NULL, mode[op], sz); @@ -632,17 +602,15 @@ static int io_flush (lua_State *L) { static int f_flush (lua_State *L) { return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); } - -#else +#endif static int io_seek (lua_State *L) { - FILE *f = tofile(L); + lua_FILE *f = tofile(L); lua_Unsigned offset = luaL_checkunsigned(L, 2); - lua_pushinteger(L, f_lseek(f, offset)); + lua_pushinteger(L, lua_fseek(f, offset, SEEK_SET)); return 1; } -#endif /* ** functions for 'io' library diff --git a/radio/src/thirdparty/Lua/src/loadlib.c b/radio/src/thirdparty/Lua/src/loadlib.c index 69ab5d2df41..130b4936ca7 100644 --- a/radio/src/thirdparty/Lua/src/loadlib.c +++ b/radio/src/thirdparty/Lua/src/loadlib.c @@ -327,16 +327,16 @@ static int ll_loadlib (lua_State *L) { static int readable (const char *filename) { -#if defined(USE_FATFS) - FIL f; - FRESULT result = f_open(&f, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) return 0; - f_close(&f); -#else - FILE *f = fopen(filename, "r"); /* try to open file */ +/* #if defined(USE_FATFS) */ +/* FIL f; */ +/* FRESULT result = f_open(&f, filename, FA_OPEN_EXISTING | FA_READ); */ +/* if (result != FR_OK) return 0; */ +/* f_close(&f); */ +/* #else */ + lua_FILE *f = lua_fopen(filename, "r"); /* try to open file */ if (f == NULL) return 0; /* open failed */ - fclose(f); -#endif + lua_fclose(f); +/* #endif */ return 1; } diff --git a/radio/src/thirdparty/Lua/src/lua.c b/radio/src/thirdparty/Lua/src/lua.c index c2c1e547d2f..638dbaf5fbf 100644 --- a/radio/src/thirdparty/Lua/src/lua.c +++ b/radio/src/thirdparty/Lua/src/lua.c @@ -75,7 +75,7 @@ #define lua_readline(L,b,p) \ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ + lua_fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ #define lua_saveline(L,idx) { (void)L; (void)idx; } #define lua_freeline(L,b) { (void)L; (void)b; } diff --git a/radio/src/thirdparty/Lua/src/lua.h b/radio/src/thirdparty/Lua/src/lua.h index a7c75f259e6..380ac799277 100644 --- a/radio/src/thirdparty/Lua/src/lua.h +++ b/radio/src/thirdparty/Lua/src/lua.h @@ -414,17 +414,11 @@ struct lua_Debug { struct CallInfo *i_ci; /* active function */ }; -#if defined(USE_FATFS) - #include "FatFs/ff.h" - int lua__getc(FIL *f); - #define lua_getc(f) lua__getc(&f) - #define lua_fclose f_close -#else - #include - #define lua_getc getc - #define lua_fclose fclose +#if 0 +#include +#define lua_getc getc +#define lua_fclose fclose #endif - /* }====================================================================== */ /* ROTable extensions to the standard API */ diff --git a/radio/src/thirdparty/Lua/src/luac.c b/radio/src/thirdparty/Lua/src/luac.c index b8e179381f8..b6b1dffef1c 100644 --- a/radio/src/thirdparty/Lua/src/luac.c +++ b/radio/src/thirdparty/Lua/src/luac.c @@ -155,7 +155,7 @@ static const Proto* combine(lua_State* L, int n) static int writer(lua_State* L, const void* p, size_t size, void* u) { UNUSED(L); - return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); + return (lua_fwrite(p,size,1,(lua_FILE*)u)!=1) && (size!=0); } static int pmain(lua_State* L) @@ -174,13 +174,13 @@ static int pmain(lua_State* L) if (listing) luaU_print(f,listing>1); if (dumping) { - FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + lua_FILE* D= (output==NULL) ? stdout : lua_fopen(output,"wb"); if (D==NULL) cannot("open"); lua_lock(L); luaU_dump(L,f,writer,D,stripping); lua_unlock(L); - if (ferror(D)) cannot("write"); - if (fclose(D)) cannot("close"); + if (lua_ferror(D)) cannot("write"); + if (lua_fclose(D)) cannot("close"); } return 0; } diff --git a/radio/src/thirdparty/Lua/src/luaconf.h b/radio/src/thirdparty/Lua/src/luaconf.h index 1e9d544a04f..80e291a9336 100644 --- a/radio/src/thirdparty/Lua/src/luaconf.h +++ b/radio/src/thirdparty/Lua/src/luaconf.h @@ -11,13 +11,27 @@ #include #include -#if defined(SDCARD) -#define USE_FATFS -#endif - // force ANSI mode: lua_number2integer() behaves the same way on all platforms (#3826) #define LUA_ANSI +// lua file system access +struct open_files_t; +#define lua_FILE struct open_files_t +lua_FILE* lua_fopen(const char* name, const char *mode); +int lua_fclose(lua_FILE* file); +lua_FILE *lua_freopen(const char *pathname, const char *mode, lua_FILE *stream); +int lua_feof(lua_FILE *stream); +int lua_fseek(lua_FILE *stream, long offset, int whence); +int lua_ferror(lua_FILE *stream); +size_t lua_fread(void *ptr, size_t size, size_t nmemb, lua_FILE *stream); +size_t lua_fwrite(const void *ptr, size_t size, size_t nmemb, + lua_FILE *stream); +char *lua_fgets(char *s, int size, lua_FILE *stream); +char lua_fgetc(lua_FILE *stream); +int lua_fputs(const char *s, lua_FILE *stream); +char lua_fputc(lua_FILE *stream); + + /* ** ================================================================== ** Search for "@@" to find all configurable definitions. @@ -223,7 +237,7 @@ #if defined(LUA_LIB) || defined(lua_c) #include -#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#define luai_writestring(s,l) lua_fwrite((s), sizeof(char), (l), stdout) #define luai_writeline() (luai_writestring("\n", 1), fflush(stdout)) #define luai_writestringerror(s,p) \ (fprintf(stderr, (s), (p)), fflush(stderr)) @@ -397,11 +411,7 @@ @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. ** CHANGE it if it uses too much C-stack space. */ -#if defined(USE_FATFS) #define LUAL_BUFFERSIZE 512 -#else -#define LUAL_BUFFERSIZE BUFSIZ -#endif diff --git a/radio/src/thirdparty/STM32_USB-Host-Device_Lib_V2.2.0/Libraries/STM32_USB_Device_Library/Class/msc/src/usbd_msc_bot.c b/radio/src/thirdparty/STM32_USB-Host-Device_Lib_V2.2.0/Libraries/STM32_USB_Device_Library/Class/msc/src/usbd_msc_bot.c index e1e9a253660..805a442a7e5 100644 --- a/radio/src/thirdparty/STM32_USB-Host-Device_Lib_V2.2.0/Libraries/STM32_USB_Device_Library/Class/msc/src/usbd_msc_bot.c +++ b/radio/src/thirdparty/STM32_USB-Host-Device_Lib_V2.2.0/Libraries/STM32_USB_Device_Library/Class/msc/src/usbd_msc_bot.c @@ -251,7 +251,7 @@ static void MSC_BOT_CBW_Decode (USB_OTG_CORE_HANDLE *pdev) if ((USBD_GetRxCount (pdev ,MSC_OUT_EP) != BOT_CBW_LENGTH) || (MSC_BOT_cbw.dSignature != BOT_CBW_SIGNATURE)|| - (MSC_BOT_cbw.bLUN > 1) || + (MSC_BOT_cbw.bLUN > 2) || (MSC_BOT_cbw.bCBLength < 1) || (MSC_BOT_cbw.bCBLength > 16)) { diff --git a/radio/src/thirdparty/libopenui/README.md b/radio/src/thirdparty/libopenui/README.md index 6d17b6d92ff..2376137221d 100644 --- a/radio/src/thirdparty/libopenui/README.md +++ b/radio/src/thirdparty/libopenui/README.md @@ -2,3 +2,6 @@ C++ UI Library primarily used in EdgeTX for Colour LCD targets. Was forked from [OpenTX/libopenui](https://github.com/opentx/libopenui) and detached so that contributors can easily work on both if they wish to do so. + +To port the library, copy openui_conf_example.h to your project, rename to openui_conf.h and modify to work within your software. + diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index cde31ffa1e3..eda0d128a57 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -7,7 +7,6 @@ if(HARWARE_TOUCH) endif() set(LIBOPENUI_SRC - libopenui_file.cpp bitmapbuffer.cpp window.cpp layer.cpp diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6afefb99da0..3bde5730b59 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -22,9 +22,8 @@ #include "board.h" #include "bitmapbuffer.h" #include "opentx_helpers.h" -#include "libopenui_file.h" #include "font.h" - +#include "libopenui_conf.h" #include "lvgl/src/draw/sw/lv_draw_sw.h" void DMAWait(); @@ -1249,17 +1248,17 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h // } //} // - +#if !defined(BOOT) BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename, BitmapFormats fmt) { //TRACE(" BitmapBuffer::loadBitmap(%s)", filename); - const char * ext = getFileExtension(filename); + const char * ext = openUiGetFileExtension(filename,0 ,0, nullptr, nullptr); if (ext && !strcmp(ext, ".bmp")) return load_bmp(filename); else return load_stb(filename, fmt); } - +#endif BitmapBuffer * BitmapBuffer::loadRamBitmap(const uint8_t * buffer, int len) { return load_stb_buffer(buffer, len); @@ -1393,33 +1392,33 @@ BitmapBuffer * BitmapBuffer::load8bitMaskOnBackground(const uint8_t * lbm, LcdFl return result; } -FIL imgFile __DMA; - +OpenUiFile imgFile __DMA; +#if !defined(BOOT) BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) { - UINT read; + size_t read; uint8_t palette[16]; uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ uint8_t * buf = &bmpBuf[0]; - FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + OpenUiFsRetType result = openUiOpenFile(&imgFile, filename, OPENUI_FS_OPEN_FLAG_OPENEXISTING | OPENUI_FS_OPEN_FLAG_READ); + if (result != OPENUI_FS_OK) { return nullptr; } - if (f_size(&imgFile) < 14) { - f_close(&imgFile); + if (openUiFileGetSize(&imgFile) < 14) { + openUiCloseFile(&imgFile);; return nullptr; } - result = f_read(&imgFile, buf, 14, &read); - if (result != FR_OK || read != 14) { - f_close(&imgFile); + result = openUiReadFile(&imgFile, buf, 14, read); + if (result != OPENUI_FS_OK || read != 14) { + openUiCloseFile(&imgFile);; return nullptr; } if (buf[0] != 'B' || buf[1] != 'M') { - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } @@ -1427,9 +1426,9 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ uint32_t len = limit(4, hsize - 14, 32); - result = f_read(&imgFile, buf, len, &read); - if (result != FR_OK || read != len) { - f_close(&imgFile); + result = openUiReadFile(&imgFile, buf, len, read); + if (result != OPENUI_FS_OK || read != len) { + openUiCloseFile(&imgFile);; return nullptr; } @@ -1437,17 +1436,17 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) /* invalid extra header size */ if (ihsize + 14 > hsize) { - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } /* sometimes file size is set to some headers size, set a real size in that case */ if (fsize == 14 || fsize == ihsize + 14) - fsize = f_size(&imgFile) - 2; + fsize = openUiFileGetSize(&imgFile) - 2; /* declared file size less than header size */ if (fsize <= hsize) { - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } @@ -1469,12 +1468,12 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf += 8; break; default: - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } //TRACE(" BitmapBuffer::load_bmp() %dx%d", w, h); if (*((uint16_t *)&buf[0]) != 1) { /* planes */ - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } @@ -1483,8 +1482,8 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf = &bmpBuf[0]; if (depth == 4) { - if (f_lseek(&imgFile, hsize - 64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) { - f_close(&imgFile); + if (openUiFileLSeek(&imgFile, hsize - 64) != OPENUI_FS_OK || openUiReadFile(&imgFile, buf, 64, read) != OPENUI_FS_OK || read != 64) { + openUiCloseFile(&imgFile);; return nullptr; } for (uint8_t i = 0; i < 16; i++) { @@ -1492,15 +1491,15 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) } } else { - if (f_lseek(&imgFile, hsize) != FR_OK) { - f_close(&imgFile); + if (openUiFileLSeek(&imgFile, hsize) != OPENUI_FS_OK) { + openUiCloseFile(&imgFile);; return nullptr; } } BitmapBuffer * bmp = new BitmapBuffer(BMP_RGB565, w, h); if (bmp == nullptr || bmp->getData() == nullptr) { - f_close(&imgFile); + openUiCloseFile(&imgFile);; return nullptr; } @@ -1512,9 +1511,9 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) for (int i = h - 1; i >= 0; i--) { pixel_t * dst = bmp->getPixelPtrAbs(0, i); for (unsigned int j = 0; j < w; j++) { - result = f_read(&imgFile, (uint8_t *)dst, 2, &read); - if (result != FR_OK || read != 2) { - f_close(&imgFile); + result = openUiReadFile(&imgFile, (uint8_t *)dst, 2, read); + if (result != OPENUI_FS_OK || read != 2) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1528,9 +1527,9 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) pixel_t * dst = bmp->getPixelPtrAbs(0, i); for (unsigned int j = 0; j < w; j++) { uint32_t pixel; - result = f_read(&imgFile, (uint8_t *)&pixel, 4, &read); - if (result != FR_OK || read != 4) { - f_close(&imgFile); + result = openUiReadFile(&imgFile, (uint8_t *)&pixel, 4, read); + if (result != OPENUI_FS_OK || read != 4) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1562,9 +1561,9 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) case 4: rowSize = ((4*w+31)/32)*4; for (int32_t i=h-1; i>=0; i--) { - result = f_read(&imgFile, buf, rowSize, &read); - if (result != FR_OK || read != rowSize) { - f_close(&imgFile); + result = openUiReadFile(&imgFile, buf, rowSize, read); + if (result != OPENUI_FS_OK || read != rowSize) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1579,15 +1578,15 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) break; default: - f_close(&imgFile); + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } - f_close(&imgFile); + openUiCloseFile(&imgFile);; return bmp; } - +#endif #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_PNG #define STBI_ONLY_JPEG @@ -1637,14 +1636,14 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) #define STBI_NO_BMP #define STB_IMAGE_IMPLEMENTATION #include "stb/stb_image.h" - +#if !defined(BOOT) // fill 'data' with 'size' bytes. return number of bytes actually read int stbc_read(void *user, char * data, int size) { - FIL * fp = (FIL *)user; - UINT br = 0; - FRESULT res = f_read(fp, data, size, &br); - if (res == FR_OK) { + OpenUiFile* fp = (OpenUiFile*)user; + size_t br = 0; + OpenUiFsRetType res = fp->read(data, size, br); + if (res == OPENUI_FS_OK) { return (int)br; } return 0; @@ -1653,15 +1652,15 @@ int stbc_read(void *user, char * data, int size) // skip the next 'n' bytes, or 'unget' the last -n bytes if negative void stbc_skip(void *user, int n) { - FIL * fp = (FIL *)user; - f_lseek(fp, f_tell(fp) + n); + OpenUiFile* fp = (OpenUiFile*)user; + fp->lseek(fp->tell() + n); } // returns nonzero if we are at end of file/data int stbc_eof(void *user) { - FIL * fp = (FIL *)user; - int res = f_eof(fp); + OpenUiFile* fp = (OpenUiFile*)user; + int res = fp->eof();; return res; } @@ -1676,24 +1675,24 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename, BitmapFormats fmt) { //TRACE(" BitmapBuffer::load_stb(%s)", filename); - FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + OpenUiFsRetType result = openUiOpenFile(&imgFile, filename, OPENUI_FS_OPEN_FLAG_OPENEXISTING | OPENUI_FS_OPEN_FLAG_READ); + if (result != OPENUI_FS_OK) { return nullptr; } int x, y, nn; stbi_info_from_callbacks(&stbCallbacks, &imgFile, &x, &y, &nn); - f_close(&imgFile); + openUiCloseFile(&imgFile); //TRACE(" BitmapBuffer::load_stb()----Info File %s, %d, %d, %d", filename, x, y, nn); - result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); - if (result != FR_OK) { + result = openUiOpenFile(&imgFile, filename, OPENUI_FS_OPEN_FLAG_OPENEXISTING | OPENUI_FS_OPEN_FLAG_READ); + if (result != OPENUI_FS_OK) { return nullptr; } int w, h, n; unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4); - f_close(&imgFile); + openUiCloseFile(&imgFile); if (!img) { TRACE("load_stb(%s) failed: %s", filename, stbi_failure_reason()); @@ -1705,7 +1704,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename, BitmapFormats fmt) stbi_image_free(img); return bmp; } - +#endif BitmapBuffer * BitmapBuffer::load_stb_buffer(const uint8_t * buffer, int len) { int w, h, n; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index c374bc6bf2c..f892c18e3ad 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -17,11 +17,11 @@ */ #include "filechoice.h" -#include "libopenui_file.h" +#include "strhelpers.h" #include "menu.h" #include "theme.h" #include "message_dialog.h" - +#include "libopenui_conf.h" #include #if !defined(STR_SDCARD) @@ -56,26 +56,25 @@ std::string FileChoice::getLabelText() bool FileChoice::openMenu() { - FILINFO fno; - DIR dir; + OpenUiFileInfo fno; + OpenUiDir dir; std::list files; const char *fnExt; uint8_t fnLen, extLen; - FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory - if (res == FR_OK) { - bool firstTime = true; + OpenUiFsRetType res = openUiOpenDir(&dir, folder.c_str()); // Open the directory + if (res == OPENUI_FS_OK) { for (;;) { - res = sdReadDir(&dir, &fno, firstTime); - if (res != FR_OK || fno.fname[0] == 0) + res = openUiReadDir(&dir, &fno); + if (res != OPENUI_FS_OK || strlen(openUiFsGetName(&fno)) == 0) break; // break on error or end of dir - if (fno.fattrib & AM_DIR) continue; // skip subfolders - if (fno.fattrib & AM_HID) continue; // skip hidden files - if (fno.fattrib & AM_SYS) continue; // skip system files + if (openUiFsIsDir(&fno)) continue; // skip subfolders + if (openUiFsIsHiddenFile(&fno)) continue; // skip hidden files + if (openUiFsIsSystemFile(&fno)) continue; // skip system files - fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); + fnExt = openUiGetFileExtension(openUiFsGetName(&fno), 0, 0, &fnLen, &extLen); - if (extension && (!fnExt || !isExtensionMatching(fnExt, extension))) + if (extension && (!fnExt || !openUiIsFileExtensionMatching(fnExt, extension, nullptr))) continue; // wrong extension if (stripExtension) fnLen -= extLen; @@ -83,12 +82,13 @@ bool FileChoice::openMenu() if (!fnLen || fnLen > maxlen) continue; // wrong size // eject duplicates - std::string newFile(fno.fname, fnLen); + const char* newFile = fno.getName(); if (std::find(files.begin(), files.end(), newFile) != files.end()) continue; files.emplace_back(newFile); } + openUiCloseDir(&dir); if (!files.empty()) { // sort files diff --git a/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h b/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h new file mode 100644 index 00000000000..ccd7c5d99ae --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) EdgeTX + * + * Source: + * https://github.com/EdgeTX/libopenui + * + * Based on code named + * opentx - https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#if !defined(_OPENUI_CONF_H) +#define _OPENUI_CONF_H + +#include "VirtualFS.h" + +#define OpenUiFile VfsFile +#define OpenUiDir VfsDir +#define OpenUiFileInfo VfsFileInfo +#define OpenUiFsRetType VfsError + +#define OPENUI_FS_OPEN_FLAG_READ VfsOpenFlags::READ +#define OPENUI_FS_OPEN_FLAG_OPENEXISTING VfsOpenFlags::OPEN_EXISTING + +#define openUiOpenFile(fileHandle, path, flags) (VirtualFS::instance().openFile(*(fileHandle), path, flags)) +#define openUiCloseFile(fileHandle) ((fileHandle)->close()) +#define openUiReadFile(fileHandle, buf, bytes, read) ((fileHandle)->read(buf, bytes, read)) +#define openUiFileGetSize(fileHandle) ((fileHandle)->size()) +#define openUiFileLSeek(fileHandle, offset) ((fileHandle)->lseek(offset)) + +#define openUiOpenDir(dirHandle, path) (VirtualFS::instance().openDirectory(*(dirHandle), path)) +#define openUiCloseDir(dirHandle) ((dirHandle)->close()) +#define openUiReadDir(dirHandle, fileInfo) ((dirHandle)->read(*(fileInfo))) + +#define openUiFsIsDir(fileInfo) ((fileInfo)->getType() == VfsType::DIR) +#define openUiFsIsHiddenFile(fileHandle) (false) +#define openUiFsIsSystemFile(fileHandle) (false) +#define openUiFsGetName(fileInfo) ((fileInfo)->getName()) + +#define openUiGetFileExtension(filename, size, extMaxLen, fnlen, extlen) (VirtualFS::getFileExtension(filename, size, extMaxLen, fnlen, extlen)) +#define openUiIsFileExtensionMatching(extension, pattern, match) (VirtualFS::isFileExtensionMatching(extension, pattern, match)) + +#define OPENUI_FS_OK VfsError::OK + +#endif // _OPENUI_CONF_H diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp deleted file mode 100644 index 1e68c187a01..00000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * libopenui - https://github.com/opentx/libopenui - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include "libopenui_file.h" - -const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMaxLen, uint8_t * fnlen, uint8_t * extlen) -{ - int len = size; - if (!size) { - len = strlen(filename); - } - if (!extMaxLen) { - extMaxLen = LEN_FILE_EXTENSION_MAX; - } - if (fnlen != nullptr) { - *fnlen = (uint8_t)len; - } - for (int i=len-1; i >= 0 && len-i <= extMaxLen; --i) { - if (filename[i] == '.') { - if (extlen) { - *extlen = len-i; - } - return &filename[i]; - } - } - if (extlen != nullptr) { - *extlen = 0; - } - return nullptr; -} - -/** - Check if given extension exists in a list of extensions. - @param extension The extension to search for, including leading period. - @param pattern One or more file extensions concatenated together, including the periods. - The list is searched backwards and the first match, if any, is returned. - eg: ".gif.jpg.jpeg.png" - @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). - @retval true if a extension was found in the lost, false otherwise. -*/ -bool isExtensionMatching(const char * extension, const char * pattern, char * match) -{ - const char *ext; - uint8_t extlen, fnlen; - int plen; - - ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); - plen = (int)fnlen; - while (plen > 0 && ext) { - if (!strncasecmp(extension, ext, extlen)) { - if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); - return true; - } - plen -= extlen; - if (plen > 0) { - ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); - } - } - return false; -} - -// returns true if current working dir is at the root level -bool isCwdAtRoot() -{ - char path[10]; - if (f_getcwd(path, sizeof(path)-1) == FR_OK) { - return (strcasecmp("/", path) == 0); - } - return false; -} - -/* - Wrapper around the f_readdir() function which - also returns ".." entry for sub-dirs. (FatFS 0.12 does - not return ".", ".." dirs anymore) -*/ -FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime) -{ - FRESULT res; - if (firstTime && !isCwdAtRoot()) { - // fake parent directory entry - strcpy(fno->fname, ".."); - fno->fattrib = AM_DIR; - res = FR_OK; - } - else { - res = f_readdir(dir, fno); /* Read a directory item */ - } - firstTime = false; - return res; -} diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h deleted file mode 100644 index f5588580c50..00000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * libopenui - https://github.com/opentx/libopenui - * - * 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 -#include -#include -#include "ff.h" - -constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. - -const char * getFileExtension(const char * filename, uint8_t size = 0, uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); -bool isExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); -FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime); - -// comparison, not case sensitive. -inline bool compare_nocase(const std::string & first, const std::string & second) -{ - return strcasecmp(first.c_str(), second.c_str()) < 0; -} diff --git a/radio/src/thirdparty/tjftl/README.md b/radio/src/thirdparty/tjftl/README.md new file mode 100644 index 00000000000..d83d0beac82 --- /dev/null +++ b/radio/src/thirdparty/tjftl/README.md @@ -0,0 +1,21 @@ +TJFTL +===== + +This is a Tiny, Journalling Flash Translation Layer. It is meant for NOR devices, mostly targeted +at the 25xxx SPI flash memories and alike (256 byte page size, 4/32/64K erase size, around 16MiB +of storage.) + + +What does this do? +================== + +One of the easiest USB protocols to implement to give a computer access to the storage on your device +is Mass-Storage. This, however, implies that you need to emulate a block device with 512b sectors. Also, +the most compatibe filesystem that OSses are going to understand is FAT, which does not have inherent +protection from corruption. FAT is also intended for 'spinning rust'-style disks that do not need wear +leveling like flash does. + +This translation layer is supposed to be a 'go-between'; on one hand, it puts data on the flash in a +way where it is aware that things like erase sizes make it hard to write 512b sectors, and where it +knows it shouldn't erase the same sector too often. On the other hand, to the upper layers, it +shows an interface that very much makes it looks like a hard disk with 512b sectors. diff --git a/radio/src/thirdparty/tjftl/tjftl.c b/radio/src/thirdparty/tjftl/tjftl.c new file mode 100644 index 00000000000..2e698b54cd9 --- /dev/null +++ b/radio/src/thirdparty/tjftl/tjftl.c @@ -0,0 +1,572 @@ +//tjflt - Tiny Janky / Journalling Flash Translation Layer +/* + * Copyright 2019 Jeroen Domburg . Licensed under + * the beer-ware license. + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. -Jeroen + * ---------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "tjftl.h" + +//Description for one block. For a valid block, lba == ~lba_inv +//A free block has lba == lba_inv == 0xffffffff +//The highest bit has a special purpose: if it's 0 in both lba as well as lba_inv, the +//block is *likely* to be superseded by a newer block. (Note: This is only used in non-cached +//mode. + +//Note that flash normally only has one block which is partially full, e.g. has a magic marker +//but lba's with 0xffffffff in it; if it exists, that's also the block with the highest serial. + +//Note that the serial for blocks is 'only' 32-bit, which means that there is a maximum of 131 terabyte of +//traffic that can ever be written to the filesystem, assuming we don't implement a way to handle rollover. +//As this means an 16MiB bit of flash is then rewritten about 8 million times, I'd think this is not a very +//large issue. + +//Note that this ftl has 1/64th overhead, plus some free blocks for garbage collection. + +//#define DEBUG 1 +#if DEBUG +#define TJ_MSG(...) do { printf("TJFTL: "); printf(__VA_ARGS__); } while(0) +#define TJ_CHECK(x, msg) do { if (!(x)) { printf("TJFTL: check fail: " #x " (%s:%d): %s\n", __FILE__, __LINE__, msg); abort(); }} while (0) +#else +#define TJ_MSG(...) +#define TJ_CHECK(x, msg) +#endif + + + +//minimal free block count before we go garbage collect +#define GC_MIN_FREE_BLK_CNT 10 +//minimum amount of blocks that the garbage collect routine will rewrite +//(if this frees up less than GC_MIN_FREE_BLK_CNT blocks, it will continue until that +//amount of blocks have been freed) +#define GC_CLEAR_BLOCKS 2 + + + +#define LBA_FREE 0xFFFFFFFF +#define LBA_MASK 0x7FFFFFFF +#define LBA_SUPERSEDED_MSK 0x80000000 + +typedef struct { + uint32_t lba; + uint32_t lba_inv; +} tjfl_blockdesc_t; + + +#define BLKSZ 32768 +#define BLKHDR_MAGIC 0x1337B33F +#define SEC_PER_BLK 63 +#define SEC_DATA_SIZE 512 + +//This struct is exactly 512 bytes large. +typedef struct { + uint32_t magic; + uint32_t serial; //always incrementing per block erased/written + tjfl_blockdesc_t bd[SEC_PER_BLK]; +} tjftl_block_t; + +_Static_assert(sizeof(tjftl_block_t)==512, "tjftl_block_t is not 512 bytes!"); + +//With this enabled, the ftl will keep a cache of where each LBA resides instead of doing a linear search +//of the entire flash every time. This takes up 8K per megabyte cached. +#define CACHE_LBALOC 1 + +struct tjftl_t { + flashcb_read_t flash_read; + flashcb_erase_32k_t flash_erase; + flashcb_program_t flash_program; + void *flashcb_arg; + int backing_blks; + int sect_cnt; + int current_serial; + int current_write_block; + int current_gc_block; + int free_blk_cnt; //This has the amount of blocks that are invalid/erased/entirely empty. + int prefer_first_sectors; //if this is 1, the first few sectors aren't entirely used. Prefer those so detecting a tjftl is easier. +#if CACHE_LBALOC + uint32_t *lba_cache; +#endif +}; + +#if CACHE_LBALOC +//Note: cache contains sectors starting from 1, not 0 like the rest of the code, so we can mark +//0 as invalid. +static uint32_t lbacache_pair(int block, int sec) { return (block*64)+sec+1; } +static int lbacache_block(uint32_t cv) { return cv/64; } +static int lbacache_sec(uint32_t cv) { return (cv&63)-1; } +#endif + +static bool find_block_for_lba(tjftl_t *tj, int lba, tjftl_block_t *blkh, int *blkno_out, int *sect_in_blk_out); + + +static bool read_blkhdr(tjftl_t *tj, int blk, tjftl_block_t *hdr) { + int addr=blk*BLKSZ; + bool ret=tj->flash_read(addr, (uint8_t*)hdr, sizeof(tjftl_block_t), tj->flashcb_arg); + return ret; +} + +static bool read_sect(tjftl_t *tj, int blk, int sect_in_blk, uint8_t *buf) { + TJ_CHECK(sect_in_blk>=0 && sect_in_blk<64, "invalid sect in blk"); + int addr=blk*BLKSZ+(sect_in_blk+1)*SEC_DATA_SIZE; + bool ret=tj->flash_read(addr, buf, SEC_DATA_SIZE, tj->flashcb_arg); + return ret; +} + +static bool write_blkhdr(tjftl_t *tj, int blk, const tjftl_block_t *hdr) { + int addr=blk*BLKSZ; + bool ret=tj->flash_program(addr, (uint8_t*)hdr, sizeof(tjftl_block_t), tj->flashcb_arg); + if (!ret) { + TJ_MSG("Write_blkhdr: flash_program failed at addr %d\n", addr); + } + return ret; +} + +static bool write_sect(tjftl_t *tj, int blk, int sect_in_blk, const uint8_t *buf) { + TJ_CHECK(sect_in_blk>=0 && sect_in_blk<64, "invalid sect in blk"); + int addr=blk*BLKSZ+(sect_in_blk+1)*SEC_DATA_SIZE; + bool ret=tj->flash_program(addr, buf, SEC_DATA_SIZE, tj->flashcb_arg); + return ret; +} + +static bool lba_valid(const tjfl_blockdesc_t *b) { + return (b->lba & LBA_MASK) == ((~b->lba_inv) & LBA_MASK); +} + +static bool lba_erased(const tjfl_blockdesc_t *b) { + return ((b->lba == 0xFFFFFFFF) && (b->lba_inv == 0xFFFFFFFF)); +} + +static int lba_sect(const tjfl_blockdesc_t *b) { + if (!lba_valid(b)) return -1; + return b->lba & LBA_MASK; +} + + +static bool lba_is_superseded(tjftl_t *tj, const tjfl_blockdesc_t *b, int blkno, int sect_in_blk) { +#if !CACHE_LBALOC + //quick check before we do needless expensive lookup work + if (!((b->lba & LBA_SUPERSEDED_MSK)==0) && ((b->lba_inv & LBA_SUPERSEDED_MSK)==0)) return false; +#endif + int f_blkno, f_sect_in_blk; + if (!lba_valid(b)) return false; + bool found=find_block_for_lba(tj, lba_sect(b), NULL, &f_blkno, &f_sect_in_blk); + if (found && (f_blkno!=blkno || f_sect_in_blk!=sect_in_blk)) { + //yep, there certainly is a newer version + return true; + } else { + return false; + } +} + +static bool lba_maybe_superseded(tjftl_t *tj, const tjfl_blockdesc_t *b, int blkno, int sect_in_blk) { +#if CACHE_LBALOC + //Cache marks all blocks as maybe superseded, but we can easily check if the block is _actually_ superseded as + //that routine is really fast. + return lba_is_superseded(tj, b, blkno, sect_in_blk); +#else + return ((b->lba & LBA_SUPERSEDED_MSK)==0) && ((b->lba_inv & LBA_SUPERSEDED_MSK)==0); +#endif +} + +static bool blkh_valid(const tjftl_block_t *blkh) { + return (blkh->magic==BLKHDR_MAGIC); +} + +static bool blkh_is_empty(const tjftl_block_t *blkh) { + for (int i=0; ibd[i])) return false; + } + return true; +} +static int blkh_next_free_sec(const tjftl_block_t *blkh) { + if (!blkh_valid(blkh)) { + TJ_MSG("blkh_next_free_sec: block not valid\n"); + return -1; + } + for (int i=0; ibd[i])) { + return i; + } + } + return -1; +} + +static bool blk_initialize(tjftl_t *tj, int blkno, tjftl_block_t *blkh) { + TJ_MSG("Initializing block %d using serial %d\n", blkno, tj->current_serial+1); + memset(blkh, 0xff, sizeof(tjftl_block_t)); + blkh->magic=BLKHDR_MAGIC; + tj->current_serial++; + blkh->serial=tj->current_serial; + bool ret; + ret=tj->flash_erase(blkno*BLKSZ, tj->flashcb_arg); + if (!ret) { + TJ_MSG("blk_initialize: flash_erase of block %d failed!\n", blkno); + return false; + } + ret=write_blkhdr(tj, blkno, blkh); + if (!ret) { + TJ_MSG("blk_initialize: write_blkhdr of block %d failed!\n", blkno); + } + return ret; +} + +static void blk_fill_cache(tjftl_t *tj, tjftl_block_t *blkh, int blkno) { +#if CACHE_LBALOC + for (int j=0; jbd[j])) { + int lba=lba_sect(&blkh->bd[j]); + if (tj->lba_cache[lba]!=0) { + //Need to see if this lba superseded the other one + tjftl_block_t oblkh; + read_blkhdr(tj, lbacache_block(tj->lba_cache[lba]), &oblkh); +// printf("cache fill: already read lba %d. old ser %d new ser %d\n", lba, oblkh.serial, blkh->serial); + if (oblkh.serial < blkh->serial) { + //Yes, we supersede the old block. + tj->lba_cache[lba]=lbacache_pair(blkno, j); + } + } else { + //this is the current version as we do not have an other version. + tj->lba_cache[lba]=lbacache_pair(blkno, j); + } + } + } +#endif +} + +static void cache_update(tjftl_t *tj, int lba, int blkno, int sec) { +#if CACHE_LBALOC + tj->lba_cache[lba]=lbacache_pair(blkno, sec); +#endif +} + +//Check the first 4 blocks. If 2 of them have valid tjftl headers, we assume this is a tjftl +//partition. +int tjftl_detect(flashcb_read_t rf, void *arg) { + tjftl_block_t blkh; + int valid_blocks=0; + for (int blk=0; blk<4; blk++) { + int addr=blk*BLKSZ; + bool ret=rf(addr, (uint8_t*)&blkh, sizeof(tjftl_block_t), arg); + if (!ret) return 0; //flash error = not detected + if (blkh_valid(&blkh)) valid_blocks++; + } + return valid_blocks>=2; +} + +static bool garbage_collect(tjftl_t *tj); + +tjftl_t *tjftl_init(flashcb_read_t rf, flashcb_erase_32k_t ef, flashcb_program_t pf, void *arg, int size, int sect_cnt, int verbose) { + TJ_MSG("Initializing tjftl with size=%d, sect_cnt %d\n", size, sect_cnt); + tjftl_t *ret=calloc(sizeof(tjftl_t), 1); + if (!ret) return NULL; +#if CACHE_LBALOC + ret->lba_cache=calloc(sect_cnt, sizeof(uint32_t)); + if (!ret->lba_cache) { + free(ret); + return NULL; + } + if (verbose) printf("tjfl: allocated %zu bytes for cache\n", sect_cnt*sizeof(uint32_t)); +#endif + ret->flash_read=rf; + ret->flash_erase=ef; + ret->flash_program=pf; + ret->flashcb_arg=arg; + ret->backing_blks=size/BLKSZ; + ret->sect_cnt=sect_cnt; + ret->current_serial=0; + ret->current_write_block=-1; + ret->current_gc_block=-1; + ret->free_blk_cnt=0; + ret->prefer_first_sectors=0; + bool all_ok=true; + for (int i=0; ibacking_blks; i++) { + tjftl_block_t blkh; + all_ok&=read_blkhdr(ret, i, &blkh); + //If block is invalid or erased it counts as a free block for free_blk_cnt. + if (blkh_valid(&blkh)) { + if (!blkh_is_empty(&blkh)) { + if (blkh.serial > ret->current_serial) ret->current_serial=blkh.serial; + blk_fill_cache(ret, &blkh, i); + } + } else { + if (i<4) ret->prefer_first_sectors=1; + ret->free_blk_cnt++; + } + } + if (verbose) printf("tjfl: %d of %d blocks free.\n", ret->free_blk_cnt, ret->backing_blks); + if (ret->free_blk_cntfree_blk_cnt); + } + if (!all_ok) { + TJ_MSG("ERROR! tjftl failed to initialize.\n"); +#if CACHE_LBALOC + free(ret->lba_cache); +#endif + free(ret); + return NULL; + } else { + TJ_MSG("Tjftl initialized and ready.\n"); + return ret; + } +} + +//Find the (most recent) block for a given LBA +static bool find_block_for_lba(tjftl_t *tj, int lba, tjftl_block_t *blkh, int *blkno_out, int *sect_in_blk_out) { +#if CACHE_LBALOC + if (tj->lba_cache[lba]==0) return false; + *blkno_out=lbacache_block(tj->lba_cache[lba]); + *sect_in_blk_out=lbacache_sec(tj->lba_cache[lba]); + if (blkh!=NULL) { + return read_blkhdr(tj, *blkno_out, blkh); + } else { + return true; + } +#else + if (blkh==NULL) blkh=alloca(sizeof(tjftl_block_t)); + + //Do it manually... + int last_ser=0; + bool found=false; + bool ret; + for (int i=0; ibacking_blks; i++) { + ret=read_blkhdr(tj, i, blkh); + if (!ret) return false; + if (blkh_valid(blkh)) { +// TJ_MSG("find_block_for_lba: looking for lba %d in block %d...\n", lba, i); + for (int j=0; jbd[j])?"is":"not", lba_sect(&blkh->bd[j]), blkh->serial, last_ser); + if (lba_valid(&blkh->bd[j]) && (lba_sect(&blkh->bd[j])==lba) && (last_ser < blkh->serial)) { + //We found a possible block. + *blkno_out=i; + *sect_in_blk_out=j; + found=true; + if (!lba_maybe_superseded(tj, &blkh->bd[j]), i, j) { +// TJ_MSG("find_block_for_lba: found lba %d in block %d, sec %d.\n", lba, i, j); + //Block is current, we found its data, yay! + return true; + } else { +// TJ_MSG("find_block_for_lba: found maybe-superseded lba %d in block %d, sec %d.\n", lba, i, j); + } + } + } + } + } + return found; +#endif +} + + +//This will find blocks with superseeded sectors in it and re-write the non-superseeded blocks +//to empty sectors. Once that is done, it will clear the sector so it can be re-used. +static bool garbage_collect(tjftl_t *tj) { + int find_start=rand()%tj->backing_blks; //random starting point, yay wear leveling! + int blkno=find_start; + int gc_todo = GC_CLEAR_BLOCKS; + tjftl_block_t blkh; + bool ret; + while (gc_todo>0 || tj->free_blk_cnt < GC_MIN_FREE_BLK_CNT) { + read_blkhdr(tj, blkno, &blkh); + bool should_gc=false; + if (blkh_valid(&blkh) && blkno!=tj->current_write_block) { + //Block is valid, see if there are superseded sectors or free sectors here. + //We also gc on free sectors, as we cannot re-use them as the serial may be wrong. + //Note that sectors are marked invalid when garbage collected, and only initialized to + //all-empty when selected for write. + for (int j=0; jcurrent_gc_block=blkno; + //Look at all the sectors, rewrite them if needed + TJ_MSG("Starting garbage collect round. ToDo=%d, free_cnt=%d; cleaning up blk %d\n", gc_todo, tj->free_blk_cnt, blkno); + int moved=0; + for (int j=0; jfree_blk_cnt++; //yaaaay + gc_todo--; + tj->current_gc_block=-1; + TJ_MSG("Did garbage collect round. ToDo=%d, free_cnt=%d; cleaned up blk %d by moving %d sects\n", gc_todo, tj->free_blk_cnt, blkno, moved); + } + blkno++; + if (blkno>=tj->backing_blks) blkno=0; + } + TJ_MSG("Garbage collection done; free_blk_cnt=%d.\n", tj->free_blk_cnt); + return true; +} + +bool tjftl_read(tjftl_t *tj, int lba, uint8_t *buf) { + bool ret; + int blkno, sect_in_blk; + bool found=find_block_for_lba(tj, lba, NULL, &blkno, §_in_blk); + if (found) { +// TJ_MSG("tjftl_read: lba %d found in blk %d sect %d.\n", lba, blkno, sect_in_blk); + ret=read_sect(tj, blkno, sect_in_blk, buf); + return ret; + } else { +// TJ_MSG("tjftl_read: lba not found.\n"); + memset(buf, 0xff, SEC_DATA_SIZE); + return true; + } +} + + +bool tjftl_write(tjftl_t *tj, int lba, const uint8_t *buf) { + tjftl_block_t blkh; + bool ret; + TJ_CHECK(lba>=0 && lbasect_cnt, "lba fucky"); + +// TJ_MSG("tjfl_write lba %d, current_write_block %d\n", lba, tj->current_write_block); + //First, find current version of the block and mark as maybe-superseded. + //cache doesn't get a speed boost from non-superseded sectors, so we mark everything as superseded + //from the start when we initially write the sector. +#if !CACHE_LBALOC + int blkno, sect_in_blk; + bool found=find_block_for_lba(tj, lba, &blkh, &blkno, §_in_blk); + if (found && !lba_maybe_superseded(tj, &blkh.bd[sect_in_blk]), blkno, sect_in_blk) { + TJ_MSG("tjfl_write: marking old sect (blk %d sec %d) as superseded\n", blkno, sect_in_blk); + blkh.bd[sect_in_blk].lba &= ~LBA_SUPERSEDED_MSK; + blkh.bd[sect_in_blk].lba_inv &= ~LBA_SUPERSEDED_MSK; + ret=write_blkhdr(tj, blkno, &blkh); + if (!ret) return false; + } +#endif + + if (tj->current_write_block == -1) { + //We don't have a block that can accept another sector. We need to do some effort to find one... + //Let's look for a block that is either entirely empty, is invalid, or has some empty sectors in it. + int find_start; + if (tj->prefer_first_sectors) { + find_start=0; //start allocating at the beginning + } else { + find_start=rand()%tj->backing_blks; //random starting point, yay wear leveling! + } + + int blkno=find_start; + TJ_MSG("tjfl_write: find new empty block, start at: %d, free_cnt=%d\n", blkno, tj->free_blk_cnt); + do { + ret=read_blkhdr(tj, blkno, &blkh); + if (!ret) return false; + if (blkno!=tj->current_gc_block && !blkh_valid(&blkh)) { + //Found an invalid/erased block! Initialize it. + TJ_MSG("tjfl_write: %d is invalid or empty: using it\n", blkno); + ret=blk_initialize(tj, blkno, &blkh); + if (!ret) { + TJ_MSG("tjftl_write: Block initialize failed\n"); + return false; + } + tj->current_write_block = blkno; + } else { + //No dice. + //Note that we don't grab any blocks that exist and may have some free sectors, as these + //may have an older serial and we can't update it without running the risk of making the + //old sectors superseded. + blkno++; + if (blkno>=tj->backing_blks) blkno=0; + } + } while (tj->current_write_block == -1 && blkno!=find_start); + if (blkno>4) tj->prefer_first_sectors=0; + } else { + //We already have an active block. Grab its header. + ret=read_blkhdr(tj, tj->current_write_block, &blkh); + if (!ret) { + TJ_MSG("Huh? Read_blkhdr failed for block %d\n", tj->current_write_block); + return false; + } + } + if (tj->current_write_block == -1) { + TJ_MSG("WtF, no free block found?\n"); + return false; + } + + //We have a currently-active block with some free space when we end up here. + //The (current) header is in blkh. + int free_sec_in_blk=blkh_next_free_sec(&blkh); + TJ_CHECK(free_sec_in_blk!=-1, "block should have free sec"); +// TJ_MSG("Going to write data to blk %d sec %d\n", tj->current_write_block, free_sec_in_blk); + ret=write_sect(tj, tj->current_write_block, free_sec_in_blk, buf); + if (!ret) { + TJ_MSG("Write sect failed\n"); + return false; + } + blkh.bd[free_sec_in_blk].lba=lba; + blkh.bd[free_sec_in_blk].lba_inv=~lba; +#if CACHE_LBALOC + //We always mark blocks as superseded. + blkh.bd[free_sec_in_blk].lba &= ~LBA_SUPERSEDED_MSK; + blkh.bd[free_sec_in_blk].lba_inv &= ~LBA_SUPERSEDED_MSK; + //Extra-special todo: if the old lba is in this block as well, nuke it, as the serial won't help us anymore. + int oldblkno, oldsec; + if (find_block_for_lba(tj, lba, NULL, &oldblkno, &oldsec)) { + if (oldblkno==tj->current_write_block) { + blkh.bd[oldsec].lba=0; + blkh.bd[oldsec].lba_inv=0; + } + } +#endif + ret=write_blkhdr(tj, tj->current_write_block, &blkh); + if (!ret) { + TJ_MSG("Write block header failed\n"); + return false; + } + cache_update(tj, lba, tj->current_write_block, free_sec_in_blk); + //see if we used up the current block; if so we need to find a new one next + //time. Also check if we need to gc. + if (blkh_next_free_sec(&blkh)==-1) { + TJ_MSG("Block %d ran out of space.\n", tj->current_write_block); + tj->current_write_block=-1; + tj->free_blk_cnt--; //technically should already decrease after the first sector is written, but + //we can safely do it here as well. + //Garbage collect if we run out of free blocks, but not if we're already collecting garbage. + if (tj->free_blk_cntcurrent_gc_block==-1) { + bool r=garbage_collect(tj); + if (!r) { + TJ_MSG("Garbage collect failed.\n"); + return false; + } + } + } + return true; +} + +size_t tjftl_getSectorCount(tjftl_t *tj) +{ + return tj->sect_cnt; +} + +size_t tjftl_getSectorSize(tjftl_t *tj) +{ + return SEC_DATA_SIZE; +} + diff --git a/radio/src/thirdparty/tjftl/tjftl.h b/radio/src/thirdparty/tjftl/tjftl.h new file mode 100644 index 00000000000..afdcb4abe02 --- /dev/null +++ b/radio/src/thirdparty/tjftl/tjftl.h @@ -0,0 +1,23 @@ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct tjftl_t tjftl_t; + +typedef bool (*flashcb_read_t)(int addr, uint8_t *buf, int len, void *arg); +typedef bool (*flashcb_erase_32k_t)(int addr, void *arg); +typedef bool (*flashcb_program_t)(int addr, const uint8_t *buf, int len, void *arg); + +int tjftl_detect(flashcb_read_t rf, void *arg); +tjftl_t *tjftl_init(flashcb_read_t rf, flashcb_erase_32k_t ef, flashcb_program_t pf, void *arg, int size, int sect_cnt, int verbose); +bool tjftl_read(tjftl_t *tj, int lba, uint8_t *buf); +bool tjftl_write(tjftl_t *tj, int lba, const uint8_t *buf); +size_t tjftl_getSectorCount(tjftl_t *tj); +size_t tjftl_getSectorSize(tjftl_t *tj); + +#ifdef __cplusplus +} +#endif diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index 74eca2d69bb..32723a5d9f9 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -205,7 +205,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]" diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index bd593437f5c..02d8ad21fe7 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -218,7 +218,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]" @@ -319,7 +319,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") diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index caee9f528b7..773fd7cc7d1 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -213,7 +213,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]" @@ -314,7 +314,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") diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index af8a0fe7fd9..bf192b02311 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -215,7 +215,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]" @@ -315,7 +315,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") diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index cbec8d6edf0..278b6fc362e 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -210,7 +210,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]" @@ -312,7 +312,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") diff --git a/radio/src/translations/fr.h b/radio/src/translations/fr.h index 70dccd99bbd..6f3083f552b 100644 --- a/radio/src/translations/fr.h +++ b/radio/src/translations/fr.h @@ -222,7 +222,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]" @@ -323,7 +323,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") diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index 85d9d3d1199..5ac1f8cd132 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -213,7 +213,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]" @@ -313,7 +313,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") diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index 7fd646559af..1e7eaa1546e 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -212,7 +212,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]" @@ -312,7 +312,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") diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 725a24f69c4..a6a18512523 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -209,7 +209,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]" @@ -311,7 +311,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") diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 3d7c6c4c7c7..f41408f02e8 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -218,7 +218,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]" @@ -318,7 +318,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") diff --git a/tools/build-companion.sh b/tools/build-companion.sh index cd7ce72762b..7dc2fee2c57 100755 --- a/tools/build-companion.sh +++ b/tools/build-companion.sh @@ -71,7 +71,7 @@ declare -a simulator_plugins=(x9lite x9lites t20 x9d x9dp x9dp2019 x9e xlite xlites - nv14 + nv14 pl18 x10 x10-access x12s t16 t18 tx16s) @@ -171,6 +171,9 @@ do commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; *) echo "Unknown target: $target_name" exit 1 diff --git a/tools/build-flysky.py b/tools/build-flysky.py index 47220180f9e..b2d7627321a 100644 --- a/tools/build-flysky.py +++ b/tools/build-flysky.py @@ -13,6 +13,10 @@ "PCB": "NV14", "DEFAULT_MODE": "1", }, + "PL18": { + "PCB": "PL18", + "DEFAULT_MODE": "1", + }, } translations = [ diff --git a/tools/build-gh.sh b/tools/build-gh.sh index 324ad1db21c..72c1870b6b4 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -185,6 +185,9 @@ do nv14) BUILD_OPTIONS+="-DPCB=NV14" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; commando8) BUILD_OPTIONS+="-DPCB=X7 -DPCBREV=COMMANDO8" ;; diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index f8ece0f706e..b1e13d6413f 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -145,6 +145,9 @@ do nv14) BUILD_OPTIONS+="-DPCB=NV14" ;; + pl18) + BUILD_OPTIONS+="-DPCB=PL18" + ;; 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" ;;