From ebe394c7df1588d1f02c4853bb125f4f0cd94695 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 23 Aug 2023 13:37:19 +0800 Subject: [PATCH 01/99] Prepare for Virtual File System. --- .../arm/stm32/{diskio_sdio.cpp => diskio_sdio_spiflash.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename radio/src/targets/common/arm/stm32/{diskio_sdio.cpp => diskio_sdio_spiflash.cpp} (100%) 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 100% rename from radio/src/targets/common/arm/stm32/diskio_sdio.cpp rename to radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp From e6ca66cde20c61f287f7281d99a79fec849521ed Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Mon, 27 Dec 2021 02:57:08 +0800 Subject: [PATCH 02/99] Virtual file system --- cmake.sh | 2 - radio/src/CMakeLists.txt | 13 +- radio/src/VirtualFS.cpp | 1909 +++++++++++++++++ radio/src/VirtualFS.h | 455 ++++ radio/src/audio.cpp | 122 +- radio/src/audio.h | 34 +- radio/src/bluetooth.cpp | 32 +- radio/src/bluetooth.h | 2 +- radio/src/cli.cpp | 32 +- radio/src/disk_cache.cpp | 13 +- radio/src/disk_cache.h | 2 +- radio/src/functions.cpp | 3 +- radio/src/gui/128x64/bmp.cpp | 53 +- radio/src/gui/128x64/gui.h | 4 - radio/src/gui/128x64/model_custom_scripts.cpp | 5 +- radio/src/gui/128x64/model_display.cpp | 5 +- radio/src/gui/128x64/model_select.cpp | 11 +- .../gui/128x64/model_special_functions.cpp | 11 +- .../src/gui/128x64/model_telemetry_sensor.cpp | 1 + radio/src/gui/128x64/widgets.cpp | 2 - radio/src/gui/212x64/bmp.cpp | 63 +- radio/src/gui/212x64/model_custom_scripts.cpp | 5 +- radio/src/gui/212x64/model_display.cpp | 5 +- radio/src/gui/212x64/model_select.cpp | 3 +- radio/src/gui/212x64/model_setup.cpp | 5 +- .../gui/212x64/model_special_functions.cpp | 7 +- .../src/gui/212x64/model_telemetry_sensor.cpp | 1 + radio/src/gui/212x64/widgets.cpp | 2 - radio/src/gui/colorlcd/file_browser.cpp | 53 +- radio/src/gui/colorlcd/file_preview.cpp | 4 +- radio/src/gui/colorlcd/libopenui.h | 1 - radio/src/gui/colorlcd/model_select.cpp | 25 +- radio/src/gui/colorlcd/model_telemetry.cpp | 1 + radio/src/gui/colorlcd/model_templates.cpp | 85 +- radio/src/gui/colorlcd/radio_sdmanager.cpp | 56 +- radio/src/gui/colorlcd/radio_tools.cpp | 39 +- radio/src/gui/colorlcd/splash.cpp | 3 - radio/src/gui/colorlcd/theme.h | 2 +- radio/src/gui/colorlcd/theme_manager.cpp | 67 +- radio/src/gui/colorlcd/theme_manager.h | 1 - radio/src/gui/colorlcd/view_text.cpp | 43 +- radio/src/gui/colorlcd/view_text.h | 5 +- radio/src/gui/common/stdlcd/model_notes.cpp | 2 +- radio/src/gui/common/stdlcd/popups.cpp | 6 - radio/src/gui/common/stdlcd/popups.h | 6 +- .../src/gui/common/stdlcd/radio_sdmanager.cpp | 112 +- radio/src/gui/common/stdlcd/radio_tools.cpp | 51 +- radio/src/gui/common/stdlcd/view_text.cpp | 25 +- radio/src/gui/gui_common.cpp | 7 +- radio/src/gui/screenshot.cpp | 37 +- radio/src/io/bootloader_flash.cpp | 28 +- radio/src/io/frsky_firmware_update.cpp | 55 +- radio/src/io/frsky_firmware_update.h | 4 +- radio/src/io/multi_firmware_update.cpp | 58 +- radio/src/io/multi_firmware_update.h | 4 +- radio/src/libopenui_conf.h | 56 + radio/src/logs.cpp | 93 +- radio/src/logs.h | 30 + radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_colorlcd.cpp | 14 +- radio/src/lua/api_filesystem.cpp | 63 +- radio/src/lua/api_general.cpp | 17 +- radio/src/lua/api_stdlcd.cpp | 12 +- radio/src/lua/interface.cpp | 73 +- radio/src/lua/lua_file_api.cpp | 526 +++++ radio/src/lua/lua_file_api.h | 16 + radio/src/lua/widgets.cpp | 29 +- radio/src/lv_conf.h | 4 + radio/src/main.cpp | 13 +- radio/src/model_init.cpp | 5 +- radio/src/nor_flash.cpp | 598 ++++++ radio/src/nor_flash.h | 206 ++ radio/src/opentx.cpp | 30 +- radio/src/opentx.h | 30 +- radio/src/pulses/pxx2.cpp | 2 +- radio/src/pulses/pxx2_ota.cpp | 28 +- radio/src/rtos.h | 36 + radio/src/sdcard.h | 147 +- radio/src/storage/modelslist.cpp | 189 +- radio/src/storage/modelslist.h | 4 +- radio/src/storage/sdcard_common.cpp | 7 +- radio/src/storage/sdcard_common.h | 4 +- radio/src/storage/sdcard_raw.h | 2 +- radio/src/storage/sdcard_yaml.cpp | 129 +- radio/src/storage/storage_common.cpp | 3 +- radio/src/strhelpers.h | 11 +- radio/src/syscalls.c | 70 +- .../arm/stm32/bootloader/CMakeLists.txt | 28 +- .../common/arm/stm32/bootloader/bin_files.cpp | 7 +- .../common/arm/stm32/bootloader/bin_files.h | 10 +- .../arm/stm32/bootloader/bin_files_vfs.cpp | 194 ++ .../arm/stm32/bootloader/bin_files_vfs.h | 92 + .../common/arm/stm32/bootloader/boot.cpp | 154 +- .../common/arm/stm32/bootloader/boot.h | 3 + .../common/arm/stm32/diskio_sdio_spiflash.cpp | 167 +- .../common/arm/stm32/flash_spi_driver.cpp | 536 +++++ radio/src/targets/common/arm/stm32/sdio_sd.h | 1 + .../common/arm/stm32/usbd_storage_msd.cpp | 231 +- radio/src/targets/horus/CMakeLists.txt | 43 +- radio/src/targets/horus/board.cpp | 9 + radio/src/targets/horus/board.h | 5 + .../targets/horus/bootloader/boot_menu.cpp | 60 +- radio/src/targets/horus/hal.h | 84 +- radio/src/targets/nv14/CMakeLists.txt | 39 +- radio/src/targets/nv14/board.cpp | 10 +- radio/src/targets/nv14/board.h | 4 + .../src/targets/nv14/bootloader/boot_menu.cpp | 63 +- radio/src/targets/nv14/hal.h | 40 +- radio/src/targets/simu/simpgmspace.h | 6 +- radio/src/targets/simu/simudisk.cpp | 82 - radio/src/targets/simu/simufatfs.cpp | 14 +- radio/src/targets/taranis/CMakeLists.txt | 8 + .../targets/taranis/bootloader/boot_menu.cpp | 10 +- radio/src/telemetry/telemetry.cpp | 6 +- radio/src/thirdparty/FatFs/ffconf.h | 2 +- radio/src/thirdparty/Lua/src/lauxlib.c | 69 +- radio/src/thirdparty/Lua/src/lauxlib.h | 8 +- radio/src/thirdparty/Lua/src/ldblib.c | 2 +- radio/src/thirdparty/Lua/src/liolib.c | 142 +- radio/src/thirdparty/Lua/src/loadlib.c | 18 +- radio/src/thirdparty/Lua/src/lua.c | 2 +- radio/src/thirdparty/Lua/src/lua.h | 14 +- radio/src/thirdparty/Lua/src/luac.c | 8 +- radio/src/thirdparty/Lua/src/luaconf.h | 28 +- .../Class/msc/src/usbd_msc_bot.c | 2 +- radio/src/thirdparty/tjftl/README.md | 21 + radio/src/thirdparty/tjftl/tjftl.c | 572 +++++ radio/src/thirdparty/tjftl/tjftl.h | 23 + 128 files changed, 7290 insertions(+), 1557 deletions(-) delete mode 100755 cmake.sh create mode 100644 radio/src/VirtualFS.cpp create mode 100644 radio/src/VirtualFS.h create mode 100644 radio/src/libopenui_conf.h create mode 100644 radio/src/logs.h create mode 100644 radio/src/lua/lua_file_api.cpp create mode 100644 radio/src/lua/lua_file_api.h create mode 100644 radio/src/nor_flash.cpp create mode 100644 radio/src/nor_flash.h create mode 100644 radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.cpp create mode 100644 radio/src/targets/common/arm/stm32/bootloader/bin_files_vfs.h create mode 100644 radio/src/targets/common/arm/stm32/flash_spi_driver.cpp create mode 100644 radio/src/thirdparty/tjftl/README.md create mode 100644 radio/src/thirdparty/tjftl/tjftl.c create mode 100644 radio/src/thirdparty/tjftl/tjftl.h 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/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 1e59d512c69..9630fba462f 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -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) @@ -251,9 +252,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..ea290a165aa --- /dev/null +++ b/radio/src/VirtualFS.cpp @@ -0,0 +1,1909 @@ +/* + * 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 "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) && !defined(USE_LITTLEFS) +static FATFS spiFatFs __DMA; +#endif + +#if defined(USE_LITTLEFS) +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(); +uint16_t flashSpiGetSectorSize(); +uint16_t flashSpiGetSectorCount(); + +int flashSpiErase(size_t address); +int flashSpiBlockErase(size_t address); +void flashSpiEraseAll(); + +void flashSpiSync(); + + +extern "C" +{ + +#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 + +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) +{ + flashSpiSync(); + return LFS_ERR_OK; +} +} + +static VfsError convertResult(lfs_error err) +{ + switch(err) + { + case LFS_ERR_OK: return VfsError::OK; + case LFS_ERR_IO: return VfsError::IO; + case LFS_ERR_CORRUPT: return VfsError::CORRUPT; + case LFS_ERR_NOENT: return VfsError::NOENT; + case LFS_ERR_EXIST: return VfsError::EXIST; + case LFS_ERR_NOTDIR: return VfsError::NOTDIR; + case LFS_ERR_ISDIR: return VfsError::ISDIR; + case LFS_ERR_NOTEMPTY: return VfsError::NOTEMPTY; + case LFS_ERR_BADF: return VfsError::BADF; + case LFS_ERR_FBIG: return VfsError::FBIG; + case LFS_ERR_INVAL: return VfsError::INVAL; + case LFS_ERR_NOSPC: return VfsError::NOSPC; + case LFS_ERR_NOMEM: return VfsError::NOMEM; + case LFS_ERR_NOATTR: return VfsError::NOATTR; + case LFS_ERR_NAMETOOLONG: return VfsError::NAMETOOLONG; + } + return VfsError::INVAL; +} + +static int convertOpenFlagsToLfs(VfsOpenFlags flags) +{ + int lfsFlags = 0; + + if((flags & VfsOpenFlags::READ) != VfsOpenFlags::NONE && (flags & VfsOpenFlags::WRITE) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_RDWR; + else if ((flags & VfsOpenFlags::READ) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_RDONLY; + else if ((flags & VfsOpenFlags::WRITE) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_WRONLY; + + if((flags & VfsOpenFlags::CREATE_NEW) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_CREAT | LFS_O_EXCL; + else if ((flags & VfsOpenFlags::CREATE_ALWAYS) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_CREAT | LFS_O_TRUNC; + else if((flags & VfsOpenFlags::OPEN_ALWAYS) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_CREAT; + else if((flags & VfsOpenFlags::OPEN_APPEND) != VfsOpenFlags::NONE) + lfsFlags |= LFS_O_CREAT | LFS_O_APPEND; + + return lfsFlags; +} +#endif + +#if defined (USE_FATFS) +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; +} +#endif + +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; +#if defined (USE_FATFS) + case VfsFileType::FAT: return(name != nullptr)?name:fatInfo.fname; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: return lfsInfo.name; +#endif + } + return ""; +}; + +size_t VfsFileInfo::getSize() const +{ + switch(type) + { + case VfsFileType::ROOT: return 0; +#if defined (USE_FATFS) + case VfsFileType::FAT: return fatInfo.fsize; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: return lfsInfo.size; +#endif + } + return 0; +} + +VfsType VfsFileInfo::getType() const +{ + switch(type) + { + case VfsFileType::ROOT: + return VfsType::DIR; +#if defined (USE_FATFS) + case VfsFileType::FAT: + if (name != nullptr) + return VfsType::DIR; + if(fatInfo.fattrib & AM_DIR) + return VfsType::DIR; + else + return VfsType::FILE; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: + + if(lfsInfo.type == LFS_TYPE_DIR) + return VfsType::DIR; + else + return VfsType::FILE; +#endif + } + return VfsType::UNKOWN; +}; + +VfsFileAttributes VfsFileInfo::getAttrib() +{ + switch(type) + { + case VfsFileType::ROOT: + return VfsFileAttributes::DIR; +#if defined (USE_FATFS) + case VfsFileType::FAT: + return (VfsFileAttributes)fatInfo.fattrib; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: + if(lfsInfo.type == LFS_TYPE_DIR) + return VfsFileAttributes::DIR; + return VfsFileAttributes::NONE; +#endif + } + return VfsFileAttributes::NONE; +} + +int VfsFileInfo::getDate(){ + switch(type) + { + case VfsFileType::ROOT: + return 0; +#if defined (USE_FATFS) + case VfsFileType::FAT: + return fatInfo.fdate; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: + return 0; +#endif + } + return 0; +} + +int VfsFileInfo::getTime() +{ + switch(type) + { + case VfsFileType::ROOT: + return 0; +#if defined (USE_FATFS) + case VfsFileType::FAT: + return fatInfo.ftime; +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: + return 0; +#endif + } + return 0; +} + +void VfsFileInfo::clear() { + type = VfsFileType::UNKNOWN; +#if defined(USE_LITTLEFS) + lfsInfo = {0}; +#endif + fatInfo = {0}; + name = nullptr; +} + + +void VfsDir::clear() +{ + type = DIR_UNKNOWN; +#if defined(USE_LITTLEFS) + lfs.dir = {0}; + lfs.handle = nullptr; +#endif + 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; +#if defined (USE_FATFS) + 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; + } +#endif +#if defined(USE_LITTLEFS) + case VfsDir::DIR_LFS: + { + info.type = VfsFileType::LFS; + int res = lfs_dir_read(lfs.handle, &lfs.dir, &info.lfsInfo); + if(res >= 0) + return VfsError::OK; + return convertResult((lfs_error)res); + } +#endif + } + return VfsError::INVAL; +} + +VfsError VfsDir::close() +{ + VfsError ret = VfsError::INVAL; + switch(type) + { + case VfsDir::DIR_ROOT: + ret = VfsError::OK; + break; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + ret = convertResult(f_closedir(&fat.dir)); + break; +#endif +#if defined(USE_LITTLEFS) + case VfsDir::DIR_LFS: + ret = convertResult((lfs_error)lfs_dir_close(lfs.handle, &lfs.dir)); + break; +#endif + } + clear(); + return ret; +} + +VfsError VfsDir::rewind() +{ + readIdx = 0; + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::OK; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + { + return convertResult(f_readdir(&fat.dir, nullptr)); + } +#endif +#if defined(USE_LITTLEFS) + case VfsDir::DIR_LFS: + { + info.type = VfsFileType::LFS; + int res = lfs_dir_read(lfs.handle, &lfs.dir, nullptr); + if(res >= 0) + return VfsError::OK; + return convertResult((lfs_error)res); + } +#endif + } + return VfsError::INVAL; +} + +void VfsFile::clear() { + type = VfsFileType::UNKNOWN; +#if defined(USE_LITTLEFS) + lfs.file = {0}; + lfs.handle = nullptr; +#endif + fat.file = {0}; +} + +VfsError VfsFile::close() +{ + VfsError ret = VfsError::INVAL; + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + ret = convertResult(f_close(&fat.file)); + break; +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + ret = convertResult((lfs_error)lfs_file_close(lfs.handle, &lfs.file)); + break; +#endif + } + + clear(); + return ret; +} + +int VfsFile::size() +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + return f_size(&fat.file); + break; +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + int res = lfs_file_size(lfs.handle, &lfs.file); + if(res < 0) + return (int)convertResult((lfs_error)res); + return res; + } +#endif + } + + return -1; +} + +VfsError VfsFile::read(void* buf, size_t size, size_t& readSize) +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: { + UINT rd = 0; + VfsError res = convertResult(f_read(&fat.file, buf, size, &rd)); + readSize = rd; + return res; + } +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + int ret = lfs_file_read(lfs.handle, &lfs.file, buf, size); + if(ret >= 0) + { + readSize = ret; + return VfsError::OK; + } else { + readSize = 0; + return convertResult((lfs_error)ret); + } + break; + } +#endif + } + + return VfsError::INVAL; +} + +char* VfsFile::gets(char* buf, size_t maxLen) +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + return f_gets(buf, maxLen, &fat.file); +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + size_t nc = 0; + char* p = buf; + char s[4]; + size_t rc; + char dc; + maxLen -= 1; /* Make a room for the terminator */ + while (nc < maxLen) { + read(s, 1, rc); + if (rc != 1) break; + dc = s[0]; + *p++ = dc; nc++; + if (dc == '\n') break; + } + *p = 0; /* Terminate the string */ + return nc ? buf : 0; /* When no data read due to EOF or error, return with error. */ + } +#endif + } + + return 0; +} + +#if !defined(BOOT) +VfsError VfsFile::write(const void* buf, size_t size, size_t& written) +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: { + UINT wrt = 0; + VfsError res = convertResult(f_write(&fat.file, buf, size, &wrt)); + written = wrt; + return res; + } +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + int ret = lfs_file_write(lfs.handle, &lfs.file, buf, size); + if(ret >= 0) + { + written = ret; + return VfsError::OK; + } else { + written = 0; + return convertResult((lfs_error)ret); + } + break; + } +#endif + } + + 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: + { + va_list args; + va_start(args, str); + + int ret = f_printf(&fat.file, str, args); + va_end(args); + return ret; + } +#endif +#if defined(USE_LITTLEFS) + case VfsFileType::LFS: + { +#if 0 + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + if (c == '*') { /* Minimum width by argument */ + w = va_arg(arp, int); + c = *fmt++; + } else { + while (IsDigit(c)) { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } + } + if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (c == 0) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Atgument type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { /* Right padded */ + while (j++ < w) putc_bfd(&pb, ' ') ; + } + while (*p) putc_bfd(&pb, *p++) ; /* String body */ + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ + continue; + + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + + case 'B' : /* Unsigned binary */ + r = 2; break; + + case 'O' : /* Unsigned octal */ + r = 8; break; + + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + + case 'X' : /* Unsigned hexdecimal */ + r = 16; break; + + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof *str); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ + } + do { + putc_bfd(&pb, str[--i]); /* Number body */ + } while (i); + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } + + va_end(arp); + + return putc_flush(&pb); +#endif + } +#endif + } + + return (int)VfsError::INVAL; +} +#endif + +size_t VfsFile::tell() +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + return f_tell(&fat.file); + #endif + #if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + int ret = lfs_file_tell(lfs.handle, &lfs.file); + if(ret < 0) + return (size_t)convertResult((lfs_error)ret); + else + return ret; + } + #endif + } + + return (size_t)VfsError::INVAL; +} + +VfsError VfsFile::lseek(size_t offset) +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + return convertResult(f_lseek(&fat.file, offset)); + break; +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + { + int ret = lfs_file_seek(lfs.handle, &lfs.file, offset, LFS_SEEK_SET); + if(ret < 0) + return convertResult((lfs_error)ret); + else + return VfsError::OK; + } +#endif + } + + return VfsError::INVAL; +} + +int VfsFile::eof() +{ + switch(type) + { +#if defined (USE_FATFS) + case VfsFileType::FAT: + return f_eof(&fat.file); +#endif +#if defined (USE_LITTLEFS) + case VfsFileType::LFS: + return lfs_file_tell(lfs.handle, &lfs.file) == lfs_file_size(lfs.handle, &lfs.file); +#endif + } + + 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) +#if defined (USE_LITTLEFS) + // 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 = 256; + lfsCfg.prog_size = flashSpiGetPageSize(); + lfsCfg.block_size = flashSpiGetSectorSize(); + lfsCfg.block_count = flashSpiGetSectorCount(); + lfsCfg.block_cycles = 500; + lfsCfg.cache_size = flashSpiGetPageSize(); + lfsCfg.lookahead_size = 256; +#else + spiFatFs = {0}; +#endif // USE_LITTLEFS +#endif // SPI_FLASH +#if defined (SDCARD) + sdFatFs = {0}; +#endif + + restart(); +} + +VirtualFS::~VirtualFS() +{ +#if defined (SPI_FLASH) +#if defined (USE_LITTLEFS) + lfs_unmount(&lfs); +#else // USE_LITTLEFS + f_unmount("1:"); +#endif // USE_LITTLEFS +#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) +#if defined (USE_LITTLEFS) + lfs_unmount(&lfs); +#else // USE_LITTLEFS + f_unmount("1:"); +#endif // USE_LITTLEFS +#endif // SPI_FLASH +} +void VirtualFS::restart() +{ + TRACE("VirtualFS::restart()"); +#if defined (SDCARD) + mountSd(); +#endif +#if defined (SPI_FLASH) +#if defined(USE_LITTLEFS) + // flashSpiEraseAll(); + lfsMounted = true; + int err = lfs_mount(&lfs, &lfsCfg); + if(err) { + flashSpiEraseAll(); + err = lfs_format(&lfs, &lfsCfg); + if(err == LFS_ERR_OK) + err = lfs_mount(&lfs, &lfsCfg); + if(err != LFS_ERR_OK) + { + lfsMounted = false; +#if !defined(BOOT) + POPUP_WARNING(STR_SDCARD_ERROR); +#endif + } + } + lfsCfg.context = this; +#else // USE_LITTLEFS +#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 // USE_LITLEFS +#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() +{ + TRACE("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) +#if defined (USE_LITTLEFS) + return lfsMounted +#else // USE_LITTLEFS + return spiFatFs.fs_type != 0; +#endif // USE_LITTLEFS +#elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE + return sdFatFs.fs_type != 0; +#endif + +} +#if !defined(BOOT) +bool VirtualFS::format() +{ +// TODO format +#if defined (USE_LITTLEFS) + + flashSpiEraseAll(); + lfs_format(&lfs, &lfsCfg); + return true; +#else + return false; +#endif +// 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(std::string& path) +{ + if(path == "/") + { + return VfsDir::DIR_ROOT; +#if defined (SPI_FLASH) + } else if(path.substr(0, 9) == "/INTERNAL") + { +#if defined (USE_LITTLEFS) + path = path.substr(6); + return VfsDir::DIR_LFS; +#else // USE_LITTLEFS + path = "1:" + path.substr(9); + if(path == "1:") + path = "1:/"; + else if (path == "") + path = "/"; + return VfsDir::DIR_FAT; +#endif // USE_LITTLEFS +#endif // SPI_FLASH +#if defined (SDCARD) + } else if(path.substr(0, 7) == "/SDCARD") { + path = path.substr(7); + if (path == "") + path = "/"; + return VfsDir::DIR_FAT; +#endif + } else if(path.substr(0, 8) == "/DEFAULT") { +#if (DEFAULT_STORAGE == INTERNAL) +#if defined (USE_LITTLEFS) + path = path.substr(8); + return VfsDir::DIR_LFS; +#else // USE_LITTLEFS + path = "1:" + path.substr(8); + if(path == "1:") + path = "1:/"; + return VfsDir::DIR_FAT; +#endif // USE_LITTLEFS +#elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE + path = path.substr(8); + return VfsDir::DIR_FAT; +#else // DEFAULT_STORAGE + #error No valid default storage selectd +#endif + } + return VfsDir::DIR_UNKNOWN; +} + +void VirtualFS::normalizePath(std::string& path) +{ + std::vector tokens; + size_t oldpos = 0; + + if(path[0] != '/') + path = curWorkDir + PATH_SEPARATOR + path; + + while (1) + { + size_t newpos = path.find_first_of(PATH_SEPARATOR, oldpos); + if(newpos == std::string::npos) + { + std::string elem = path.substr(oldpos); + if(elem == "..") + tokens.pop_back(); + else if (elem == ".") + ; + else + tokens.push_back(path.substr(oldpos)); + break; + } + tokens.push_back(path.substr(oldpos,newpos-oldpos)); + oldpos=newpos+1; + } + + if(tokens.empty()) + return; + + path = ""; + for(auto token: tokens) + { + if(token.length() == 0) + continue; + path += PATH_SEPARATOR; + path += token; + } + if(path.length() == 0) + path = "/"; +} +#if !defined(BOOT) +VfsError VirtualFS::unlink(const std::string& path) +{ + std::string p = path; + normalizePath(p); + VfsDir::DirType type = getDirTypeAndPath(p); + + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + return convertResult(f_unlink(p.c_str())); +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + return convertResult((lfs_error)lfs_remove(&lfs, p.c_str())); +#endif + } + + return VfsError::INVAL; +} +#endif +VfsError VirtualFS::changeDirectory(const std::string& path) +{ + if(path.length() == 0) + return VfsError::INVAL; + + std::string newWorkDir = path; + normalizePath(newWorkDir); + curWorkDir = newWorkDir; + + return VfsError::OK; +} + +VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) +{ + dir.clear(); + if(path == nullptr) + return VfsError::INVAL; + + if(path[0] == 0) + return VfsError::INVAL; + + std::string dirPath(path); + + normalizePath(dirPath); + + VfsDir::DirType type = getDirTypeAndPath(dirPath); + dir.type = type; + switch(type) + { + case VfsDir::DIR_ROOT: + return VfsError::OK; +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + dir.lfs.handle = &lfs; + return convertResult((lfs_error)lfs_dir_open(&lfs, &dir.lfs.dir, dirPath.c_str())); +#endif +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + return convertResult(f_opendir(&dir.fat.dir, dirPath.c_str())); +#endif + } + + return VfsError::INVAL; +} +#if !defined(BOOT) +VfsError VirtualFS::makeDirectory(const std::string& path) +{ + std::string normPath(path); + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + break; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + { + DIR dir; + FRESULT result = f_opendir(&dir, normPath.c_str()); + if (result == FR_OK) { + f_closedir(&dir); + return VfsError::OK; + } else { + if (result == FR_NO_PATH) + result = f_mkdir(normPath.c_str()); + if (result != FR_OK) + return convertResult(result); + } + break; + } +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + { + lfs_dir_t dir; + lfs_error res = (lfs_error)lfs_dir_open(&lfs, &dir, normPath.c_str()); + if(res == LFS_ERR_OK) + { + lfs_dir_close(&lfs, &dir); + return VfsError::OK; + } else { + if(res == LFS_ERR_NOENT) + res = (lfs_error)lfs_mkdir(&lfs, normPath.c_str()); + if(res != LFS_ERR_OK) + { + return convertResult(res); + } + } +break; + } +#endif + } + return VfsError::INVAL; +} +VfsError VirtualFS::rename(const char* oldPath, const char* newPath) +{ + std::string oldP = oldPath; + std::string newP = newPath; + + 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; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + return convertResult(f_rename(oldP.c_str(), newP.c_str())); +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + return convertResult((lfs_error)lfs_rename(&lfs, oldP.c_str(), newP.c_str())); +#endif + } + } else { + VfsError err = copyFile(oldPath, newPath); + if(err == VfsError::OK) + return unlink(oldPath); + return err; + } + return VfsError::INVAL; +} + +VfsError VirtualFS::copyFile(const std::string& source, const std::string& 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 std::string& srcFile, const std::string& srcDir, + const std::string& destDir, const std::string& destFile) +{ + return copyFile(srcDir +"/" + srcFile, destDir + "/" + destFile); +} + +// Will overwrite if destination exists +const char * VirtualFS::moveFile(const std::string& srcPath, const std::string& destPath) +{ + + 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 std::string& srcFilename, const std::string& srcDir, const std::string& destFilename, const std::string& 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.c_str(), CLIPBOARD_PATH_LEN); + *tmp++ = '/'; + strAppend(tmp, srcFilename.c_str(), 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 std::string& path, VfsFileInfo& fileInfo) +{ + std::string normPath(path); + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + fileInfo.type = VfsFileType::FAT; + return convertResult(f_stat(normPath.c_str(), &fileInfo.fatInfo)); +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + fileInfo.type = VfsFileType::LFS; + return convertResult((lfs_error)lfs_stat(&lfs, normPath.c_str(), fileInfo.lfsInfo)); +#endif + } + return VfsError::INVAL; +} +#if !defined(BOOT) +VfsError VirtualFS::utime(const std::string& path, const VfsFileInfo& fileInfo) +{ + std::string normPath(path); + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + return convertResult(f_utime(normPath.c_str(), &fileInfo.fatInfo)); +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + return VfsError::OK; +#endif + } + return VfsError::INVAL; +} +#endif +VfsError VirtualFS::openFile(VfsFile& file, const std::string& path, VfsOpenFlags flags) +{ + file.clear(); + std::string normPath(path); + normalizePath(normPath); + VfsDir::DirType dirType = getDirTypeAndPath(normPath); + + VfsError ret = VfsError::INVAL; + switch(dirType) + { + case VfsDir::DIR_ROOT: + return VfsError::INVAL; + break; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + { + file.type = VfsFileType::FAT; + ret = convertResult(f_open(&file.fat.file, normPath.c_str(), + convertOpenFlagsToFat(flags))); + break; + } +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + { + file.type = VfsFileType::LFS; + file.lfs.handle = &lfs; + ret = convertResult((lfs_error)lfs_file_open(&lfs, &file.lfs.file, normPath.c_str(), + convertOpenFlagsToLfs(flags))); + break; + } +#endif + } + + 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) +{ + std::string p = path; + VfsDir::DirType dirType = getDirTypeAndPath(p); + + switch(dirType) + { + case VfsDir::DIR_ROOT: + return false; + break; +#if defined (USE_FATFS) + case VfsDir::DIR_FAT: + { + if (exclDir) { + FILINFO fno; + return (f_stat(p.c_str(), &fno) == FR_OK && !(fno.fattrib & AM_DIR)); + } + return f_stat(p.c_str(), nullptr) == FR_OK; + } +#endif +#if defined (USE_LITTLEFS) + case VfsDir::DIR_LFS: + { + lfs_file_t file; + int res = lfs_file_open(&lfs, &file, p.c_str(), LFS_O_RDONLY); + if(res != LFS_ERR_OK) + { + if(res == LFS_ERR_ISDIR) + return(!exclDir); + return false; + } else { + lfs_file_close(&lfs, &file); + return true; + } + } +#endif + } + + return false; +} + +const char * VirtualFS::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 VirtualFS::isFileExtensionMatching(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; +} + + +/** + 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..69b7021788e --- /dev/null +++ b/radio/src/VirtualFS.h @@ -0,0 +1,455 @@ +/* + * 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 + +#if defined (SPI_FLASH) +#if defined (USE_LITTLEFS) +#include "littlefs_v2.4.1/lfs.h" +#else +#include "tjftl/tjftl.h" +#include "FatFs/ff.h" +#endif +#endif +#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 "/" +#define ROOT_PATH PATH_SEPARATOR "DEFAULT" PATH_SEPARATOR +#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 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, LFS }; + +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 { +#if defined(USE_LITTLEFS) + lfs_info lfsInfo = {0}; +#endif + 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, DIR_LFS}; + + void clear(); + + DirType type = DIR_UNKNOWN; + union { +#if defined(USE_LITTLEFS) + struct { + lfs_dir_t dir; + lfs* handle; + } lfs; +#endif + 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 { +#if defined(USE_LITTLEFS) + struct { + lfs_file file = {0}; + lfs* handle = nullptr; + } lfs; +#endif + 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 std::string& path); +#endif // !BOOT + VfsError changeDirectory(const std::string& path); + VfsError openDirectory(VfsDir& dir, const char * path); + VfsError makeDirectory(const std::string& path); + + VfsError fstat(const std::string& path, VfsFileInfo& fileInfo); + VfsError utime(const std::string& path, const VfsFileInfo& fileInfo); + VfsError openFile(VfsFile& file, const std::string& path, VfsOpenFlags flags); + +#if !defined(BOOT) + VfsError rename(const char* oldPath, const char* newPath); + VfsError copyFile(const std::string& source, const std::string& destination); + VfsError copyFile(const std::string& srcFile, const std::string& srcDir, + const std::string& destDir, const std::string& destFile); + const char * moveFile(const std::string& source, const std::string& destination); + const char * moveFile(const std::string& srcFile, const std::string& srcDir, + const std::string& destDir, const std::string& 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; + + +#if defined(USE_LITTLEFS) + lfs_config lfsCfg = {0}; + lfs_t lfs = {0}; + bool lfsMounted = false; +#endif + + std::string curWorkDir = "/"; + + void normalizePath(std::string &path); + + VfsDir::DirType getDirTypeAndPath(std::string& path); + + void startLogs(); + void stopLogs(); +}; + +#endif // _VIRTUALFS_H_ diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 2944274b32e..4f275a9da7f 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 */ + 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(); // Eliminates directories / non wav files - if (len < 5 || strcasecmp(fno.fname+len-4, SOUNDS_EXT) || (fno.fattrib & AM_DIR)) continue; + if (len < 5 || strcasecmp(fname.c_str()+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 */ + std::string name = fno.getName(); + if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ + uint8_t len = name.length(); 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.c_str()+len-4, SOUNDS_EXT) || (fno.getType() == VfsType::DIR)) continue; + TRACE("referenceModelAudioFiles(): using file: %s", name.c_str()); // 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..bf7f66463ae 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, @@ -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/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..82510b67d3c 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 */ + std::string name = fno.getName(); + if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ + cliSerialPrint(name.c_str()); } - 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; 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/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/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/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..d65bdb8d1da 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 ((fno.getAttrib() & (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/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_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index e38bb624a77..c1bd3385798 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) 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::function 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..276ed9c5d29 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 */ + if (res != VfsError::OK || strlen(fno.getName()) == 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 */ + auto fname = fno.getName(); + 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/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..3c000213eb5 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, 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, 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/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..b17ca05abb0 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); + std::string name = fno.getName(); + if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ + if (name.length() > 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.c_str(), 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.c_str()); 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.c_str(), reusableBuffer.sdManager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, name.c_str(), 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.c_str()); 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.c_str(), reusableBuffer.sdManager.lines[1]) && isFilenameGreater(isfile, name.c_str(), 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.c_str()); 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..5147296a966 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,41 @@ void menuRadioTools(event_t event) uint8_t index = 0; #if defined(LUA) - 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) { 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 */ + std::string name = fno.getName(); + if (res != VfsError::OK || name.length() == 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; + + strcat(path, name.c_str()); + if (isRadioScriptTool(fno.getName())) { 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..230ca838520 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,8 +223,8 @@ 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); @@ -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..56ed7121607 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, 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..b532ee2ece2 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" @@ -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); 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 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..4ab2928d8cd 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,17 @@ 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(); + if (vfs.openFile(D, filename, 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(filename, *finfo); // set the file mod time TRACE("luaDumpState(%s): Saved bytecode to file.", filename); } } else @@ -433,9 +432,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 +445,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 +500,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 +524,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 +545,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 +587,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,6 +1188,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) } else if (lua_isstring(lsScripts, -1)) { char nextScript[FF_MAX_LFN+1]; + const char* luaFile = lua_tostring(lsScripts, -1); strncpy(nextScript, lua_tostring(lsScripts, -1), FF_MAX_LFN); nextScript[FF_MAX_LFN] = '\0'; luaExec(nextScript); @@ -1365,18 +1369,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 +1407,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..e559935611c --- /dev/null +++ b/radio/src/lua/lua_file_api.cpp @@ -0,0 +1,526 @@ +/* + * 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; +}; + +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) + +static int remap_handle(int fh) +{ + return fh - FILE_HANDLE_OFFSET; +} + +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 = 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, 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); + + int res = _lua_fopen(&open_files[slot], name, mode); + if(res == 0) + { + set_errno(0); + return &open_files[slot]; + } + + 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(); + 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/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/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..81056349d93 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; @@ -199,7 +200,6 @@ void per10ms() #if defined(SDCARD) && defined(PCBTARANIS) sdPoll10ms(); -#endif outputTelemetryBuffer.per10ms(); @@ -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..704bb87f679 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, @@ -655,9 +649,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 +782,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 +815,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 +879,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 +1065,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 +1086,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/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.h b/radio/src/sdcard.h index 4a83cfe7948..2934f1ab66c 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -24,156 +24,11 @@ #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(); -uint32_t sdGetFreeKB(); -bool sdIsFull(); - -#if defined(PCBTARANIS) -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/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 7d2ecab02b4..1d3a87a33c7 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); + if (res != VfsError::OK || finfo.getName()[0] == 0) break; + const char* fName = finfo.getName(); + 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/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/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/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 77df75fa3f9..aa36b893537 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -44,9 +44,30 @@ 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 + ../../../../../thirdparty/tjftl/tjftl.c + ) +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} @@ -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..6e82656028d 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; @@ -137,7 +134,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..52dc904db3d 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) 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() {} @@ -397,15 +433,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 +472,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 +519,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; @@ -483,7 +571,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 +591,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 +607,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 +632,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 +678,7 @@ int bootloaderMain() } } + if (state == ST_FLASH_DONE) { if (unlocked) { lockFlash(); @@ -613,6 +735,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_spiflash.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index a156849a13e..81974ce72c8 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -44,15 +44,80 @@ extern volatile uint32_t WriteStatus; extern volatile uint32_t ReadStatus; +#if defined(SPI_FLASH) +#include "tjftl/tjftl.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(); +uint16_t flashSpiGetSectorSize(); +uint16_t flashSpiGetSectorCount(); + +int flashSpiErase(size_t address); +int flashSpiBlockErase(size_t address); +void flashSpiEraseAll(); + +void flashSpiSync(); + +static tjftl_t* tjftl = nullptr; +extern "C" { +static bool flashRead(int addr, uint8_t* buf, int len, void* arg) +{ + flashSpiRead(addr, buf, len); + return true; +} + +static bool flashWrite(int addr, const uint8_t *buf, int len, void *arg) +{ + 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(int addr, void *arg) +{ + flashSpiBlockErase(addr); + return true; +} +} +#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(tjftl != nullptr) + return stat; + if(!tjftl_detect(flashRead, nullptr)) + flashSpiEraseAll(); + + size_t flashSize = flashSpiGetSectorSize()*flashSpiGetSectorCount(); + // tjftl requires at least 10 free blocks after garbage collection. + // To ensure a working tjftl the fuilesystem must be at least 10 blocks smaller than the flash memory. + // the block and sector sizes used by tjftl are fixed. A block has 32k bytes and a sector has 512 bytes + tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize/512)-((32768/512)*10), 0); + + if(tjftl == nullptr) + stat |= STA_NOINIT; + return stat; + } +#endif /* Supports only single drive */ if (drv) { @@ -60,12 +125,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 +157,14 @@ DSTATUS disk_status ( ) { DSTATUS stat = 0; - +#if defined(SPI_FLASH) + if(drv == 1) + { + if(tjftl == nullptr) + stat |= STA_NODISK; + return stat; + } +#endif if (SD_Detect() != SD_PRESENT) stat |= STA_NODISK; @@ -154,6 +231,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(tjftl == nullptr) + { + res = RES_ERROR; + return res; + } + while(count) + { + if(!tjftl_read(tjftl, 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 +268,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 +308,25 @@ DRESULT __disk_write( ) { DRESULT res = RES_OK; - +#if defined(SPI_FLASH) + if(drv == 1) + { + if(tjftl == nullptr) + { + res = RES_ERROR; + return res; + } + while(count) + { + if(!tjftl_write(tjftl, sector, (uint8_t*)buff)) + return RES_ERROR; + buff += 512; + sector++; + count --; + } + return res; + } +#endif // TRACE("disk_write %d %p %10d %d", drv, buff, sector, count); if (SD_Detect() != SD_PRESENT) @@ -282,11 +397,53 @@ DRESULT disk_ioctl ( { DRESULT res; uint32_t tmp; +#if defined(SPI_FLASH) + if(drv == 1) + { + disk_initialize(1); + if(tjftl == 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 = tjftl_getSectorCount(tjftl); + res = RES_OK; + break; + } + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = tjftl_getSectorSize(tjftl); + 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 = 512; + res = RES_OK; + break; + + case CTRL_SYNC: + 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/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp new file mode 100644 index 00000000000..31e27e48d9a --- /dev/null +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -0,0 +1,536 @@ +/* + * 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 "rtos.h" +#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; +}; + +// * 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[] = +{ + { // GD25Q127C + .id = 0xC817, + .pageSize = 256, + .sectorSize = 4096, + .blockSize = 65536, + .blockCount = 256, + + .readStatusCmd = 0x05, + .readCmd = 0x03, + .writeCmd = 0x02, + .writeEnableCmd = 0x06, + .eraseSectorCmd = 0x20, + .eraseBlockCmd = 0x52, + .eraseChipCmd = 0x60 + }, + { // W25Q64JV + .id = 0xEF16, + .pageSize = 256, + .sectorSize = 4096, + .blockSize = 65536, + .blockCount = 128, + + .readStatusCmd = 0x05, + .readCmd = 0x03, + .writeCmd = 0x02, + .writeEnableCmd = 0x06, + .eraseSectorCmd = 0x20, + .eraseBlockCmd = 0x52, + .eraseChipCmd = 0xC7 + } +}; + +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; +} + +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); + 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); + 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%4096 != 0) + return -1; + + flashSpiSync(); + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->eraseSectorCmd); + 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%32768 != 0) + return -1; + + flashSpiSync(); + + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->writeEnableCmd); + delay_01us(100); // 10us + CS_HIGH(); + delay_01us(100); // 10us + CS_LOW(); + flashSpiReadWriteByte(flashDescriptor->eraseBlockCmd); + 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/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/usbd_storage_msd.cpp b/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp index 8d7152fc431..16272325993 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,41 @@ 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 return 0; } -#endif +#endif + 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 +246,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 +274,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 +306,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 +342,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 +383,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 +535,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 +551,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 +567,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 +619,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(); 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..7a2b3e56219 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -7,7 +7,9 @@ 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) @@ -21,8 +23,43 @@ 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} + ../../thirdparty/tjftl/tjftl.c + ) + 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 + ../../thirdparty/tjftl/tjftl.c + ) else() set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) endif() @@ -148,7 +185,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..404fd001672 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" @@ -112,7 +114,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 +137,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 +237,7 @@ void boardInit() init1msTimer(); TouchInit(); usbInit(); + flashInit(); uint32_t press_start = 0; uint32_t press_end = 0; 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/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/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..56ff5d4ba7b 100644 --- a/radio/src/targets/taranis/bootloader/boot_menu.cpp +++ b/radio/src/targets/taranis/bootloader/boot_menu.cpp @@ -78,6 +78,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 +98,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/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/thirdparty/FatFs/ffconf.h b/radio/src/thirdparty/FatFs/ffconf.h index fda61e9edcf..5fa552052db 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) */ 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/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..fef426822d1 --- /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 8 +//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 From d43996c6fa61355d6f40250c08742775a659faef Mon Sep 17 00:00:00 2001 From: raphaelcoeffic Date: Thu, 5 Jan 2023 15:10:56 +0100 Subject: [PATCH 03/99] Fix paths in unit tests --- radio/src/tests/gtests.cpp | 4 ++++ radio/src/tests/lcd.cpp | 18 +++++++++++------- radio/src/tests/lcd_480x272.cpp | 6 +++--- radio/src/tests/switches.cpp | 8 ++++---- 4 files changed, 22 insertions(+), 14 deletions(-) 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); From 9505e787ddc0f70fc2c5bd782c3c3cb7e92cc53e Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 5 Jan 2023 18:01:58 +0100 Subject: [PATCH 04/99] dynamically alloc memory for file in lua file api --- radio/src/lua/lua_file_api.cpp | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/radio/src/lua/lua_file_api.cpp b/radio/src/lua/lua_file_api.cpp index e559935611c..ecf680e39ba 100644 --- a/radio/src/lua/lua_file_api.cpp +++ b/radio/src/lua/lua_file_api.cpp @@ -44,7 +44,7 @@ struct open_files_t int handle = -1; int pos = 0; int flags = 0; - VfsFile vfs_file; + VfsFile* vfs_file = nullptr; }; static open_files_t open_files[MAX_OPEN_FILES]; @@ -111,6 +111,7 @@ static int remap_vfs_errors(VfsError e) #define FILE_HANDLE_OFFSET (0x20) +#pragma unused static int remap_handle(int fh) { return fh - FILE_HANDLE_OFFSET; @@ -213,7 +214,7 @@ static int _lua_fopen(open_files_t* file, const char* name, const char *mode) return ret; std::string n = normalizeLuaPath(name); - VfsError res = VirtualFS::instance().openFile(file->vfs_file, n, vfsFlags); + VfsError res = VirtualFS::instance().openFile(*file->vfs_file, n, vfsFlags); if(res == VfsError::OK) { set_errno(0); @@ -234,12 +235,17 @@ open_files_t* lua_fopen(const char* name, const char *mode) } int slot = findslot(-1); - - int res = _lua_fopen(&open_files[slot], name, mode); - if(res == 0) + if(slot < MAX_OPEN_FILES) { - set_errno(0); - return &open_files[slot]; + 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; @@ -250,7 +256,9 @@ open_files_t* lua_fopen(const char* name, const char *mode) int lua_fclose(open_files_t* file) { - file->vfs_file.close(); + file->vfs_file->close(); + delete file->vfs_file; + file->vfs_file = nullptr; file->handle = -1; file->pos = 0; file->flags = 0; @@ -259,7 +267,7 @@ int lua_fclose(open_files_t* file) open_files_t *lua_freopen(const char *pathname, const char *mode, open_files_t *stream) { - stream->vfs_file.close(); + stream->vfs_file->close(); stream->pos = 0; stream->flags = 0; int res = _lua_fopen(stream, pathname, mode); @@ -272,7 +280,7 @@ open_files_t *lua_freopen(const char *pathname, const char *mode, open_files_t * int lua_feof(open_files_t *stream) { - return stream->vfs_file.eof(); + return stream->vfs_file->eof(); } int lua_fseek(open_files_t *stream, long offset, int whence) @@ -281,13 +289,13 @@ int lua_fseek(open_files_t *stream, long offset, int whence) switch(whence) { case SEEK_SET: - err = remap_vfs_errors(stream->vfs_file.lseek(offset)); + 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)); + 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)); + err = remap_vfs_errors(stream->vfs_file->lseek(stream->vfs_file->size() - offset)); break; default: err = EINVAL; @@ -307,7 +315,7 @@ 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); + stream->vfs_file->read(ptr, count, readSize); return readSize/size; } @@ -317,21 +325,21 @@ size_t lua_fwrite(const void *ptr, size_t size, size_t nmemb, size_t count = size*nmemb; size_t writtenSize = 0; #if !defined(BOOT) - stream->vfs_file.write(ptr, count, writtenSize); + 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); + 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) + if (stream->vfs_file->read(&c, 1, result) == VfsError::OK && result == 1) return c; else return -1; @@ -340,7 +348,7 @@ char lua_fgetc(open_files_t *stream) int lua_fputs(const char *s, open_files_t *stream) { #if !defined(BOOT) - stream->vfs_file.puts(s); + stream->vfs_file->puts(s); #endif return strlen(s); } From 9873ab773b169a9c99661e7e51a411ad68f1f490 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 5 Jan 2023 18:17:55 +0100 Subject: [PATCH 05/99] fix compiler warnings --- radio/src/VirtualFS.cpp | 26 +++++++++++++++++++++++++- radio/src/VirtualFS.h | 9 ++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index ea290a165aa..d479b7eccf2 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -246,6 +246,7 @@ const char* VfsFileInfo::getName() const #if defined(USE_LITTLEFS) case VfsFileType::LFS: return lfsInfo.name; #endif + default: break; } return ""; }; @@ -261,6 +262,7 @@ size_t VfsFileInfo::getSize() const #if defined(USE_LITTLEFS) case VfsFileType::LFS: return lfsInfo.size; #endif + default: break; } return 0; } @@ -288,6 +290,7 @@ VfsType VfsFileInfo::getType() const else return VfsType::FILE; #endif + default: break; } return VfsType::UNKOWN; }; @@ -308,6 +311,7 @@ VfsFileAttributes VfsFileInfo::getAttrib() return VfsFileAttributes::DIR; return VfsFileAttributes::NONE; #endif + default: break; } return VfsFileAttributes::NONE; } @@ -325,6 +329,7 @@ int VfsFileInfo::getDate(){ case VfsFileType::LFS: return 0; #endif + default: break; } return 0; } @@ -343,6 +348,7 @@ int VfsFileInfo::getTime() case VfsFileType::LFS: return 0; #endif + default: break; } return 0; } @@ -417,6 +423,7 @@ VfsError VfsDir::read(VfsFileInfo& info) return convertResult((lfs_error)res); } #endif + default: break; } return VfsError::INVAL; } @@ -439,6 +446,7 @@ VfsError VfsDir::close() ret = convertResult((lfs_error)lfs_dir_close(lfs.handle, &lfs.dir)); break; #endif + default: break; } clear(); return ret; @@ -467,6 +475,7 @@ VfsError VfsDir::rewind() return convertResult((lfs_error)res); } #endif + default: break; } return VfsError::INVAL; } @@ -495,6 +504,7 @@ VfsError VfsFile::close() ret = convertResult((lfs_error)lfs_file_close(lfs.handle, &lfs.file)); break; #endif + default: break; } clear(); @@ -519,6 +529,7 @@ int VfsFile::size() return res; } #endif + default: break; } return -1; @@ -551,6 +562,7 @@ VfsError VfsFile::read(void* buf, size_t size, size_t& readSize) break; } #endif + default: break; } return VfsError::INVAL; @@ -584,6 +596,7 @@ char* VfsFile::gets(char* buf, size_t maxLen) return nc ? buf : 0; /* When no data read due to EOF or error, return with error. */ } #endif + default: break; } return 0; @@ -617,6 +630,7 @@ VfsError VfsFile::write(const void* buf, size_t size, size_t& written) break; } #endif + default: break; } return VfsError::INVAL; @@ -754,6 +768,7 @@ int VfsFile::fprintf(const char* str, ...) return putc_flush(&pb); #endif + default: break; } #endif } @@ -780,6 +795,7 @@ size_t VfsFile::tell() return ret; } #endif + default: break; } return (size_t)VfsError::INVAL; @@ -804,6 +820,7 @@ VfsError VfsFile::lseek(size_t offset) return VfsError::OK; } #endif + default: break; } return VfsError::INVAL; @@ -821,6 +838,7 @@ int VfsFile::eof() case VfsFileType::LFS: return lfs_file_tell(lfs.handle, &lfs.file) == lfs_file_size(lfs.handle, &lfs.file); #endif + default: break; } return 0; @@ -1208,6 +1226,7 @@ VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) case VfsDir::DIR_FAT: return convertResult(f_opendir(&dir.fat.dir, dirPath.c_str())); #endif + default: break; } return VfsError::INVAL; @@ -1258,9 +1277,10 @@ VfsError VirtualFS::makeDirectory(const std::string& path) return convertResult(res); } } -break; + break; } #endif + default: break; } return VfsError::INVAL; } @@ -1413,6 +1433,7 @@ VfsError VirtualFS::fstat(const std::string& path, VfsFileInfo& fileInfo) fileInfo.type = VfsFileType::LFS; return convertResult((lfs_error)lfs_stat(&lfs, normPath.c_str(), fileInfo.lfsInfo)); #endif + default: break; } return VfsError::INVAL; } @@ -1435,6 +1456,7 @@ VfsError VirtualFS::utime(const std::string& path, const VfsFileInfo& fileInfo) case VfsDir::DIR_LFS: return VfsError::OK; #endif + default: break; } return VfsError::INVAL; } @@ -1471,6 +1493,7 @@ VfsError VirtualFS::openFile(VfsFile& file, const std::string& path, VfsOpenFlag break; } #endif + default: break; } return ret; @@ -1525,6 +1548,7 @@ bool VirtualFS::isFileAvailable(const char * path, bool exclDir) } } #endif + default: break; } return false; diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 69b7021788e..2e0f6217b09 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -188,7 +188,14 @@ extern uint8_t logDelay100ms; // this does not belong here, this is just the way class VirtualFS; enum class VfsType { UNKOWN, DIR, FILE }; -enum class VfsFileType { UNKNOWN, ROOT, FAT, LFS }; +enum class VfsFileType { + UNKNOWN + ,ROOT + ,FAT +#if defined(USE_LITTLEFS) + ,LFS +#endif +}; enum class VfsError { OK = 0, // No error From db20324671e354eb6aea964a948fd02231c32ab8 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 5 Jan 2023 19:30:58 +0100 Subject: [PATCH 06/99] chore: more compiler warning fixes --- radio/src/VirtualFS.cpp | 14 ++++++++++++-- radio/src/VirtualFS.h | 8 ++++++++ radio/src/gui/colorlcd/file_browser.cpp | 2 +- radio/src/lua/interface.cpp | 2 +- radio/src/lua/lua_file_api.cpp | 5 +++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index d479b7eccf2..1d0df4942a9 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -358,7 +358,9 @@ void VfsFileInfo::clear() { #if defined(USE_LITTLEFS) lfsInfo = {0}; #endif +#if defined (USE_FATFS) fatInfo = {0}; +#endif name = nullptr; } @@ -370,7 +372,9 @@ void VfsDir::clear() lfs.dir = {0}; lfs.handle = nullptr; #endif +#if defined (USE_FATFS) fat.dir = {0}; +#endif readIdx = 0; } @@ -486,7 +490,9 @@ void VfsFile::clear() { lfs.file = {0}; lfs.handle = nullptr; #endif +#if defined (USE_FATFS) fat.file = {0}; +#endif } VfsError VfsFile::close() @@ -1024,8 +1030,10 @@ bool VirtualFS::defaultStorageAvailable() #if (DEFAULT_STORAGE == INTERNAL) #if defined (USE_LITTLEFS) return lfsMounted -#else // USE_LITTLEFS +#elif defined (USE_FATFS) return spiFatFs.fs_type != 0; +#else +#error at least one file system must be enabled #endif // USE_LITTLEFS #elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE return sdFatFs.fs_type != 0; @@ -1106,11 +1114,13 @@ VfsDir::DirType VirtualFS::getDirTypeAndPath(std::string& path) #if defined (USE_LITTLEFS) path = path.substr(8); return VfsDir::DIR_LFS; -#else // USE_LITTLEFS +#elif defined (USE_FATFS) path = "1:" + path.substr(8); if(path == "1:") path = "1:/"; return VfsDir::DIR_FAT; +#else +#error no supported filesystem selected #endif // USE_LITTLEFS #elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE path = path.substr(8); diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 2e0f6217b09..7dad4a29414 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -191,7 +191,9 @@ enum class VfsType { UNKOWN, DIR, FILE }; enum class VfsFileType { UNKNOWN ,ROOT +#if defined (USE_FATFS) ,FAT +#endif #if defined(USE_LITTLEFS) ,LFS #endif @@ -280,7 +282,9 @@ struct VfsFileInfo #if defined(USE_LITTLEFS) lfs_info lfsInfo = {0}; #endif +#if defined (USE_FATFS) FILINFO fatInfo; +#endif }; const char* name = nullptr; @@ -312,9 +316,11 @@ struct VfsDir lfs* handle; } lfs; #endif +#if defined (USE_FATFS) struct { DIR dir; } fat; +#endif }; size_t readIdx = 0; @@ -358,9 +364,11 @@ struct VfsFile lfs* handle = nullptr; } lfs; #endif +#if defined (USE_FATFS) struct { FIL file = {0}; } fat; +#endif }; }; diff --git a/radio/src/gui/colorlcd/file_browser.cpp b/radio/src/gui/colorlcd/file_browser.cpp index d65bdb8d1da..d9b4e518e9d 100644 --- a/radio/src/gui/colorlcd/file_browser.cpp +++ b/radio/src/gui/colorlcd/file_browser.cpp @@ -151,7 +151,7 @@ static int scan_files(std::list& files, auto attribs = fno.getAttrib(); // Ignore hidden files - if ((fno.getAttrib() & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) + if ((attribs & (VfsFileAttributes::HID | VfsFileAttributes::SYS)) != VfsFileAttributes::NONE) continue; diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 4ab2928d8cd..178047999e3 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -1189,7 +1189,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) else if (lua_isstring(lsScripts, -1)) { char nextScript[FF_MAX_LFN+1]; const char* luaFile = lua_tostring(lsScripts, -1); - strncpy(nextScript, lua_tostring(lsScripts, -1), FF_MAX_LFN); + strncpy(nextScript, luaFile, FF_MAX_LFN); nextScript[FF_MAX_LFN] = '\0'; luaExec(nextScript); return scriptWasRun; diff --git a/radio/src/lua/lua_file_api.cpp b/radio/src/lua/lua_file_api.cpp index ecf680e39ba..8f083fd948d 100644 --- a/radio/src/lua/lua_file_api.cpp +++ b/radio/src/lua/lua_file_api.cpp @@ -111,11 +111,12 @@ static int remap_vfs_errors(VfsError e) #define FILE_HANDLE_OFFSET (0x20) -#pragma unused +#if 0 static int remap_handle(int fh) { return fh - FILE_HANDLE_OFFSET; } +#endif static int set_errno(int errval) { @@ -139,7 +140,7 @@ static int convertOpenMode(VfsOpenFlags &vfsFlags, const char* mode) } bool update = false; - bool binary = false; + bool binary __attribute__((unused)) = false; if(modeLen>1) { if(mode[1] == '+') From c0582ecca9526b4ed3cd79f84c26e3a4ad120860 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 20 Jan 2023 21:19:17 +0100 Subject: [PATCH 07/99] remove LittleFS compat code, change VirtualFS API to use const char* instead of std::String& in most places --- radio/src/VirtualFS.cpp | 619 ++------------------------- radio/src/VirtualFS.h | 62 +-- radio/src/gui/colorlcd/view_text.cpp | 4 +- radio/src/lua/api_filesystem.cpp | 2 +- radio/src/lua/api_general.cpp | 2 +- radio/src/lua/lua_file_api.cpp | 2 +- 6 files changed, 62 insertions(+), 629 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index 1d0df4942a9..dcb9ba7253e 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -53,112 +53,10 @@ VfsFile g_bluetoothFile = {}; #endif #endif -#if defined(SPI_FLASH) && !defined(USE_LITTLEFS) +#if defined(SPI_FLASH) static FATFS spiFatFs __DMA; #endif -#if defined(USE_LITTLEFS) -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(); -uint16_t flashSpiGetSectorSize(); -uint16_t flashSpiGetSectorCount(); - -int flashSpiErase(size_t address); -int flashSpiBlockErase(size_t address); -void flashSpiEraseAll(); - -void flashSpiSync(); - - -extern "C" -{ - -#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 - -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) -{ - flashSpiSync(); - return LFS_ERR_OK; -} -} - -static VfsError convertResult(lfs_error err) -{ - switch(err) - { - case LFS_ERR_OK: return VfsError::OK; - case LFS_ERR_IO: return VfsError::IO; - case LFS_ERR_CORRUPT: return VfsError::CORRUPT; - case LFS_ERR_NOENT: return VfsError::NOENT; - case LFS_ERR_EXIST: return VfsError::EXIST; - case LFS_ERR_NOTDIR: return VfsError::NOTDIR; - case LFS_ERR_ISDIR: return VfsError::ISDIR; - case LFS_ERR_NOTEMPTY: return VfsError::NOTEMPTY; - case LFS_ERR_BADF: return VfsError::BADF; - case LFS_ERR_FBIG: return VfsError::FBIG; - case LFS_ERR_INVAL: return VfsError::INVAL; - case LFS_ERR_NOSPC: return VfsError::NOSPC; - case LFS_ERR_NOMEM: return VfsError::NOMEM; - case LFS_ERR_NOATTR: return VfsError::NOATTR; - case LFS_ERR_NAMETOOLONG: return VfsError::NAMETOOLONG; - } - return VfsError::INVAL; -} - -static int convertOpenFlagsToLfs(VfsOpenFlags flags) -{ - int lfsFlags = 0; - - if((flags & VfsOpenFlags::READ) != VfsOpenFlags::NONE && (flags & VfsOpenFlags::WRITE) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_RDWR; - else if ((flags & VfsOpenFlags::READ) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_RDONLY; - else if ((flags & VfsOpenFlags::WRITE) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_WRONLY; - - if((flags & VfsOpenFlags::CREATE_NEW) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_CREAT | LFS_O_EXCL; - else if ((flags & VfsOpenFlags::CREATE_ALWAYS) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_CREAT | LFS_O_TRUNC; - else if((flags & VfsOpenFlags::OPEN_ALWAYS) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_CREAT; - else if((flags & VfsOpenFlags::OPEN_APPEND) != VfsOpenFlags::NONE) - lfsFlags |= LFS_O_CREAT | LFS_O_APPEND; - - return lfsFlags; -} -#endif - -#if defined (USE_FATFS) static VfsError convertResult(FRESULT err) { switch(err) @@ -191,7 +89,6 @@ static int convertOpenFlagsToFat(VfsOpenFlags flags) { return (int)flags; } -#endif VfsOpenFlags operator|(VfsOpenFlags lhs,VfsOpenFlags rhs) { @@ -240,12 +137,7 @@ const char* VfsFileInfo::getName() const switch(type) { case VfsFileType::ROOT: return name; -#if defined (USE_FATFS) case VfsFileType::FAT: return(name != nullptr)?name:fatInfo.fname; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: return lfsInfo.name; -#endif default: break; } return ""; @@ -256,12 +148,7 @@ size_t VfsFileInfo::getSize() const switch(type) { case VfsFileType::ROOT: return 0; -#if defined (USE_FATFS) case VfsFileType::FAT: return fatInfo.fsize; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: return lfsInfo.size; -#endif default: break; } return 0; @@ -273,7 +160,6 @@ VfsType VfsFileInfo::getType() const { case VfsFileType::ROOT: return VfsType::DIR; -#if defined (USE_FATFS) case VfsFileType::FAT: if (name != nullptr) return VfsType::DIR; @@ -281,15 +167,6 @@ VfsType VfsFileInfo::getType() const return VfsType::DIR; else return VfsType::FILE; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: - - if(lfsInfo.type == LFS_TYPE_DIR) - return VfsType::DIR; - else - return VfsType::FILE; -#endif default: break; } return VfsType::UNKOWN; @@ -301,16 +178,8 @@ VfsFileAttributes VfsFileInfo::getAttrib() { case VfsFileType::ROOT: return VfsFileAttributes::DIR; -#if defined (USE_FATFS) case VfsFileType::FAT: return (VfsFileAttributes)fatInfo.fattrib; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: - if(lfsInfo.type == LFS_TYPE_DIR) - return VfsFileAttributes::DIR; - return VfsFileAttributes::NONE; -#endif default: break; } return VfsFileAttributes::NONE; @@ -321,14 +190,8 @@ int VfsFileInfo::getDate(){ { case VfsFileType::ROOT: return 0; -#if defined (USE_FATFS) case VfsFileType::FAT: return fatInfo.fdate; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: - return 0; -#endif default: break; } return 0; @@ -340,14 +203,8 @@ int VfsFileInfo::getTime() { case VfsFileType::ROOT: return 0; -#if defined (USE_FATFS) case VfsFileType::FAT: return fatInfo.ftime; -#endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: - return 0; -#endif default: break; } return 0; @@ -355,12 +212,7 @@ int VfsFileInfo::getTime() void VfsFileInfo::clear() { type = VfsFileType::UNKNOWN; -#if defined(USE_LITTLEFS) - lfsInfo = {0}; -#endif -#if defined (USE_FATFS) fatInfo = {0}; -#endif name = nullptr; } @@ -368,13 +220,7 @@ void VfsFileInfo::clear() { void VfsDir::clear() { type = DIR_UNKNOWN; -#if defined(USE_LITTLEFS) - lfs.dir = {0}; - lfs.handle = nullptr; -#endif -#if defined (USE_FATFS) fat.dir = {0}; -#endif readIdx = 0; } @@ -403,7 +249,6 @@ VfsError VfsDir::read(VfsFileInfo& info) #endif // SDCARD readIdx++; return VfsError::OK; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: { info.type = VfsFileType::FAT; @@ -416,17 +261,6 @@ VfsError VfsDir::read(VfsFileInfo& info) VfsError ret = convertResult(f_readdir(&fat.dir, &info.fatInfo)); return ret; } -#endif -#if defined(USE_LITTLEFS) - case VfsDir::DIR_LFS: - { - info.type = VfsFileType::LFS; - int res = lfs_dir_read(lfs.handle, &lfs.dir, &info.lfsInfo); - if(res >= 0) - return VfsError::OK; - return convertResult((lfs_error)res); - } -#endif default: break; } return VfsError::INVAL; @@ -440,16 +274,9 @@ VfsError VfsDir::close() case VfsDir::DIR_ROOT: ret = VfsError::OK; break; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: ret = convertResult(f_closedir(&fat.dir)); break; -#endif -#if defined(USE_LITTLEFS) - case VfsDir::DIR_LFS: - ret = convertResult((lfs_error)lfs_dir_close(lfs.handle, &lfs.dir)); - break; -#endif default: break; } clear(); @@ -463,22 +290,10 @@ VfsError VfsDir::rewind() { case VfsDir::DIR_ROOT: return VfsError::OK; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: { return convertResult(f_readdir(&fat.dir, nullptr)); } -#endif -#if defined(USE_LITTLEFS) - case VfsDir::DIR_LFS: - { - info.type = VfsFileType::LFS; - int res = lfs_dir_read(lfs.handle, &lfs.dir, nullptr); - if(res >= 0) - return VfsError::OK; - return convertResult((lfs_error)res); - } -#endif default: break; } return VfsError::INVAL; @@ -486,13 +301,7 @@ VfsError VfsDir::rewind() void VfsFile::clear() { type = VfsFileType::UNKNOWN; -#if defined(USE_LITTLEFS) - lfs.file = {0}; - lfs.handle = nullptr; -#endif -#if defined (USE_FATFS) fat.file = {0}; -#endif } VfsError VfsFile::close() @@ -500,16 +309,9 @@ VfsError VfsFile::close() VfsError ret = VfsError::INVAL; switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: ret = convertResult(f_close(&fat.file)); break; -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - ret = convertResult((lfs_error)lfs_file_close(lfs.handle, &lfs.file)); - break; -#endif default: break; } @@ -521,20 +323,9 @@ int VfsFile::size() { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: return f_size(&fat.file); break; -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - int res = lfs_file_size(lfs.handle, &lfs.file); - if(res < 0) - return (int)convertResult((lfs_error)res); - return res; - } -#endif default: break; } @@ -545,29 +336,12 @@ VfsError VfsFile::read(void* buf, size_t size, size_t& readSize) { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: { UINT rd = 0; VfsError res = convertResult(f_read(&fat.file, buf, size, &rd)); readSize = rd; return res; } -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - int ret = lfs_file_read(lfs.handle, &lfs.file, buf, size); - if(ret >= 0) - { - readSize = ret; - return VfsError::OK; - } else { - readSize = 0; - return convertResult((lfs_error)ret); - } - break; - } -#endif default: break; } @@ -578,30 +352,8 @@ char* VfsFile::gets(char* buf, size_t maxLen) { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: return f_gets(buf, maxLen, &fat.file); -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - size_t nc = 0; - char* p = buf; - char s[4]; - size_t rc; - char dc; - maxLen -= 1; /* Make a room for the terminator */ - while (nc < maxLen) { - read(s, 1, rc); - if (rc != 1) break; - dc = s[0]; - *p++ = dc; nc++; - if (dc == '\n') break; - } - *p = 0; /* Terminate the string */ - return nc ? buf : 0; /* When no data read due to EOF or error, return with error. */ - } -#endif default: break; } @@ -613,29 +365,12 @@ VfsError VfsFile::write(const void* buf, size_t size, size_t& written) { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: { UINT wrt = 0; VfsError res = convertResult(f_write(&fat.file, buf, size, &wrt)); written = wrt; return res; } -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - int ret = lfs_file_write(lfs.handle, &lfs.file, buf, size); - if(ret >= 0) - { - written = ret; - return VfsError::OK; - } else { - written = 0; - return convertResult((lfs_error)ret); - } - break; - } -#endif default: break; } @@ -672,108 +407,7 @@ int VfsFile::fprintf(const char* str, ...) #if defined(USE_LITTLEFS) case VfsFileType::LFS: { -#if 0 - va_list arp; - putbuff pb; - BYTE f, r; - UINT i, j, w; - DWORD v; - TCHAR c, d, str[32], *p; - - - putc_init(&pb, fp); - - va_start(arp, fmt); - - for (;;) { - c = *fmt++; - if (c == 0) break; /* End of string */ - if (c != '%') { /* Non escape character */ - putc_bfd(&pb, c); - continue; - } - w = f = 0; - c = *fmt++; - if (c == '0') { /* Flag: '0' padding */ - f = 1; c = *fmt++; - } else { - if (c == '-') { /* Flag: left justified */ - f = 2; c = *fmt++; - } - } - if (c == '*') { /* Minimum width by argument */ - w = va_arg(arp, int); - c = *fmt++; - } else { - while (IsDigit(c)) { /* Minimum width */ - w = w * 10 + c - '0'; - c = *fmt++; - } - } - if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ - f |= 4; c = *fmt++; - } - if (c == 0) break; - d = c; - if (IsLower(d)) d -= 0x20; - switch (d) { /* Atgument type is... */ - case 'S' : /* String */ - p = va_arg(arp, TCHAR*); - for (j = 0; p[j]; j++) ; - if (!(f & 2)) { /* Right padded */ - while (j++ < w) putc_bfd(&pb, ' ') ; - } - while (*p) putc_bfd(&pb, *p++) ; /* String body */ - while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ - continue; - - case 'C' : /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; - - case 'B' : /* Unsigned binary */ - r = 2; break; - - case 'O' : /* Unsigned octal */ - r = 8; break; - - case 'D' : /* Signed decimal */ - case 'U' : /* Unsigned decimal */ - r = 10; break; - - case 'X' : /* Unsigned hexdecimal */ - r = 16; break; - - default: /* Unknown type (pass-through) */ - putc_bfd(&pb, c); continue; - } - - /* Get an argument and put it in numeral */ - v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); - if (d == 'D' && (v & 0x80000000)) { - v = 0 - v; - f |= 8; - } - i = 0; - do { - d = (TCHAR)(v % r); v /= r; - if (d > 9) d += (c == 'x') ? 0x27 : 0x07; - str[i++] = d + '0'; - } while (v && i < sizeof str / sizeof *str); - if (f & 8) str[i++] = '-'; - j = i; d = (f & 1) ? '0' : ' '; - if (!(f & 2)) { - while (j++ < w) putc_bfd(&pb, d); /* Right pad */ - } - do { - putc_bfd(&pb, str[--i]); /* Number body */ - } while (i); - while (j++ < w) putc_bfd(&pb, d); /* Left pad */ - } - - va_end(arp); - - return putc_flush(&pb); -#endif + default: break; } #endif @@ -785,47 +419,23 @@ int VfsFile::fprintf(const char* str, ...) size_t VfsFile::tell() { - switch(type) - { -#if defined (USE_FATFS) - case VfsFileType::FAT: - return f_tell(&fat.file); - #endif - #if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - int ret = lfs_file_tell(lfs.handle, &lfs.file); - if(ret < 0) - return (size_t)convertResult((lfs_error)ret); - else - return ret; - } - #endif + switch(type) + { + case VfsFileType::FAT: + return f_tell(&fat.file); default: break; - } + } - return (size_t)VfsError::INVAL; + return (size_t)VfsError::INVAL; } VfsError VfsFile::lseek(size_t offset) { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: return convertResult(f_lseek(&fat.file, offset)); break; -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - { - int ret = lfs_file_seek(lfs.handle, &lfs.file, offset, LFS_SEEK_SET); - if(ret < 0) - return convertResult((lfs_error)ret); - else - return VfsError::OK; - } -#endif default: break; } @@ -836,14 +446,8 @@ int VfsFile::eof() { switch(type) { -#if defined (USE_FATFS) case VfsFileType::FAT: return f_eof(&fat.file); -#endif -#if defined (USE_LITTLEFS) - case VfsFileType::LFS: - return lfs_file_tell(lfs.handle, &lfs.file) == lfs_file_size(lfs.handle, &lfs.file); -#endif default: break; } @@ -863,24 +467,8 @@ const char * VirtualFS::getBasename(const char * path) VirtualFS::VirtualFS() { #if defined (SPI_FLASH) -#if defined (USE_LITTLEFS) - // 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 = 256; - lfsCfg.prog_size = flashSpiGetPageSize(); - lfsCfg.block_size = flashSpiGetSectorSize(); - lfsCfg.block_count = flashSpiGetSectorCount(); - lfsCfg.block_cycles = 500; - lfsCfg.cache_size = flashSpiGetPageSize(); - lfsCfg.lookahead_size = 256; -#else + spiFatFs = {0}; -#endif // USE_LITTLEFS #endif // SPI_FLASH #if defined (SDCARD) sdFatFs = {0}; @@ -892,11 +480,7 @@ VirtualFS::VirtualFS() VirtualFS::~VirtualFS() { #if defined (SPI_FLASH) -#if defined (USE_LITTLEFS) - lfs_unmount(&lfs); -#else // USE_LITTLEFS f_unmount("1:"); -#endif // USE_LITTLEFS #endif // SPI_FLASH } @@ -911,11 +495,7 @@ void VirtualFS::stop() #endif #if defined (SPI_FLASH) -#if defined (USE_LITTLEFS) - lfs_unmount(&lfs); -#else // USE_LITTLEFS f_unmount("1:"); -#endif // USE_LITTLEFS #endif // SPI_FLASH } void VirtualFS::restart() @@ -925,25 +505,6 @@ void VirtualFS::restart() mountSd(); #endif #if defined (SPI_FLASH) -#if defined(USE_LITTLEFS) - // flashSpiEraseAll(); - lfsMounted = true; - int err = lfs_mount(&lfs, &lfsCfg); - if(err) { - flashSpiEraseAll(); - err = lfs_format(&lfs, &lfsCfg); - if(err == LFS_ERR_OK) - err = lfs_mount(&lfs, &lfsCfg); - if(err != LFS_ERR_OK) - { - lfsMounted = false; -#if !defined(BOOT) - POPUP_WARNING(STR_SDCARD_ERROR); -#endif - } - } - lfsCfg.context = this; -#else // USE_LITTLEFS #if !defined(BOOT) diskCache[1].clear(); #endif @@ -987,7 +548,6 @@ void VirtualFS::restart() } #endif } -#endif // USE_LITLEFS #endif // SPI_FLASH #if !defined(BOOT) checkAndCreateDirectory("/DEFAULT/RADIO"); @@ -1028,13 +588,7 @@ bool VirtualFS::defaultStorageAvailable() return true; #endif #if (DEFAULT_STORAGE == INTERNAL) -#if defined (USE_LITTLEFS) - return lfsMounted -#elif defined (USE_FATFS) - return spiFatFs.fs_type != 0; -#else -#error at least one file system must be enabled -#endif // USE_LITTLEFS + return spiFatFs.fs_type != 0; #elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE return sdFatFs.fs_type != 0; #endif @@ -1044,14 +598,7 @@ bool VirtualFS::defaultStorageAvailable() bool VirtualFS::format() { // TODO format -#if defined (USE_LITTLEFS) - - flashSpiEraseAll(); - lfs_format(&lfs, &lfsCfg); - return true; -#else return false; -#endif // BYTE work[FF_MAX_SS]; // FRESULT res = f_mkfs("", FM_FAT32, 0, work, sizeof(work)); // switch(res) { @@ -1090,17 +637,12 @@ VfsDir::DirType VirtualFS::getDirTypeAndPath(std::string& path) #if defined (SPI_FLASH) } else if(path.substr(0, 9) == "/INTERNAL") { -#if defined (USE_LITTLEFS) - path = path.substr(6); - return VfsDir::DIR_LFS; -#else // USE_LITTLEFS path = "1:" + path.substr(9); if(path == "1:") path = "1:/"; else if (path == "") path = "/"; return VfsDir::DIR_FAT; -#endif // USE_LITTLEFS #endif // SPI_FLASH #if defined (SDCARD) } else if(path.substr(0, 7) == "/SDCARD") { @@ -1111,17 +653,10 @@ VfsDir::DirType VirtualFS::getDirTypeAndPath(std::string& path) #endif } else if(path.substr(0, 8) == "/DEFAULT") { #if (DEFAULT_STORAGE == INTERNAL) -#if defined (USE_LITTLEFS) - path = path.substr(8); - return VfsDir::DIR_LFS; -#elif defined (USE_FATFS) path = "1:" + path.substr(8); if(path == "1:") path = "1:/"; return VfsDir::DIR_FAT; -#else -#error no supported filesystem selected -#endif // USE_LITTLEFS #elif (DEFAULT_STORAGE == SDCARD) // DEFAULT_STORAGE path = path.substr(8); return VfsDir::DIR_FAT; @@ -1173,7 +708,7 @@ void VirtualFS::normalizePath(std::string& path) path = "/"; } #if !defined(BOOT) -VfsError VirtualFS::unlink(const std::string& path) +VfsError VirtualFS::unlink(const char* path) { std::string p = path; normalizePath(p); @@ -1183,22 +718,18 @@ VfsError VirtualFS::unlink(const std::string& path) { case VfsDir::DIR_ROOT: return VfsError::INVAL; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: return convertResult(f_unlink(p.c_str())); -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - return convertResult((lfs_error)lfs_remove(&lfs, p.c_str())); -#endif } return VfsError::INVAL; } #endif -VfsError VirtualFS::changeDirectory(const std::string& path) +VfsError VirtualFS::changeDirectory(const char* path) { - if(path.length() == 0) + if(path == nullptr) + return VfsError::INVAL; + if(strlen(path) == 0) return VfsError::INVAL; std::string newWorkDir = path; @@ -1227,22 +758,15 @@ VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) { case VfsDir::DIR_ROOT: return VfsError::OK; -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - dir.lfs.handle = &lfs; - return convertResult((lfs_error)lfs_dir_open(&lfs, &dir.lfs.dir, dirPath.c_str())); -#endif -#if defined (USE_FATFS) case VfsDir::DIR_FAT: return convertResult(f_opendir(&dir.fat.dir, dirPath.c_str())); -#endif default: break; } return VfsError::INVAL; } #if !defined(BOOT) -VfsError VirtualFS::makeDirectory(const std::string& path) +VfsError VirtualFS::makeDirectory(const char* path) { std::string normPath(path); normalizePath(normPath); @@ -1253,7 +777,6 @@ VfsError VirtualFS::makeDirectory(const std::string& path) case VfsDir::DIR_ROOT: return VfsError::INVAL; break; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: { DIR dir; @@ -1269,27 +792,6 @@ VfsError VirtualFS::makeDirectory(const std::string& path) } break; } -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - { - lfs_dir_t dir; - lfs_error res = (lfs_error)lfs_dir_open(&lfs, &dir, normPath.c_str()); - if(res == LFS_ERR_OK) - { - lfs_dir_close(&lfs, &dir); - return VfsError::OK; - } else { - if(res == LFS_ERR_NOENT) - res = (lfs_error)lfs_mkdir(&lfs, normPath.c_str()); - if(res != LFS_ERR_OK) - { - return convertResult(res); - } - } - break; - } -#endif default: break; } return VfsError::INVAL; @@ -1311,14 +813,8 @@ VfsError VirtualFS::rename(const char* oldPath, const char* newPath) { case VfsDir::DIR_ROOT: return VfsError::INVAL; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: return convertResult(f_rename(oldP.c_str(), newP.c_str())); -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - return convertResult((lfs_error)lfs_rename(&lfs, oldP.c_str(), newP.c_str())); -#endif } } else { VfsError err = copyFile(oldPath, newPath); @@ -1329,7 +825,7 @@ VfsError VirtualFS::rename(const char* oldPath, const char* newPath) return VfsError::INVAL; } -VfsError VirtualFS::copyFile(const std::string& source, const std::string& destination) +VfsError VirtualFS::copyFile(const char* source, const char* destination) { VfsFile src; VfsFile dest; @@ -1371,14 +867,14 @@ VfsError VirtualFS::copyFile(const std::string& source, const std::string& desti return err; } -VfsError VirtualFS::copyFile(const std::string& srcFile, const std::string& srcDir, - const std::string& destDir, const std::string& destFile) +VfsError VirtualFS::copyFile(const char* srcFile, const char* srcDir, + const char* destDir, const char* destFile) { - return copyFile(srcDir +"/" + srcFile, destDir + "/" + destFile); + return copyFile((std::string(srcDir) +"/" + srcFile).c_str(), (std::string(destDir) + "/" + destFile).c_str()); } // Will overwrite if destination exists -const char * VirtualFS::moveFile(const std::string& srcPath, const std::string& destPath) +const char * VirtualFS::moveFile(const char* srcPath, const char* destPath) { auto res = copyFile(srcPath, destPath); @@ -1394,7 +890,7 @@ const char * VirtualFS::moveFile(const std::string& srcPath, const std::string& } // Will overwrite if destination exists -const char * VirtualFS::moveFile(const std::string& srcFilename, const std::string& srcDir, const std::string& destFilename, const std::string& destDir) +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) { @@ -1402,9 +898,9 @@ const char * VirtualFS::moveFile(const std::string& srcFilename, const std::stri } char srcPath[2*CLIPBOARD_PATH_LEN+1] = { 0 }; - char * tmp = strAppend(srcPath, srcDir.c_str(), CLIPBOARD_PATH_LEN); + char * tmp = strAppend(srcPath, srcDir, CLIPBOARD_PATH_LEN); *tmp++ = '/'; - strAppend(tmp, srcFilename.c_str(), CLIPBOARD_PATH_LEN); + strAppend(tmp, srcFilename, CLIPBOARD_PATH_LEN); res = unlink(srcPath); if(res != VfsError::OK) { return STORAGE_ERROR(res); @@ -1423,8 +919,14 @@ uint32_t sdGetNoSectors() } #endif -VfsError VirtualFS::fstat(const std::string& path, VfsFileInfo& fileInfo) +VfsError VirtualFS::fstat(const char* path, VfsFileInfo& fileInfo) { + if(path == nullptr) + return VfsError::INVAL; + + if(path[0] == 0) + return VfsError::INVAL; + std::string normPath(path); normalizePath(normPath); VfsDir::DirType dirType = getDirTypeAndPath(normPath); @@ -1433,23 +935,22 @@ VfsError VirtualFS::fstat(const std::string& path, VfsFileInfo& fileInfo) { case VfsDir::DIR_ROOT: return VfsError::INVAL; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: fileInfo.type = VfsFileType::FAT; return convertResult(f_stat(normPath.c_str(), &fileInfo.fatInfo)); -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - fileInfo.type = VfsFileType::LFS; - return convertResult((lfs_error)lfs_stat(&lfs, normPath.c_str(), fileInfo.lfsInfo)); -#endif default: break; } return VfsError::INVAL; } #if !defined(BOOT) -VfsError VirtualFS::utime(const std::string& path, const VfsFileInfo& fileInfo) +VfsError VirtualFS::utime(const char* path, const VfsFileInfo& fileInfo) { + if(path == nullptr) + return VfsError::INVAL; + + if(path[0] == 0) + return VfsError::INVAL; + std::string normPath(path); normalizePath(normPath); VfsDir::DirType dirType = getDirTypeAndPath(normPath); @@ -1458,21 +959,21 @@ VfsError VirtualFS::utime(const std::string& path, const VfsFileInfo& fileInfo) { case VfsDir::DIR_ROOT: return VfsError::INVAL; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: return convertResult(f_utime(normPath.c_str(), &fileInfo.fatInfo)); -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - return VfsError::OK; -#endif default: break; } return VfsError::INVAL; } #endif -VfsError VirtualFS::openFile(VfsFile& file, const std::string& path, VfsOpenFlags flags) +VfsError VirtualFS::openFile(VfsFile& file, const char* path, VfsOpenFlags flags) { + if(path == nullptr) + return VfsError::INVAL; + + if(path[0] == 0) + return VfsError::INVAL; + file.clear(); std::string normPath(path); normalizePath(normPath); @@ -1484,7 +985,6 @@ VfsError VirtualFS::openFile(VfsFile& file, const std::string& path, VfsOpenFlag case VfsDir::DIR_ROOT: return VfsError::INVAL; break; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: { file.type = VfsFileType::FAT; @@ -1492,17 +992,6 @@ VfsError VirtualFS::openFile(VfsFile& file, const std::string& path, VfsOpenFlag convertOpenFlagsToFat(flags))); break; } -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - { - file.type = VfsFileType::LFS; - file.lfs.handle = &lfs; - ret = convertResult((lfs_error)lfs_file_open(&lfs, &file.lfs.file, normPath.c_str(), - convertOpenFlagsToLfs(flags))); - break; - } -#endif default: break; } @@ -1532,7 +1021,6 @@ bool VirtualFS::isFileAvailable(const char * path, bool exclDir) case VfsDir::DIR_ROOT: return false; break; -#if defined (USE_FATFS) case VfsDir::DIR_FAT: { if (exclDir) { @@ -1541,23 +1029,6 @@ bool VirtualFS::isFileAvailable(const char * path, bool exclDir) } return f_stat(p.c_str(), nullptr) == FR_OK; } -#endif -#if defined (USE_LITTLEFS) - case VfsDir::DIR_LFS: - { - lfs_file_t file; - int res = lfs_file_open(&lfs, &file, p.c_str(), LFS_O_RDONLY); - if(res != LFS_ERR_OK) - { - if(res == LFS_ERR_ISDIR) - return(!exclDir); - return false; - } else { - lfs_file_close(&lfs, &file); - return true; - } - } -#endif default: break; } diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 7dad4a29414..21daa1d67d5 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -23,14 +23,8 @@ #include -#if defined (SPI_FLASH) -#if defined (USE_LITTLEFS) -#include "littlefs_v2.4.1/lfs.h" -#else #include "tjftl/tjftl.h" #include "FatFs/ff.h" -#endif -#endif #if defined (SDCARD) #include "sdcard.h" #endif @@ -191,12 +185,7 @@ enum class VfsType { UNKOWN, DIR, FILE }; enum class VfsFileType { UNKNOWN ,ROOT -#if defined (USE_FATFS) ,FAT -#endif -#if defined(USE_LITTLEFS) - ,LFS -#endif }; enum class VfsError { @@ -279,12 +268,7 @@ struct VfsFileInfo VfsFileType type = VfsFileType::UNKNOWN; union { -#if defined(USE_LITTLEFS) - lfs_info lfsInfo = {0}; -#endif -#if defined (USE_FATFS) FILINFO fatInfo; -#endif }; const char* name = nullptr; @@ -310,17 +294,9 @@ struct VfsDir DirType type = DIR_UNKNOWN; union { -#if defined(USE_LITTLEFS) - struct { - lfs_dir_t dir; - lfs* handle; - } lfs; -#endif -#if defined (USE_FATFS) struct { DIR dir; } fat; -#endif }; size_t readIdx = 0; @@ -358,17 +334,9 @@ struct VfsFile VfsFileType type = VfsFileType::UNKNOWN; union { -#if defined(USE_LITTLEFS) - struct { - lfs_file file = {0}; - lfs* handle = nullptr; - } lfs; -#endif -#if defined (USE_FATFS) struct { FIL file = {0}; } fat; -#endif }; }; @@ -415,24 +383,24 @@ class VirtualFS #endif const std::string& getCurWorkDir() const { return curWorkDir;} #if !defined(BOOT) - VfsError unlink(const std::string& path); + VfsError unlink(const char* path); #endif // !BOOT - VfsError changeDirectory(const std::string& path); + VfsError changeDirectory(const char* path); VfsError openDirectory(VfsDir& dir, const char * path); - VfsError makeDirectory(const std::string& path); + VfsError makeDirectory(const char* path); - VfsError fstat(const std::string& path, VfsFileInfo& fileInfo); - VfsError utime(const std::string& path, const VfsFileInfo& fileInfo); - VfsError openFile(VfsFile& file, const std::string& path, VfsOpenFlags flags); + 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 std::string& source, const std::string& destination); - VfsError copyFile(const std::string& srcFile, const std::string& srcDir, - const std::string& destDir, const std::string& destFile); - const char * moveFile(const std::string& source, const std::string& destination); - const char * moveFile(const std::string& srcFile, const std::string& srcDir, - const std::string& destDir, const std::string& destFile); + 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(); @@ -451,12 +419,6 @@ class VirtualFS static VirtualFS* _instance; -#if defined(USE_LITTLEFS) - lfs_config lfsCfg = {0}; - lfs_t lfs = {0}; - bool lfsMounted = false; -#endif - std::string curWorkDir = "/"; void normalizePath(std::string &path); diff --git a/radio/src/gui/colorlcd/view_text.cpp b/radio/src/gui/colorlcd/view_text.cpp index 3c000213eb5..56249b88c9f 100644 --- a/radio/src/gui/colorlcd/view_text.cpp +++ b/radio/src/gui/colorlcd/view_text.cpp @@ -50,7 +50,7 @@ void ViewTextWindow::buildBody(Window *window) } VirtualFS &vfs = VirtualFS::instance(); - auto res = vfs.fstat(fullPath, info); + auto res = vfs.fstat(fullPath.c_str(), info); if (res == VfsError::OK) { int fsize = int(info.getSize()); fileLength = int(fsize); @@ -97,7 +97,7 @@ VfsError ViewTextWindow::sdReadTextFileBlock(const uint32_t bufSize, char escape_chars[4]; int escape = 0; - auto res = vfs.openFile(file, fullPath, VfsOpenFlags::OPEN_EXISTING | VfsOpenFlags::READ); + 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) { diff --git a/radio/src/lua/api_filesystem.cpp b/radio/src/lua/api_filesystem.cpp index 56ed7121607..8ea46b7f510 100644 --- a/radio/src/lua/api_filesystem.cpp +++ b/radio/src/lua/api_filesystem.cpp @@ -133,7 +133,7 @@ int luaFstat(lua_State* L) VfsFileInfo info; std::string p = normalizeLuaPath(path); - res = vfs.fstat(p, info); + res = vfs.fstat(p.c_str(), info); if (res != VfsError::OK) { printf("luaFstat cannot open %s\n", path); return 0; diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index b532ee2ece2..3da534210d9 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1998,7 +1998,7 @@ static int luaChdir(lua_State * L) dir = directory; } - VirtualFS::instance().changeDirectory(dir); + VirtualFS::instance().changeDirectory(dir.c_str()); return 0; } diff --git a/radio/src/lua/lua_file_api.cpp b/radio/src/lua/lua_file_api.cpp index 8f083fd948d..de1345b2bbe 100644 --- a/radio/src/lua/lua_file_api.cpp +++ b/radio/src/lua/lua_file_api.cpp @@ -215,7 +215,7 @@ static int _lua_fopen(open_files_t* file, const char* name, const char *mode) return ret; std::string n = normalizeLuaPath(name); - VfsError res = VirtualFS::instance().openFile(*file->vfs_file, n, vfsFlags); + VfsError res = VirtualFS::instance().openFile(*file->vfs_file, n.c_str(), vfsFlags); if(res == VfsError::OK) { set_errno(0); From 0f796d5faeba23fb5df5a5a52b3be10d39f0b25a Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sat, 21 Jan 2023 20:15:39 +0100 Subject: [PATCH 08/99] use only char* and const char* for VirtualFS to save space added ARM-BKPT instruction to assert --- radio/src/VirtualFS.cpp | 255 ++++++++++++++++---------- radio/src/VirtualFS.h | 4 +- radio/src/edgetx_assert.h | 32 ++++ radio/src/gui/128x64/lcd.cpp | 1 + radio/src/gui/212x64/lcd.cpp | 5 +- radio/src/gui/common/stdlcd/menus.cpp | 1 + radio/src/mixer.cpp | 1 + radio/src/opentx.h | 1 - radio/src/storage/rlc.cpp | 2 +- 9 files changed, 202 insertions(+), 100 deletions(-) create mode 100644 radio/src/edgetx_assert.h diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index dcb9ba7253e..fe0704740aa 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -29,6 +29,7 @@ #if !defined(BOOT) #include "opentx.h" #endif +#include "edgetx_assert.h" #include "VirtualFS.h" #include "board.h" #include "audio.h" @@ -57,6 +58,11 @@ VfsFile g_bluetoothFile = {}; static FATFS spiFatFs __DMA; #endif + +#if !defined(CLIPBOARD_PATH_LEN) +#define CLIPBOARD_PATH_LEN 32 +#endif + static VfsError convertResult(FRESULT err) { switch(err) @@ -629,36 +635,50 @@ bool VirtualFS::format() } #endif -VfsDir::DirType VirtualFS::getDirTypeAndPath(std::string& path) +VfsDir::DirType VirtualFS::getDirTypeAndPath(char* path) { - if(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(path.substr(0, 9) == "/INTERNAL") + } else if(strncmp("/INTERNAL", path, 9) == 0) { - path = "1:" + path.substr(9); - if(path == "1:") - path = "1:/"; - else if (path == "") - path = "/"; + 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(path.substr(0, 7) == "/SDCARD") { - path = path.substr(7); - if (path == "") - path = "/"; + } 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(path.substr(0, 8) == "/DEFAULT") { + } else if(strncmp("/DEFAULT", path, 8) == 0) { #if (DEFAULT_STORAGE == INTERNAL) - path = "1:" + path.substr(8); - if(path == "1:") - path = "1:/"; + 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 - path = path.substr(8); + 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 @@ -667,50 +687,67 @@ VfsDir::DirType VirtualFS::getDirTypeAndPath(std::string& path) return VfsDir::DIR_UNKNOWN; } -void VirtualFS::normalizePath(std::string& path) +void VirtualFS::normalizePath(char* path) { - std::vector tokens; - size_t oldpos = 0; + char buf[2 * CLIPBOARD_PATH_LEN+1]; + char* wPtr = buf; if(path[0] != '/') - path = curWorkDir + PATH_SEPARATOR + path; - - while (1) { - size_t newpos = path.find_first_of(PATH_SEPARATOR, oldpos); - if(newpos == std::string::npos) - { - std::string elem = path.substr(oldpos); - if(elem == "..") - tokens.pop_back(); - else if (elem == ".") - ; - else - tokens.push_back(path.substr(oldpos)); - break; - } - tokens.push_back(path.substr(oldpos,newpos-oldpos)); - oldpos=newpos+1; + wPtr = strAppend(wPtr, curWorkDir.c_str(), curWorkDir.length()); + *wPtr++ = PATH_SEPARATOR[0]; } - if(tokens.empty()) - return; + wPtr = strAppend(wPtr, path, std::min(strlen(path), sizeof(buf) - (wPtr - buf))); + buf[sizeof(buf)-1] = '\0'; + char* tokens[20]; + size_t tokenCount = 0; - path = ""; - for(auto token: tokens) + char* cur = strtok(buf, "/" ); + while(cur) { - if(token.length() == 0) + char* old = cur; + cur = strtok(nullptr, "/"); + if(old[0] == 0) + continue; + if(strcmp("..", old) == 0) + { + if(tokenCount > 0) + tokenCount--; continue; - path += PATH_SEPARATOR; - path += token; + } + 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(path.length() == 0) - path = "/"; } + #if !defined(BOOT) VfsError VirtualFS::unlink(const char* path) { - std::string p = 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); @@ -719,22 +756,22 @@ VfsError VirtualFS::unlink(const char* path) case VfsDir::DIR_ROOT: return VfsError::INVAL; case VfsDir::DIR_FAT: - return convertResult(f_unlink(p.c_str())); + return convertResult(f_unlink(p)); } return VfsError::INVAL; } #endif + VfsError VirtualFS::changeDirectory(const char* path) { - if(path == nullptr) - return VfsError::INVAL; - if(strlen(path) == 0) - return VfsError::INVAL; + assert(path != nullptr); - std::string newWorkDir = path; - normalizePath(newWorkDir); - curWorkDir = newWorkDir; + char p[CLIPBOARD_PATH_LEN + 1]; + strncpy(p, path, sizeof(p)); + p[sizeof(p)-1] = '\0'; + normalizePath(p); + curWorkDir = p; return VfsError::OK; } @@ -742,13 +779,11 @@ VfsError VirtualFS::changeDirectory(const char* path) VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) { dir.clear(); - if(path == nullptr) - return VfsError::INVAL; + assert(path != nullptr); - if(path[0] == 0) - return VfsError::INVAL; - - std::string dirPath(path); + char dirPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(dirPath, path, sizeof(dirPath)); + dirPath[sizeof(dirPath)-1] = '\0'; normalizePath(dirPath); @@ -759,7 +794,7 @@ VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) case VfsDir::DIR_ROOT: return VfsError::OK; case VfsDir::DIR_FAT: - return convertResult(f_opendir(&dir.fat.dir, dirPath.c_str())); + return convertResult(f_opendir(&dir.fat.dir, dirPath)); default: break; } @@ -768,7 +803,12 @@ VfsError VirtualFS::openDirectory(VfsDir& dir, const char * path) #if !defined(BOOT) VfsError VirtualFS::makeDirectory(const char* path) { - std::string normPath(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); @@ -780,13 +820,13 @@ VfsError VirtualFS::makeDirectory(const char* path) case VfsDir::DIR_FAT: { DIR dir; - FRESULT result = f_opendir(&dir, normPath.c_str()); + 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.c_str()); + result = f_mkdir(normPath); if (result != FR_OK) return convertResult(result); } @@ -798,8 +838,15 @@ VfsError VirtualFS::makeDirectory(const char* path) } VfsError VirtualFS::rename(const char* oldPath, const char* newPath) { - std::string oldP = oldPath; - std::string newP = 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); @@ -814,7 +861,7 @@ VfsError VirtualFS::rename(const char* oldPath, const char* newPath) case VfsDir::DIR_ROOT: return VfsError::INVAL; case VfsDir::DIR_FAT: - return convertResult(f_rename(oldP.c_str(), newP.c_str())); + return convertResult(f_rename(oldP, newP)); } } else { VfsError err = copyFile(oldPath, newPath); @@ -870,12 +917,29 @@ VfsError VirtualFS::copyFile(const char* source, const char* destination) VfsError VirtualFS::copyFile(const char* srcFile, const char* srcDir, const char* destDir, const char* destFile) { - return copyFile((std::string(srcDir) +"/" + srcFile).c_str(), (std::string(destDir) + "/" + destFile).c_str()); + 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) { @@ -921,13 +985,12 @@ uint32_t sdGetNoSectors() #endif VfsError VirtualFS::fstat(const char* path, VfsFileInfo& fileInfo) { - if(path == nullptr) - return VfsError::INVAL; + assert(path != nullptr); - if(path[0] == 0) - return VfsError::INVAL; + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; - std::string normPath(path); normalizePath(normPath); VfsDir::DirType dirType = getDirTypeAndPath(normPath); @@ -937,7 +1000,7 @@ VfsError VirtualFS::fstat(const char* path, VfsFileInfo& fileInfo) return VfsError::INVAL; case VfsDir::DIR_FAT: fileInfo.type = VfsFileType::FAT; - return convertResult(f_stat(normPath.c_str(), &fileInfo.fatInfo)); + return convertResult(f_stat(normPath, &fileInfo.fatInfo)); default: break; } return VfsError::INVAL; @@ -945,13 +1008,12 @@ VfsError VirtualFS::fstat(const char* path, VfsFileInfo& fileInfo) #if !defined(BOOT) VfsError VirtualFS::utime(const char* path, const VfsFileInfo& fileInfo) { - if(path == nullptr) - return VfsError::INVAL; + assert(path != nullptr); - if(path[0] == 0) - return VfsError::INVAL; + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; - std::string normPath(path); normalizePath(normPath); VfsDir::DirType dirType = getDirTypeAndPath(normPath); @@ -960,7 +1022,7 @@ VfsError VirtualFS::utime(const char* path, const VfsFileInfo& fileInfo) case VfsDir::DIR_ROOT: return VfsError::INVAL; case VfsDir::DIR_FAT: - return convertResult(f_utime(normPath.c_str(), &fileInfo.fatInfo)); + return convertResult(f_utime(normPath, &fileInfo.fatInfo)); default: break; } return VfsError::INVAL; @@ -968,14 +1030,12 @@ VfsError VirtualFS::utime(const char* path, const VfsFileInfo& fileInfo) #endif VfsError VirtualFS::openFile(VfsFile& file, const char* path, VfsOpenFlags flags) { - if(path == nullptr) - return VfsError::INVAL; - - if(path[0] == 0) - return VfsError::INVAL; + assert(path != nullptr); file.clear(); - std::string normPath(path); + char normPath[CLIPBOARD_PATH_LEN + 1]; + strncpy(normPath, path, sizeof(normPath)); + normPath[sizeof(normPath)-1] = '\0'; normalizePath(normPath); VfsDir::DirType dirType = getDirTypeAndPath(normPath); @@ -988,8 +1048,7 @@ VfsError VirtualFS::openFile(VfsFile& file, const char* path, VfsOpenFlags flags case VfsDir::DIR_FAT: { file.type = VfsFileType::FAT; - ret = convertResult(f_open(&file.fat.file, normPath.c_str(), - convertOpenFlagsToFat(flags))); + ret = convertResult(f_open(&file.fat.file, normPath, convertOpenFlagsToFat(flags))); break; } default: break; @@ -1013,8 +1072,13 @@ const char* VirtualFS::checkAndCreateDirectory(const char * path) bool VirtualFS::isFileAvailable(const char * path, bool exclDir) { - std::string p = path; - VfsDir::DirType dirType = getDirTypeAndPath(p); + 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) { @@ -1025,9 +1089,9 @@ bool VirtualFS::isFileAvailable(const char * path, bool exclDir) { if (exclDir) { FILINFO fno; - return (f_stat(p.c_str(), &fno) == FR_OK && !(fno.fattrib & AM_DIR)); + return (f_stat(normPath, &fno) == FR_OK && !(fno.fattrib & AM_DIR)); } - return f_stat(p.c_str(), nullptr) == FR_OK; + return f_stat(normPath, nullptr) == FR_OK; } default: break; } @@ -1037,6 +1101,8 @@ bool VirtualFS::isFileAvailable(const char * path, bool exclDir) 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); @@ -1072,6 +1138,9 @@ const char * VirtualFS::getFileExtension(const char * filename, uint8_t size, ui */ 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; diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 21daa1d67d5..29a0657b4e7 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -421,9 +421,9 @@ class VirtualFS std::string curWorkDir = "/"; - void normalizePath(std::string &path); + void normalizePath(char* path); - VfsDir::DirType getDirTypeAndPath(std::string& path); + VfsDir::DirType getDirTypeAndPath(char* path); void startLogs(); void stopLogs(); 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/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/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/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/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/opentx.h b/radio/src/opentx.h index 704bb87f679..6f1b76ed27b 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -314,7 +314,6 @@ struct CustomFunctionsContext { #include "gui.h" #if !defined(SIMU) - #define assert(x) #if !defined(DEBUG) #define printf printf_not_allowed #endif 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" From 6956306cd728a14af6851d2871ed71252943336c Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 22 Jan 2023 11:04:46 +0100 Subject: [PATCH 09/99] fix storing compiled lua scripts --- radio/src/VirtualFS.cpp | 1 - radio/src/lua/interface.cpp | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index fe0704740aa..46a500fb395 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -567,7 +567,6 @@ void VirtualFS::restart() void VirtualFS::mountSd() { - TRACE("VirtualFS::mountSd"); #if defined(SDCARD) if(sdCardMounted()) return; diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 178047999e3..4a054ef10ad 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -374,13 +374,14 @@ static void luaDumpState(lua_State * L, const char * filename, const VfsFileInfo { VfsFile D; VirtualFS& vfs = VirtualFS::instance(); - if (vfs.openFile(D, filename, VfsOpenFlags::WRITE | VfsOpenFlags::CREATE_ALWAYS) == VfsError::OK) { + 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 (D.close() == VfsError::OK) { if (finfo != nullptr) - vfs.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 From df04c590a893f5d4de4a2d468ebb2609e8172eaa Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 22 Jan 2023 17:50:07 +0100 Subject: [PATCH 10/99] fix B&W bootloader --- radio/src/VirtualFS.cpp | 6 ------ radio/src/VirtualFS.h | 7 +++++-- .../src/targets/common/arm/stm32/bootloader/bin_files.cpp | 5 +---- radio/src/targets/taranis/bootloader/boot_menu.cpp | 3 ++- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index 46a500fb395..cc143e25c44 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -410,14 +410,8 @@ int VfsFile::fprintf(const char* str, ...) return ret; } #endif -#if defined(USE_LITTLEFS) - case VfsFileType::LFS: - { - default: break; } -#endif - } return (int)VfsError::INVAL; } diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 29a0657b4e7..640c30db428 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -83,7 +83,11 @@ constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the do #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 @@ -99,7 +103,6 @@ constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the do #define SYSTEM_SUBDIR "SYSTEM" #define BITMAPS_PATH ROOT_PATH "IMAGES" #define FIRMWARES_PATH ROOT_PATH "FIRMWARE" -#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" @@ -288,7 +291,7 @@ struct VfsDir friend class VirtualFS; VfsDir(const VfsDir&); - enum DirType {DIR_UNKNOWN, DIR_ROOT, DIR_FAT, DIR_LFS}; + enum DirType {DIR_UNKNOWN, DIR_ROOT, DIR_FAT}; void clear(); 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 6e82656028d..32b483fe40d 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/bin_files.cpp @@ -46,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) diff --git a/radio/src/targets/taranis/bootloader/boot_menu.cpp b/radio/src/targets/taranis/bootloader/boot_menu.cpp index 56ff5d4ba7b..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) From 4081613d1e3b91cd0e8d56797f3ec1bdd15153d6 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 19 Jul 2023 04:56:41 +0800 Subject: [PATCH 11/99] Fixed problematic fprintf that will output garbage characters. --- radio/src/VirtualFS.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/VirtualFS.cpp b/radio/src/VirtualFS.cpp index cc143e25c44..c0155ff2051 100644 --- a/radio/src/VirtualFS.cpp +++ b/radio/src/VirtualFS.cpp @@ -402,12 +402,15 @@ int VfsFile::fprintf(const char* str, ...) #if defined (USE_FATFS) case VfsFileType::FAT: { + char line[256]; va_list args; va_start(args, str); - - int ret = f_printf(&fat.file, str, args); + vsprintf(line, str, args); va_end(args); - return ret; + + size_t written; + this->write(line, strlen(line), written); + return written; } #endif default: break; From 00f29a2eb199c64b72db331d1ca7fee885734196 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 11 May 2023 17:43:16 +0800 Subject: [PATCH 12/99] Added flash chip MX25L256. --- .../common/arm/stm32/diskio_sdio_spiflash.cpp | 9 ++- .../common/arm/stm32/flash_spi_driver.cpp | 60 +++++++++++++++---- radio/src/thirdparty/tjftl/tjftl.c | 2 +- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index 81974ce72c8..29e7703ba40 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -50,8 +50,7 @@ extern volatile uint32_t ReadStatus; 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(); -uint16_t flashSpiGetSectorSize(); -uint16_t flashSpiGetSectorCount(); +size_t flashSpiGetSize(); int flashSpiErase(size_t address); int flashSpiBlockErase(size_t address); @@ -107,11 +106,11 @@ DSTATUS disk_initialize ( if(!tjftl_detect(flashRead, nullptr)) flashSpiEraseAll(); - size_t flashSize = flashSpiGetSectorSize()*flashSpiGetSectorCount(); - // tjftl requires at least 10 free blocks after garbage collection. + size_t flashSize = flashSpiGetSize(); + // tjftl requires at least 10 free blocks after garbage collection. Give it 16 free blocks. // To ensure a working tjftl the fuilesystem must be at least 10 blocks smaller than the flash memory. // the block and sector sizes used by tjftl are fixed. A block has 32k bytes and a sector has 512 bytes - tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize/512)-((32768/512)*10), 0); + tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize - 32768 * 16)/512, 0); if(tjftl == nullptr) stat |= STA_NOINIT; diff --git a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp index 31e27e48d9a..fd4a6b6100d 100644 --- a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -70,6 +70,7 @@ struct SpiFlashDescriptor uint8_t eraseSectorCmd; uint8_t eraseBlockCmd; uint8_t eraseChipCmd; + bool use4BytesAddress; }; // * RadioMaster/Eachine TX16S, RadioKing TX18S and Jumper T18 use GD25Q127C (16 MByte) @@ -77,35 +78,53 @@ struct SpiFlashDescriptor 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 = 65536, - .blockCount = 256, + .blockSize = 32768, + .blockCount = 512, .readStatusCmd = 0x05, .readCmd = 0x03, .writeCmd = 0x02, .writeEnableCmd = 0x06, - .eraseSectorCmd = 0x20, - .eraseBlockCmd = 0x52, - .eraseChipCmd = 0x60 + .eraseSectorCmd = 0x20, // 4k block erase command + .eraseBlockCmd = 0x52, // 32k block erase command + .eraseChipCmd = 0x60, + .use4BytesAddress = false }, { // W25Q64JV .id = 0xEF16, .pageSize = 256, .sectorSize = 4096, - .blockSize = 65536, - .blockCount = 128, + .blockSize = 32768, + .blockCount = 256, .readStatusCmd = 0x05, .readCmd = 0x03, .writeCmd = 0x02, .writeEnableCmd = 0x06, - .eraseSectorCmd = 0x20, - .eraseBlockCmd = 0x52, - .eraseChipCmd = 0xC7 + .eraseSectorCmd = 0x20, // 4k block erase command + .eraseBlockCmd = 0x52, // 32k block erase command + .eraseChipCmd = 0xC7, + .use4BytesAddress = false } }; @@ -280,6 +299,10 @@ size_t flashSpiRead(size_t address, uint8_t* data, size_t size) CS_LOW(); flashSpiReadWriteByte(flashDescriptor->readCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } flashSpiReadWriteByte((address>>16)&0xFF); flashSpiReadWriteByte((address>>8)&0xFF); flashSpiReadWriteByte(address&0xFF); @@ -337,6 +360,10 @@ size_t flashSpiWrite(size_t address, const uint8_t* data, size_t size) CS_LOW(); flashSpiReadWriteByte(flashDescriptor->writeCmd); + if (flashDescriptor->use4BytesAddress) + { + flashSpiReadWriteByte((address>>24)&0xFF); + } flashSpiReadWriteByte((address>>16)&0xFF); flashSpiReadWriteByte((address>>8)&0xFF); flashSpiReadWriteByte(address&0xFF); @@ -363,7 +390,7 @@ size_t flashSpiWrite(size_t address, const uint8_t* data, size_t size) int flashSpiErase(size_t address) { - if(address%4096 != 0) + if(address%flashDescriptor->sectorSize != 0) return -1; flashSpiSync(); @@ -375,6 +402,10 @@ int flashSpiErase(size_t address) 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); @@ -388,7 +419,7 @@ int flashSpiErase(size_t address) int flashSpiBlockErase(size_t address) { - if(address%32768 != 0) + if(address%flashDescriptor->blockSize != 0) return -1; flashSpiSync(); @@ -400,6 +431,10 @@ int flashSpiBlockErase(size_t address) 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); @@ -443,6 +478,7 @@ uint16_t flashSpiGetSectorCount() { return flashDescriptor->blockCount * (flashDescriptor->blockSize / flashDescriptor->sectorSize); } + #if !defined(BOOT) && 0 extern "C" void FLASH_SPI_TX_DMA_IRQHandler(void) { diff --git a/radio/src/thirdparty/tjftl/tjftl.c b/radio/src/thirdparty/tjftl/tjftl.c index fef426822d1..2e698b54cd9 100644 --- a/radio/src/thirdparty/tjftl/tjftl.c +++ b/radio/src/thirdparty/tjftl/tjftl.c @@ -44,7 +44,7 @@ //minimal free block count before we go garbage collect -#define GC_MIN_FREE_BLK_CNT 8 +#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) From 8a32daca730b0280a9324a4940a1ff8bd1fd299d Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sat, 17 Jun 2023 20:24:57 +0800 Subject: [PATCH 13/99] Fine tuned the tjftl init parameters to balance between wearing, overhead and performance. --- .../common/arm/stm32/diskio_sdio_spiflash.cpp | 16 +++++++++++----- .../common/arm/stm32/flash_spi_driver.cpp | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index 29e7703ba40..71f8e0e6d93 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -51,6 +51,7 @@ 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); int flashSpiBlockErase(size_t address); @@ -106,11 +107,16 @@ DSTATUS disk_initialize ( if(!tjftl_detect(flashRead, nullptr)) flashSpiEraseAll(); - size_t flashSize = flashSpiGetSize(); - // tjftl requires at least 10 free blocks after garbage collection. Give it 16 free blocks. - // To ensure a working tjftl the fuilesystem must be at least 10 blocks smaller than the flash memory. - // the block and sector sizes used by tjftl are fixed. A block has 32k bytes and a sector has 512 bytes - tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize - 32768 * 16)/512, 0); + 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); if(tjftl == nullptr) stat |= STA_NOINIT; diff --git a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp index fd4a6b6100d..336f88ef49e 100644 --- a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -285,6 +285,11 @@ size_t flashSpiGetSize() return flashDescriptor->blockSize * flashDescriptor->blockCount; } +uint32_t flashSpiGetBlockCount() +{ + return flashDescriptor->blockCount; +} + size_t flashSpiRead(size_t address, uint8_t* data, size_t size) { #if !defined(BOOT) && 0 From df201e7914745ddcac0389cc256d6a5c9d9560a0 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:21:37 +0800 Subject: [PATCH 14/99] remove dependencies to FatFS. --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 96 +++++++++---------- .../thirdparty/libopenui/src/filechoice.cpp | 24 ++--- .../libopenui/src/libopenui_file.cpp | 33 +------ .../thirdparty/libopenui/src/libopenui_file.h | 92 ++++++++++++++++-- 4 files changed, 147 insertions(+), 98 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6afefb99da0..62976e83ae3 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1393,33 +1393,33 @@ BitmapBuffer * BitmapBuffer::load8bitMaskOnBackground(const uint8_t * lbm, LcdFl return result; } -FIL imgFile __DMA; +OpenUiFileP imgFile __DMA; 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) { + OUiFsError result = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); + if (result != OUiFsError::OK) { return nullptr; } - if (f_size(&imgFile) < 14) { - f_close(&imgFile); + if (imgFile->size() < 14) { + imgFile->close();; return nullptr; } - result = f_read(&imgFile, buf, 14, &read); - if (result != FR_OK || read != 14) { - f_close(&imgFile); + result = imgFile->read(buf, 14, read); + if (result != OUiFsError::OK || read != 14) { + imgFile->close();; return nullptr; } if (buf[0] != 'B' || buf[1] != 'M') { - f_close(&imgFile); + imgFile->close();; return nullptr; } @@ -1427,9 +1427,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 = imgFile->read(buf, len, read); + if (result != OUiFsError::OK || read != len) { + imgFile->close();; return nullptr; } @@ -1437,17 +1437,17 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) /* invalid extra header size */ if (ihsize + 14 > hsize) { - f_close(&imgFile); + imgFile->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(&imgFile) - 2; + fsize = imgFile->size() - 2; /* declared file size less than header size */ if (fsize <= hsize) { - f_close(&imgFile); + imgFile->close();; return nullptr; } @@ -1469,12 +1469,12 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf += 8; break; default: - f_close(&imgFile); + imgFile->close();; return nullptr; } //TRACE(" BitmapBuffer::load_bmp() %dx%d", w, h); if (*((uint16_t *)&buf[0]) != 1) { /* planes */ - f_close(&imgFile); + imgFile->close();; return nullptr; } @@ -1483,8 +1483,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 (imgFile->lseek(hsize - 64) != OUiFsError::OK || imgFile->read(buf, 64, read) != OUiFsError::OK || read != 64) { + imgFile->close();; return nullptr; } for (uint8_t i = 0; i < 16; i++) { @@ -1492,15 +1492,15 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) } } else { - if (f_lseek(&imgFile, hsize) != FR_OK) { - f_close(&imgFile); + if (imgFile->lseek(hsize) != OUiFsError::OK) { + imgFile->close();; return nullptr; } } BitmapBuffer * bmp = new BitmapBuffer(BMP_RGB565, w, h); if (bmp == nullptr || bmp->getData() == nullptr) { - f_close(&imgFile); + imgFile->close();; return nullptr; } @@ -1512,9 +1512,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 = imgFile->read((uint8_t *)dst, 2, read); + if (result != OUiFsError::OK || read != 2) { + imgFile->close();; delete bmp; return nullptr; } @@ -1528,9 +1528,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 = imgFile->read((uint8_t *)&pixel, 4, read); + if (result != OUiFsError::OK || read != 4) { + imgFile->close();; delete bmp; return nullptr; } @@ -1562,9 +1562,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 = imgFile->read(buf, rowSize, read); + if (result != OUiFsError::OK || read != rowSize) { + imgFile->close();; delete bmp; return nullptr; } @@ -1579,12 +1579,12 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) break; default: - f_close(&imgFile); + imgFile->close();; delete bmp; return nullptr; } - f_close(&imgFile); + imgFile->close();; return bmp; } @@ -1641,10 +1641,10 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) // 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) { + OpenUiFileP& fp = *(OpenUiFileP*)user; + size_t br = 0; + OUiFsError res = fp->read(data, size, br); + if (res == OUiFsError::OK) { return (int)br; } return 0; @@ -1653,15 +1653,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); + OpenUiFileP& fp = *(OpenUiFileP*)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); + OpenUiFileP& fp = *(OpenUiFileP*)user; + int res = fp->eof();; return res; } @@ -1676,24 +1676,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) { + OUiFsError result = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); + if (result != OUiFsError::OK) { return nullptr; } int x, y, nn; stbi_info_from_callbacks(&stbCallbacks, &imgFile, &x, &y, &nn); - f_close(&imgFile); + imgFile->close();; //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 = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); + if (result != OUiFsError::OK) { return nullptr; } int w, h, n; unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4); - f_close(&imgFile); + imgFile->close();; if (!img) { TRACE("load_stb(%s) failed: %s", filename, stbi_failure_reason()); diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index c374bc6bf2c..a1f83ad7479 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -56,26 +56,26 @@ std::string FileChoice::getLabelText() bool FileChoice::openMenu() { - FILINFO fno; - DIR dir; + OpenUiFileInfoP fno; + OpenUiDirP 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) { + OUiFsError res = OUiOpenDir(dir, folder.c_str()); // Open the directory + if (res == OUiFsError::OK) { bool firstTime = true; for (;;) { - res = sdReadDir(&dir, &fno, firstTime); - if (res != FR_OK || fno.fname[0] == 0) + res = dir->read(fno); + if (res != OUiFsError::OK || fno->getName().length() == 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 (fno->isDir()) continue; // skip subfolders + if (fno->isHidden()) continue; // skip hidden files + if (fno->isSystem()) continue; // skip system files - fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); + fnExt = getFileExtension(fno->getName().c_str(), 0, 0, &fnLen, &extLen); - if (extension && (!fnExt || !isExtensionMatching(fnExt, extension))) + if (extension && (!fnExt || !isFileExtensionMatching(fnExt, extension))) continue; // wrong extension if (stripExtension) fnLen -= extLen; @@ -83,7 +83,7 @@ bool FileChoice::openMenu() if (!fnLen || fnLen > maxlen) continue; // wrong size // eject duplicates - std::string newFile(fno.fname, fnLen); + std::string newFile = fno->getName(); if (std::find(files.begin(), files.end(), newFile) != files.end()) continue; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp index 1e68c187a01..7dc86b4f983 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -55,7 +55,7 @@ const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMa @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) +bool isFileExtensionMatching(const char * extension, const char * pattern, char * match) { const char *ext; uint8_t extlen, fnlen; @@ -75,34 +75,3 @@ bool isExtensionMatching(const char * extension, const char * pattern, char * ma } 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 index f5588580c50..c969f3c3253 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -21,16 +21,96 @@ #include #include #include -#include "ff.h" +#include 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); +bool isFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); -// comparison, not case sensitive. -inline bool compare_nocase(const std::string & first, const std::string & second) +enum class OUiFsError { + OK, + ERROR, +}; + +class OpenUiFileInfo { - return strcasecmp(first.c_str(), second.c_str()) < 0; +public: + virtual std::string getName() = 0; + virtual bool isDir() = 0; + virtual bool isHidden() = 0; + virtual bool isSystem() = 0; + +protected: + OpenUiFileInfo(){} + virtual ~OpenUiFileInfo(){} + +private: + OpenUiFileInfo(const OpenUiFileInfo&) = delete; +}; + +typedef std::shared_ptr OpenUiFileInfoP; + +class OpenUiFile +{ +public: + virtual size_t size() = 0; + virtual void close() = 0; + virtual OUiFsError read(void* buf, size_t size, size_t& readSize) = 0; + virtual OUiFsError lseek(size_t offset) = 0; + virtual size_t tell() = 0; + virtual int eof() = 0; + +protected: + OpenUiFile(){} + virtual ~OpenUiFile(){} +private: + OpenUiFile(const OpenUiFile&) = delete; +}; + +typedef std::shared_ptr OpenUiFileP; + +class OpenUiDir +{ +public: + virtual OUiFsError read(OpenUiFileInfoP& fileInfo) = 0; +protected: + OpenUiDir(){} + virtual ~OpenUiDir(){} +private: + OpenUiDir(const OpenUiDir&) = delete; +}; + +typedef std::shared_ptr OpenUiDirP; + +enum class OUiFsOpenFlags { + NONE = 0x00, + READ = 0x01, + OPEN_EXISTING = 0x00, +}; + +//OUiFsOpenFlags operator|(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) +//OUiFsOpenFlags operator&(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs); +inline OUiFsOpenFlags operator|(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + | static_cast(rhs)); +} +inline OUiFsOpenFlags operator&(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) +{ + typedef typename + std::underlying_type::type underlying; + return static_cast( + static_cast(lhs) + & static_cast(rhs)); } + +// the reference to the share pointer is by desgin, to allow the open function to set the pointer. +OUiFsError OUiOpenDir(OpenUiDirP& dir, const char* path); +OUiFsError OUiOpenFile(OpenUiFileP& file, const char* path, OUiFsOpenFlags flags); +const char * OUiGetFileExtension(const char * filename, uint8_t size = 0, + uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); +bool OUiGisFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); From dba70560ad32a6bf67fbcd9ecc7867bee88a94b3 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:27:39 +0800 Subject: [PATCH 15/99] remove file system api dependencies, added file system compat layer via libopenui_conf.h --- radio/src/thirdparty/libopenui/README.md | 3 + .../thirdparty/libopenui/src/bitmapbuffer.cpp | 90 +++++++++--------- .../thirdparty/libopenui/src/filechoice.cpp | 27 +++--- .../libopenui/src/libopenui_conf_example.h | 53 +++++++++++ .../thirdparty/libopenui/src/libopenui_file.h | 91 +------------------ 5 files changed, 119 insertions(+), 145 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_conf_example.h 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/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 62976e83ae3..a2e2961d122 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -24,7 +24,7 @@ #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(); @@ -1393,7 +1393,7 @@ BitmapBuffer * BitmapBuffer::load8bitMaskOnBackground(const uint8_t * lbm, LcdFl return result; } -OpenUiFileP imgFile __DMA; +OpenUiFile imgFile __DMA; BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) { @@ -1402,24 +1402,24 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ uint8_t * buf = &bmpBuf[0]; - OUiFsError result = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); - if (result != OUiFsError::OK) { + OpenUiFsRetType result = openUiOpenFile(&imgFile, filename, OPENUI_FS_OPEN_FLAG_OPENEXISTING | OPENUI_FS_OPEN_FLAG_READ); + if (result != OPENUI_FS_OK) { return nullptr; } - if (imgFile->size() < 14) { - imgFile->close();; + if (openUiFileGetSize(&imgFile) < 14) { + openUiCloseFile(&imgFile);; return nullptr; } - result = imgFile->read(buf, 14, read); - if (result != OUiFsError::OK || read != 14) { - imgFile->close();; + result = openUiReadFile(&imgFile, buf, 14, read); + if (result != OPENUI_FS_OK || read != 14) { + openUiCloseFile(&imgFile);; return nullptr; } if (buf[0] != 'B' || buf[1] != 'M') { - imgFile->close();; + openUiCloseFile(&imgFile);; return nullptr; } @@ -1427,9 +1427,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 = imgFile->read(buf, len, read); - if (result != OUiFsError::OK || read != len) { - imgFile->close();; + result = openUiReadFile(&imgFile, buf, len, read); + if (result != OPENUI_FS_OK || read != len) { + openUiCloseFile(&imgFile);; return nullptr; } @@ -1437,17 +1437,17 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) /* invalid extra header size */ if (ihsize + 14 > hsize) { - imgFile->close();; + 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 = imgFile->size() - 2; + fsize = openUiFileGetSize(&imgFile) - 2; /* declared file size less than header size */ if (fsize <= hsize) { - imgFile->close();; + openUiCloseFile(&imgFile);; return nullptr; } @@ -1469,12 +1469,12 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf += 8; break; default: - imgFile->close();; + openUiCloseFile(&imgFile);; return nullptr; } //TRACE(" BitmapBuffer::load_bmp() %dx%d", w, h); if (*((uint16_t *)&buf[0]) != 1) { /* planes */ - imgFile->close();; + openUiCloseFile(&imgFile);; return nullptr; } @@ -1483,8 +1483,8 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf = &bmpBuf[0]; if (depth == 4) { - if (imgFile->lseek(hsize - 64) != OUiFsError::OK || imgFile->read(buf, 64, read) != OUiFsError::OK || read != 64) { - imgFile->close();; + 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 +1492,15 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) } } else { - if (imgFile->lseek(hsize) != OUiFsError::OK) { - imgFile->close();; + 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) { - imgFile->close();; + openUiCloseFile(&imgFile);; return nullptr; } @@ -1512,9 +1512,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 = imgFile->read((uint8_t *)dst, 2, read); - if (result != OUiFsError::OK || read != 2) { - imgFile->close();; + result = openUiReadFile(&imgFile, (uint8_t *)dst, 2, read); + if (result != OPENUI_FS_OK || read != 2) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1528,9 +1528,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 = imgFile->read((uint8_t *)&pixel, 4, read); - if (result != OUiFsError::OK || read != 4) { - imgFile->close();; + result = openUiReadFile(&imgFile, (uint8_t *)&pixel, 4, read); + if (result != OPENUI_FS_OK || read != 4) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1562,9 +1562,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 = imgFile->read(buf, rowSize, read); - if (result != OUiFsError::OK || read != rowSize) { - imgFile->close();; + result = openUiReadFile(&imgFile, buf, rowSize, read); + if (result != OPENUI_FS_OK || read != rowSize) { + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } @@ -1579,12 +1579,12 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) break; default: - imgFile->close();; + openUiCloseFile(&imgFile);; delete bmp; return nullptr; } - imgFile->close();; + openUiCloseFile(&imgFile);; return bmp; } @@ -1641,10 +1641,10 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) // fill 'data' with 'size' bytes. return number of bytes actually read int stbc_read(void *user, char * data, int size) { - OpenUiFileP& fp = *(OpenUiFileP*)user; + OpenUiFile* fp = (OpenUiFile*)user; size_t br = 0; - OUiFsError res = fp->read(data, size, br); - if (res == OUiFsError::OK) { + OpenUiFsRetType res = fp->read(data, size, br); + if (res == OPENUI_FS_OK) { return (int)br; } return 0; @@ -1653,14 +1653,14 @@ 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) { - OpenUiFileP& fp = *(OpenUiFileP*)user; + OpenUiFile* fp = (OpenUiFile*)user; fp->lseek(fp->tell() + n); } // returns nonzero if we are at end of file/data int stbc_eof(void *user) { - OpenUiFileP& fp = *(OpenUiFileP*)user; + OpenUiFile* fp = (OpenUiFile*)user; int res = fp->eof();; return res; } @@ -1676,24 +1676,24 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename, BitmapFormats fmt) { //TRACE(" BitmapBuffer::load_stb(%s)", filename); - OUiFsError result = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); - if (result != OUiFsError::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); - imgFile->close();; + openUiCloseFile(&imgFile); //TRACE(" BitmapBuffer::load_stb()----Info File %s, %d, %d, %d", filename, x, y, nn); - result = OUiOpenFile(imgFile, filename, OUiFsOpenFlags::OPEN_EXISTING | OUiFsOpenFlags::READ); - if (result != OUiFsError::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); - imgFile->close();; + openUiCloseFile(&imgFile); if (!img) { TRACE("load_stb(%s) failed: %s", filename, stbi_failure_reason()); diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index a1f83ad7479..e21a5523adb 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -17,11 +17,12 @@ */ #include "filechoice.h" +#include "strhelpers.h" #include "libopenui_file.h" #include "menu.h" #include "theme.h" #include "message_dialog.h" - +#include "libopenui_conf.h" #include #if !defined(STR_SDCARD) @@ -56,24 +57,23 @@ std::string FileChoice::getLabelText() bool FileChoice::openMenu() { - OpenUiFileInfoP fno; - OpenUiDirP dir; + OpenUiFileInfo fno; + OpenUiDir dir; std::list files; const char *fnExt; uint8_t fnLen, extLen; - OUiFsError res = OUiOpenDir(dir, folder.c_str()); // Open the directory - if (res == OUiFsError::OK) { - bool firstTime = true; + OpenUiFsRetType res = openUiOpenDir(&dir, folder.c_str()); // Open the directory + if (res == OPENUI_FS_OK) { for (;;) { - res = dir->read(fno); - if (res != OUiFsError::OK || fno->getName().length() == 0) + res = openUiReadDir(&dir, &fno); + if (res != OPENUI_FS_OK || openUiFsGetName(&fno).length() == 0) break; // break on error or end of dir - if (fno->isDir()) continue; // skip subfolders - if (fno->isHidden()) continue; // skip hidden files - if (fno->isSystem()) 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->getName().c_str(), 0, 0, &fnLen, &extLen); + fnExt = getFileExtension(openUiFsGetName(&fno).c_str(), 0, 0, &fnLen, &extLen); if (extension && (!fnExt || !isFileExtensionMatching(fnExt, extension))) continue; // wrong extension @@ -83,12 +83,13 @@ bool FileChoice::openMenu() if (!fnLen || fnLen > maxlen) continue; // wrong size // eject duplicates - std::string newFile = fno->getName(); + std::string 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..de723bfc288 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h @@ -0,0 +1,53 @@ +/* + * 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 OPENUI_FS_OK VfsError::OK + +#endif // _OPENUI_CONF_H diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index c969f3c3253..b27c3d3aeea 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -27,90 +27,7 @@ constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the do const char * getFileExtension(const char * filename, uint8_t size = 0, uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); bool isFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); - -enum class OUiFsError { - OK, - ERROR, -}; - -class OpenUiFileInfo -{ -public: - virtual std::string getName() = 0; - virtual bool isDir() = 0; - virtual bool isHidden() = 0; - virtual bool isSystem() = 0; - -protected: - OpenUiFileInfo(){} - virtual ~OpenUiFileInfo(){} - -private: - OpenUiFileInfo(const OpenUiFileInfo&) = delete; -}; - -typedef std::shared_ptr OpenUiFileInfoP; - -class OpenUiFile -{ -public: - virtual size_t size() = 0; - virtual void close() = 0; - virtual OUiFsError read(void* buf, size_t size, size_t& readSize) = 0; - virtual OUiFsError lseek(size_t offset) = 0; - virtual size_t tell() = 0; - virtual int eof() = 0; - -protected: - OpenUiFile(){} - virtual ~OpenUiFile(){} -private: - OpenUiFile(const OpenUiFile&) = delete; -}; - -typedef std::shared_ptr OpenUiFileP; - -class OpenUiDir -{ -public: - virtual OUiFsError read(OpenUiFileInfoP& fileInfo) = 0; -protected: - OpenUiDir(){} - virtual ~OpenUiDir(){} -private: - OpenUiDir(const OpenUiDir&) = delete; -}; - -typedef std::shared_ptr OpenUiDirP; - -enum class OUiFsOpenFlags { - NONE = 0x00, - READ = 0x01, - OPEN_EXISTING = 0x00, -}; - -//OUiFsOpenFlags operator|(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) -//OUiFsOpenFlags operator&(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs); -inline OUiFsOpenFlags operator|(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) -{ - typedef typename - std::underlying_type::type underlying; - return static_cast( - static_cast(lhs) - | static_cast(rhs)); -} -inline OUiFsOpenFlags operator&(OUiFsOpenFlags lhs,OUiFsOpenFlags rhs) -{ - typedef typename - std::underlying_type::type underlying; - return static_cast( - static_cast(lhs) - & static_cast(rhs)); -} - -// the reference to the share pointer is by desgin, to allow the open function to set the pointer. -OUiFsError OUiOpenDir(OpenUiDirP& dir, const char* path); -OUiFsError OUiOpenFile(OpenUiFileP& file, const char* path, OUiFsOpenFlags flags); -const char * OUiGetFileExtension(const char * filename, uint8_t size = 0, - uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); -bool OUiGisFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); +// +//const char * OUiGetFileExtension(const char * filename, uint8_t size = 0, +// uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); +//bool OUiGisFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); From e26fc1934b377a2e996c1946c13cdc70584ec04c Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:30:31 +0800 Subject: [PATCH 16/99] removed now unused libopenui_file --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 - .../thirdparty/libopenui/src/bitmapbuffer.cpp | 3 +- .../thirdparty/libopenui/src/filechoice.cpp | 5 +- .../libopenui/src/libopenui_file.cpp | 77 ------------------- .../thirdparty/libopenui/src/libopenui_file.h | 33 -------- 5 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.h 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 a2e2961d122..cbbcfb60d00 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -22,7 +22,6 @@ #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" @@ -1253,7 +1252,7 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h 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 diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index e21a5523adb..3d4682ac030 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -18,7 +18,6 @@ #include "filechoice.h" #include "strhelpers.h" -#include "libopenui_file.h" #include "menu.h" #include "theme.h" #include "message_dialog.h" @@ -73,9 +72,9 @@ bool FileChoice::openMenu() if (openUiFsIsHiddenFile(&fno)) continue; // skip hidden files if (openUiFsIsSystemFile(&fno)) continue; // skip system files - fnExt = getFileExtension(openUiFsGetName(&fno).c_str(), 0, 0, &fnLen, &extLen); + fnExt = openUiGetFileExtension(openUiFsGetName(&fno).c_str(), 0, 0, &fnLen, &extLen); - if (extension && (!fnExt || !isFileExtensionMatching(fnExt, extension))) + if (extension && (!fnExt || !openUiIsFileExtensionMatching(fnExt, extension, nullptr))) continue; // wrong extension if (stripExtension) fnLen -= extLen; 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 7dc86b4f983..00000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ /dev/null @@ -1,77 +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 isFileExtensionMatching(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; -} 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 b27c3d3aeea..00000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ /dev/null @@ -1,33 +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 - -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 isFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); -// -//const char * OUiGetFileExtension(const char * filename, uint8_t size = 0, -// uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); -//bool OUiGisFileExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); From 39f23bd1e48a6b536cbc141cc5d618cfde92a702 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:31:50 +0800 Subject: [PATCH 17/99] update example conf --- radio/src/thirdparty/libopenui/src/libopenui_conf_example.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h b/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h index de723bfc288..ccd7c5d99ae 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_conf_example.h @@ -48,6 +48,9 @@ #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 From f725c632cf1610cc4c466566a1321c4d7ee0bc24 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:33:12 +0800 Subject: [PATCH 18/99] disable file access when used in bootloader (should be in libopenui_conf.h) --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index cbbcfb60d00..3bde5730b59 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1248,7 +1248,7 @@ 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); @@ -1258,7 +1258,7 @@ BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename, BitmapFormats fmt else return load_stb(filename, fmt); } - +#endif BitmapBuffer * BitmapBuffer::loadRamBitmap(const uint8_t * buffer, int len) { return load_stb_buffer(buffer, len); @@ -1393,7 +1393,7 @@ BitmapBuffer * BitmapBuffer::load8bitMaskOnBackground(const uint8_t * lbm, LcdFl } OpenUiFile imgFile __DMA; - +#if !defined(BOOT) BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) { size_t read; @@ -1586,7 +1586,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) openUiCloseFile(&imgFile);; return bmp; } - +#endif #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_PNG #define STBI_ONLY_JPEG @@ -1636,7 +1636,7 @@ 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) { @@ -1704,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; From b1bc83b2045a12388e8aaa82a9f2ed32983612e0 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 19 May 2023 14:34:44 +0800 Subject: [PATCH 19/99] libopenui changes to sync with VirtualFS changes --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 3d4682ac030..f892c18e3ad 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -66,13 +66,13 @@ bool FileChoice::openMenu() if (res == OPENUI_FS_OK) { for (;;) { res = openUiReadDir(&dir, &fno); - if (res != OPENUI_FS_OK || openUiFsGetName(&fno).length() == 0) + if (res != OPENUI_FS_OK || strlen(openUiFsGetName(&fno)) == 0) break; // break on error or end of dir if (openUiFsIsDir(&fno)) continue; // skip subfolders if (openUiFsIsHiddenFile(&fno)) continue; // skip hidden files if (openUiFsIsSystemFile(&fno)) continue; // skip system files - fnExt = openUiGetFileExtension(openUiFsGetName(&fno).c_str(), 0, 0, &fnLen, &extLen); + fnExt = openUiGetFileExtension(openUiFsGetName(&fno), 0, 0, &fnLen, &extLen); if (extension && (!fnExt || !openUiIsFileExtensionMatching(fnExt, extension, nullptr))) continue; // wrong extension @@ -82,7 +82,7 @@ bool FileChoice::openMenu() if (!fnLen || fnLen > maxlen) continue; // wrong size // eject duplicates - std::string newFile = fno.getName(); + const char* newFile = fno.getName(); if (std::find(files.begin(), files.end(), newFile) != files.end()) continue; From 654892ca87ddcf0c2bbb870d116d5c8f52bc1032 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jun 2023 16:41:35 +0800 Subject: [PATCH 20/99] Fixed system audio problem. --- radio/src/audio.cpp | 26 +++++++++++++------------- radio/src/audio.h | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 4f275a9da7f..b7580cde396 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -287,16 +287,16 @@ void referenceSystemAudioFiles() if (res == VfsError::OK) { for (;;) { 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(); + 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(fname.c_str()+len-4, SOUNDS_EXT) || (fno.getType() == VfsType::DIR)) continue; + if (len < 5 || strcasecmp(name+len-4, SOUNDS_EXT) || (fno.getType() == VfsType::DIR)) continue; for (int i=0; i-[on|off].wav for (int i=0; i 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); From 3dc492f66cba48ec95f5178a5101cae848473345 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 21 Aug 2023 14:25:29 +0800 Subject: [PATCH 21/99] Clean up string usage to save memory. --- radio/src/cli.cpp | 15 ++++++++----- radio/src/gui/colorlcd/radio_tools.cpp | 10 ++++----- .../src/gui/common/stdlcd/radio_sdmanager.cpp | 22 +++++++++---------- radio/src/gui/common/stdlcd/radio_tools.cpp | 13 ++++++----- radio/src/storage/modelslist.cpp | 2 +- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 82510b67d3c..7d421f2ba29 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -264,9 +264,9 @@ int cliLs(const char ** argv) if (res == VfsError::OK) { for (;;) { res = dir.read(fno); /* Read a directory item */ - std::string name = fno.getName(); - if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ - cliSerialPrint(name.c_str()); + const char *name = fno.getName(); + if (res != VfsError::OK || name[0] == 0) break; /* Break on error or end of dir */ + cliSerialPrint(name); } dir.close(); } @@ -1382,9 +1382,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) { diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 276ed9c5d29..ffac9c6cabf 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -114,14 +114,14 @@ static void scanLuaTools(std::list& scripts) for (;;) { TCHAR path[FF_MAX_LFN+1] = ":" SCRIPTS_TOOLS_PATH "/"; res = dir.read(fno); /* Read a directory item */ - if (res != VfsError::OK || strlen(fno.getName()) == 0) break; /* Break on error or end of dir */ + 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 */ - auto fname = fno.getName(); - if (fname[0] == '.') continue; - strcat(path, fname); - if (isRadioScriptTool(fname)) { + if (fName[0] == '.') continue; + strcat(path, fName); + if (isRadioScriptTool(fName)) { char toolName[RADIO_TOOL_NAME_MAXLEN + 1] = {0}; const char * label; char * ext = (char *)VirtualFS::getFileExtension(path+1); diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index b17ca05abb0..840985232e6 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -508,9 +508,9 @@ void menuRadioSdManager(event_t _event) if (res == VfsError::OK) { for (;;) { res = dir.read(fno); - std::string name = fno.getName(); - if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ - if (name.length() > STORAGE_SCREEN_FILE_LENGTH) continue; + 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; @@ -523,10 +523,10 @@ void menuRadioSdManager(event_t _event) if (menuVerticalOffset == 0) { for (uint8_t i=0; i=0; i--) { char * line = reusableBuffer.sdManager.lines[i]; - if (line[0] == '\0' || isFilenameGreater(isfile, name.c_str(), 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, name.c_str()); + strcpy(line, name); NODE_TYPE(line) = isfile; break; } } } else if (menuVerticalOffset > reusableBuffer.sdManager.offset) { - if (isFilenameGreater(isfile, name.c_str(), reusableBuffer.sdManager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, name.c_str(), 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], name.c_str()); + strcpy(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], name); NODE_TYPE(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1]) = isfile; } } else { - if (isFilenameLower(isfile, name.c_str(), reusableBuffer.sdManager.lines[1]) && isFilenameGreater(isfile, name.c_str(), 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], name.c_str()); + strcpy(reusableBuffer.sdManager.lines[0], name); NODE_TYPE(reusableBuffer.sdManager.lines[0]) = isfile; } } diff --git a/radio/src/gui/common/stdlcd/radio_tools.cpp b/radio/src/gui/common/stdlcd/radio_tools.cpp index 5147296a966..3218e042e84 100644 --- a/radio/src/gui/common/stdlcd/radio_tools.cpp +++ b/radio/src/gui/common/stdlcd/radio_tools.cpp @@ -118,10 +118,11 @@ void menuRadioTools(event_t event) uint8_t index = 0; #if defined(LUA) + VirtualFS &vfs = VirtualFS::instance(); VfsFileInfo fno; VfsDir dir; - VfsError res = VirtualFS::instance().openDirectory(dir, SCRIPTS_TOOLS_PATH); + VfsError res = vfs.openDirectory(dir, SCRIPTS_TOOLS_PATH); if (res == VfsError::OK) { std::vector luaScripts; // gather and sort before adding to menu @@ -129,15 +130,15 @@ void menuRadioTools(event_t event) char path[FF_MAX_LFN + 1] = SCRIPTS_TOOLS_PATH "/"; res = dir.read(fno); /* Read a directory item */ - std::string name = fno.getName(); - if (res != VfsError::OK || name.length() == 0) break; /* Break on error or end of dir */ + 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; - - strcat(path, name.c_str()); - if (isRadioScriptTool(fno.getName())) { + 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 *)VirtualFS::getFileExtension(path); diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 1d3a87a33c7..2f789585f4d 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -1006,8 +1006,8 @@ bool ModelsList::loadYaml() if (vfs.openDirectory(moddir, MODELS_PATH) == VfsError::OK) { for (;;) { VfsError res = moddir.read(finfo); - if (res != VfsError::OK || finfo.getName()[0] == 0) break; const char* fName = finfo.getName(); + if (res != VfsError::OK || fName[0] == 0) break; if (finfo.getType() == VfsType::DIR) continue; unsigned int len = strlen(fName); From 103c2c5d17614617ecb295171322cf6911412db1 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 16 Aug 2023 18:08:38 +0800 Subject: [PATCH 22/99] Updated FTL implementation to FrFTL. --- radio/src/VirtualFS.h | 2 +- radio/src/frftl.cpp | 990 ++++++++++++++++++ radio/src/frftl.h | 82 ++ .../arm/stm32/bootloader/CMakeLists.txt | 9 +- .../common/arm/stm32/diskio_sdio_spiflash.cpp | 75 +- .../common/arm/stm32/flash_spi_driver.cpp | 37 +- radio/src/targets/horus/CMakeLists.txt | 9 +- radio/src/targets/horus/board.cpp | 4 + radio/src/targets/nv14/CMakeLists.txt | 8 +- radio/src/targets/nv14/board.cpp | 4 + radio/src/thirdparty/FatFs/ffconf.h | 2 +- 11 files changed, 1178 insertions(+), 44 deletions(-) create mode 100644 radio/src/frftl.cpp create mode 100644 radio/src/frftl.h diff --git a/radio/src/VirtualFS.h b/radio/src/VirtualFS.h index 640c30db428..0b01fae89ad 100644 --- a/radio/src/VirtualFS.h +++ b/radio/src/VirtualFS.h @@ -23,7 +23,7 @@ #include -#include "tjftl/tjftl.h" +#include "frftl.h" #include "FatFs/ff.h" #if defined (SDCARD) #include "sdcard.h" 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/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index aa36b893537..6ff29f7ee51 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -47,10 +47,11 @@ set(BOOTLOADER_SRC ) if(SPI_FLASH) - set(BOOTLOADER_SRC - ${BOOTLOADER_SRC} - ../../../../../targets/common/arm/stm32/flash_spi_driver.cpp - ../../../../../thirdparty/tjftl/tjftl.c +set(BOOTLOADER_SRC + ${BOOTLOADER_SRC} + ../../../../../targets/common/arm/stm32/flash_spi_driver.cpp + ../../../../../frftl.cpp + ../../../../../crc.cpp ) endif() diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index 71f8e0e6d93..966c0376cdc 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -45,29 +45,30 @@ extern volatile uint32_t WriteStatus; extern volatile uint32_t ReadStatus; #if defined(SPI_FLASH) -#include "tjftl/tjftl.h" +#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(); +//uint32_t flashSpiGetBlockCount(); int flashSpiErase(size_t address); +bool flashSpiIsErased(size_t address); int flashSpiBlockErase(size_t address); void flashSpiEraseAll(); void flashSpiSync(); -static tjftl_t* tjftl = nullptr; +static FrFTL* frftl = nullptr; extern "C" { -static bool flashRead(int addr, uint8_t* buf, int len, void* arg) +static bool flashRead(uint32_t addr, uint8_t* buf, uint32_t len) { flashSpiRead(addr, buf, len); return true; } -static bool flashWrite(int addr, const uint8_t *buf, int len, void *arg) +static bool flashWrite(uint32_t addr, const uint8_t *buf, uint32_t len) { size_t pageSize = flashSpiGetPageSize(); if(len%pageSize != 0) @@ -84,11 +85,22 @@ static bool flashWrite(int addr, const uint8_t *buf, int len, void *arg) return true; } -static bool flashErase(int addr, void *arg) +static bool flashErase(uint32_t addr) { - flashSpiBlockErase(addr); + flashSpiErase(addr); return true; } + +static bool isFlashErased(uint32_t addr) +{ + return flashSpiIsErased(addr); +} + +void flushFTL() +{ + ftlSync(frftl); +} + } #endif /*-----------------------------------------------------------------------*/ @@ -102,23 +114,24 @@ DSTATUS disk_initialize ( #if defined(SPI_FLASH) if(drv == 1) { - if(tjftl != nullptr) + if(frftl != nullptr) return stat; - if(!tjftl_detect(flashRead, nullptr)) - flashSpiEraseAll(); +// if(!tjftl_detect(flashRead, nullptr)) +// flashSpiEraseAll(); int flashSize = flashSpiGetSize(); int flashSizeMB = flashSize / 1024 / 1024; - int blockCount = flashSpiGetBlockCount(); + // 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); +// int overheadBlockCount = blockCount / 64 + (flashSizeMB >= 32 ? flashSizeMB * 2 : 32); - tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize - overheadBlockCount * 32768)/512, 0); +// tjftl = tjftl_init(flashRead, flashErase, flashWrite, nullptr, flashSize, (flashSize - overheadBlockCount * 32768)/512, 0); + frftl = ftlInit(flashRead, flashWrite, flashErase, isFlashErased, flashSizeMB); - if(tjftl == nullptr) + if(frftl == nullptr) stat |= STA_NOINIT; return stat; } @@ -165,7 +178,7 @@ DSTATUS disk_status ( #if defined(SPI_FLASH) if(drv == 1) { - if(tjftl == nullptr) + if(frftl == nullptr) stat |= STA_NODISK; return stat; } @@ -240,14 +253,14 @@ DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) #if defined(SPI_FLASH) if(drv == 1) { - if(tjftl == nullptr) + if(frftl == nullptr) { res = RES_ERROR; return res; } while(count) { - if(!tjftl_read(tjftl, sector, (uint8_t*)buff)) + if(!ftlRead(frftl, sector, (uint8_t*)buff)) return RES_ERROR; buff += 512; sector++; @@ -316,19 +329,13 @@ DRESULT __disk_write( #if defined(SPI_FLASH) if(drv == 1) { - if(tjftl == nullptr) + if(frftl == nullptr) { res = RES_ERROR; return res; } - while(count) - { - if(!tjftl_write(tjftl, sector, (uint8_t*)buff)) - return RES_ERROR; - buff += 512; - sector++; - count --; - } + if (!ftlWrite(frftl, sector, count, (uint8_t*)buff)) + return RES_ERROR; return res; } #endif @@ -406,7 +413,7 @@ DRESULT disk_ioctl ( if(drv == 1) { disk_initialize(1); - if(tjftl == nullptr) + if(frftl == nullptr) { res = RES_ERROR; return res; @@ -416,23 +423,29 @@ DRESULT disk_ioctl ( switch (ctrl) { case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ { - *(DWORD*)buff = tjftl_getSectorCount(tjftl); + *(DWORD*)buff = frftl->usableSectorCount; res = RES_OK; break; } case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ - *(WORD*)buff = tjftl_getSectorSize(tjftl); + *(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 = 512; + *(DWORD*)buff = 4096; res = RES_OK; break; case CTRL_SYNC: - res = RES_OK; + 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: diff --git a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp index 336f88ef49e..4c8b5093296 100644 --- a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -285,9 +285,44 @@ size_t flashSpiGetSize() return flashDescriptor->blockSize * flashDescriptor->blockCount; } -uint32_t flashSpiGetBlockCount() +/*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) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 197c40ac886..e4052857d25 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -49,11 +49,13 @@ if(SPI_FLASH) set(TARGET_SRC ${TARGET_SRC} ../../thirdparty/littlefs_v2.4.1/lfs_util.c ../../thirdparty/littlefs_v2.4.1/lfs.c - ) + ../../crc.cpp add_definitions(-DUSE_LITTLEFS) else() set(TARGET_SRC ${TARGET_SRC} - ../../thirdparty/tjftl/tjftl.c + ../../frftl.cpp + ../../crc.cpp + ) ) endif() endif() @@ -64,7 +66,8 @@ if(BOOTLOADER) set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} ../common/arm/stm32/diskio_sdio_spiflash.cpp ../common/arm/stm32/flash_spi_driver.cpp - ../../thirdparty/tjftl/tjftl.c + ../../frftl.cpp + ../../crc.cpp ) else() set(LINKER_SCRIPT targets/horus/stm32f4_flash.ld) diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index cbd3010d8e3..c441fe8e94c 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -215,6 +215,10 @@ extern void rtcDisableBackupReg(); void boardOff() { +#if defined(SPI_FLASH) + flushFTL(); +#endif + ledOff(); backlightEnable(0); diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 7a2b3e56219..b38a05a8d50 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -48,7 +48,8 @@ if(SPI_FLASH) add_definitions(-DUSE_LITTLEFS) else() set(TARGET_SRC ${TARGET_SRC} - ../../thirdparty/tjftl/tjftl.c + ../../frftl.cpp + ../../crc.cpp ) endif() endif() @@ -58,8 +59,9 @@ if(BOOTLOADER) set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} ../common/arm/stm32/diskio_sdio_spiflash.cpp ../common/arm/stm32/flash_spi_driver.cpp - ../../thirdparty/tjftl/tjftl.c - ) + ../../frftl.cpp + ../../crc.cpp + ) else() set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) endif() diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 404fd001672..589650e709e 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -304,6 +304,10 @@ extern void rtcDisableBackupReg(); void boardOff() { +#if defined(SPI_FLASH) + flushFTL(); +#endif + lcdOff(); while (pwrPressed()) { diff --git a/radio/src/thirdparty/FatFs/ffconf.h b/radio/src/thirdparty/FatFs/ffconf.h index 5fa552052db..5fd25c451c4 100644 --- a/radio/src/thirdparty/FatFs/ffconf.h +++ b/radio/src/thirdparty/FatFs/ffconf.h @@ -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. */ From 97ee6c79a9cef45dfbf223d8a25613efed4f5979 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 23 Aug 2023 10:30:52 +0800 Subject: [PATCH 23/99] Enable SPI flash by default for NV14/EL18. --- radio/src/targets/common/arm/stm32/flash_spi_driver.cpp | 1 - radio/src/targets/nv14/CMakeLists.txt | 1 + radio/src/targets/nv14/board.cpp | 4 ++++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp index 4c8b5093296..417b85b8129 100644 --- a/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp +++ b/radio/src/targets/common/arm/stm32/flash_spi_driver.cpp @@ -20,7 +20,6 @@ */ #include -#include "rtos.h" #include "board.h" #if !defined(SIMU) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index b38a05a8d50..c29c32918ae 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -15,6 +15,7 @@ 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) diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 589650e709e..783abc011ef 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -104,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--) From 883d0b65fffd18058b5114527986bc82cc184f4d Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 23 Aug 2023 12:10:18 +0800 Subject: [PATCH 24/99] Fixed some more problems for EL18 using internal SPI flash. --- radio/src/gui/colorlcd/model_telemetry.cpp | 2 ++ radio/src/targets/nv14/CMakeLists.txt | 3 +++ 2 files changed, 5 insertions(+) diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index c1bd3385798..30d9eba03df 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -720,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/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index c29c32918ae..7cef2a4ae3b 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -63,6 +63,9 @@ if(BOOTLOADER) ../../frftl.cpp ../../crc.cpp ) + add_definitions(-DSPI_FLASH) + add_definitions(-DUSE_FATFS) + else() set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) endif() From 8c3ded83b5a70ac3790022595994c7f3a90f230b Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 24 Aug 2023 10:48:43 +0800 Subject: [PATCH 25/99] Fixed problems after rebase. --- radio/src/logs.cpp | 8 +- radio/src/opentx.cpp | 1 + radio/src/sdcard.cpp | 410 +----------------- radio/src/sdcard.h | 6 + .../common/arm/stm32/diskio_sdio_spiflash.cpp | 35 ++ 5 files changed, 50 insertions(+), 410 deletions(-) diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index 230ca838520..97531e483ee 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -231,8 +231,8 @@ void writeHeader() 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++) { @@ -364,7 +364,7 @@ void logsWrite() auto offset = adcGetInputOffset(ADC_INPUT_MAIN); for (uint8_t i = 0; i < n_inputs; i++) { - g_oLogFile.fprint("%d,", calibratedAnalogs[inputMappingConvertMode(offset + i)]); + g_oLogFile.fprintf("%d,", calibratedAnalogs[inputMappingConvertMode(offset + i)]); } n_inputs = adcGetMaxInputs(ADC_INPUT_POT); @@ -372,7 +372,7 @@ void logsWrite() for (uint8_t i = 0; i < n_inputs; i++) { if (IS_POT_AVAILABLE(i)) - g_oLogFile.fprint("%d,", calibratedAnalogs[offset + i]); + g_oLogFile.fprintf("%d,", calibratedAnalogs[offset + i]); } for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 81056349d93..8a1eb1d0874 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -200,6 +200,7 @@ void per10ms() #if defined(SDCARD) && defined(PCBTARANIS) sdPoll10ms(); +#endif outputTelemetryBuffer.per10ms(); 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 2934f1ab66c..2146df0dd36 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -30,5 +30,11 @@ bool sdCardFormat(); uint32_t sdGetNoSectors(); uint32_t sdGetSize(); uint32_t sdGetFreeSectors(); +uint32_t sdGetFreeKB(); +bool sdIsFull(); + +#if defined(PCBTARANIS) +void sdPoll10ms(); +#endif #endif // _SDCARD_H_ diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp index 966c0376cdc..e87888486f5 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio_spiflash.cpp @@ -43,7 +43,42 @@ // 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" From f6aa7a07b156c928b140f5f375d54972813a0930 Mon Sep 17 00:00:00 2001 From: rotorman Date: Sun, 26 Dec 2021 19:57:08 +0100 Subject: [PATCH 26/99] Initial rough target for FlySky PL18, based off NV14 target. --- .github/workflows/actions.yml | 2 + .github/workflows/nightly.yml | 1 + companion/src/firmwares/boards.cpp | 35 +- companion/src/firmwares/boards.h | 10 +- companion/src/firmwares/generalsettings.cpp | 8 +- .../src/firmwares/opentx/opentxeeprom.cpp | 22 +- .../src/firmwares/opentx/opentxinterface.cpp | 15 +- .../src/images/simulator/PL18/bottom.png | Bin 0 -> 178 bytes companion/src/images/simulator/PL18/left.png | Bin 0 -> 1273 bytes companion/src/images/simulator/PL18/right.png | Bin 0 -> 1273 bytes companion/src/images/simulator/PL18/top.png | Bin 0 -> 178 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 13 + .../src/simulation/simulateduiwidgetPL18.cpp | 76 + .../src/simulation/simulateduiwidgetPL18.ui | 206 ++ companion/src/simulation/simulatorwidget.cpp | 3 + fw.json | 1 + radio/src/CMakeLists.txt | 4 +- radio/src/audio.h | 2 +- radio/src/bitmaps/480x272/CMakeLists.txt | 2 + radio/src/dataconstants.h | 5 +- radio/src/datastructs.h | 2 +- radio/src/datastructs_private.h | 2 +- radio/src/gui/colorlcd/radio_calibration.cpp | 2 +- radio/src/gui/colorlcd/radio_diagkeys.cpp | 6 +- radio/src/gui/colorlcd/radio_version.cpp | 2 +- radio/src/lua/api_general.cpp | 2 +- radio/src/opentx.h | 2 +- radio/src/pulses/flysky.cpp | 23 +- radio/src/pulses/modules_helpers.h | 15 + radio/src/pulses/multi.cpp | 2 +- radio/src/simu.cpp | 2 +- radio/src/storage/yaml/CMakeLists.txt | 2 + radio/src/storage/yaml/yaml_datastructs.cpp | 2 + .../storage/yaml/yaml_datastructs_pl18.cpp | 894 +++++++++ radio/src/targets/common/arm/CMakeLists.txt | 6 +- .../arm/stm32/bootloader/CMakeLists.txt | 3 +- radio/src/targets/pl18/CMakeLists.txt | 230 +++ radio/src/targets/pl18/audio_spi_driver.cpp | 495 +++++ radio/src/targets/pl18/backlight_driver.cpp | 75 + radio/src/targets/pl18/battery_driver.cpp | 196 ++ radio/src/targets/pl18/battery_driver.h | 58 + radio/src/targets/pl18/board.cpp | 243 +++ radio/src/targets/pl18/board.h | 558 ++++++ .../src/targets/pl18/bootloader/boot_menu.cpp | 242 +++ radio/src/targets/pl18/diskio.cpp | 386 ++++ radio/src/targets/pl18/extmodule_helper.cpp | 64 + radio/src/targets/pl18/hal.h | 569 ++++++ radio/src/targets/pl18/hallStick_driver.cpp | 615 ++++++ radio/src/targets/pl18/hallStick_driver.h | 220 +++ radio/src/targets/pl18/haptic_driver.cpp | 63 + radio/src/targets/pl18/keys_driver.cpp | 181 ++ radio/src/targets/pl18/lcd_driver.cpp | 1652 +++++++++++++++++ radio/src/targets/pl18/lcd_driver.h | 146 ++ radio/src/targets/pl18/libopenui_config.h | 147 ++ radio/src/targets/pl18/pulses_driver.cpp | 130 ++ radio/src/targets/pl18/sdram_driver.c | 256 +++ .../src/targets/pl18/startup_stm32f42_43xxx.s | 565 ++++++ radio/src/targets/pl18/stm32_ramboot.ld | 188 ++ radio/src/targets/pl18/stm32f4_flash.ld | 197 ++ .../targets/pl18/stm32f4_flash_bootloader.ld | 195 ++ radio/src/targets/pl18/telemetry_driver.cpp | 238 +++ radio/src/targets/pl18/touch_driver.cpp | 562 ++++++ radio/src/targets/pl18/touch_driver.h | 339 ++++ radio/src/targets/pl18/trainer_driver.cpp | 142 ++ radio/src/targets/simu/opentxsimulator.cpp | 2 + radio/src/telemetry/flysky_pl18.cpp | 250 +++ radio/src/telemetry/flysky_pl18.h | 71 + radio/src/telemetry/telemetry.h | 4 + radio/src/telemetry/telemetry_sensors.cpp | 16 +- radio/src/translations.cpp | 2 +- radio/src/translations/cn.h | 2 +- radio/src/translations/cz.h | 4 +- radio/src/translations/de.h | 4 +- radio/src/translations/en.h | 4 +- radio/src/translations/es.h | 4 +- radio/src/translations/fr.h | 4 +- radio/src/translations/it.h | 4 +- radio/src/translations/nl.h | 4 +- radio/src/translations/pl.h | 4 +- radio/src/translations/pt.h | 4 +- tools/build-companion.sh | 5 +- tools/build-flysky.py | 4 + tools/build-gh.sh | 3 + tools/commit-tests.sh | 3 + tools/generate-yaml.sh | 5 +- 86 files changed, 10666 insertions(+), 63 deletions(-) create mode 100644 companion/src/images/simulator/PL18/bottom.png create mode 100644 companion/src/images/simulator/PL18/left.png create mode 100644 companion/src/images/simulator/PL18/right.png create mode 100644 companion/src/images/simulator/PL18/top.png create mode 100644 companion/src/simulation/simulateduiwidgetPL18.cpp create mode 100644 companion/src/simulation/simulateduiwidgetPL18.ui create mode 100644 radio/src/storage/yaml/yaml_datastructs_pl18.cpp create mode 100644 radio/src/targets/pl18/CMakeLists.txt create mode 100644 radio/src/targets/pl18/audio_spi_driver.cpp create mode 100644 radio/src/targets/pl18/backlight_driver.cpp create mode 100644 radio/src/targets/pl18/battery_driver.cpp create mode 100644 radio/src/targets/pl18/battery_driver.h create mode 100644 radio/src/targets/pl18/board.cpp create mode 100644 radio/src/targets/pl18/board.h create mode 100644 radio/src/targets/pl18/bootloader/boot_menu.cpp create mode 100644 radio/src/targets/pl18/diskio.cpp create mode 100644 radio/src/targets/pl18/extmodule_helper.cpp create mode 100644 radio/src/targets/pl18/hal.h create mode 100644 radio/src/targets/pl18/hallStick_driver.cpp create mode 100644 radio/src/targets/pl18/hallStick_driver.h create mode 100644 radio/src/targets/pl18/haptic_driver.cpp create mode 100644 radio/src/targets/pl18/keys_driver.cpp create mode 100644 radio/src/targets/pl18/lcd_driver.cpp create mode 100644 radio/src/targets/pl18/lcd_driver.h create mode 100644 radio/src/targets/pl18/libopenui_config.h create mode 100644 radio/src/targets/pl18/pulses_driver.cpp create mode 100644 radio/src/targets/pl18/sdram_driver.c create mode 100644 radio/src/targets/pl18/startup_stm32f42_43xxx.s create mode 100644 radio/src/targets/pl18/stm32_ramboot.ld create mode 100644 radio/src/targets/pl18/stm32f4_flash.ld create mode 100644 radio/src/targets/pl18/stm32f4_flash_bootloader.ld create mode 100644 radio/src/targets/pl18/telemetry_driver.cpp create mode 100644 radio/src/targets/pl18/touch_driver.cpp create mode 100644 radio/src/targets/pl18/touch_driver.h create mode 100644 radio/src/targets/pl18/trainer_driver.cpp create mode 100644 radio/src/telemetry/flysky_pl18.cpp create mode 100644 radio/src/telemetry/flysky_pl18.h 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/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 87baaaa6a84..d8cffed0c73 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_TOGGLE, "SC"}, + {SWITCH_2POS, "SD"}, + {SWITCH_TOGGLE, "SE"}, + {SWITCH_3POS, "SF"}, + {SWITCH_3POS, "SG"}, + {SWITCH_TOGGLE, "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 2; else return 3; @@ -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,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case NumTrims: - 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 6; else if (IS_IFLIGHT_COMMANDO8(board)) return 0; @@ -749,6 +771,11 @@ 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"}, + }); } else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) { if (version < adcVersion) { tbl.insert(tbl.end(), { @@ -863,6 +890,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 0000000000000000000000000000000000000000..5fc10a49d42fe209dea8c83e3b8a9eeae29e6b43 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yoxj5K>M(rM(rsetupUi(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/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 9630fba462f..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) @@ -96,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() diff --git a/radio/src/audio.h b/radio/src/audio.h index 765d6f5daff..0e18fda2b06 100644 --- a/radio/src/audio.h +++ b/radio/src/audio.h @@ -106,7 +106,7 @@ enum AudioBufferState #define AUDIO_DATA_MIN 0 #define AUDIO_DATA_MAX 0xffff #define AUDIO_BITS_PER_SAMPLE 16 -#elif defined(PCBX12S) || defined(PCBNV14) +#elif defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) typedef int16_t audio_data_t; #define AUDIO_DATA_SILENCE 0 #define AUDIO_DATA_MIN INT16_MIN 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/dataconstants.h b/radio/src/dataconstants.h index 08ea06e26d3..da8fa3f65cc 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 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/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 9c54cbea837..e61d020aa80 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -93,7 +93,7 @@ void RadioCalibrationPage::buildBody(FormWindow * window) deco->setSlidersVisible(true); deco->setFlightModeVisible(false); -#if defined(PCBNV14) +#if defined(PCBNV14) // TODO! Check || 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_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index 8814e19064c..ba7013db7da 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) // TODO! Check && !defined(PCBPL18) constexpr coord_t SWITCHES_COLUMN = LCD_W / 2 - 20; constexpr coord_t TRIM_COLUMN = LCD_W - 120; #else @@ -94,7 +94,7 @@ class RadioKeyDiagsWindow : public Window dc->drawText(TRIM_MINUS_COLUMN, 1, "-", COLOR_THEME_PRIMARY1); dc->drawText(TRIM_PLUS_COLUMN, 1, "+", COLOR_THEME_PRIMARY1); -#if !defined(PCBNV14) +#if !defined(PCBNV14) && !defined(PCBPL18) // TODO! Check if can be removed !defined(PCBPL18) here // KEYS coord_t y = 1; for (uint8_t i = 0; i < keysGetMaxKeys(); i++) { @@ -108,7 +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) +#else // defined(PCBNV14) || defined(PCBPL18) // KEYS { coord_t y = 1; diff --git a/radio/src/gui/colorlcd/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp index 6c5f23c1d88..740858cb807 100644 --- a/radio/src/gui/colorlcd/radio_version.cpp +++ b/radio/src/gui/colorlcd/radio_version.cpp @@ -349,7 +349,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/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 3da534210d9..9c12fa72e69 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -3104,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/opentx.h b/radio/src/opentx.h index 6f1b76ed27b..39e607a1d13 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -361,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 diff --git a/radio/src/pulses/flysky.cpp b/radio/src/pulses/flysky.cpp index 6c99e324302..8a572a39b1f 100644 --- a/radio/src/pulses/flysky.cpp +++ b/radio/src/pulses/flysky.cpp @@ -18,13 +18,18 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Dedicate for FlySky NV14 board. + * For FlySky NV14 & PL18 boards. */ #include "opentx.h" #include "flysky.h" + +#if defined(PCBNV14) #include "telemetry/flysky_nv14.h" +#else +#include "telemetry/flysky_pl18.h" +#endif #define IS_VALID_COMMAND_ID(id) ((id) < CMD_LAST) @@ -52,7 +57,11 @@ enum DEBUG_RF_FRAME_PRINT_E { //#define DEBUG_RF_FRAME_PRINT BOTH_FRAME_PRINT #define FLYSKY_MODULE_TIMEOUT 155 /* ms */ #define FLYSKY_PERIOD 4 /*ms*/ +#if defined(PCBNV14) #define NUM_OF_NV14_CHANNELS (14) +#else +#define NUM_OF_PL18_CHANNELS (14) +#endif #define VALID_CH_DATA(v) ((v) > 900 && (v) < 2100) #define FAILSAVE_SEND_COUNTER_MAX (400) @@ -126,6 +135,7 @@ static uint8_t _flysky_timeout; static uint8_t _esc_state; uint32_t NV14internalModuleFwVersion = 0; +uint32_t PL18internalModuleFwVersion = 0; static rf_info_t rf_info = { .bind_power = BIND_LOW_POWER, @@ -409,7 +419,11 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) _flysky_timeout = FLYSKY_MODULE_TIMEOUT; break; case CMD_RX_SENSOR_DATA: +#if defined(PCBNV14) flySkyNv14ProcessTelemetryPacket(&resp->value, dataLen - 3); +#else + flySkyPl18ProcessTelemetryPacket(&resp->value, dataLen - 3); +#endif if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_NORMAL && _flysky_state >= STATE_IDLE) { setFlyskyState(STATE_SEND_CHANNELS); @@ -450,8 +464,13 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) } case CMD_GET_VERSION_INFO: { if (_flysky_state == STATE_GET_FW_VERSION_INIT) { +#if defined(PCBNV14) memcpy(&NV14internalModuleFwVersion, &resp->value + 1, sizeof(NV14internalModuleFwVersion)); +#else + memcpy(&PL18internalModuleFwVersion, &resp->value + 1, + sizeof(PL18internalModuleFwVersion)); +#endif setFlyskyState(STATE_SET_RECEIVER_ID); break; } @@ -498,6 +517,8 @@ void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* void resetPulsesAFHDS2() { NV14internalModuleFwVersion = 0; + PL18internalModuleFwVersion = 0; + intmodulePulsesData.flysky.frame_index = 1; _flysky_frame_index = 1; setFlyskyState(STATE_SET_TX_POWER); _flysky_timeout = 0; diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index e5c918e918f..f9e98f2056a 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -36,6 +36,9 @@ #if defined(PCBNV14) extern uint32_t NV14internalModuleFwVersion; #endif +#if defined(PCBPL18) +extern uint32_t PL18internalModuleFwVersion; +#endif #if defined(AFHDS3) #include "pulses/afhds3_module.h" @@ -648,12 +651,24 @@ inline uint32_t getNV14RfFwVersion() #endif } +inline uint32_t getPL18RfFwVersion() +{ +#if defined(PCBPL18) + return PL18internalModuleFwVersion; +#else + return 0; +#endif +} + inline bool isModuleRangeAvailable(uint8_t moduleIdx) { bool ret = isModuleBindRangeAvailable(moduleIdx) && !IS_RX_MULTI(moduleIdx); #if defined(PCBNV14) ret = ret && (!isModuleFlySky(moduleIdx) || NV14internalModuleFwVersion >= 0x1000E); +#elif defined(PCBPL18) + ret = ret && + (!isModuleFlySky(moduleIdx) || PL18internalModuleFwVersion >= 0x1000E); #else ret = ret && (!isModuleFlySky(moduleIdx)); #endif 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/simu.cpp b/radio/src/simu.cpp index feb3cd06011..249e267ab61 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -348,7 +348,7 @@ long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v) void OpenTxSim::updateKeysAndSwitches(bool start) { static int keys[] = { -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) // no keys #elif defined(PCBHORUS) KEY_Page_Up, KEY_PAGEUP, diff --git a/radio/src/storage/yaml/CMakeLists.txt b/radio/src/storage/yaml/CMakeLists.txt index b320ff016a9..6a7e537bbe6 100644 --- a/radio/src/storage/yaml/CMakeLists.txt +++ b/radio/src/storage/yaml/CMakeLists.txt @@ -20,6 +20,8 @@ elseif(PCB STREQUAL X10) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_x10.cpp) elseif(PCB STREQUAL NV14) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_nv14.cpp) +elseif(PCB STREQUAL PL18) + set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_pl18.cpp) elseif(PCB STREQUAL X7) if(PCBREV STREQUAL TPRO) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tpro.cpp) diff --git a/radio/src/storage/yaml/yaml_datastructs.cpp b/radio/src/storage/yaml/yaml_datastructs.cpp index fe77e7c5220..3b1a2254cf1 100644 --- a/radio/src/storage/yaml/yaml_datastructs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs.cpp @@ -34,6 +34,8 @@ #include "yaml_datastructs_x10.cpp" #elif defined(PCBNV14) #include "yaml_datastructs_nv14.cpp" +#elif defined(PCBPL18) + #include "yaml_datastructs_pl18.cpp" #elif defined(PCBX7) #if defined(RADIO_TPRO) || defined(RADIO_TPROV2) #include "yaml_datastructs_tpro.cpp" diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp new file mode 100644 index 00000000000..71e0dc87973 --- /dev/null +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -0,0 +1,894 @@ +// 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" }, + { 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" }, + { 0, NULL } +}; +const struct YamlIdStr enum_UartModes[] = { + { UART_MODE_NONE, "MODE_NONE" }, + { UART_MODE_TELEMETRY_MIRROR, "MODE_TELEMETRY_MIRROR" }, + { UART_MODE_TELEMETRY, "MODE_TELEMETRY" }, + { UART_MODE_SBUS_TRAINER, "MODE_SBUS_TRAINER" }, + { UART_MODE_LUA, "MODE_LUA" }, + { 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_MAX, "MAX" }, + { MIXSRC_CYC1, "CYC1" }, + { MIXSRC_CYC2, "CYC2" }, + { MIXSRC_CYC3, "CYC3" }, + { MIXSRC_TrimRud, "TrimRud" }, + { MIXSRC_TrimEle, "TrimEle" }, + { MIXSRC_TrimThr, "TrimThr" }, + { MIXSRC_TrimAil, "TrimAil" }, + { 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_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_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 } +}; + +// +// 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( "version", 8 ), + YAML_PADDING( 16 ), + 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_UNSIGNED( "telemetryBaudrate", 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_PADDING( 8 ), + YAML_UNSIGNED( "stickReverse", 8 ), + YAML_SIGNED_CUST( "beepLength", 3, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "hapticStrength", 3, r_5pos, w_5pos ), + YAML_UNSIGNED( "gpsFormat", 1 ), + YAML_UNSIGNED( "unexpectedShutdown", 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_UNSIGNED( "imperial", 1 ), + YAML_UNSIGNED( "jitterFilter", 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_ENUM("auxSerialMode", 4, enum_UartModes), + YAML_ENUM("aux2SerialMode", 4, enum_UartModes), + 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_PADDING( 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_SIGNED( "uartSampleMode", 2 ), + 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_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_STRING("name", 8), + YAML_END +}; +static const struct YamlNode struct_CurveRef[] = { + YAML_UNSIGNED( "type", 8 ), + YAML_SIGNED( "value", 8 ), + 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( "offset", 14 ), + 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( "min", 11 ), + YAML_SIGNED( "max", 11 ), + YAML_SIGNED( "ppmCenter", 10 ), + YAML_SIGNED( "offset", 11 ), + 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_UNSIGNED_CUST( "srcRaw", 10, r_mixSrcRaw, w_mixSrcRaw ), + YAML_SIGNED( "carryTrim", 6 ), + 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( "offset", 8 ), + 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, 4, 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_SIGNED( "disabled", 1 ), + YAML_UNSIGNED( "flysky_telemetry", 1 ), + YAML_SIGNED( "warning", 6 ), + YAML_PADDING( 2 ), + YAML_SIGNED( "critical", 6 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_5[] = { + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_UNSIGNED( "outputType", 1 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_6[] = { + YAML_PADDING( 3 ), + YAML_UNSIGNED( "disableTelemetry", 1 ), + YAML_UNSIGNED( "disableMapping", 1 ), + YAML_PADDING( 1 ), + YAML_UNSIGNED( "autoBindMode", 1 ), + YAML_UNSIGNED( "lowPowerMode", 1 ), + YAML_SIGNED( "optionValue", 8 ), + YAML_UNSIGNED( "receiverTelemetryOff", 1 ), + YAML_UNSIGNED( "receiverHigherChannels", 1 ), + YAML_PADDING( 6 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_7[] = { + 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_8[] = { + 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_9[] = { + 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_10[] = { + 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_11[] = { + YAML_UNSIGNED( "bindPower", 3 ), + YAML_UNSIGNED( "runPower", 3 ), + YAML_UNSIGNED( "emi", 1 ), + YAML_UNSIGNED( "telemetry", 1 ), + YAML_UNSIGNED( "failsafeTimeout", 16 ), + YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), + YAML_UNSIGNED( "mode", 2 ), + YAML_UNSIGNED( "reserved", 6 ), + YAML_END +}; +static const struct YamlNode union_anonymous_4_elmts[] = { + YAML_ARRAY("raw", 8, 25, struct_unsigned_8, NULL), + YAML_STRUCT("ppm", 16, struct_anonymous_5, NULL), + YAML_STRUCT("multi", 24, struct_anonymous_6, NULL), + YAML_STRUCT("pxx", 16, struct_anonymous_7, NULL), + YAML_STRUCT("sbus", 16, struct_anonymous_8, NULL), + YAML_STRUCT("pxx2", 200, struct_anonymous_9, NULL), + YAML_STRUCT("flysky", 56, struct_anonymous_10, NULL), + YAML_STRUCT("afhds3", 64, struct_anonymous_11, NULL), + YAML_END +}; +static const struct YamlNode struct_ModuleData[] = { + YAML_IDX, + YAML_ENUM("type", 4, enum_ModuleType), + YAML_PADDING( 4 ), + 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( 3 ), + YAML_PADDING( 1 ), + 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_12_elmts[] = { + YAML_UNSIGNED( "id", 16 ), + YAML_UNSIGNED( "persistentValue", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_14[] = { + YAML_UNSIGNED( "physID", 5 ), + YAML_UNSIGNED( "rxIndex", 3 ), + YAML_END +}; +static const struct YamlNode union_anonymous_13_elmts[] = { + YAML_STRUCT("frskyInstance", 8, struct_anonymous_14, NULL), + YAML_UNSIGNED( "instance", 8 ), + YAML_ENUM("formula", 8, enum_TelemetrySensorFormula), + YAML_END +}; +static const struct YamlNode struct_anonymous_16[] = { + YAML_UNSIGNED( "ratio", 16 ), + YAML_SIGNED( "offset", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_17[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_UNSIGNED( "index", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_18[] = { + YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_19[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_PADDING( 24 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_20[] = { + YAML_UNSIGNED( "gps", 8 ), + YAML_UNSIGNED( "alt", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode union_anonymous_15_elmts[] = { + YAML_STRUCT("custom", 32, struct_anonymous_16, NULL), + YAML_STRUCT("cell", 32, struct_anonymous_17, NULL), + YAML_STRUCT("calc", 32, struct_anonymous_18, NULL), + YAML_STRUCT("consumption", 32, struct_anonymous_19, NULL), + YAML_STRUCT("dist", 32, struct_anonymous_20, NULL), + YAML_UNSIGNED( "param", 32 ), + YAML_END +}; +static const struct YamlNode struct_TelemetrySensor[] = { + YAML_IDX, + YAML_UNION("id1", 16, union_anonymous_12_elmts, select_id1), + YAML_UNION("id2", 8, union_anonymous_13_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_15_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", 10), + YAML_STRUCT("layoutData", 6720, struct_LayoutPersistentData, NULL), + YAML_END +}; +static const struct YamlNode struct_TopBarPersistentData[] = { + YAML_ARRAY("zones", 576, 2, struct_ZonePersistentData, NULL), + YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_ModelData[] = { + YAML_STRUCT("header", 248, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 128, 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( "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", 320, 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", 16, struct_RssiAlarmData, NULL), + YAML_PADDING( 3 ), + YAML_UNSIGNED( "thrTrimSw", 3 ), + YAML_ENUM("potsWarnMode", 2, enum_PotsWarnMode), + 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", 6800, 5, struct_CustomScreenData, NULL), + YAML_STRUCT("topbarData", 1248, struct_TopBarPersistentData, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_STRING("modelRegistrationID", 8), + YAML_END +}; +static const struct YamlNode struct_PartialModel[] = { + YAML_STRUCT("header", 248, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 128, 3, struct_TimerData, NULL), + YAML_END +}; + +#define MAX_RADIODATA_MODELDATA_PARTIALMODEL_STR_LEN 24 + +static const struct YamlNode __RadioData_root_node = YAML_ROOT( struct_RadioData ); + +const YamlNode* get_radiodata_nodes() +{ + return &__RadioData_root_node; +} +static const struct YamlNode __ModelData_root_node = YAML_ROOT( struct_ModelData ); + +const YamlNode* get_modeldata_nodes() +{ + return &__ModelData_root_node; +} +static const struct YamlNode __PartialModel_root_node = YAML_ROOT( struct_PartialModel ); + +const YamlNode* get_partialmodel_nodes() +{ + return &__PartialModel_root_node; +} + diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 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 6ff29f7ee51..69fd467d1b4 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -90,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 diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt new file mode 100644 index 00000000000..a3ad57e9f82 --- /dev/null +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -0,0 +1,230 @@ +option(DISK_CACHE "Enable SD card disk cache" ON) +option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) +option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) +option(AFHDS2 "Support for AFHDS2" ON) +option(GHOST "Ghost TX Module" ON) +option(PXX1 "PXX1 protocol support" ON) +option(PXX2 "PXX2 protocol support" OFF) + +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") +set(CPU_TYPE STM32F4) +set(HSE_VALUE 12000000) +set(SDCARD YES) +set(STORAGE SDCARD) +set(STORAGE_FORMAT RAW) +set(STORAGE_MODELSLIST YES) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +set(BITMAPS_DIR 480x272) +set(HARDWARE_EXTERNAL_MODULE YES) +#set(AUX_SERIAL ON) +# set(NAVIGATION_TYPE touch) +set(TARGET_DIR pl18) +add_definitions(-DHARDWARE_TRAINER_JACK) + +if(BOOTLOADER) + set(LINKER_SCRIPT targets/pl18/stm32f4_flash_bootloader.ld) +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 -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) +add_definitions(-DPCBPL18 -DPCBFLYSKY -DINTERNAL_MODULE_SERIAL -DAFHDS2_BAUDRATE=${PCB_RF_BAUD}) +add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME +add_definitions(-DINTERNAL_MODULE_AFHDS2A) + +# defines existing internal modules +set(INTERNAL_MODULES AFHDS2A CACHE STRING "Internal modules") +set(DEFAULT_INTERNAL_MODULE FLYSKY CACHE STRING "Default internal module") + +set(TARGET_SRC + ${TARGET_SRC} + audio_spi_driver.cpp + ../common/arm/stm32/mixer_scheduler_driver.cpp + ../common/arm/stm32/timers_driver.cpp + ) + +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + battery_driver.cpp + ../horus/flyskyHallStick_driver.cpp + ../common/arm/stm32/stm32_hal_adc.cpp + ../../hal/adc_driver.cpp + ) + +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 touch_driver.cpp) +set(HARDWARE_TOUCH YES) + +set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET} truetype_fonts) +set(FIRMWARE_DEPENDENCIES datacopy) + +set(HARDWARE_TOUCH ON) +set(SOFTWARE_KEYBOARD ON) + + +add_definitions(-DSTM32F429_439xx -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(-DAFHDS2) +add_definitions(-DHARDWARE_EXTERNAL_MODULE) +#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() + +set(AFHDS2 ON) + +# disable for now as it crashes on radio +#set(AFHDS3 ON) + +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) + +set(TARGET_SRC + ${TARGET_SRC} + extmodule_helper.cpp + ../horus/extmodule_driver.cpp + trainer_driver.cpp + ../common/arm/stm32/intmodule_serial_driver.cpp + ) + +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ${LCD_DRIVER} + ${TOUCH_DRIVER} + board.cpp + backlight_driver.cpp + sdram_driver.c + startup_stm32f42_43xxx.s + ../common/arm/stm32/pwr_driver.cpp + ../common/arm/stm32/sdio_sd.c + ) + +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() + +# 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 + ) + +MACRO(GET_GCC_INCLUDE_PATH gcc_include_path) + if (WIN32) + execute_process(COMMAND arm-none-eabi-gcc -v -x c -E NUL ERROR_VARIABLE _gcc_output OUTPUT_QUIET) + else() + execute_process(COMMAND arm-none-eabi-gcc -v -x c -E - INPUT_FILE /dev/null ERROR_VARIABLE _gcc_output OUTPUT_QUIET) + endif() + + # Build an array of string from the GCC output + string(REPLACE "\n" ";" _gcc_output "${_gcc_output}") + + set(_capture_include FALSE) + set(_include_path "") + + # Go through the lines and capture between '"#include <...> search starts here:"' and 'End of search list.' + foreach(_line ${_gcc_output}) + if(${_line} STREQUAL "End of search list.") + set(_capture_include FALSE) + endif() + + if(_capture_include) + # Remove the leading and trailing empty characters + string(REPLACE "\r" "" _line ${_line}) + string(SUBSTRING "${_line}" 1 -1 _line) + set(_include_path "${_include_path}" "-I\"${_line}\"") + endif() + + if(${_line} STREQUAL "#include <...> search starts here:") + set(_capture_include TRUE) + endif() + endforeach() + set(${gcc_include_path} ${_include_path}) +ENDMACRO() + +if(PYTHONINTERP_FOUND) + GET_GCC_INCLUDE_PATH(gcc_include_path) + + add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp + WORKING_DIRECTORY ${RADIO_DIRECTORY}/src + COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs_private.h -DPCBFLYSKY -DPCBPL18 -DCPUARM -DSTM32F4 -DSTM32F429_439xx -DCOLORLCD -DBACKUP -DSIMU -I. -Igui/colorlcd -Itargets/pl18 -Itargets/common/arm/stm32 -I${THIRDPARTY_DIR} -I${THIRDPARTY_DIR}/libopenui/src -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/STM32F4xx_StdPeriph_Driver/inc -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Include ${gcc_include_path} -Wno-asm-operand-widths -Wno-pragma-once-outside-header ${SYSROOT_ARG} > ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp + DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py + ) + + add_custom_target(datacopy + DEPENDS ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp + ) +endif() + 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..2308939f93a --- /dev/null +++ b/radio/src/targets/pl18/backlight_driver.cpp @@ -0,0 +1,75 @@ +/* + * 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 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..2f92e415b24 --- /dev/null +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -0,0 +1,196 @@ +/* + * 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 320 +#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) + +void battery_charge_init() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN; + 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_UP; + GPIO_Init(PWR_CHARGING_GPIO, &GPIO_InitStructure); + GPIO_SetBits(PWR_CHARGING_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN); +} + +#define CHARGE_SAMPLES 10 + +uint16_t get_battery_charge_state() +{ + static uint16_t chargeSamples[CHARGE_SAMPLES] = {0}; + static uint16_t chargeSampleIndex = 0; + uint16_t chargeState = CHARGE_UNKNOWN; + int maxSamples = CHARGE_SAMPLES; +#if !defined(SIMU) + bool isFinished = !READ_CHARGE_FINISHED_STATE(); + bool isCharging = !READ_CHARGING_STATE(); + //maxSamples = boardState == BOARD_POWER_OFF ? CHARGE_SAMPLES/2 : CHARGE_SAMPLES; + if(chargeSampleIndex >= maxSamples) chargeSampleIndex = 0; + uint16_t currentChargeState = isFinished ? CHARGE_FINISHED : isCharging ? CHARGE_STARTED : CHARGE_NONE; + chargeSamples[chargeSampleIndex++] = currentChargeState; + //TRACE("CHARGE sample %d value %d", chargeSampleIndex -1, currentChargeState); +#endif + uint8_t temp1 = 0, temp2 = 0, temp3 = 0; + for(int index = 0; index < maxSamples; index++) { + if(chargeSamples[index] == CHARGE_FINISHED) + { + temp1++; + } + else if(chargeSamples[index] == CHARGE_STARTED) + { + temp2++; + } + else + { + temp3++; + } + + if(temp1>=temp2&& temp1>temp3) + { + chargeState = CHARGE_FINISHED; + } + else if(temp2>=temp1&& temp2>temp3) + { + chargeState = CHARGE_STARTED; + } + else + { + chargeState = CHARGE_NONE; + } + } + return chargeState; +} + +void drawChargingInfo(uint16_t chargeState){ + static int progress = 0; + const char* text = chargeState == CHARGE_STARTED ? STR_BATTERYCHARGING : STR_BATTERYFULL; + int h = 0; + LcdFlags color = 0; + if(CHARGE_STARTED == chargeState) + { + if(progress >= 100) + { + progress = 0; + } + else + { + progress+=25; + } + text = STR_BATTERYCHARGING; + h = ((BATTERY_H_INNER * progress) / 100); + color = COLOR_THEME_EDIT; + } + else if(CHARGE_FINISHED == chargeState) + { + text = STR_BATTERYFULL; + h = BATTERY_H_INNER; + color = COLOR_THEME_EDIT; + } + else + { + text = STR_BATTERYNONE; + h = BATTERY_H_INNER; + color = COLOR_THEME_PRIMARY1; + } + + BACKLIGHT_ENABLE(); + lcd->drawSizedText(LCD_W/2, LCD_H-50, text, strlen(text), CENTERED|COLOR_THEME_PRIMARY2); + + lcd->drawFilledRect((LCD_W - BATTERY_W)/2, BATTERY_TOP, BATTERY_W, BATTERY_H, SOLID, COLOR_THEME_PRIMARY2); + lcd->drawFilledRect((LCD_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER, BATTERY_W_INNER, BATTERY_H_INNER, SOLID, COLOR_THEME_PRIMARY1); + + lcd->drawFilledRect((LCD_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER + BATTERY_H_INNER - h , BATTERY_W_INNER, h, SOLID, color); + lcd->drawFilledRect((LCD_W - BATTERY_CONNECTOR_W)/2, BATTERY_TOP-BATTERY_CONNECTOR_H , BATTERY_CONNECTOR_W, BATTERY_CONNECTOR_H, SOLID, COLOR_THEME_PRIMARY2); +} +#define CHARGE_INFO_DURATION 500 +//this method should be called by timer interrupt or by GPIO interrupt +void handle_battery_charge(uint32_t last_press_time) +{ +#if !defined(SIMU) + static uint32_t updateTime = 0; + static uint16_t lastState = CHARGE_UNKNOWN; + static uint32_t info_until = 0; + static bool lcdInited = false; + + uint32_t now = get_tmr10ms(); + uint16_t chargeState = get_battery_charge_state(); + if(chargeState != CHARGE_UNKNOWN) { + + if(lastState != chargeState) { + //avoid positive check when none and unknown + if(lastState + chargeState > 1) { + //charge state changed - last state known + info_until = now + (CHARGE_INFO_DURATION); + } + } + //power buttons pressed + else if(now - last_press_time < POWER_ON_DELAY) { + info_until = now + CHARGE_INFO_DURATION; + } + lastState = chargeState; + } + + if(now > info_until) { + info_until = 0; + lcd->clear(); + BACKLIGHT_DISABLE(); + if(lcdInited) { + lcdOff(); + } + return; + } + + if(updateTime == 0 || ((get_tmr10ms() - updateTime) >= 50)) + { + if(!lcdInited) { + backlightInit(); + lcdInit(); + lcdInited = true; + } + else { + lcdOn(); + } + updateTime = get_tmr10ms(); + lcd->clear(); + drawChargingInfo(chargeState); + lcdRefresh(); + } +#endif +} + +uint16_t getBatteryVoltage() +{ + int32_t instant_vbat = anaIn(TX_VOLTAGE); // using filtered ADC value on purpose + return (uint16_t)((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) / 2942); +} diff --git a/radio/src/targets/pl18/battery_driver.h b/radio/src/targets/pl18/battery_driver.h new file mode 100644 index 00000000000..1eace4d8cb9 --- /dev/null +++ b/radio/src/targets/pl18/battery_driver.h @@ -0,0 +1,58 @@ +/* + * 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" + +enum ChargeState +{ + CHARGE_UNKNOWN, + CHARGE_NONE, + CHARGE_STARTED, + CHARGE_FINISHED +}; + + +#define PWR_CHARGE_FINISHED_GPIO GPIOB +#define PWR_CHARGE_FINISHED_GPIO_REG PWR_CHARGE_FINISHED_GPIO->IDR +#define PWR_CHARGE_FINISHED_GPIO_PIN GPIO_Pin_13 // PB.13 + +#define PWR_CHARGING_GPIO GPIOB +#define PWR_CHARGING_GPIO_REG PWR_CHARGING_GPIO->IDR +#define PWR_CHARGING_GPIO_PIN GPIO_Pin_14 // PB.14 + +#define READ_CHARGE_FINISHED_STATE() GPIO_ReadInputDataBit( PWR_CHARGE_FINISHED_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN ) +#define READ_CHARGING_STATE() GPIO_ReadInputDataBit( PWR_CHARGING_GPIO, PWR_CHARGING_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 + +#endif diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp new file mode 100644 index 00000000000..379e6d935f4 --- /dev/null +++ b/radio/src/targets/pl18/board.cpp @@ -0,0 +1,243 @@ +/* + * 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 "touch.h" + +#include "hal/adc_driver.h" +#include "../common/arm/stm32/stm32_hal_adc.h" + +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif +#include "usb_dcd_int.h" +#include "usb_bsp.h" +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +extern void flysky_hall_stick_init( void ); + +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 + +void delay_self(int count) +{ + for (int i = 50000; i > 0; i--) + { + for (; count > 0; count--); + } +} +#define RCC_AHB1PeriphMinimum (PWR_RCC_AHB1Periph |\ + LCD_RCC_AHB1Periph |\ + BACKLIGHT_RCC_AHB1Periph |\ + SDRAM_RCC_AHB1Periph \ + ) +#define RCC_AHB1PeriphOther (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 |\ + AUDIO_RCC_AHB1Periph |\ + HAPTIC_RCC_AHB1Periph |\ + INTMODULE_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 |\ + TRAINER_RCC_APB1Periph |\ + INTMODULE_RCC_APB1Periph |\ + FLYSKY_HALL_RCC_APB1Periph |\ + EXTMODULE_RCC_APB1Periph |\ + INTMODULE_RCC_APB1Periph |\ + AUX_SERIAL_RCC_APB1Periph |\ + MIXER_SCHEDULER_TIMER_RCC_APB1Periph \ + ) +#define RCC_APB2PeriphMinimum (LCD_RCC_APB2Periph) + +#define RCC_APB2PeriphOther (ADC_RCC_APB2Periph |\ + HAPTIC_RCC_APB2Periph |\ + AUX_SERIAL_RCC_APB2Periph |\ + AUDIO_RCC_APB2Periph |\ + EXTMODULE_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) && defined(AUX_SERIAL) + auxSerialInit(UART_MODE_DEBUG, 0); // default serial mode (None if DEBUG not defined) +#endif +#if defined(DEBUG) && defined(AUX2_SERIAL) + aux2SerialInit(UART_MODE_DEBUG, 0); // default serial mode (None if DEBUG not defined) +#endif + + TRACE("\nPL18 board started :)"); + delay_ms(10); + TRACE("RCC->CSR = %08x", RCC->CSR); + + pwrInit(); + extModuleInit(); + battery_charge_init(); + globalData.flyskygimbals = true; + flysky_hall_stick_init(); + init2MhzTimer(); + init1msTimer(); + TouchInit(); + usbInit(); + + uint32_t press_start = 0; + uint32_t press_end = 0; + + if (UNEXPECTED_SHUTDOWN()) { + pwrOn(); + } else { + + // prime debounce state... + usbPlugged(); + + while (usbPlugged()) { + 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()) { + touchPanelRead(); + press_end_touch = get_tmr10ms(); + } + press_start = 0; + handle_battery_charge(press_end_touch); + delay_ms(20); + press_end = 0; + } + } + } + + keysInit(); + audioInit(); + // we need to initialize g_FATFS_Obj here, because it is in .ram section (because of DMA access) + // and this section is un-initialized + memset(&g_FATFS_Obj, 0, sizeof(g_FATFS_Obj)); + monitorInit(); + adcInit(&stm32_hal_adc_driver); + backlightInit(); + lcdInit(); + 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_WIDTH, LCD_HEIGHT, SOLID, COLOR_THEME_FOCUS); + lcdOff(); + + SysTick->CTRL = 0; // turn off systick + + while (pwrPressed()) { + WDG_RESET(); + } + +#if defined(RTC_BACKUP_RAM) + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, DISABLE); + PWR_BackupRegulatorCmd(DISABLE); +#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) { + + } +} diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h new file mode 100644 index 00000000000..b45619071b8 --- /dev/null +++ b/radio/src/targets/pl18/board.h @@ -0,0 +1,558 @@ +/* + * 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" + +#if !defined(LUA_EXPORT_GENERATION) +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_dma2d.h" +#include "stm32f4xx_ltdc.h" +#include "stm32f4xx_fmc.h" +#endif + +#include "touch_driver.h" +//#include "hallStick_driver.h" +#include "lcd_driver.h" +#include "battery_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 + +// 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 + +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()) +#define SD_CARD_PRESENT() (~SD_PRESENT_GPIO->IDR & SD_PRESENT_GPIO_PIN) +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 +#define INTERNAL_MODULE_OFF() GPIO_SetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +#define INTERNAL_MODULE_ON() GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +void EXTERNAL_MODULE_ON(); +void EXTERNAL_MODULE_OFF(); +#define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF +#define BLUETOOTH_MODULE_ON() GPIO_ResetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) +#define BLUETOOTH_MODULE_OFF() GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) +#define IS_INTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) == Bit_SET) +#define IS_EXTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) == Bit_SET) +#define IS_UART_MODULE(port) (port == INTERNAL_MODULE) +#define IS_PXX2_INTERNAL_ENABLED() (false) + +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_LAST = TRM_LS_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_TOGGLE << 14) + (SWITCH_3POS << 12) + (SWITCH_3POS << 10) + (SWITCH_TOGGLE << 8) + (SWITCH_2POS << 6) + (SWITCH_TOGGLE << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); + +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 NUM_STICKS +#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) + +#define WDG_DURATION 500 /*ms*/ +void watchdogInit(unsigned int duration); +#if defined(SIMU) + #define WAS_RESET_BY_WATCHDOG() (false) + #define WAS_RESET_BY_SOFTWARE() (false) + #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (false) + #define WDG_ENABLE(x) + #define WDG_RESET() +#else + #if defined(WATCHDOG) + #define WDG_ENABLE(x) watchdogInit(x) + #define WDG_RESET() IWDG->KR = 0xAAAA + #else + #define WDG_ENABLE(x) + #define WDG_RESET() + #endif + #define WAS_RESET_BY_WATCHDOG() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF)) + #define WAS_RESET_BY_SOFTWARE() (RCC->CSR & RCC_CSR_SFTRSTF) + #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_SFTRSTF)) +#endif + +// ADC driver +#define NUM_POTS 2 +#define NUM_XPOTS 0 // NUM_POTS +#define NUM_SLIDERS 0 +#define NUM_PWMSTICKS 0 +#define NUM_MOUSE_ANALOGS 0 +#define STORAGE_NUM_POTS 5 +#define STORAGE_NUM_SLIDERS 0 +#define STORAGE_NUM_MOUSE_ANALOGS 0 + +enum Analogs { + STICK1, + STICK2, + STICK3, + STICK4, + POT_FIRST, + POT1 = POT_FIRST, + POT2, + POT_LAST = POT2, + SWITCH_FIRST, + SWA = SWITCH_FIRST, + SWB, + SWC, + SWD, + SWE, + SWF, + SWG, + SWH, + SUB_ANALOG_POS = SWH, + SWITCH_END = SWH, + TX_VOLTAGE, + TX_VBAT, + NUM_ANALOGS +}; + +#define SLIDER_FIRST 0 +#define SLIDER_LAST -1 + +#define DEFAULT_POTS_CONFIG (POT_WITHOUT_DETENT << 0) + (POT_WITHOUT_DETENT << 2) // 2 pots without detent + +enum CalibratedAnalogs { + CALIBRATED_STICK1, + CALIBRATED_STICK2, + CALIBRATED_STICK3, + CALIBRATED_STICK4, + CALIBRATED_POT1, + CALIBRATED_POT2, + CALIBRATED_SWA, + CALIBRATED_SWB, + CALIBRATED_SWC, + 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) (false) + +extern uint16_t adcValues[NUM_ANALOGS]; + + +#define BATTERY_WARN 36 // 3.6V +#define BATTERY_MIN 35 // 3.5V +#define BATTERY_MAX 42 // 4.2V + +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();; + +#define AUX_SERIAL_POWER_ON() +#define AUX_SERIAL_POWER_OFF() + +// LCD driver +#define LCD_W 480 +#define LCD_H 320 +#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 + +// 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) || defined(PCBPL18) +#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 TELEMETRY_FIFO_SIZE 512 +void telemetryPortInit(uint32_t baudrate, uint8_t mode); +void telemetryPortSetDirectionOutput(); +void telemetryPortSetDirectionInput(); +void sportSendBuffer(const uint8_t * buffer, uint32_t count); +bool telemetryGetByte(uint8_t * byte); +void telemetryClearFifo(); +void sportSendByte(uint8_t byte); +extern uint32_t telemetryErrors; + +// soft-serial +void telemetryPortInvertedInit(uint32_t baudrate); + +// Sport update driver +#define SPORT_UPDATE_POWER_ON() +#define SPORT_UPDATE_POWER_OFF() +#define SPORT_UPDATE_POWER_INIT() +#define IS_SPORT_UPDATE_POWER_ON() (false) + +// 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 auxSerialMode; +#if defined __cplusplus +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); +#endif +void auxSerialInit(unsigned int mode, unsigned int protocol); +void auxSerialPutc(char c); +#define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) +void auxSerialSbusInit(); +void auxSerialStop(); +#if defined(AUX_SERIAL_PWR_GPIO) +#define AUX_SERIAL_POWER_ON() auxSerialPowerOn() +#define AUX_SERIAL__POWER_OFF() auxSerialPowerOff() +#else +#define AUX_SERIAL_POWER_ON() +#define AUX_SERIAL__POWER_OFF() +#endif +#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) + +extern uint8_t currentTrainerMode; +void checkTrainerSettings(); + +#if defined(__cplusplus) +#include "fifo.h" +#include "dmafifo.h" +extern DMAFifo<512> telemetryFifo; +typedef Fifo AuxSerialRxFifo; +extern AuxSerialRxFifo auxSerialRxFifo; +#endif + +// Touch panel driver +bool touchPanelEventOccured(); +struct TouchState touchPanelRead(); + +#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..af6d49cee99 --- /dev/null +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -0,0 +1,242 @@ +/* + * 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 "../../common/arm/stm32/bootloader/boot.h" +#include "../../common/arm/stm32/bootloader/bin_files.h" + +#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_rle[] { +#include "bmp_plug_usb.lbm" +}; +RLEBitmap BMP_PLUG_USB(BMP_ARGB4444, __bmp_plug_usb_rle); + +const uint8_t __bmp_usb_plugged_rle[] { +#include "bmp_usb_plugged.lbm" +}; +RLEBitmap BMP_USB_PLUGGED(BMP_ARGB4444, __bmp_usb_plugged_rle); + +const uint8_t __bmp_background_rle[] { +#include "bmp_background.lbm" +}; +RLEBitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background_rle); + +const uint8_t LBM_FLASH[] = { +#include "icon_flash.lbm" +}; + +const uint8_t LBM_EXIT[] = { +#include "icon_exit.lbm" +}; + +const uint8_t LBM_SD[] = { +#include "icon_sd.lbm" +}; + +const uint8_t LBM_FILE[] = { +#include "icon_file.lbm" +}; + +const uint8_t LBM_ERROR[] = { +#include "icon_error.lbm" +}; + +const uint8_t LBM_OK[] = { +#include "icon_ok.lbm" +}; + +#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 + +void bootloaderInitScreen() +{ + backlightEnable(BACKLIGHT_LEVEL_MAX); + + // TODO: load/decompress bitmaps + extern void loadFonts(); + loadFonts(); + +} + +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, &BMP_BACKGROUND); + } + } + _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) +{ + // clear screen + bootloaderDrawBackground(); + + int center = LCD_W/2; + if (st == ST_START) { + + bootloaderDrawTitle(BOOTLOADER_TITLE); + + lcd->drawBitmapPattern(50, 72, LBM_FLASH, BL_FOREGROUND); + lcd->drawText(84, 75, "Write Firmware", BL_FOREGROUND); + + lcd->drawBitmapPattern(50, 107, LBM_EXIT, BL_FOREGROUND); + lcd->drawText(84, 110, "Exit", BL_FOREGROUND); + + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, LCD_W - 79 - 28, 26, 2, BL_SELECTED); + + lcd->drawBitmap(center - 55, 165, &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); + } + else if (st == ST_USB) { + lcd->drawBitmap(center - 26, 98, &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("SD>FIRMWARE"); + lcd->drawBitmapPattern(DEFAULT_PADDING, 16, LBM_SD, BL_FOREGROUND); + + 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, "Directory is missing", BL_FOREGROUND); + } + else { + lcd->drawText(20, MESSAGE_TOP, "Directory is empty", BL_FOREGROUND); + } + + lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); + } + else if (st == ST_FLASH_CHECK) { + + bootloaderDrawFilename(str, 0, true); + + if (opt == FC_ERROR) { + lcd->drawText(20, MESSAGE_TOP, STR_INVALID_FIRMWARE, BL_FOREGROUND); + lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); + } + 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->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_OK, BL_GREEN); + } + } + + bootloaderDrawFooter(); + + if ( st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { + + lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, LBM_FLASH, 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->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING - 2, LBM_EXIT, BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit", BL_FOREGROUND); + } + } +} + +void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) +{ + lcd->drawBitmapPattern(DEFAULT_PADDING, 76 + (line * 25), LBM_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); + } +} diff --git a/radio/src/targets/pl18/diskio.cpp b/radio/src/targets/pl18/diskio.cpp new file mode 100644 index 00000000000..c44c85a974c --- /dev/null +++ b/radio/src/targets/pl18/diskio.cpp @@ -0,0 +1,386 @@ +/* + * 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 +#include "opentx.h" +#include "targets/common/arm/stm32/sdio_sd.h" + +// 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 +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..40ff0f9c2e0 --- /dev/null +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -0,0 +1,64 @@ +/* + * 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 EXTERNAL_MODULE_ON() +{ + GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_ResetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); +} + +void EXTERNAL_MODULE_OFF() +{ + GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); +} + +void extModuleInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_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_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); + + //for additional transistor to ensuring module is completely disabled + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + //pin must be pulled to V+ (voltage of board - VCC is not enough to fully close transistor) + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_FIX_GPIO_PIN; + GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); + GPIO_Init(EXTMODULE_PWR_FIX_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + 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); + + GPIO_ResetBits(EXTMODULE_TX_INVERT_GPIO, EXTMODULE_TX_INVERT_GPIO_PIN); + GPIO_ResetBits(EXTMODULE_RX_INVERT_GPIO, EXTMODULE_RX_INVERT_GPIO_PIN); +} diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h new file mode 100644 index 00000000000..566bcdc13b4 --- /dev/null +++ b/radio/src/targets/pl18/hal.h @@ -0,0 +1,569 @@ +/* + * 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 + +/* Timers Allocation: + * TIM1 = Haptic + * TIM4 = Trainer + * TIM6 = Audio + * TIM7 = 2MHz counter + * + * + * TIM14 = 5ms counter + */ + +/* DMA Allocation: + DMA/Stream/Channel + 1/5/7 DAC/Audio + 2/0/0 ADC1 + 2/3/4 SDIO +*/ + + +// Trims +#define TRIMS_GPIO_REG_RHL GPIOD->IDR +#define TRIMS_GPIO_PIN_RHL GPIO_Pin_7 // PD.07 +#define TRIMS_GPIO_REG_RHR GPIOG->IDR +#define TRIMS_GPIO_PIN_RHR GPIO_Pin_10 // PG.10 +#define TRIMS_GPIO_REG_RVD GPIOJ->IDR +#define TRIMS_GPIO_PIN_RVD GPIO_Pin_0 // PJ.00 +#define TRIMS_GPIO_REG_RVU GPIOB->IDR +#define TRIMS_GPIO_PIN_RVU GPIO_Pin_15 // PB.15 +#define TRIMS_GPIO_REG_RPRESS GPIOC->IDR +#define TRIMS_GPIO_PIN_RPRESS GPIO_Pin_13 // PC.13 + +#define TRIMS_GPIO_REG_LHL GPIOH->IDR +#define TRIMS_GPIO_PIN_LHL GPIO_Pin_2 // PH.02 +#define TRIMS_GPIO_REG_LHR GPIOG->IDR +#define TRIMS_GPIO_PIN_LHR GPIO_Pin_2 // PG.02 +#define TRIMS_GPIO_REG_LVU GPIOH->IDR +#define TRIMS_GPIO_PIN_LVU GPIO_Pin_7 // PH.07 +#define TRIMS_GPIO_REG_LVD GPIOJ->IDR +#define TRIMS_GPIO_PIN_LVD GPIO_Pin_12 // PJ.12 +#define TRIMS_GPIO_REG_LPRESS GPIOG->IDR +#define TRIMS_GPIO_PIN_LPRESS GPIO_Pin_11 // PG.11 + +// 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 +#define HARDWARE_SWITCH_B +#define STORAGE_SWITCH_B +#define HARDWARE_SWITCH_C +#define STORAGE_SWITCH_C +#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_13) +#define KEYS_GPIOD_PINS (GPIO_Pin_7) +#define KEYS_GPIOG_PINS (GPIO_Pin_2 | GPIO_Pin_10 | GPIO_Pin_11) +#define KEYS_GPIOH_PINS (GPIO_Pin_2 | GPIO_Pin_7) +#define KEYS_GPIOJ_PINS (GPIO_Pin_0 | GPIO_Pin_12) + +// 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) +// FLYSKY_HALL_STICKS +#define ADC_GPIO_PIN_STICK_LH 0 +#define ADC_GPIO_PIN_STICK_LV 0 +#define ADC_GPIO_PIN_STICK_RH 0 +#define ADC_GPIO_PIN_STICK_RV 0 + +#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_SWA GPIO_Pin_1 // PB.01 +#define ADC_GPIO_PIN_SWB GPIO_Pin_8 // PF.08 +#define ADC_GPIO_PIN_SWC GPIO_Pin_0 // PB.00 +#define ADC_GPIO_PIN_SWD GPIO_Pin_10 // PF.10 +#define ADC_GPIO_PIN_SWE GPIO_Pin_2 // PC.02 +#define ADC_GPIO_PIN_SWF GPIO_Pin_7 // PA.07 +#define ADC_GPIO_PIN_SWG GPIO_Pin_0 // PC.00 +#define ADC_GPIO_PIN_SWH GPIO_Pin_1 // PC.01 +#define ADC_GPIO_PIN_BATT GPIO_Pin_5 // PC.05 + +// FLYSKY_HALL_STICKS +#define ADC_GPIOA_PINS_FS (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_8 | 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_SWA ADC_Channel_9 // ADC12_IN9 -> ADC1_IN9 +#define ADC_CHANNEL_SWB ADC_Channel_6 // ADC3_IN6 -> ADC3_IN6 +#define ADC_CHANNEL_SWC ADC_Channel_8 // ADC12_IN8 -> ADC1_IN8 +#define ADC_CHANNEL_SWD ADC_Channel_8 // ADC3_IN8 -> ADC3_IN8 +#define ADC_CHANNEL_SWE ADC_Channel_10 // ADC123_IN10-> ADC1_IN10 +#define ADC_CHANNEL_SWF ADC_Channel_11 // ADC123_IN11-> ADC1_IN11 +#define ADC_CHANNEL_SWG ADC_Channel_7 // ADC12_IN7 -> ADC1_IN7 +#define ADC_CHANNEL_SWH ADC_Channel_12 // ADC123_IN12-> ADC1_IN12 + +#define ADC_CHANNEL_BATT ADC_Channel_15 // ADC12_IN15 -> ADC1_IN15 +#define ADC_MAIN ADC1 +#define ADC_EXT ADC3 +#define ADC_SAMPTIME 2 +#define ADC_DMA DMA2 +#define ADC_DMA_Channel DMA_Channel_0 +#define ADC_DMA_Stream DMA2_Stream4 +#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 + +// 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 + +// 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_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_Stream_RX DMA1_Stream5 +#define TELEMETRY_DMA_Channel_RX DMA_Channel_4 +#define TELEMETRY_DMA_Stream_TX DMA1_Stream6 +#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_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 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 + +// Audio +#define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH) +#define AUDIO_RCC_APB2Periph RCC_APB2Periph_SPI1 +#define AUDIO_SHUTDOWN_GPIO GPIOH +#define AUDIO_SHUTDOWN_GPIO_PIN GPIO_Pin_8 // PH.08 audio amp control pin +#define AUDIO_XDCS_GPIO GPIOH +#define AUDIO_XDCS_GPIO_PIN GPIO_Pin_14 // PH.14 +#define AUDIO_CS_GPIO GPIOH +#define AUDIO_CS_GPIO_PIN GPIO_Pin_13 // PH.13 +#define AUDIO_DREQ_GPIO GPIOH +#define AUDIO_DREQ_GPIO_PIN GPIO_Pin_15 // PH.15 +#define AUDIO_RST_GPIO GPIOD +#define AUDIO_RST_GPIO_PIN GPIO_Pin_4 // PD.4 +#define AUDIO_SPI SPI1 +#define AUDIO_SPI_GPIO_AF GPIO_AF_SPI1 +#define AUDIO_SPI_SCK_GPIO GPIOB +#define AUDIO_SPI_SCK_GPIO_PIN GPIO_Pin_3 // PB.03 +#define AUDIO_SPI_SCK_GPIO_PinSource GPIO_PinSource3 +#define AUDIO_SPI_MISO_GPIO GPIOB +#define AUDIO_SPI_MISO_GPIO_PIN GPIO_Pin_4 // PB.04 +#define AUDIO_SPI_MISO_GPIO_PinSource GPIO_PinSource4 +#define AUDIO_SPI_MOSI_GPIO GPIOB +#define AUDIO_SPI_MOSI_GPIO_PIN GPIO_Pin_5 // PB.05 +#define AUDIO_SPI_MOSI_GPIO_PinSource GPIO_PinSource5 + +// I2C Bus +#define I2C_B1_RCC_AHB1Periph 0 +#define I2C_B1_RCC_APB1Periph 0 + +// 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 DMA_Channel_4 +#define FLYSKY_HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 +#define FLYSKY_HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 +#define FLYSKY_HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 +#define FLYSKY_HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 +#define FLYSKY_HALL_SERIAL_GPIO_AF GPIO_AF_UART4 + +#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_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn +#define FLYSKY_HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn +#define FLYSKY_HALL_DMA_Stream_RX DMA1_Stream2 +#define FLYSKY_HALL_DMA_Stream_TX DMA1_Stream4 +#define FLYSKY_HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 + +#define FLYSKY_HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler +#define FLYSKY_HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + +// Internal Module +#define HARDWARE_INTERNAL_MODULE +#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) +#define INTMODULE_PWR_GPIO GPIOH +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#define INTMODULE_GPIO GPIOF +//#define INTMODULE_TX_GPIO GPIOF +#define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 +//#define INTMODULE_RX_GPIO GPIOF +#define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 +#define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 +#define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 +#define INTMODULE_USART UART7 +#define INTMODULE_GPIO_AF GPIO_AF_UART7 +#define INTMODULE_USART_IRQn UART7_IRQn +#define INTMODULE_USART_IRQHandler UART7_IRQHandler +#define INTMODULE_DMA_STREAM DMA1_Stream1 +#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn +#define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 +#define INTMODULE_DMA_CHANNEL DMA_Channel_5 + +/*#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 +#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn +#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream3_IRQHandler +#define INTMODULE_TX_DMA_STREAM DMA1_Stream1 +#define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn +#define INTMODULE_TX_DMA_Stream_IRQHandler DMA1_Stream1_IRQHandler +#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1*/ + +#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) +#define INTMODULE_RCC_APB2Periph 0 +#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_PWR_FIX_GPIO GPIOA +#define EXTMODULE_PWR_FIX_GPIO_PIN GPIO_Pin_2 // PA.02 +#define EXTMODULE_RCC_AHB1Periph \ + (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ + RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) +#define EXTMODULE_RCC_APB1Periph 0 +#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) +#define EXTMODULE_TX_GPIO GPIOC +#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 +#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 +#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 +#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_RX_GPIO GPIOC +#define EXTMODULE_RX_GPIO_PIN GPIO_Pin_7 // PC.07 +#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 +#define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_TIMER TIM8 +#define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_CC_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF GPIO_AF_TIM8 +//USART +#define EXTMODULE_USART USART6 +#define EXTMODULE_USART_GPIO GPIOC +#define EXTMODULE_USART_GPIO_AF GPIO_AF_USART6 + +#define EXTMODULE_USART_TX_DMA_CHANNEL DMA_Channel_5 +#define EXTMODULE_USART_TX_DMA_STREAM DMA2_Stream7 +#define EXTMODULE_USART_TX_DMA_IRQn DMA2_Stream7_IRQn +#define EXTMODULE_USART_TX_DMA_IRQHandler DMA2_Stream7_IRQHandler +#define EXTMODULE_USART_TX_DMA_FLAG_TC DMA_IT_TCIF7 + +#define EXTMODULE_USART_RX_DMA_CHANNEL DMA_Channel_5 +#define EXTMODULE_USART_RX_DMA_STREAM DMA2_Stream2 +#define EXTMODULE_USART_RX_DMA_IRQn DMA2_Stream2_IRQn +#define EXTMODULE_USART_RX_DMA_IRQHandler DMA2_Stream2_IRQHandler +#define EXTMODULE_USART_RX_DMA_FLAG_TC DMA_IT_TCIF2 + +#define EXTMODULE_USART_IRQHandler USART6_IRQHandler +#define EXTMODULE_USART_IRQn USART6_IRQn + +#define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) +//TIMER +#define EXTMODULE_DMA_CHANNEL DMA_Channel_7 +#define EXTMODULE_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler +#define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 + +#define EXTMODULE_TIMER_DMA_CHANNEL DMA_Channel_7 +#define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream1_IRQHandler +#define EXTMODULE_TIMER_DMA_FLAG_TC DMA_IT_TCIF1 + +#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_TX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN +#define EXTMODULE_RX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_RX_INVERT_GPIO_PIN + +// Heartbeat (not used) +#define HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD +#define HEARTBEAT_GPIO GPIOD +#define HEARTBEAT_GPIO_PIN GPIO_Pin_12 // PD.12 + +// Trainer Port +#define TRAINERMODULE +#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD) +#define TRAINER_RCC_APB1Periph RCC_APB1Periph_TIM4 +#define TRAINER_GPIO GPIOD +#define TRAINER_IN_GPIO_PIN GPIO_Pin_12 // PD.12 +#define TRAINER_IN_GPIO_PinSource GPIO_PinSource12 +#define TRAINER_OUT_GPIO_PIN GPIO_Pin_13 // PD.13 +#define TRAINER_OUT_GPIO_PinSource GPIO_PinSource13 + +#define TRAINER_TIMER TIM4 +#define TRAINER_GPIO_AF GPIO_AF_TIM4 // TIM4_CH1 (in) + TIM4_CH2 (out) +#define TRAINER_TIMER_IRQn TIM4_IRQn +#define TRAINER_TIMER_IRQHandler TIM4_IRQHandler +#define TRAINER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +#define TRAINER_OUT_CCMR1 TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE; +#define TRAINER_IN_CCMR1 TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1 | TIM_CCMR1_CC1S_0; + +#define TRAINER_OUT_COUNTER_REGISTER TRAINER_TIMER->CCR2 +#define TRAINER_IN_COUNTER_REGISTER TRAINER_TIMER->CCR1 +#define TRAINER_SETUP_REGISTER TRAINER_TIMER->CCR3 +#define TRAINER_OUT_INTERRUPT_FLAG TIM_SR_CC1IF +#define TRAINER_OUT_INTERRUPT_ENABLE TIM_DIER_CC1IE +#define TRAINER_IN_INTERRUPT_ENABLE TIM_DIER_CC1IE +#define TRAINER_IN_INTERRUPT_FLAG TIM_SR_CC1IF +#define TRAINER_OUT_CCER TIM_CCER_CC2E +#define TRAINER_CCER_POLARYTY TIM_CCER_CC2P +#define TRAINER_IN_CCER TIM_CCER_CC1E + +//BLUETOOTH +#define BLUETOOTH_ON_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define BLUETOOTH_ON_GPIO GPIOI +#define BLUETOOTH_ON_GPIO_PIN GPIO_Pin_8 // PI.8 + +// Bluetooth +#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_EN_GPIO GPIOI +#define BT_EN_GPIO_PIN GPIO_Pin_8 // PI.08 + +#define BT_CONNECTED_GPIO GPIOJ +#define BT_CONNECTED_GPIO_PIN GPIO_Pin_1 // PJ.10 + +#define BT_CMD_MODE_GPIO GPIOH +#define BT_CMD_MODE_GPIO_PIN GPIO_Pin_6 // PH.6 + +// Xms 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_TIM13 +#define MIXER_SCHEDULER_TIMER TIM13 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler + +#endif // _HAL_H_ diff --git a/radio/src/targets/pl18/hallStick_driver.cpp b/radio/src/targets/pl18/hallStick_driver.cpp new file mode 100644 index 00000000000..d6a872714de --- /dev/null +++ b/radio/src/targets/pl18/hallStick_driver.cpp @@ -0,0 +1,615 @@ +/* + * 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" + +DMAFifo hallDMAFifo __DMA (HALL_DMA_Stream_RX); +Fifo hallStickTxFifo; +static uint8_t hallStickSendState = HALLSTICK_SEND_STATE_IDLE; +unsigned char HallCmd[264] __DMA; + +STRUCT_HALL HallProtocol = { 0 }; +STRUCT_HALL HallProtocolTx = { 0 }; +signed short hall_raw_values[FLYSKY_HALL_CHANNEL_COUNT]; +STRUCT_STICK_CALIBRATION hall_calibration[FLYSKY_HALL_CHANNEL_COUNT] = { {0, 0, 0} }; +unsigned short hall_adc_values[FLYSKY_HALL_CHANNEL_COUNT]; + +/* crc16 implementation according to CCITT standards */ +const unsigned short CRC16Table[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +//const uint8_t sticks_mapping[4] = { 0 /*STICK1*/, 1/*STICK2*/, 2/*STICK3*/, 3 /*STICK4*/}; + +unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize) +{ + unsigned short crc16; + crc16 = 0xffff; + while (BufferSize) + { + crc16 = (crc16 << 8) ^ CRC16Table[((crc16>>8) ^ (*(unsigned char *)pBuffer)) & 0x00ff]; + pBuffer = (void *)((unsigned char *)pBuffer + 1); + BufferSize--; + } + return crc16; +} + +uint16_t get_hall_adc_value(uint8_t ch) +{ + if (ch >= FLYSKY_HALL_CHANNEL_COUNT) + { + return 0; + } + + if (ch < 2) + { + return MAX_ADC_CHANNEL_VALUE - hall_adc_values[ch]; + } + + return hall_adc_values[ch]; +} + + +void hall_stick_init(uint32_t baudrate) +{ + if (baudrate == 0) + { + USART_DeInit(HALL_SERIAL_USART); + return; + } + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = HALL_SERIAL_RX_DMA_Stream_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + USART_InitTypeDef USART_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_RX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); + GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_TX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); + + GPIO_InitStructure.GPIO_Pin = HALL_SERIAL_TX_GPIO_PIN | HALL_SERIAL_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 = GPIO_Speed_2MHz; + GPIO_Init(HALL_SERIAL_GPIO, &GPIO_InitStructure); + + USART_InitStructure.USART_BaudRate = baudrate; + 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; + USART_Init(HALL_SERIAL_USART, &USART_InitStructure); + + DMA_Cmd(HALL_DMA_Stream_RX, DISABLE); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, DISABLE); + DMA_DeInit(HALL_DMA_Stream_RX); + + DMA_InitTypeDef DMA_InitStructure; + hallDMAFifo.clear(); + + USART_ITConfig(HALL_SERIAL_USART, USART_IT_RXNE, DISABLE); + USART_ITConfig(HALL_SERIAL_USART, USART_IT_TXE, DISABLE); + + DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(hallDMAFifo.buffer()); + DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; + DMA_InitStructure.DMA_BufferSize = hallDMAFifo.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(HALL_DMA_Stream_RX, &DMA_InitStructure); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, ENABLE); + USART_Cmd(HALL_SERIAL_USART, ENABLE); + DMA_Cmd(HALL_DMA_Stream_RX, ENABLE); + + reset_hall_stick(); +} + +void HallSendBuffer(uint8_t * buffer, uint32_t count) +{ + for(uint32_t idx = 0; buffer != HallCmd && idx < count; idx++) + { + HallCmd[idx] = buffer[idx]; + } + DMA_InitTypeDef DMA_InitStructure; + DMA_DeInit(HALL_DMA_Stream_TX); + DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(HallCmd); + 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(HALL_DMA_Stream_TX, &DMA_InitStructure); + DMA_Cmd(HALL_DMA_Stream_TX, ENABLE); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(HALL_DMA_Stream_TX, DMA_IT_TC, ENABLE); + + /* enable interrupt and set it's priority */ + NVIC_EnableIRQ(HALL_SERIAL_TX_DMA_Stream_IRQn); + NVIC_SetPriority(HALL_SERIAL_TX_DMA_Stream_IRQn, 7); +} + +extern "C" void HALL_TX_DMA_Stream_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_DMA); + + if (DMA_GetITStatus(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC)) + { + DMA_ClearITPendingBit(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC); + HALL_SERIAL_USART->CR1 |= USART_CR1_TCIE; + } +} + +uint8_t HallGetByte(uint8_t * byte) +{ + return hallDMAFifo.pop(*byte); +} + + +void reset_hall_stick( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = HALL_PROTOLO_HEAD; + HallCmd[1] = 0xD1; + HallCmd[2] = 0x01; + HallCmd[3] = 0x01; + + crc16 = calc_crc16(HallCmd, 4); + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff; + + HallSendBuffer( HallCmd, 6); +} + +void get_hall_config( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = HALL_PROTOLO_HEAD; + HallCmd[1] = 0xD1; + HallCmd[2] = 0x01; + HallCmd[3] = 0x00; + + crc16 = calc_crc16(HallCmd, 4); // 2B 2C + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 6); +} + +void get_hall_firmware_info() +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = HALL_PROTOLO_HEAD; + HallCmd[1] = 0xA2; + HallCmd[2] = 0x00; + + crc16 = calc_crc16(HallCmd, 3); // BE 02 + + HallCmd[3] = crc16 & 0xff; + HallCmd[4] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 5); +} + +void hallStickUpdatefwEnd( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = HALL_PROTOLO_HEAD; + HallCmd[1] = 0xA2; + HallCmd[2] = 0x01; + HallCmd[3] = 0x07; + + crc16 = calc_crc16(HallCmd, 4); + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 6);// 94 DD +} + +static int parse_ps_state = 0; +void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch) +{ + if (parse_ps_state != 0) return; + parse_ps_state = 1; + + switch( hallBuffer->status ) + { + case GET_START: + { + if ( HALL_PROTOLO_HEAD == ch ) + { + hallBuffer->head = HALL_PROTOLO_HEAD; + hallBuffer->status = GET_ID; + hallBuffer->msg_OK = 0; + } + break; + } + case GET_ID: + { + hallBuffer->hallID.ID = ch; + hallBuffer->status = GET_LENGTH; + break; + } + case GET_LENGTH: + { + hallBuffer->length = ch; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_DATA; + if( 0 == hallBuffer->length ) + { + hallBuffer->status = GET_CHECKSUM; + hallBuffer->checkSum=0; + } + break; + } + case GET_DATA: + { + hallBuffer->data[hallBuffer->dataIndex++] = ch; + if( hallBuffer->dataIndex >= hallBuffer->length) + { + hallBuffer->checkSum = 0; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_STATE; + } + break; + } + case GET_STATE: + { + hallBuffer->checkSum = 0; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_CHECKSUM; + } + case GET_CHECKSUM: + { + hallBuffer->checkSum |= ch << ((hallBuffer->dataIndex++) * 8); + if( hallBuffer->dataIndex >= 2 ) + { + hallBuffer->dataIndex = 0; + hallBuffer->status = CHECKSUM; + } + else + { + break; + } + } + case CHECKSUM: + { + if(hallBuffer->checkSum == calc_crc16( (U8*)&hallBuffer->head, hallBuffer->length + 3 ) ) + { + hallBuffer->msg_OK = 1; + goto Label_restart; + } + else + { + goto Label_error; + } + } + } + + goto exit; + + Label_error: + Label_restart: + hallBuffer->status = GET_START; +exit: parse_ps_state = 0; + return ; +} + +#define ERROR_OFFSET 10 +void convert_hall_to_adcVaule( void ) +{ + uint16_t value; + + for ( uint8_t channel = 0; channel < 4; channel++ ) + { + if (hall_raw_values[channel] < hall_calibration[channel].mid) + { + value = hall_calibration[channel].mid - (hall_calibration[channel].min+ERROR_OFFSET); + value = ( MIDDLE_ADC_CHANNLE_VALUE * (hall_calibration[channel].mid - hall_raw_values[channel] ) ) / ( value ); + + if (value >= MIDDLE_ADC_CHANNLE_VALUE ) { + value = MIDDLE_ADC_CHANNLE_VALUE; + } + + hall_adc_values[channel] = MIDDLE_ADC_CHANNLE_VALUE - value; + } + else + { + value = (hall_calibration[channel].max - ERROR_OFFSET) - hall_calibration[channel].mid; + + value = ( MIDDLE_ADC_CHANNLE_VALUE * (hall_raw_values[channel] - hall_calibration[channel].mid ) ) / (value ); + + if (value >= MIDDLE_ADC_CHANNLE_VALUE ) + { + value = MIDDLE_ADC_CHANNLE_VALUE; + } + + hall_adc_values[channel] = MIDDLE_ADC_CHANNLE_VALUE + value + 1; + } + } +} + +uint8_t HallGetByteTx(uint8_t * byte) +{ + return hallStickTxFifo.pop(*byte); +} + +bool isHallStickUpdateFirmware( void ) +{ + return hallStickSendState == HALLSTICK_STATE_UPDATE_FW; +} + +void hallstick_send_by_state( void ) +{ + switch ( hallStickSendState ) + { + case HALLSTICK_STATE_SEND_RESET: + TRACE("HALLSTICK_STATE_SEND_RESET"); + reset_hall_stick(); + hallStickSendState = HALLSTICK_STATE_GET_FIRMWARE; + break; + + case HALLSTICK_STATE_GET_FIRMWARE: + TRACE("HALLSTICK_STATE_GET_FIRMWARE"); + get_hall_firmware_info(); + hallStickSendState = HALLSTICK_STATE_UPDATE_FW; + break; + + case HALLSTICK_STATE_UPDATE_FW: + return; + + default: break; + } +} + +void hallstick_wait_send_done(uint32_t timeOut) +{ + static unsigned int startTime = get_tmr10ms(); + + while ( hallStickSendState != HALLSTICK_SEND_STATE_IDLE ) + { + if ( (get_tmr10ms() - startTime) < timeOut ) + { + break; + } + } +} + +static uint32_t HallProtocolCount = 0; +bool isHallProtocolTxMsgOK( void ) +{ + bool isMsgOK = HallProtocolCount != 0; + HallProtocolCount = 0; + return isMsgOK; +} + +/* HallStick send main program */ +void hallStick_GetTxDataFromUSB( void ) +{ + unsigned char abyte; + uint8_t *pt = (uint8_t *)&HallProtocolTx; + + while( HallGetByteTx(&abyte) ) + { + Parse_Character(&HallProtocolTx, abyte ); + + if ( HallProtocolTx.msg_OK ) + { + HallProtocolTx.msg_OK = 0; + + pt[HallProtocolTx.length + 3] = HallProtocolTx.checkSum & 0xFF; + pt[HallProtocolTx.length + 4] = HallProtocolTx.checkSum >> 8; + + //TRACE("USB: %02X %02X %02X ...%04X; CRC:%04X", pt[0], pt[1], pt[2], + // HallProtocolTx.checkSum, calc_crc16(pt, HallProtocolTx.length+3)); + + switch ( HallProtocolTx.hallID.hall_Id.receiverID ) + { + case TRANSFER_DIR_TXMCU: + break; + + case TRANSFER_DIR_HALLSTICK: + onFlySkyUsbDownloadStart(TRANSFER_DIR_HALLSTICK); + + if ( 0xA2 == HallProtocolTx.hallID.ID ) + { + if ( 0 == HallProtocolTx.length ) // 55 A2 00 BE 02 + { + hallStickSendState = HALLSTICK_STATE_SEND_RESET; + break; + } + + else if ( 0x01 == HallProtocolTx.length && 0x07 == HallProtocol.data[0] ) + { + hallStickSendState = HALLSTICK_SEND_STATE_IDLE; + } + } + HallSendBuffer( pt, HallProtocolTx.length + 3 + 2 ); + break; + + case TRANSFER_DIR_RFMODULE: + onFlySkyUsbDownloadStart(TRANSFER_DIR_RFMODULE); + + if ( 0xAE == HallProtocolTx.hallID.ID && HallProtocolTx.length == 0 ) + { + setFlyskyState(INTERNAL_MODULE, FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE); + break; + } + + if ( 0x0D == HallProtocolTx.hallID.hall_Id.packetID && HallProtocolTx.data[0] == 1 ) + { + onFlySkyGetVersionInfoStart(INTERNAL_MODULE, 1); + break; + } + + //if ( isFlySkyUsbDownload() ) + { + intmoduleSendBuffer( pt, HallProtocolTx.length + 3 + 2 ); + } + break; + } + } + } + + if ( !usbPlugged() ) + { + onFlySkyUsbDownloadStart(0); + } +} + + +/* Run it in 1ms timer routine */ +void hall_stick_loop(void) +{ + static uint8_t count = 0; + static tmr10ms_t lastConfigTime = get_tmr10ms(); + bool log = 0; + + hallStick_GetTxDataFromUSB(); + + if(count>10) + { + count = 0; + hallstick_send_by_state(); + } + count++; + uint8_t byte; + while(HallGetByte(&byte)) + { + HallProtocol.index++; + + Parse_Character(&HallProtocol, byte); + + if ( HallProtocol.msg_OK ) + { + HallProtocol.msg_OK = 0; + HallProtocol.stickState = HallProtocol.data[HallProtocol.length - 1]; + + switch ( HallProtocol.hallID.hall_Id.receiverID ) + { + case TRANSFER_DIR_TXMCU: + if(HallProtocol.hallID.hall_Id.packetID == HALL_RESP_TYPE_CALIB) { + memcpy(&hall_calibration, HallProtocol.data, sizeof(hall_calibration)); + } + else if(HallProtocol.hallID.hall_Id.packetID == HALL_RESP_TYPE_VALUES) { + memcpy(hall_raw_values, HallProtocol.data, sizeof(hall_raw_values)); + convert_hall_to_adcVaule(); + } + break; + case TRANSFER_DIR_HOSTPC: + if (HallProtocol.length == 0x01 && (HallProtocol.data[0] == 0x05 || HallProtocol.data[0] == 0x06) ) + { + hallStickSendState = HALLSTICK_SEND_STATE_IDLE; + } + case TRANSFER_DIR_HALLSTICK: + HallProtocolCount++; + uint8_t *pt = (uint8_t*)&HallProtocol; + //HallProtocol.head = HALL_PROTOLO_HEAD; + //TRACE("HALL: %02X %02X %02X ...%04X", pt[0], pt[1], pt[2], HallProtocol.checkSum); + pt[HallProtocol.length + 3] = HallProtocol.checkSum & 0xFF; + pt[HallProtocol.length + 4] = HallProtocol.checkSum >> 8; + usbDownloadTransmit( pt, HallProtocol.length + 5 ); + break; + } + } + } + //check periodically if calibration is correct + if (get_tmr10ms() - lastConfigTime > 200) + { + //invalid calibration + if(hall_calibration[0].max - hall_calibration[0].min < 1024) { + TRACE("GET HALL CONFIG"); + get_hall_config(); + lastConfigTime = get_tmr10ms(); + } + + if (log) + { + TRACE_NOCRLF("Hall(%0d):", FLYSKY_HALL_BAUDRATE); + for (int idx = 0; idx < HallProtocol.length + 5; idx++) + { + TRACE_NOCRLF(" %02X", *((uint8_t*)&HallProtocol + idx)); + } + TRACE(";"); + } + } +} diff --git a/radio/src/targets/pl18/hallStick_driver.h b/radio/src/targets/pl18/hallStick_driver.h new file mode 100644 index 00000000000..a3359fe7bc7 --- /dev/null +++ b/radio/src/targets/pl18/hallStick_driver.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/*************************************************************************************************** + +***************************************************************************************************/ +#ifndef __HALLSTICK_DRIVER_H__ + #define __HALLSTICK_DRIVER_H__ +/*************************************************************************************************** + +***************************************************************************************************/ + #ifdef EXTERN + #undef EXTERN + #endif + + #ifdef __HALLSTICK_DRIVER_C__ + #define EXTERN + #else + #define EXTERN extern + #endif +/*************************************************************************************************** + +***************************************************************************************************/ +#define HALLSTICK_BUFF_SIZE ( 512 ) +#define FLYSKY_HALL_BAUDRATE ( 921600 ) +#define FLYSKY_HALL_CHANNEL_COUNT ( 4 ) + +#define MAX_ADC_CHANNEL_VALUE ( 4095 ) +#define MIN_ADC_CHANNLE_VALUE ( 0 ) +#define MIDDLE_ADC_CHANNLE_VALUE ( 2047 ) + +typedef struct +{ + signed short min; + signed short mid; + signed short max; +} STRUCT_STICK_CALIBRATION; + +typedef struct +{ + STRUCT_STICK_CALIBRATION sticksCalibration[4]; + unsigned char reststate; + unsigned short CRC16; +} STRUCT_STICK_CALIBRATION_PACK; + +typedef struct +{ + signed short channel[4]; + unsigned char stickState; + unsigned short CRC16; +} STRUCT_CHANNEL_PACK; + +typedef union +{ + STRUCT_STICK_CALIBRATION_PACK channelPack; + STRUCT_CHANNEL_PACK sticksCalibrationPack; +} UNION_DATA; + +typedef struct +{ + unsigned char start; + unsigned char senderID:2; + unsigned char receiverID:2; + unsigned char packetID:4; + unsigned char length; + UNION_DATA payload; +} STRUCT_HALLDATA; + +typedef struct +{ + unsigned char senderID:2; + unsigned char receiverID:2; + unsigned char packetID:4; +} STRUCT_HALLID; + +typedef union +{ + STRUCT_HALLID hall_Id; + unsigned char ID; +} STRUCT_ID; + + +typedef union +{ + STRUCT_HALLDATA halldat; + unsigned char buffer[30]; +} UNION_HALLDATA; + + +typedef struct +{ + unsigned char head; + STRUCT_ID hallID; + unsigned char length; + unsigned char data[HALLSTICK_BUFF_SIZE]; + unsigned char reserved[15]; + unsigned short checkSum; + unsigned char stickState; + unsigned char startIndex; + unsigned char endIndex; + unsigned char index; + unsigned char dataIndex; + unsigned char deindex; + unsigned char completeFlg; + unsigned char status; + unsigned char recevied; + unsigned char msg_OK; +} STRUCT_HALL; + +enum +{ + GET_START = 0, + GET_ID, + GET_LENGTH, + GET_DATA, + GET_STATE, + GET_CHECKSUM, + CHECKSUM, +}; + +enum HALLSTICK_SEND_STATE_E { + HALLSTICK_SEND_STATE_IDLE, + HALLSTICK_STATE_SEND_RESET, + HALLSTICK_STATE_GET_CONFIG, + HALLSTICK_STATE_GET_FIRMWARE, + HALLSTICK_STATE_UPDATE_FW +}; + +enum TRANSFER_DIR_E { + TRANSFER_DIR_HALLSTICK, + TRANSFER_DIR_TXMCU, + TRANSFER_DIR_HOSTPC, + TRANSFER_DIR_RFMODULE, +}; + +#define HALL_PROTOLO_HEAD 0x55 +#define HALL_RESP_TYPE_CALIB 0x0e +#define HALL_RESP_TYPE_VALUES 0x0c + +#define HALL_SERIAL_USART UART4 +#define HALL_SERIAL_GPIO GPIOA +#define HALL_DMA_Channel DMA_Channel_4 +#define HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 +#define HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 +#define HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 +#define HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 +#define HALL_SERIAL_GPIO_AF GPIO_AF_UART4 + +#define HALL_SERIAL_RCC_APB2Periph RCC_APB2Periph_USART6 +#define HALL_RCC_AHB1Periph RCC_AHB1Periph_DMA1 +#define HALL_RCC_APB1Periph RCC_APB1Periph_UART4 + +#define HALL_SERIAL_USART_IRQHandler UART4_IRQHandler +#define HALL_SERIAL_USART_IRQn UART4_IRQn +#define HALL_SERIAL_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn +#define HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn +#define HALL_DMA_Stream_RX DMA1_Stream2 +#define HALL_DMA_Stream_TX DMA1_Stream4 +#define HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 + +#define HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler +#define HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + +//#include "fifo.h" +//extern Fifo hallStickTxFifo; + + +/*************************************************************************************************** + interface function +***************************************************************************************************/ +extern unsigned short hall_adc_values[FLYSKY_HALL_CHANNEL_COUNT]; +extern signed short hall_raw_values[FLYSKY_HALL_CHANNEL_COUNT]; +extern STRUCT_STICK_CALIBRATION hall_calibration[FLYSKY_HALL_CHANNEL_COUNT]; + + +extern void reset_hall_stick( void ); +extern void get_hall_config( void ); +extern void hall_stick_init(uint32_t baudrate); +extern void hall_stick_loop( void ); +extern uint16_t get_hall_adc_value(uint8_t ch); +extern void hallSerialPutc(char c); +unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize); +void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch); +extern bool isFlySkyUsbDownload(void); +extern void onFlySkyUsbDownloadStart(uint8_t fw_state); +#endif + + + + + + + + + + + + + + + + diff --git a/radio/src/targets/pl18/haptic_driver.cpp b/radio/src/targets/pl18/haptic_driver.cpp new file mode 100644 index 00000000000..b4a45357f90 --- /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 "opentx.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..ca2766dacda --- /dev/null +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -0,0 +1,181 @@ +/* + * 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 "hal/adc_driver.h" + +uint32_t readKeys() +{ + uint32_t result = 0; + bool getKeys = true; +#if defined(LUA) + if (!isLuaStandaloneRunning()) { + getKeys = false; + } +#endif + + if (getKeys) { + if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) + result |= 1 << KEY_RADIO; + if (TRIMS_GPIO_REG_LHR & TRIMS_GPIO_PIN_LHR) + result |= 1 << KEY_MODEL; + if (TRIMS_GPIO_REG_LVD & TRIMS_GPIO_PIN_LVD) + result |= 1 << KEY_TELEM; + if (TRIMS_GPIO_REG_LVU & TRIMS_GPIO_PIN_LVU) + result |= 1 << KEY_PGUP; + if (TRIMS_GPIO_REG_RVD & TRIMS_GPIO_PIN_RVD) + result |= 1 << KEY_DOWN; + if (TRIMS_GPIO_REG_RVU & TRIMS_GPIO_PIN_RVU) + result |= 1 << KEY_UP; + if (TRIMS_GPIO_REG_RHL & TRIMS_GPIO_PIN_RHL) + result |= 1 << KEY_LEFT; + if (TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) + result |= 1 << KEY_RIGHT; + } + + // Enter and Exit are always supported + if (TRIMS_GPIO_REG_RPRESS & TRIMS_GPIO_PIN_RPRESS) + result |= 1 << KEY_ENTER; + if (TRIMS_GPIO_REG_LPRESS & TRIMS_GPIO_PIN_LPRESS) + result |= 1 << KEY_EXIT; + + return result; +} + +uint32_t readTrims() +{ + uint32_t result = 0; + + bool getTrim = true; +#if defined(LUA) + if (isLuaStandaloneRunning()) { + getTrim = false; + } +#endif + if(!getTrim) return result; + if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) + result |= 1 << (TRM_LH_DWN - TRM_BASE); + if (TRIMS_GPIO_REG_LHR & TRIMS_GPIO_PIN_LHR) + result |= 1 << (TRM_LH_UP - TRM_BASE); + if (TRIMS_GPIO_REG_LVD & TRIMS_GPIO_PIN_LVD) + result |= 1 << (TRM_LV_DWN - TRM_BASE); + if (TRIMS_GPIO_REG_LVU & TRIMS_GPIO_PIN_LVU) + result |= 1 << (TRM_LV_UP - TRM_BASE); + if (TRIMS_GPIO_REG_RVD & TRIMS_GPIO_PIN_RVD) + result |= 1 << (TRM_RV_DWN - TRM_BASE); + if (TRIMS_GPIO_REG_RVU & TRIMS_GPIO_PIN_RVU) + result |= 1 << (TRM_RV_UP - TRM_BASE); + if (TRIMS_GPIO_REG_RHL & TRIMS_GPIO_PIN_RHL) + result |= 1 << (TRM_RH_DWN - TRM_BASE); + if (TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) + 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) +{ + uint16_t value = getAnalogValue(SWITCH_FIRST + index / 3); + 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_GPIOG_PINS; + GPIO_Init(GPIOG, &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); +} diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp new file mode 100644 index 00000000000..035790ba34e --- /dev/null +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -0,0 +1,1652 @@ +/* + * 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 LCD_FIRST_LAYER 0 +#define LCD_SECOND_LAYER 1 + +uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; +uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; +uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; +uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; +uint8_t currentLayer = LCD_FIRST_LAYER; + +BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); +BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); + +BitmapBuffer * lcdFront = &lcdBuffer1; +BitmapBuffer * lcd = &lcdBuffer2; + +lcdSpiInitFucPtr lcdInitFunction; +lcdSpiInitFucPtr lcdOffFunction; +lcdSpiInitFucPtr lcdOnFunction; + +volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; + +static void LCD_Delay(void) { + volatile unsigned int i; + + for (i = 0; i < 20; i++) { + ; + } +} + +enum ENUM_IO_SPEED +{ + IO_SPEED_LOW, + IO_SPEED_MID, + IO_SPEED_QUICK, + IO_SPEED_HIGH +}; + +enum ENUM_IO_MODE +{ + IO_MODE_INPUT, + IO_MODE_OUTPUT, + IO_MODE_ALTERNATE, + IO_MODE_ANALOG +}; + + +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 */ + CLR_LCD_CS(); +} + +void lcdDelay() { + delay_01us(1); +} + +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(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + SET_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + 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; + + lcdWriteByte(0, Register); + LCD_DELAY(); + LCD_DELAY(); + ReadData = LCD_ReadByte(); + return (ReadData); +} + +void lcdWriteCommand(uint8_t command) { + lcdWriteByte(0, command); +} + +void lcdWriteData(uint8_t data) { + lcdWriteByte(1, data); +} + +void LCD_HX8357D_Init(void) { +#if 0 + lcdWriteCommand(0x11); + delay_ms(200); + + lcdWriteCommand(0xB9); + lcdWriteData(0xFF); + lcdWriteData(0x83); + lcdWriteData(0x57); + + lcdWriteCommand(0xB1); + lcdWriteData(0x00); + lcdWriteData(0x14); + lcdWriteData(0x1C); + lcdWriteData(0x1C); + lcdWriteData(0xC7); + lcdWriteData(0x21); + + lcdWriteCommand(0xB3); + lcdWriteData(0x83); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x06); + + lcdWriteCommand(0xB4); + lcdWriteData(0x11); + lcdWriteData(0x40); + lcdWriteData(0x00); + lcdWriteData(0x2A); + lcdWriteData(0x2A); + lcdWriteData(0x20); + lcdWriteData(0x4E); + + lcdWriteCommand(0xB5); + lcdWriteData(0x03); + lcdWriteData(0x03); + + lcdWriteCommand(0xB6); + lcdWriteData(0x38); + + lcdWriteCommand(0xC0); + lcdWriteData(0x24); + lcdWriteData(0x24); + lcdWriteData(0x00); + lcdWriteData(0x10); + lcdWriteData(0xc8); + lcdWriteData(0x08); + + lcdWriteCommand(0xC2); + lcdWriteData(0x00); + lcdWriteData(0x08); + lcdWriteData(0x04); + + lcdWriteCommand(0xCC); + lcdWriteData(0x00); + +//GAMMA 2.5" + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x06); + lcdWriteData(0x0D); + lcdWriteData(0x18); + lcdWriteData(0x23); + lcdWriteData(0x3B); + lcdWriteData(0x45); + lcdWriteData(0x4D); + lcdWriteData(0x4D); + lcdWriteData(0x46); + lcdWriteData(0x40); + lcdWriteData(0x37); + lcdWriteData(0x34); + lcdWriteData(0x2F); + lcdWriteData(0x2B); + lcdWriteData(0x21); + lcdWriteData(0x00); + lcdWriteData(0x01); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); + + lcdWriteCommand(0x36); + lcdWriteData(0x08); + + lcdWriteCommand(0x29); + delay_ms(10); +#else + 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(0x18); + + lcdWriteCommand(0x11); // SLPOUT + delay_ms(200); + + lcdWriteCommand(0x29); // Display On + delay_ms(25); + lcdWriteCommand(0x2C); + +#endif + +} + +void LCD_HX8357D_On(void) { + lcdWriteCommand(0x29); + lcdWriteCommand(0x22); +} + +void LCD_HX8357D_Off(void) { + lcdWriteCommand(0x22); + lcdWriteCommand(0x28); +} + +unsigned int LCD_HX8357D_ReadID(void) { + int ID = 0; + + 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( 0x88 ); + + 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 ); + //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) { + 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(); + LCD_DELAY(); + LCD_DELAY(); + SET_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + + LCD_ReadByte(); + ID += (uint16_t)(LCD_ReadByte())<<8; + ID += LCD_ReadByte(); + + return (ID); + } + +static void lcdReset() { + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET(); + delay_ms(100); + + LCD_NRST_HIGH(); + delay_ms(100); +} + +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 * PLLSAI_N = 192 Mhz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLL_LTDC = 192/3 = 64 Mhz */ + /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = 64/4 = 16 Mhz */ + RCC_PLLSAIConfig(192 * 2 / 3, 6, 3); + 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; + + /* 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_W + HBP; + /* Configure accumulated active height */ + LTDC_InitStruct.LTDC_AccumulatedActiveH = LCD_H + VBP; + /* Configure total width */ + LTDC_InitStruct.LTDC_TotalWidth = LCD_W + HBP + HFP; + /* Configure total height */ + LTDC_InitStruct.LTDC_TotalHeigh = LCD_H + VBP + VFP; + + LTDC_Init(<DC_InitStruct); + + //Configure IRQ + // LTDC_ITConfig(LTDC_IER_RRIE, ENABLE); + // 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); + + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; + 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); + LTDC_LIPConfig(LCD_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_W + HBP); + LTDC_Layer_InitStruct.LTDC_VerticalStart = VBP + 1; + LTDC_Layer_InitStruct.LTDC_VerticalStop = (LCD_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_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_W * 2); + + /* Configure the number of lines */ + LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_H; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_FIRST_FRAME_BUFFER; + + /* Initialize LTDC layer 1 */ + LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); + +#if defined(LTDC_DOUBLELAYER) + /* Configure Layer 2 */ + LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA; + LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_SECOND_FRAME_BUFFER; + + /* Initialize LTDC layer 2 */ + LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct); +#endif + /* LTDC configuration reload */ + LTDC_ReloadConfig(LTDC_IMReload); + + LTDC_LayerCmd(LTDC_Layer1, ENABLE); + LTDC_LayerAlpha(LTDC_Layer1, 255); + +#if defined(LTDC_DOUBLELAYER) + LTDC_LayerCmd(LTDC_Layer2, ENABLE); + LTDC_LayerAlpha(LTDC_Layer2, 0); +#endif + + LTDC_ReloadConfig(LTDC_IMReload); + /* dithering activation */ + LTDC_DitherCmd(ENABLE); +} + +extern void loadFonts(); +const char* boardLcdType = ""; + +void LCD_SetLayer(uint32_t layer) +{ + if (layer == LCD_FIRST_LAYER) { + lcdFront = &lcdBuffer1; + lcd = &lcdBuffer2; + } + else { + lcdFront = &lcdBuffer2; + lcd = &lcdBuffer1; + } + currentLayer = layer; +} + +void lcdInit(void) { + // Clear buffers first + memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); + memset(LCD_SECOND_FRAME_BUFFER, 0, sizeof(LCD_SECOND_FRAME_BUFFER)); + + loadFonts(); + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + /* Reset the LCD --------------------------------------------------------*/ + lcdReset(); + + /* Configure the LCD Control pins */ + LCD_AF_GPIOConfig(); + + /* Send LCD initializaiton 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; + } 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; + } 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; + } 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; + } else if (LCD_ST7796S_ReadID() == LCD_ST7796S_ID) { + TRACE("LCD INIT (default): ST7796S"); + boardLcdType = "ST7796S"; + lcdInitFunction = LCD_ST7796S_Init; + lcdOffFunction = LCD_ST7796S_Off; + lcdOnFunction = LCD_ST7796S_On; + } else { + TRACE("LCD INIT: unknown LCD controller"); + boardLcdType = "unknown"; + } + + lcdInitFunction(); + + LCD_Init_LTDC(); + LCD_LayerInit(); + LTDC_Cmd(ENABLE); + LTDC_ReloadConfig(LTDC_IMReload); +} + +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) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputGreen = (0x07E0 & color) >> 5; + DMA2D_InitStruct.DMA2D_OutputBlue = 0x001F & color; + DMA2D_InitStruct.DMA2D_OutputRed = (0xF800 & color) >> 11; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest) + 2*(destw*y + x); + DMA2D_InitStruct.DMA2D_OutputOffset = (destw - w); + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +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) +{ + 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(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +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) +{ + 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(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +// 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) +{ + 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(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) +{ + 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(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void lcdCopy(void * dest, void * src) +{ + 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); + 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 = LCD_H; + DMA2D_InitStruct.DMA2D_PixelPerLine = LCD_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_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(); + + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void lcdStoreBackupBuffer() +{ + lcdCopy(LCD_BACKUP_FRAME_BUFFER, lcd->getData()); +} + +int lcdRestoreBackupBuffer() +{ + lcdCopy(lcd->getData(), LCD_BACKUP_FRAME_BUFFER); + return 1; +} + +uint16_t* lcdGetBackupBuffer() +{ + return (uint16_t*)LCD_BACKUP_FRAME_BUFFER; +} + +uint16_t* lcdGetScratchBuffer() +{ + return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; +} + +//static volatile uint8_t refreshRequested = 0; +static volatile uint8_t _frameBufferAddressReloaded = 0; + +extern "C" void LTDC_IRQHandler(void) +{ + LTDC_ClearFlag(LTDC_ICR_CLIF); + _frameBufferAddressReloaded = 1; +} + +static void lcdSwitchLayers() +{ + if (currentLayer == LCD_FIRST_LAYER) { + LTDC_Layer1->CFBAR = (uint32_t)LCD_SECOND_FRAME_BUFFER; + LCD_SetLayer(LCD_SECOND_LAYER); + } + else { + LTDC_Layer1->CFBAR = (uint32_t)LCD_FIRST_FRAME_BUFFER; + LCD_SetLayer(LCD_FIRST_LAYER); + } + + // reload shadow registers on vertical blank + _frameBufferAddressReloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait + while(_frameBufferAddressReloaded == 0); +} + +void lcdRefresh() +{ + lcdSwitchLayers(); +} 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..679c94bab94 --- /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 = 80; +constexpr coord_t INPUT_EDIT_CURVE_WIDTH = LCD_W; +constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = 158; +constexpr coord_t INPUT_EDIT_CURVE_LEFT = 0; +constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT; +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..53936f9148f --- /dev/null +++ b/radio/src/targets/pl18/telemetry_driver.cpp @@ -0,0 +1,238 @@ +/* + * 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" + +Fifo telemetryNoDMAFifo; +uint32_t telemetryErrors = 0; + +#if defined(PCBX12S) +DMAFifo telemetryDMAFifo __DMA (TELEMETRY_DMA_Stream_RX); +uint8_t telemetryFifoMode; +#endif + +void telemetryPortInitCommon(uint32_t baudrate, uint8_t mode, uint8_t noinv = 0) +{ + if (baudrate == 0) { + USART_DeInit(TELEMETRY_USART); + return; + } + + 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 = GPIO_Speed_2MHz; + GPIO_Init(TELEMETRY_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_DIR_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TELEMETRY_DIR_GPIO, &GPIO_InitStructure); + TELEMETRY_DIR_INPUT(); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_REV_GPIO_PIN | TELEMETRY_RX_REV_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TELEMETRY_REV_GPIO, &GPIO_InitStructure); + + if (noinv != 0) { + TELEMETRY_TX_POL_NORM(); + TELEMETRY_RX_POL_NORM(); + } else { + TELEMETRY_TX_POL_INV(); + TELEMETRY_RX_POL_INV(); + } + + 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; + USART_Init(TELEMETRY_USART, &USART_InitStructure); + + USART_Cmd(TELEMETRY_USART, ENABLE); + USART_ITConfig(TELEMETRY_USART, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(TELEMETRY_USART_IRQn, 6); + NVIC_EnableIRQ(TELEMETRY_USART_IRQn); +} + +void telemetryPortInit(uint32_t baudrate, uint8_t mode) +{ + telemetryPortInitCommon(baudrate, mode, 0); +} + +void telemetryPortInvertedInit(uint32_t baudrate) +{ + telemetryPortInitCommon(baudrate, TELEMETRY_SERIAL_DEFAULT, 1); +} + +/*void extmoduleSendInvertedByte(uint8_t byte) +{ +#warning "TODO extmoduleSendInvertedByte"; +}*/ + +void telemetryPortSetDirectionOutput() +{ + TELEMETRY_DIR_OUTPUT(); + TELEMETRY_USART->CR1 &= ~USART_CR1_RE; // turn off receiver +} + +void telemetryPortSetDirectionInput() +{ + 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 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); + + // 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; + } +} + +// TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp +bool telemetryGetByte(uint8_t * byte) +{ +#if defined(PCBX12S) + 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) + telemetryDMAFifo.clear(); +#endif + + telemetryNoDMAFifo.clear(); +} + diff --git a/radio/src/targets/pl18/touch_driver.cpp b/radio/src/targets/pl18/touch_driver.cpp new file mode 100644 index 00000000000..f844fa25807 --- /dev/null +++ b/radio/src/targets/pl18/touch_driver.cpp @@ -0,0 +1,562 @@ +/* + * 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 "touch_driver.h" + +volatile static bool touchEventOccured; + +#define FT6x06_MAX_INSTANCE 1 + +#define I2C_TOUCH_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define I2C_TOUCH_RCC_APB1Periph RCC_APB1Periph_I2C1 +#define I2C_TOUCH I2C1 +#define I2C_TOUCH_GPIO GPIOB +#define I2C_TOUCH_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 +#define I2C_TOUCH_SDA_GPIO_PIN GPIO_Pin_7 // PB.09 + +#define I2C_TOUCH_RESET_GPIO GPIOB +#define I2C_TOUCH_RESET_GPIO_PIN GPIO_Pin_12 // PB.12 +#define I2C_TOUCH_INT_GPIO GPIOB +#define I2C_TOUCH_INT_GPIO_PIN GPIO_Pin_9 // PB.09 + +#define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) + +uint8_t ft6x06[FT6x06_MAX_INSTANCE] = {0}; +static ft6x06_handle_TypeDef ft6x06_handle = {FT6206_I2C_NOT_INITIALIZED, 0, 0}; + +tmr10ms_t downTime = 0; +tmr10ms_t tapTime = 0; +short tapCount = 0; +#define TAP_TIME 25 + +static TouchState internalTouchState = {}; + +void I2C_FreeBus() +{ + GPIO_InitTypeDef GPIO_InitStructure; + // reset i2c bus by setting clk as output and sending manual clock pulses + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SCL_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SDA_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); + + //send 100khz clock train for some 100ms + tmr10ms_t until = get_tmr10ms() + 11; + while (get_tmr10ms() < until) { + if (GPIO_ReadInputDataBit(I2C_TOUCH_GPIO, I2C_TOUCH_SDA_GPIO_PIN) == 1) { + TRACE("touch: i2c free again\n"); + break; + } + TRACE("FREEEEE"); + I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; //BSRRL + delay_us(10); + I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SCL_GPIO_PIN; + delay_us(10); + } + + //send stop condition: + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SDA_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); + + //clock is low + I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; + delay_us(10); + //sda = lo + I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SDA_GPIO_PIN; + delay_us(10); + //clock goes high + I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; + delay_us(10); + //sda = hi + I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SDA_GPIO_PIN; + delay_us(10); + TRACE("FREE BUS"); +} + +void Touch_DeInit() +{ + I2C_DeInit(I2C_TOUCH); + (RCC->APB1RSTR |= (RCC_APB1RSTR_I2C1RST)); + delay_ms(150); + (RCC->APB1RSTR &= ~(RCC_APB1RSTR_I2C1RST)); +} + +void I2C_Init() +{ + Touch_DeInit(); + + GPIO_InitTypeDef GPIO_InitStructure; + EXTI_InitTypeDef EXTI_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + I2C_InitTypeDef I2C_InitStructure; + + + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); + + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE); + RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE); + + I2C_FreeBus(); + + GPIO_PinAFConfig(I2C_TOUCH_GPIO, GPIO_PinSource7, GPIO_AF_I2C1); + GPIO_PinAFConfig(I2C_TOUCH_GPIO, GPIO_PinSource8, GPIO_AF_I2C1); + + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SCL_GPIO_PIN | I2C_TOUCH_SDA_GPIO_PIN; + GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_RESET_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL; // + GPIO_Init(I2C_TOUCH_RESET_GPIO, &GPIO_InitStructure); + + + //https://community.st.com/s/question/0D50X00009XkZ9FSAV/stm32f4-i2c-issues-solved + RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); + I2C_StructInit(&I2C_InitStructure); + I2C_InitStructure.I2C_ClockSpeed = 400000; + I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; + I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; + I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; + I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_Init(I2C_TOUCH, &I2C_InitStructure); + + I2C_Init(I2C_TOUCH, &I2C_InitStructure); + //I2C_StretchClockCmd(I2C_TOUCH, ENABLE); + I2C_Cmd(I2C_TOUCH, ENABLE); + + //ext interupt + GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_INT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(I2C_TOUCH_INT_GPIO, &GPIO_InitStructure); + + SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource9); + EXTI_InitStructure.EXTI_Line = EXTI_Line9; + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; + EXTI_InitStructure.EXTI_LineCmd = ENABLE; + EXTI_Init(&EXTI_InitStructure); + + NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} + +#define I2C_TIMEOUT_MAX 1000 + +bool I2C_WaitEvent(uint32_t event) +{ + uint32_t timeout = I2C_TIMEOUT_MAX; + while (!I2C_CheckEvent(I2C_TOUCH, event)) { + if ((timeout--) == 0) return false; + } + return true; +} + +bool I2C_WaitEventCleared(uint32_t event) +{ + uint32_t timeout = I2C_TIMEOUT_MAX; + while (I2C_CheckEvent(I2C_TOUCH, event)) { + if ((timeout--) == 0) return false; + } + return true; +} + +bool I2C_Send7BitAddress(uint8_t address, uint16_t direction) +{ + I2C_SendData(I2C_TOUCH, (address << 1) | ((direction == I2C_Direction_Receiver) ? 1 : 0)); + // check if slave acknowledged his address within timeout + if (!I2C_WaitEvent(direction == I2C_Direction_Transmitter ? I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) + return false; + return true; +} + +bool touch_i2c_read(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) +{ + if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) return false; + I2C_GenerateSTART(I2C_TOUCH, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; + if (!I2C_Send7BitAddress(addr, I2C_Direction_Transmitter)) return false; + I2C_SendData(I2C_TOUCH, reg); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return false; + I2C_GenerateSTART(I2C_TOUCH, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; + if (!I2C_Send7BitAddress(addr, I2C_Direction_Receiver)) return false; + + if (len > 1) I2C_AcknowledgeConfig(I2C_TOUCH, ENABLE); + + while (len) { + if (len == 1) I2C_AcknowledgeConfig(I2C_TOUCH, DISABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_RECEIVED))return false; + *data++ = I2C_ReceiveData(I2C_TOUCH);; + len--; + } + I2C_GenerateSTOP(I2C_TOUCH, ENABLE); + return true; +} + +static bool touch_i2c_write(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) +{ + if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) return false; + I2C_GenerateSTART(I2C_TOUCH, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; + + if (!I2C_Send7BitAddress(addr, I2C_Direction_Transmitter)) return false; + I2C_SendData(I2C_TOUCH, (uint8_t) ((reg & 0xFF00) >> 8)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; + I2C_SendData(I2C_TOUCH, (uint8_t) (reg & 0x00FF)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; + while (len--) { + I2C_SendData(I2C_TOUCH, *data); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; + data++; + } + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return false; + I2C_GenerateSTOP(I2C_TOUCH, ENABLE); + 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(); + } +} + +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 uint8_t TS_IO_Read(uint8_t reg) +{ + return TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, reg); +} +*/ + +static void touch_ft6236_debug_info(void) +{ +#if defined(DEBUG) + TRACE("ft6x36: thrhld = %d", TS_IO_Read(TOUCH_FT6236_REG_TH_GROUP) * 4); + TRACE("ft6x36: rep rate=", TS_IO_Read(TOUCH_FT6236_REG_PERIODACTIVE) * 10); + TRACE("ft6x36: fw lib 0x%02X %02X", TS_IO_Read(TOUCH_FT6236_REG_LIB_VER_H), TS_IO_Read(TOUCH_FT6236_REG_LIB_VER_L)); + TRACE("ft6x36: fw v 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_FIRMID)); + TRACE("ft6x36: CHIP ID 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_CIPHER)); + TRACE("ft6x36: CTPM ID 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_FOCALTECH_ID)); + TRACE("ft6x36: rel code 0x%02X", TS_IO_Read(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(uint16_t DeviceAddr) +{ + volatile uint8_t nbTouch = 0; + + /* Read register FT6206_TD_STAT_REG to check number of touches detection */ + nbTouch = TS_IO_Read(DeviceAddr, 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 */ + ft6x06_handle.currActiveTouchNb = nbTouch; + + /* Reset current active touch index on which to work on */ + ft6x06_handle.currActiveTouchIdx = 0; + return (nbTouch); +} + +/** + * @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) +} +*/ + +/** + * @brief Get the touch screen X and Y positions values + * Manage multi touch thanks to touch Index global + * variable 'ft6x06_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 DeviceAddr, uint16_t * X, uint16_t * Y, uint32_t * event) +{ + uint8_t regAddress = 0; + uint8_t dataxy[4]; + + if (ft6x06_handle.currActiveTouchIdx < ft6x06_handle.currActiveTouchNb) { + switch (ft6x06_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(DeviceAddr, 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); + */ + ft6x06_handle.currActiveTouchIdx++; + } +} + +void TouchReset() +{ + GPIO_ResetBits(I2C_TOUCH_RESET_GPIO, I2C_TOUCH_RESET_GPIO_PIN); + delay_ms(20); + GPIO_SetBits(I2C_TOUCH_RESET_GPIO, I2C_TOUCH_RESET_GPIO_PIN); + delay_ms(300); +} + +void TouchInit(void) +{ + I2C_Init(); + TouchReset(); + touch_ft6236_debug_info(); + /* INT generation for new touch available */ + /* Note TS_INT is active low */ + uint8_t regValue = 0; + regValue = (FT6206_G_MODE_INTERRUPT_TRIGGER & (FT6206_G_MODE_INTERRUPT_MASK >> FT6206_G_MODE_INTERRUPT_SHIFT)) << FT6206_G_MODE_INTERRUPT_SHIFT; + /* Set interrupt TOUCH_FT6236_I2C_ADDRESS mode in FT6206_GMODE_REG */ + TS_IO_Write(TOUCH_FT6236_I2C_ADDRESS, FT6206_GMODE_REG, regValue); + /*trigger reset */ + TouchReset(); +} + +void handleTouch() +{ + unsigned short touchX; + unsigned short touchY; + uint32_t tEvent = 0; + ft6x06_TS_GetXY(TOUCH_FT6236_I2C_ADDRESS, &touchX, &touchY, &tEvent); + // uint32_t gesture; + // ft6x06_TS_GetGestureID(TOUCH_FT6236_I2C_ADDRESS, &gesture); +#if defined( LCD_DIRECTION ) && (LCD_DIRECTION == LCD_VERTICAL) + touchX = LCD_WIDTH - touchX; + touchY = LCD_HEIGHT - touchY; +#else + unsigned short tmp = (LCD_WIDTH - 1) - touchY; + touchY = touchX; + touchX = tmp; +#endif + if (tEvent == FT6206_TOUCH_EVT_FLAG_CONTACT) { + 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; + } + + } +} + +extern "C" void EXTI9_5_IRQHandler(void) +{ + if (EXTI_GetITStatus(EXTI_Line9) != RESET) { + EXTI_ClearITPendingBit(EXTI_Line9); + touchEventOccured = true; + } +} + +bool touchPanelEventOccured() +{ + return touchEventOccured; +} + +TouchState touchPanelRead() +{ + if (!touchEventOccured) return internalTouchState; + + touchEventOccured = false; + + tmr10ms_t now = get_tmr10ms(); + internalTouchState.tapCount = 0; + + if (ft6x06_TS_DetectTouch(TOUCH_FT6236_I2C_ADDRESS)) { + 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 { + internalTouchState.x = LCD_WIDTH; + internalTouchState.y = LCD_HEIGHT; + internalTouchState.event = TE_SLIDE_END; + } + } + TouchState ret = internalTouchState; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + if(internalTouchState.event == TE_UP) + internalTouchState.event = TE_NONE; + return ret; +} diff --git a/radio/src/targets/pl18/touch_driver.h b/radio/src/targets/pl18/touch_driver.h new file mode 100644 index 00000000000..7fe3eae4b58 --- /dev/null +++ b/radio/src/targets/pl18/touch_driver.h @@ -0,0 +1,339 @@ +/* + * 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. + */ + +/* + Copyright 2016 fishpepper gmail.com + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + 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. + You should have received a copy of the GNU General Public License + along with this program. If not, see . + author: fishpepper gmail.com +*/ + +/** + ****************************************************************************** + * @file ft6x06.h + * @author MCD Application Team + * @version V1.0.0 + * @date 03-August-2015 + * @brief This file contains all the functions prototypes for the + * ft6x06.c IO expander driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ +#ifndef __FT6X06_H +#define __FT6X06_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Set Multi-touch as non supported */ +#ifndef TS_MULTI_TOUCH_SUPPORTED + #define TS_MULTI_TOUCH_SUPPORTED 0 +#endif + +/* Set Auto-calibration as non supported */ +#ifndef TS_AUTO_CALIBRATION_SUPPORTED + #define TS_AUTO_CALIBRATION_SUPPORTED 0 +#endif + +/* Macros --------------------------------------------------------------------*/ + +/** @typedef ft6x06_handle_TypeDef + * ft6x06 Handle definition. + */ +typedef struct +{ + uint8_t i2cInitialized; + + /* field holding the current number of simultaneous active touches */ + uint8_t currActiveTouchNb; + + /* field holding the touch index currently managed */ + uint8_t currActiveTouchIdx; + +} ft6x06_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 + + /* Possible values of global variable 'TS_I2C_Initialized' */ +#define FT6206_I2C_NOT_INITIALIZED 0 +#define FT6206_I2C_INITIALIZED 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 LCD_VERTICAL ( 0x00 ) +#define LCD_HORIZONTAL ( 0x01 ) +#define LCD_DIRECTION ( LCD_VERTICAL ) + +#define LCD_WIDTH ( 320 ) +#define LCD_HEIGHT ( 480 ) + +extern void TouchInit( void ); +extern void TouchDriver( void ); + + +#ifdef __cplusplus +} +#endif +#endif /* __FT6X06_H */ 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/telemetry/flysky_pl18.cpp b/radio/src/telemetry/flysky_pl18.cpp new file mode 100644 index 00000000000..4c03dac6348 --- /dev/null +++ b/radio/src/telemetry/flysky_pl18.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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 uint32_t PL18internalModuleFwVersion; + +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.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/translations.cpp b/radio/src/translations.cpp index d9ce70ba9be..053e9577370 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -955,7 +955,7 @@ const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; const char STR_RACING_MODE[] = TR_RACING_MODE; -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) ISTR(RFPOWER_AFHDS2); #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" ;; From f6c31acf4c7cd70f0bd809697271f13fca0891f6 Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 28 Dec 2021 14:54:44 +0100 Subject: [PATCH 27/99] Added CST340 touch controller to support PL18/PL18EV. --- radio/src/targets/pl18/CMakeLists.txt | 5 +- radio/src/targets/pl18/board.cpp | 3 +- radio/src/targets/pl18/board.h | 6 +- radio/src/targets/pl18/hal.h | 149 ++++--- radio/src/targets/pl18/touch_driver.cpp | 562 ------------------------ radio/src/targets/pl18/touch_driver.h | 339 -------------- radio/src/targets/pl18/tp_cst340.cpp | 317 +++++++++++++ radio/src/targets/pl18/tp_cst340.h | 60 +++ radio/src/tasks.cpp | 2 +- 9 files changed, 468 insertions(+), 975 deletions(-) delete mode 100644 radio/src/targets/pl18/touch_driver.cpp delete mode 100644 radio/src/targets/pl18/touch_driver.h create mode 100644 radio/src/targets/pl18/tp_cst340.cpp create mode 100644 radio/src/targets/pl18/tp_cst340.h diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index a3ad57e9f82..63a4cae3338 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -73,16 +73,13 @@ 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 touch_driver.cpp) -set(HARDWARE_TOUCH YES) - +set(TOUCH_DRIVER tp_cst340.cpp ../horus/i2c_driver.cpp) set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET} truetype_fonts) set(FIRMWARE_DEPENDENCIES datacopy) set(HARDWARE_TOUCH ON) set(SOFTWARE_KEYBOARD ON) - add_definitions(-DSTM32F429_439xx -DSDRAM -DCOLORLCD -DLIBOPENUI -DHARDWARE_TOUCH -DHARDWARE_KEYS -DSOFTWARE_KEYBOARD) add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 379e6d935f4..3ea434e056a 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -137,7 +137,6 @@ void boardInit() flysky_hall_stick_init(); init2MhzTimer(); init1msTimer(); - TouchInit(); usbInit(); uint32_t press_start = 0; @@ -203,7 +202,7 @@ void boardInit() void boardOff() { - lcd->drawFilledRect(0, 0, LCD_WIDTH, LCD_HEIGHT, SOLID, COLOR_THEME_FOCUS); + lcd->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_THEME_FOCUS); lcdOff(); SysTick->CTRL = 0; // turn off systick diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index b45619071b8..8b9a891bf63 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -34,7 +34,7 @@ #include "stm32f4xx_fmc.h" #endif -#include "touch_driver.h" +#include "tp_cst340.h" //#include "hallStick_driver.h" #include "lcd_driver.h" #include "battery_driver.h" @@ -409,8 +409,8 @@ uint32_t pwrPressedDuration();; #define AUX_SERIAL_POWER_OFF() // LCD driver -#define LCD_W 480 -#define LCD_H 320 +#define LCD_W 320 /* TODO! should be 480 */ +#define LCD_H 480 /* TODO! should be 320 */ #define LCD_DEPTH 16 #define LCD_CONTRAST_DEFAULT 20 void lcdInit(); diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 566bcdc13b4..9e2309d0f75 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -28,10 +28,10 @@ * TIM1 = Haptic * TIM4 = Trainer * TIM6 = Audio - * TIM7 = 2MHz counter + * TIM7 = 2 MHz counter * * - * TIM14 = 5ms counter + * TIM14 = 5 ms counter */ /* DMA Allocation: @@ -335,9 +335,31 @@ #define AUDIO_SPI_MOSI_GPIO_PIN GPIO_Pin_5 // PB.05 #define AUDIO_SPI_MOSI_GPIO_PinSource GPIO_PinSource5 -// I2C Bus -#define I2C_B1_RCC_AHB1Periph 0 -#define I2C_B1_RCC_APB1Periph 0 +// 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 GPIO_Pin_7 // PB.07 +#define I2C_B1_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 +#define I2C_B1_GPIO_AF GPIO_AF_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_RST_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_RST_GPIO GPIOB +#define TOUCH_RST_GPIO_PIN GPIO_Pin_12 // PB.12 + +#define TOUCH_INT_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_INT_GPIO GPIOB +#define TOUCH_INT_GPIO_PIN GPIO_Pin_9 // PB.09 +#define TOUCH_INT_EXTI_LINE1 EXTI_Line9 +#define TOUCH_INT_EXTI_IRQn1 EXTI9_5_IRQn +#define TOUCH_INT_EXTI_IRQHandler1 EXTI9_5_IRQHandler +#define TOUCH_INT_EXTI_PortSource EXTI_PortSourceGPIOB +#define TOUCH_INT_EXTI_PinSource1 EXTI_PinSource9 +#define TOUCH_INT_STATUS() (GPIO_ReadInputDataBit(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN)) // Haptic: TIM1_CH1 #define HAPTIC_PWM @@ -378,68 +400,68 @@ // Internal Module #define HARDWARE_INTERNAL_MODULE -#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) -#define INTMODULE_PWR_GPIO GPIOH -#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 -#define INTMODULE_GPIO GPIOF -//#define INTMODULE_TX_GPIO GPIOF -#define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 -//#define INTMODULE_RX_GPIO GPIOF -#define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 -#define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 -#define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 -#define INTMODULE_USART UART7 -#define INTMODULE_GPIO_AF GPIO_AF_UART7 -#define INTMODULE_USART_IRQn UART7_IRQn -#define INTMODULE_USART_IRQHandler UART7_IRQHandler -#define INTMODULE_DMA_STREAM DMA1_Stream1 -#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn -#define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 -#define INTMODULE_DMA_CHANNEL DMA_Channel_5 - -/*#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 -#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn +#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) +#define INTMODULE_PWR_GPIO GPIOH +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#define INTMODULE_GPIO GPIOF +//#define INTMODULE_TX_GPIO GPIOF +#define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 +//#define INTMODULE_RX_GPIO GPIOF +#define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 +#define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 +#define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 +#define INTMODULE_USART UART7 +#define INTMODULE_GPIO_AF GPIO_AF_UART7 +#define INTMODULE_USART_IRQn UART7_IRQn +#define INTMODULE_USART_IRQHandler UART7_IRQHandler +#define INTMODULE_DMA_STREAM DMA1_Stream1 +#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn +#define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 +#define INTMODULE_DMA_CHANNEL DMA_Channel_5 + +/*#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 +#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn #define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream3_IRQHandler -#define INTMODULE_TX_DMA_STREAM DMA1_Stream1 -#define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn +#define INTMODULE_TX_DMA_STREAM DMA1_Stream1 +#define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn #define INTMODULE_TX_DMA_Stream_IRQHandler DMA1_Stream1_IRQHandler -#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1*/ +#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1 */ -#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) -#define INTMODULE_RCC_APB2Periph 0 -#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) +#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) +#define INTMODULE_RCC_APB2Periph 0 +#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_PWR_FIX_GPIO GPIOA -#define EXTMODULE_PWR_FIX_GPIO_PIN GPIO_Pin_2 // PA.02 +#define EXTMODULE_PWR_GPIO GPIOD +#define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_11 +#define EXTMODULE_PWR_FIX_GPIO GPIOA +#define EXTMODULE_PWR_FIX_GPIO_PIN GPIO_Pin_2 // PA.02 #define EXTMODULE_RCC_AHB1Periph \ (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) #define EXTMODULE_RCC_APB1Periph 0 -#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) -#define EXTMODULE_TX_GPIO GPIOC -#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 -#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 -#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 -#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 -#define EXTMODULE_RX_GPIO GPIOC -#define EXTMODULE_RX_GPIO_PIN GPIO_Pin_7 // PC.07 -#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 -#define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 -#define EXTMODULE_TIMER TIM8 -#define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn -#define EXTMODULE_TIMER_CC_IRQn TIM8_CC_IRQn -#define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler -#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#define EXTMODULE_TIMER_TX_GPIO_AF GPIO_AF_TIM8 +#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) +#define EXTMODULE_TX_GPIO GPIOC +#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 +#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 +#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 +#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_RX_GPIO GPIOC +#define EXTMODULE_RX_GPIO_PIN GPIO_Pin_7 // PC.07 +#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 +#define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_TIMER TIM8 +#define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_CC_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF GPIO_AF_TIM8 //USART #define EXTMODULE_USART USART6 #define EXTMODULE_USART_GPIO GPIOC @@ -462,11 +484,11 @@ #define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) //TIMER -#define EXTMODULE_DMA_CHANNEL DMA_Channel_7 -#define EXTMODULE_DMA_STREAM DMA2_Stream1 -#define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn -#define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler -#define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 +#define EXTMODULE_DMA_CHANNEL DMA_Channel_7 +#define EXTMODULE_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler +#define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 #define EXTMODULE_TIMER_DMA_CHANNEL DMA_Channel_7 #define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 @@ -525,7 +547,6 @@ #define BLUETOOTH_ON_GPIO GPIOI #define BLUETOOTH_ON_GPIO_PIN GPIO_Pin_8 // PI.8 -// Bluetooth #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 @@ -543,13 +564,13 @@ #define BT_EN_GPIO GPIOI #define BT_EN_GPIO_PIN GPIO_Pin_8 // PI.08 -#define BT_CONNECTED_GPIO GPIOJ -#define BT_CONNECTED_GPIO_PIN GPIO_Pin_1 // PJ.10 +#define BT_CONNECTED_GPIO GPIOJ +#define BT_CONNECTED_GPIO_PIN GPIO_Pin_1 // PJ.10 #define BT_CMD_MODE_GPIO GPIOH #define BT_CMD_MODE_GPIO_PIN GPIO_Pin_6 // PH.6 -// Xms Interrupt +// 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 diff --git a/radio/src/targets/pl18/touch_driver.cpp b/radio/src/targets/pl18/touch_driver.cpp deleted file mode 100644 index f844fa25807..00000000000 --- a/radio/src/targets/pl18/touch_driver.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* - * 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 "touch_driver.h" - -volatile static bool touchEventOccured; - -#define FT6x06_MAX_INSTANCE 1 - -#define I2C_TOUCH_RCC_AHB1Periph RCC_AHB1Periph_GPIOB -#define I2C_TOUCH_RCC_APB1Periph RCC_APB1Periph_I2C1 -#define I2C_TOUCH I2C1 -#define I2C_TOUCH_GPIO GPIOB -#define I2C_TOUCH_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 -#define I2C_TOUCH_SDA_GPIO_PIN GPIO_Pin_7 // PB.09 - -#define I2C_TOUCH_RESET_GPIO GPIOB -#define I2C_TOUCH_RESET_GPIO_PIN GPIO_Pin_12 // PB.12 -#define I2C_TOUCH_INT_GPIO GPIOB -#define I2C_TOUCH_INT_GPIO_PIN GPIO_Pin_9 // PB.09 - -#define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) - -uint8_t ft6x06[FT6x06_MAX_INSTANCE] = {0}; -static ft6x06_handle_TypeDef ft6x06_handle = {FT6206_I2C_NOT_INITIALIZED, 0, 0}; - -tmr10ms_t downTime = 0; -tmr10ms_t tapTime = 0; -short tapCount = 0; -#define TAP_TIME 25 - -static TouchState internalTouchState = {}; - -void I2C_FreeBus() -{ - GPIO_InitTypeDef GPIO_InitStructure; - // reset i2c bus by setting clk as output and sending manual clock pulses - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SCL_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SDA_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); - - //send 100khz clock train for some 100ms - tmr10ms_t until = get_tmr10ms() + 11; - while (get_tmr10ms() < until) { - if (GPIO_ReadInputDataBit(I2C_TOUCH_GPIO, I2C_TOUCH_SDA_GPIO_PIN) == 1) { - TRACE("touch: i2c free again\n"); - break; - } - TRACE("FREEEEE"); - I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; //BSRRL - delay_us(10); - I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SCL_GPIO_PIN; - delay_us(10); - } - - //send stop condition: - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SDA_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); - - //clock is low - I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; - delay_us(10); - //sda = lo - I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SDA_GPIO_PIN; - delay_us(10); - //clock goes high - I2C_TOUCH_GPIO->BSRRH = I2C_TOUCH_SCL_GPIO_PIN; - delay_us(10); - //sda = hi - I2C_TOUCH_GPIO->BSRRL = I2C_TOUCH_SDA_GPIO_PIN; - delay_us(10); - TRACE("FREE BUS"); -} - -void Touch_DeInit() -{ - I2C_DeInit(I2C_TOUCH); - (RCC->APB1RSTR |= (RCC_APB1RSTR_I2C1RST)); - delay_ms(150); - (RCC->APB1RSTR &= ~(RCC_APB1RSTR_I2C1RST)); -} - -void I2C_Init() -{ - Touch_DeInit(); - - GPIO_InitTypeDef GPIO_InitStructure; - EXTI_InitTypeDef EXTI_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - I2C_InitTypeDef I2C_InitStructure; - - - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); - RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); - - RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE); - RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE); - - I2C_FreeBus(); - - GPIO_PinAFConfig(I2C_TOUCH_GPIO, GPIO_PinSource7, GPIO_AF_I2C1); - GPIO_PinAFConfig(I2C_TOUCH_GPIO, GPIO_PinSource8, GPIO_AF_I2C1); - - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_SCL_GPIO_PIN | I2C_TOUCH_SDA_GPIO_PIN; - GPIO_Init(I2C_TOUCH_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_RESET_GPIO_PIN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL; // - GPIO_Init(I2C_TOUCH_RESET_GPIO, &GPIO_InitStructure); - - - //https://community.st.com/s/question/0D50X00009XkZ9FSAV/stm32f4-i2c-issues-solved - RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); - I2C_StructInit(&I2C_InitStructure); - I2C_InitStructure.I2C_ClockSpeed = 400000; - I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; - I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; - I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; - I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; - I2C_Init(I2C_TOUCH, &I2C_InitStructure); - - I2C_Init(I2C_TOUCH, &I2C_InitStructure); - //I2C_StretchClockCmd(I2C_TOUCH, ENABLE); - I2C_Cmd(I2C_TOUCH, ENABLE); - - //ext interupt - GPIO_InitStructure.GPIO_Pin = I2C_TOUCH_INT_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(I2C_TOUCH_INT_GPIO, &GPIO_InitStructure); - - SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource9); - EXTI_InitStructure.EXTI_Line = EXTI_Line9; - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; - EXTI_InitStructure.EXTI_LineCmd = ENABLE; - EXTI_Init(&EXTI_InitStructure); - - NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); -} - -#define I2C_TIMEOUT_MAX 1000 - -bool I2C_WaitEvent(uint32_t event) -{ - uint32_t timeout = I2C_TIMEOUT_MAX; - while (!I2C_CheckEvent(I2C_TOUCH, event)) { - if ((timeout--) == 0) return false; - } - return true; -} - -bool I2C_WaitEventCleared(uint32_t event) -{ - uint32_t timeout = I2C_TIMEOUT_MAX; - while (I2C_CheckEvent(I2C_TOUCH, event)) { - if ((timeout--) == 0) return false; - } - return true; -} - -bool I2C_Send7BitAddress(uint8_t address, uint16_t direction) -{ - I2C_SendData(I2C_TOUCH, (address << 1) | ((direction == I2C_Direction_Receiver) ? 1 : 0)); - // check if slave acknowledged his address within timeout - if (!I2C_WaitEvent(direction == I2C_Direction_Transmitter ? I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) - return false; - return true; -} - -bool touch_i2c_read(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) -{ - if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) return false; - I2C_GenerateSTART(I2C_TOUCH, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; - if (!I2C_Send7BitAddress(addr, I2C_Direction_Transmitter)) return false; - I2C_SendData(I2C_TOUCH, reg); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return false; - I2C_GenerateSTART(I2C_TOUCH, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; - if (!I2C_Send7BitAddress(addr, I2C_Direction_Receiver)) return false; - - if (len > 1) I2C_AcknowledgeConfig(I2C_TOUCH, ENABLE); - - while (len) { - if (len == 1) I2C_AcknowledgeConfig(I2C_TOUCH, DISABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_RECEIVED))return false; - *data++ = I2C_ReceiveData(I2C_TOUCH);; - len--; - } - I2C_GenerateSTOP(I2C_TOUCH, ENABLE); - return true; -} - -static bool touch_i2c_write(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) -{ - if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) return false; - I2C_GenerateSTART(I2C_TOUCH, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) return false; - - if (!I2C_Send7BitAddress(addr, I2C_Direction_Transmitter)) return false; - I2C_SendData(I2C_TOUCH, (uint8_t) ((reg & 0xFF00) >> 8)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; - I2C_SendData(I2C_TOUCH, (uint8_t) (reg & 0x00FF)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; - while (len--) { - I2C_SendData(I2C_TOUCH, *data); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) return false; - data++; - } - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return false; - I2C_GenerateSTOP(I2C_TOUCH, ENABLE); - 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(); - } -} - -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 uint8_t TS_IO_Read(uint8_t reg) -{ - return TS_IO_Read(TOUCH_FT6236_I2C_ADDRESS, reg); -} -*/ - -static void touch_ft6236_debug_info(void) -{ -#if defined(DEBUG) - TRACE("ft6x36: thrhld = %d", TS_IO_Read(TOUCH_FT6236_REG_TH_GROUP) * 4); - TRACE("ft6x36: rep rate=", TS_IO_Read(TOUCH_FT6236_REG_PERIODACTIVE) * 10); - TRACE("ft6x36: fw lib 0x%02X %02X", TS_IO_Read(TOUCH_FT6236_REG_LIB_VER_H), TS_IO_Read(TOUCH_FT6236_REG_LIB_VER_L)); - TRACE("ft6x36: fw v 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_FIRMID)); - TRACE("ft6x36: CHIP ID 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_CIPHER)); - TRACE("ft6x36: CTPM ID 0x%02X", TS_IO_Read(TOUCH_FT6236_REG_FOCALTECH_ID)); - TRACE("ft6x36: rel code 0x%02X", TS_IO_Read(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(uint16_t DeviceAddr) -{ - volatile uint8_t nbTouch = 0; - - /* Read register FT6206_TD_STAT_REG to check number of touches detection */ - nbTouch = TS_IO_Read(DeviceAddr, 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 */ - ft6x06_handle.currActiveTouchNb = nbTouch; - - /* Reset current active touch index on which to work on */ - ft6x06_handle.currActiveTouchIdx = 0; - return (nbTouch); -} - -/** - * @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) -} -*/ - -/** - * @brief Get the touch screen X and Y positions values - * Manage multi touch thanks to touch Index global - * variable 'ft6x06_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 DeviceAddr, uint16_t * X, uint16_t * Y, uint32_t * event) -{ - uint8_t regAddress = 0; - uint8_t dataxy[4]; - - if (ft6x06_handle.currActiveTouchIdx < ft6x06_handle.currActiveTouchNb) { - switch (ft6x06_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(DeviceAddr, 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); - */ - ft6x06_handle.currActiveTouchIdx++; - } -} - -void TouchReset() -{ - GPIO_ResetBits(I2C_TOUCH_RESET_GPIO, I2C_TOUCH_RESET_GPIO_PIN); - delay_ms(20); - GPIO_SetBits(I2C_TOUCH_RESET_GPIO, I2C_TOUCH_RESET_GPIO_PIN); - delay_ms(300); -} - -void TouchInit(void) -{ - I2C_Init(); - TouchReset(); - touch_ft6236_debug_info(); - /* INT generation for new touch available */ - /* Note TS_INT is active low */ - uint8_t regValue = 0; - regValue = (FT6206_G_MODE_INTERRUPT_TRIGGER & (FT6206_G_MODE_INTERRUPT_MASK >> FT6206_G_MODE_INTERRUPT_SHIFT)) << FT6206_G_MODE_INTERRUPT_SHIFT; - /* Set interrupt TOUCH_FT6236_I2C_ADDRESS mode in FT6206_GMODE_REG */ - TS_IO_Write(TOUCH_FT6236_I2C_ADDRESS, FT6206_GMODE_REG, regValue); - /*trigger reset */ - TouchReset(); -} - -void handleTouch() -{ - unsigned short touchX; - unsigned short touchY; - uint32_t tEvent = 0; - ft6x06_TS_GetXY(TOUCH_FT6236_I2C_ADDRESS, &touchX, &touchY, &tEvent); - // uint32_t gesture; - // ft6x06_TS_GetGestureID(TOUCH_FT6236_I2C_ADDRESS, &gesture); -#if defined( LCD_DIRECTION ) && (LCD_DIRECTION == LCD_VERTICAL) - touchX = LCD_WIDTH - touchX; - touchY = LCD_HEIGHT - touchY; -#else - unsigned short tmp = (LCD_WIDTH - 1) - touchY; - touchY = touchX; - touchX = tmp; -#endif - if (tEvent == FT6206_TOUCH_EVT_FLAG_CONTACT) { - 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; - } - - } -} - -extern "C" void EXTI9_5_IRQHandler(void) -{ - if (EXTI_GetITStatus(EXTI_Line9) != RESET) { - EXTI_ClearITPendingBit(EXTI_Line9); - touchEventOccured = true; - } -} - -bool touchPanelEventOccured() -{ - return touchEventOccured; -} - -TouchState touchPanelRead() -{ - if (!touchEventOccured) return internalTouchState; - - touchEventOccured = false; - - tmr10ms_t now = get_tmr10ms(); - internalTouchState.tapCount = 0; - - if (ft6x06_TS_DetectTouch(TOUCH_FT6236_I2C_ADDRESS)) { - 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 { - internalTouchState.x = LCD_WIDTH; - internalTouchState.y = LCD_HEIGHT; - internalTouchState.event = TE_SLIDE_END; - } - } - TouchState ret = internalTouchState; - internalTouchState.deltaX = 0; - internalTouchState.deltaY = 0; - if(internalTouchState.event == TE_UP) - internalTouchState.event = TE_NONE; - return ret; -} diff --git a/radio/src/targets/pl18/touch_driver.h b/radio/src/targets/pl18/touch_driver.h deleted file mode 100644 index 7fe3eae4b58..00000000000 --- a/radio/src/targets/pl18/touch_driver.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - * 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. - */ - -/* - Copyright 2016 fishpepper gmail.com - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - 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. - You should have received a copy of the GNU General Public License - along with this program. If not, see . - author: fishpepper gmail.com -*/ - -/** - ****************************************************************************** - * @file ft6x06.h - * @author MCD Application Team - * @version V1.0.0 - * @date 03-August-2015 - * @brief This file contains all the functions prototypes for the - * ft6x06.c IO expander driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2015 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ -#ifndef __FT6X06_H -#define __FT6X06_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* Set Multi-touch as non supported */ -#ifndef TS_MULTI_TOUCH_SUPPORTED - #define TS_MULTI_TOUCH_SUPPORTED 0 -#endif - -/* Set Auto-calibration as non supported */ -#ifndef TS_AUTO_CALIBRATION_SUPPORTED - #define TS_AUTO_CALIBRATION_SUPPORTED 0 -#endif - -/* Macros --------------------------------------------------------------------*/ - -/** @typedef ft6x06_handle_TypeDef - * ft6x06 Handle definition. - */ -typedef struct -{ - uint8_t i2cInitialized; - - /* field holding the current number of simultaneous active touches */ - uint8_t currActiveTouchNb; - - /* field holding the touch index currently managed */ - uint8_t currActiveTouchIdx; - -} ft6x06_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 - - /* Possible values of global variable 'TS_I2C_Initialized' */ -#define FT6206_I2C_NOT_INITIALIZED 0 -#define FT6206_I2C_INITIALIZED 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 LCD_VERTICAL ( 0x00 ) -#define LCD_HORIZONTAL ( 0x01 ) -#define LCD_DIRECTION ( LCD_VERTICAL ) - -#define LCD_WIDTH ( 320 ) -#define LCD_HEIGHT ( 480 ) - -extern void TouchInit( void ); -extern void TouchDriver( void ); - - -#ifdef __cplusplus -} -#endif -#endif /* __FT6X06_H */ diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp new file mode 100644 index 00000000000..c47cb30b8a8 --- /dev/null +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -0,0 +1,317 @@ +/* + * 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 "../horus/i2c_driver.h" +#include "tp_cst340.h" + +bool touchCST340Flag = false; +volatile static bool touchEventOccured = false; +struct TouchData touchData; +uint32_t touchI2Chiccups = 0; +tmr10ms_t downTime = 0; +tmr10ms_t tapTime = 0; +short tapCount = 0; +#define TAP_TIME 25 + +I2C_HandleTypeDef hi2c1; + +static TouchState internalTouchState = {}; + +static void TOUCH_AF_ExtiStop(void) +{ + SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); + + EXTI_InitTypeDef EXTI_InitStructure; + EXTI_StructInit(&EXTI_InitStructure); + EXTI_InitStructure.EXTI_Line = TOUCH_INT_EXTI_LINE1; + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; + EXTI_InitStructure.EXTI_LineCmd = DISABLE; + EXTI_Init(&EXTI_InitStructure); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = TOUCH_INT_EXTI_IRQn1; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // Not used as 4 bits are used for the pre-emption priority + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); +} + +static void TOUCH_AF_ExtiConfig(void) +{ + SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); + + EXTI_InitTypeDef EXTI_InitStructure; + EXTI_StructInit(&EXTI_InitStructure); + EXTI_InitStructure.EXTI_Line = TOUCH_INT_EXTI_LINE1; + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; + EXTI_InitStructure.EXTI_LineCmd = ENABLE; + EXTI_Init(&EXTI_InitStructure); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = TOUCH_INT_EXTI_IRQn1; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; + 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); +} + +/* Currently not used, as no CST340 register needs to be written +bool I2C_CST340_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) +{ + uint8_t uAddrAndBuf[6]; + uAddrAndBuf[0] = (uint8_t)((reg & 0xFF00) >> 8); + uAddrAndBuf[1] = (uint8_t)(reg & 0x00FF); + + if (len > 0) + { + for (int i = 0;i < len;i++) + { + uAddrAndBuf[i + 2] = buf[i]; + } + } + + if (HAL_I2C_Master_Transmit(&hi2c1, CST340_I2C_ADDR << 1, uAddrAndBuf, len + 2, 100) != HAL_OK) + { + TRACE("I2C B1 ERROR: WriteRegister failed"); + return false; + } + return true; +} */ + +bool I2C_CST340_ReadRegister(uint16_t reg, uint8_t * buf, uint8_t len) +{ + uint8_t uRegAddr[2]; + uRegAddr[0] = (uint8_t)((reg & 0xFF00) >> 8); + uRegAddr[1] = (uint8_t)(reg & 0x00FF); + + if (HAL_I2C_Master_Transmit(&hi2c1, CST340_I2C_ADDR << 1, uRegAddr, 2, 10) != HAL_OK) + { + TRACE("I2C B1 ERROR: ReadRegister write reg address failed"); + return false; + } + + if (HAL_I2C_Master_Receive(&hi2c1, CST340_I2C_ADDR << 1, buf, len, 100) != HAL_OK) + { + TRACE("I2C B1 ERROR: ReadRegister read reg address failed"); + return false; + } + return true; +} + +void touchPanelDeInit(void) +{ + TOUCH_AF_ExtiStop(); + touchCST340Flag = false; +} + +bool touchPanelInit(void) +{ + uint8_t tmp[4] = {0}; + + if (touchCST340Flag) { + TOUCH_AF_ExtiConfig(); + return true; + } + else { + TRACE("Touchpanel init start ..."); + + delay_ms(1); + // TOUCH RST + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = TOUCH_RST_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TOUCH_RST_GPIO, &GPIO_InitStructure); + + // TOUCH INT + GPIO_InitStructure.GPIO_Pin = TOUCH_INT_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_Init(TOUCH_INT_GPIO, &GPIO_InitStructure); + + // I2C configuration + hi2c1.Instance = I2C_B1; + hi2c1.Init.ClockSpeed = I2C_B1_CLK_RATE; + hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_16_9; + hi2c1.Init.OwnAddress1 = 0; + hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + hi2c1.Init.OwnAddress2 = 0; + hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&hi2c1) != HAL_OK) + { + TRACE("I2C B1 ERROR: HAL_I2C_Init() failed"); + return false; + } + // Configure analog filter + if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) + { + TRACE("I2C B1 ERROR: HAL_I2CEx_ConfigAnalogFilter() failed"); + return false; + } + // Configure digital filter + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) + { + TRACE("I2C B1 ERROR: HAL_I2CEx_ConfigDigitalFilter() failed"); + return false; + } + + TPRST_LOW(); + delay_ms(1); // FlySky example uses 10ms, datasheet says 0.1us minimally + TPRST_HIGH(); + delay_ms(400); // FlySky example uses 20ms only, datasheet says 300ms + + TOUCH_AF_ExtiConfig(); + + if (!I2C_CST340_ReadRegister(CST340_READ_XY_REG, tmp, 4)) { + TRACE("CST340 chip NOT FOUND"); + return false; + } + touchCST340Flag = true; + } + return true; +} + +bool I2C_ReInit(void) +{ + TRACE("I2C B1 ReInit"); + touchPanelDeInit(); + if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) + TRACE("I2C B1 ReInit - I2C DeInit failed"); + + // If DeInit fails, try to re-init anyway + if (!touchPanelInit()) + { + TRACE("I2C B1 ReInit - touchPanelInit failed"); + return false; + } + return true; +} + +#if defined(SIMU) || defined(SEMIHOSTING) || defined(DEBUG) +static const char* event2str(uint8_t ev) +{ + switch(ev){ + case TE_NONE: + return "NONE"; + case TE_UP: + return "UP"; + case TE_DOWN: + return "DOWN"; + case TE_SLIDE_END: + return "SLIDE_END"; + case TE_SLIDE: + return "SLIDE"; + default: + return "UNKNOWN"; + } +} +#endif + +struct TouchState touchPanelRead() +{ + static uint8_t touchData[4]; + static uint16_t x = 0; + static uint16_t y = 0; + + if (!touchEventOccured) + return internalTouchState; + + touchEventOccured = false; + + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + tmr10ms_t now = get_tmr10ms(); + internalTouchState.tapCount = 0; + + if (!I2C_CST340_ReadRegister(CST340_READ_XY_REG, touchData, 4)) { + touchI2Chiccups++; + TRACE("CST340 I2C read XY error"); + if (!I2C_ReInit()) + TRACE("I2C B1 ReInit failed"); + return internalTouchState; + } + + if( touchData[0] == 0x06 ) + { + x = (touchData[1]<<4) + ((touchData[3]>>4)&0x0f); + y = (touchData[2]<<4) + ((touchData[3])&0x0f); + if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { + internalTouchState.event = TE_DOWN; + internalTouchState.startX = internalTouchState.x = x; + internalTouchState.startY = internalTouchState.y = y; + downTime = now; + } else { + internalTouchState.deltaX = x - internalTouchState.x; + internalTouchState.deltaY = y - internalTouchState.y; + if (internalTouchState.event == TE_SLIDE || + abs(internalTouchState.deltaX) >= SLIDE_RANGE || + abs(internalTouchState.deltaY) >= SLIDE_RANGE) + { + internalTouchState.event = TE_SLIDE; + internalTouchState.x = x; + internalTouchState.y = y; + } + } + } + else + { + if (internalTouchState.event == TE_SLIDE) { + internalTouchState.event = TE_SLIDE_END; + } 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 if (internalTouchState.event != TE_SLIDE_END) { + internalTouchState.event = TE_NONE; + } + } + + TRACE("touch event = %s", event2str(internalTouchState.event)); + return internalTouchState; +} + +extern "C" void TOUCH_INT_EXTI_IRQHandler1(void) +{ + if (EXTI_GetITStatus(TOUCH_INT_EXTI_LINE1) != RESET) { + touchEventOccured = true; + EXTI_ClearITPendingBit(TOUCH_INT_EXTI_LINE1); + } +} + +bool touchPanelEventOccured() +{ + return touchEventOccured; +} diff --git a/radio/src/targets/pl18/tp_cst340.h b/radio/src/targets/pl18/tp_cst340.h new file mode 100644 index 00000000000..82269578e74 --- /dev/null +++ b/radio/src/targets/pl18/tp_cst340.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. + */ + +#pragma once + +#include "touch.h" + +#define HAS_TOUCH_PANEL() touchCST340Flag == true + +#define CST340_READ_XY_REG 0xD000 // Touch info register +#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 bool touchPanelInit(); + +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/tasks.cpp b/radio/src/tasks.cpp index ed779acc8ee..4309034b674 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(SIMU) touchPanelInit(); #endif From 7d4c4bee01efff9e6ef7f1b4846c59bce5d99c23 Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 28 Dec 2021 16:30:51 +0100 Subject: [PATCH 28/99] Typofix (parenthesis). --- companion/src/firmwares/boards.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index d8cffed0c73..37eb5c16856 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -549,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 || IS_FLYSKY_PL18(board))) + if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_PL18(board)) return getCapability(board, Board::Switches) * 3; else return 9; From 58e5ec21cbc41b0dd97808863d30bc3bcc3e2e0b Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 28 Dec 2021 19:21:51 +0100 Subject: [PATCH 29/99] Touch coordinates need to be inverted horizontally and vertically. Removed SD presence detection to get Trim 2 Up button also free, assuming SD card is always there. Added trim 1 and 2 sampling plus emulated keys from physical trim buttons 3 to 8. Trim 6 up == ENTER Trim 6 down == RTN/Exit --- radio/src/targets/pl18/board.h | 24 +++--- radio/src/targets/pl18/hal.h | 65 +++++++------- radio/src/targets/pl18/keys_driver.cpp | 112 ++++++++++++++++--------- radio/src/targets/pl18/tp_cst340.cpp | 4 +- 4 files changed, 125 insertions(+), 80 deletions(-) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 8b9a891bf63..abc2c4c5bbd 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -84,7 +84,7 @@ uint32_t sdGetSpeed(); #define SD_IS_HC() (sdIsHC()) #define SD_GET_SPEED() (sdGetSpeed()) #define SD_GET_FREE_BLOCKNR() (sdGetFreeSectors()) -#define SD_CARD_PRESENT() (~SD_PRESENT_GPIO->IDR & SD_PRESENT_GPIO_PIN) +#define SD_CARD_PRESENT() true void sdInit(); void sdMount(); void sdDone(); @@ -152,17 +152,17 @@ enum EnumKeys 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_LAST = TRM_LS_UP, + TRM1_DWN = TRM_BASE, + TRM1_UP, + TRM2_DWN, + TRM2_UP, + TRM3_DWN, + TRM3_UP, + TRM4_DWN, + TRM4_UP, + TRM5_DWN, + TRM5_UP, + TRM_LAST = TRM5_UP, NUM_KEYS }; diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 9e2309d0f75..d03f51e6462 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -41,29 +41,35 @@ 2/3/4 SDIO */ - // Trims -#define TRIMS_GPIO_REG_RHL GPIOD->IDR -#define TRIMS_GPIO_PIN_RHL GPIO_Pin_7 // PD.07 -#define TRIMS_GPIO_REG_RHR GPIOG->IDR -#define TRIMS_GPIO_PIN_RHR GPIO_Pin_10 // PG.10 -#define TRIMS_GPIO_REG_RVD GPIOJ->IDR -#define TRIMS_GPIO_PIN_RVD GPIO_Pin_0 // PJ.00 -#define TRIMS_GPIO_REG_RVU GPIOB->IDR -#define TRIMS_GPIO_PIN_RVU GPIO_Pin_15 // PB.15 -#define TRIMS_GPIO_REG_RPRESS GPIOC->IDR -#define TRIMS_GPIO_PIN_RPRESS GPIO_Pin_13 // PC.13 - -#define TRIMS_GPIO_REG_LHL GPIOH->IDR -#define TRIMS_GPIO_PIN_LHL GPIO_Pin_2 // PH.02 -#define TRIMS_GPIO_REG_LHR GPIOG->IDR -#define TRIMS_GPIO_PIN_LHR GPIO_Pin_2 // PG.02 -#define TRIMS_GPIO_REG_LVU GPIOH->IDR -#define TRIMS_GPIO_PIN_LVU GPIO_Pin_7 // PH.07 -#define TRIMS_GPIO_REG_LVD GPIOJ->IDR -#define TRIMS_GPIO_PIN_LVD GPIO_Pin_12 // PJ.12 -#define TRIMS_GPIO_REG_LPRESS GPIOG->IDR -#define TRIMS_GPIO_PIN_LPRESS GPIO_Pin_11 // PG.11 +#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 +#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) @@ -90,12 +96,14 @@ // 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_13) +#define KEYS_GPIOB_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_14) +#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_13 ) /* TODO! PC8-PC11, currently allocated for SDIO */ #define KEYS_GPIOD_PINS (GPIO_Pin_7) -#define KEYS_GPIOG_PINS (GPIO_Pin_2 | GPIO_Pin_10 | GPIO_Pin_11) -#define KEYS_GPIOH_PINS (GPIO_Pin_2 | GPIO_Pin_7) -#define KEYS_GPIOJ_PINS (GPIO_Pin_0 | GPIO_Pin_12) +#define KEYS_GPIOF_PINS (GPIO_Pin_10) +#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) @@ -275,8 +283,9 @@ // 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 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 diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index ca2766dacda..22787cb51e1 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -26,37 +26,68 @@ uint32_t readKeys() { uint32_t result = 0; bool getKeys = true; + +/* TODO! Uncomment, only for testing #if defined(LUA) if (!isLuaStandaloneRunning()) { getKeys = false; } #endif +*/ if (getKeys) { - if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) - result |= 1 << KEY_RADIO; - if (TRIMS_GPIO_REG_LHR & TRIMS_GPIO_PIN_LHR) - result |= 1 << KEY_MODEL; - if (TRIMS_GPIO_REG_LVD & TRIMS_GPIO_PIN_LVD) - result |= 1 << KEY_TELEM; - if (TRIMS_GPIO_REG_LVU & TRIMS_GPIO_PIN_LVU) - result |= 1 << KEY_PGUP; - if (TRIMS_GPIO_REG_RVD & TRIMS_GPIO_PIN_RVD) - result |= 1 << KEY_DOWN; - if (TRIMS_GPIO_REG_RVU & TRIMS_GPIO_PIN_RVU) - result |= 1 << KEY_UP; - if (TRIMS_GPIO_REG_RHL & TRIMS_GPIO_PIN_RHL) - result |= 1 << KEY_LEFT; - if (TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) - result |= 1 << KEY_RIGHT; + 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 << KEY_RADIO; // TR7 left + + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << KEY_MODEL; // TR7 right + + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << KEY_TELEM; // TR5 down + + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << KEY_PGUP; // TR5 up + + 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 << KEY_DOWN; // TR3 down + + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << KEY_UP; // TR3 up + + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << KEY_LEFT; // TR4 up + + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << KEY_RIGHT; // TR4 down + + GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + GPIO_ResetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); } // Enter and Exit are always supported - if (TRIMS_GPIO_REG_RPRESS & TRIMS_GPIO_PIN_RPRESS) - result |= 1 << KEY_ENTER; - if (TRIMS_GPIO_REG_LPRESS & TRIMS_GPIO_PIN_LPRESS) - result |= 1 << KEY_EXIT; + GPIO_SetBits(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_PIN); + GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); + GPIO_SetBits(TRIMS_GPIO_OUT4, TRIMS_GPIO_OUT4_PIN); + GPIO_ResetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + delay_us(10); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) + result |= 1 << KEY_ENTER; // TR6 up + + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << KEY_EXIT; // TR6 down + + GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); return result; } @@ -71,22 +102,17 @@ uint32_t readTrims() } #endif if(!getTrim) return result; - if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) - result |= 1 << (TRM_LH_DWN - TRM_BASE); - if (TRIMS_GPIO_REG_LHR & TRIMS_GPIO_PIN_LHR) - result |= 1 << (TRM_LH_UP - TRM_BASE); - if (TRIMS_GPIO_REG_LVD & TRIMS_GPIO_PIN_LVD) - result |= 1 << (TRM_LV_DWN - TRM_BASE); - if (TRIMS_GPIO_REG_LVU & TRIMS_GPIO_PIN_LVU) - result |= 1 << (TRM_LV_UP - TRM_BASE); - if (TRIMS_GPIO_REG_RVD & TRIMS_GPIO_PIN_RVD) - result |= 1 << (TRM_RV_DWN - TRM_BASE); - if (TRIMS_GPIO_REG_RVU & TRIMS_GPIO_PIN_RVU) - result |= 1 << (TRM_RV_UP - TRM_BASE); - if (TRIMS_GPIO_REG_RHL & TRIMS_GPIO_PIN_RHL) - result |= 1 << (TRM_RH_DWN - TRM_BASE); - if (TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) - result |= 1 << (TRM_RH_UP - TRM_BASE); + if (TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) + result |= 1 << (TRM1_UP - TRM_BASE); + if (TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) + result |= 1 << (TRM1_DWN - TRM_BASE); + + if (TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) + result |= 1 << (TRM2_UP - TRM_BASE); + if (TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) + result |= 1 << (TRM2_DWN - TRM_BASE); + + // TODO! Extract the matrix trims return result; } @@ -170,12 +196,22 @@ void keysInit() GPIO_InitStructure.GPIO_Pin = KEYS_GPIOD_PINS; GPIO_Init(GPIOD, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = KEYS_GPIOG_PINS; - GPIO_Init(GPIOG, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOF_PINS; + GPIO_Init(GPIOF, &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/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index c47cb30b8a8..c5cc14d22d4 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -260,8 +260,8 @@ struct TouchState touchPanelRead() if( touchData[0] == 0x06 ) { - x = (touchData[1]<<4) + ((touchData[3]>>4)&0x0f); - y = (touchData[2]<<4) + ((touchData[3])&0x0f); + x = LCD_W - ((touchData[1]<<4) + ((touchData[3]>>4)&0x0f)); + y = LCD_H - ((touchData[2]<<4) + ((touchData[3])&0x0f)); if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { internalTouchState.event = TE_DOWN; internalTouchState.startX = internalTouchState.x = x; From afa7a9e71931b63f272034122b1a518f8da0bf42 Mon Sep 17 00:00:00 2001 From: rotorman Date: Wed, 29 Dec 2021 10:25:04 +0100 Subject: [PATCH 30/99] Enable touch button on calib screen (bugfix). --- radio/src/gui/colorlcd/radio_calibration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index e61d020aa80..8ddba702d29 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -93,7 +93,7 @@ void RadioCalibrationPage::buildBody(FormWindow * window) deco->setSlidersVisible(true); deco->setFlightModeVisible(false); -#if defined(PCBNV14) // TODO! Check || defined (PCBPL18) +#if defined(PCBNV14) || defined(PCBPL18) new TextButton(window, {LCD_W - 120, LCD_H - 140, 90, 40}, "Next", [=]() -> uint8_t { nextStep(); From afb955379e44c384ce3fcd3fe4d0b7cd5444f01c Mon Sep 17 00:00:00 2001 From: rotorman Date: Wed, 29 Dec 2021 17:08:39 +0100 Subject: [PATCH 31/99] Check CST340 presence and read out firmware version and display it on analogs diagnose page. Unify the variable name used to display touch chip firmware version with GT911. --- radio/src/targets/pl18/tp_cst340.cpp | 38 ++++++++++++++++++++++++---- radio/src/targets/pl18/tp_cst340.h | 10 +++++++- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index c5cc14d22d4..005ec107028 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -26,6 +26,7 @@ bool touchCST340Flag = false; volatile static bool touchEventOccured = false; struct TouchData touchData; +uint16_t touchICfwver = 0; uint32_t touchI2Chiccups = 0; tmr10ms_t downTime = 0; tmr10ms_t tapTime = 0; @@ -76,7 +77,6 @@ static void TOUCH_AF_ExtiConfig(void) NVIC_Init(&NVIC_InitStructure); } -/* Currently not used, as no CST340 register needs to be written bool I2C_CST340_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) { uint8_t uAddrAndBuf[6]; @@ -97,7 +97,7 @@ bool I2C_CST340_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) return false; } return true; -} */ +} bool I2C_CST340_ReadRegister(uint16_t reg, uint8_t * buf, uint8_t len) { @@ -188,11 +188,39 @@ bool touchPanelInit(void) delay_ms(400); // FlySky example uses 20ms only, datasheet says 300ms TOUCH_AF_ExtiConfig(); + + // Enter info mode + if (!I2C_CST340_WriteRegister(CST340_MODE_DEBUG_INFO, tmp, 0)) { + TRACE("CST340 chip NOT FOUND"); + return false; + } - if (!I2C_CST340_ReadRegister(CST340_READ_XY_REG, tmp, 4)) { - TRACE("CST340 chip NOT FOUND"); + if (!I2C_CST340_ReadRegister(CST340_CHIPTYPE_REG, tmp, 4)) { + TRACE("Error reading CST340 chip type!"); return false; } + + // Check the value, expected ChipID + if ((tmp[0] << 8) + tmp[1] != CST340_CHIP_ID) { + TRACE("Error identifying CST340 touch controller chip!"); + return false; + } + + TRACE("CST340 chip detected"); + + if (!I2C_CST340_ReadRegister(CST340_FWVER_REG, tmp, 4)) { + TRACE("Error reading CST340 firmware version!"); + return false; + } + + // Enter normal mode + if (!I2C_CST340_WriteRegister(CST340_MODE_NORMAL, tmp, 0)) { + TRACE("ERROR chaning CST340 mode back to normal!"); + return false; + } + + touchICfwver = (tmp[0] << 8) + tmp[1]; + TRACE("CST340 FW version: %u", touchICfwver); touchCST340Flag = true; } return true; @@ -250,7 +278,7 @@ struct TouchState touchPanelRead() tmr10ms_t now = get_tmr10ms(); internalTouchState.tapCount = 0; - if (!I2C_CST340_ReadRegister(CST340_READ_XY_REG, touchData, 4)) { + if (!I2C_CST340_ReadRegister(CST340_FINGER1_REG, touchData, 4)) { touchI2Chiccups++; TRACE("CST340 I2C read XY error"); if (!I2C_ReInit()) diff --git a/radio/src/targets/pl18/tp_cst340.h b/radio/src/targets/pl18/tp_cst340.h index 82269578e74..053641588c0 100644 --- a/radio/src/targets/pl18/tp_cst340.h +++ b/radio/src/targets/pl18/tp_cst340.h @@ -25,7 +25,14 @@ #define HAS_TOUCH_PANEL() touchCST340Flag == true -#define CST340_READ_XY_REG 0xD000 // Touch info register +#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_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 @@ -35,6 +42,7 @@ extern bool touchCST340Flag; extern uint32_t touchI2Chiccups; +extern uint16_t touchICfwver; extern bool touchPanelInit(); struct TouchState touchPanelRead(); From e30884406da836b8f6a847d066ecfc187009a768 Mon Sep 17 00:00:00 2001 From: rotorman Date: Wed, 29 Dec 2021 17:24:07 +0100 Subject: [PATCH 32/99] Better to read CST340 once at init to clear internal CST340 status. --- radio/src/targets/pl18/tp_cst340.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index 005ec107028..7a8b8279633 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -222,6 +222,12 @@ bool touchPanelInit(void) touchICfwver = (tmp[0] << 8) + tmp[1]; TRACE("CST340 FW version: %u", touchICfwver); touchCST340Flag = true; + + // Read once the finger register + if (!I2C_CST340_ReadRegister(CST340_FINGER1_REG, tmp, 4)) { + TRACE("Initial CST340 finger1 data register read failed!"); + return false; + } } return true; } From 943959161aed4abb44af11f39fe9ba6717eab881 Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 4 Jan 2022 18:51:00 +0100 Subject: [PATCH 33/99] Change to analog audio output at PA4 DAC0. --- radio/src/targets/pl18/CMakeLists.txt | 5 +++-- radio/src/targets/pl18/board.cpp | 1 - radio/src/targets/pl18/board.h | 2 +- radio/src/targets/pl18/hal.h | 16 ++++++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 63a4cae3338..1ad023d4386 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -47,7 +47,8 @@ set_property(CACHE PCB_RF_BAUD PROPERTY STRINGS ${RF_BAUD_RATE}) set(FLAVOUR pl18) #add_definitions(-DPCBPL18 -DPCBFLYSKY -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) add_definitions(-DPCBPL18 -DPCBFLYSKY -DINTERNAL_MODULE_SERIAL -DAFHDS2_BAUDRATE=${PCB_RF_BAUD}) -add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME +add_definitions(-DBATTERY_CHARGE) +add_definitions(-DSOFTWARE_VOLUME) add_definitions(-DINTERNAL_MODULE_AFHDS2A) # defines existing internal modules @@ -56,7 +57,7 @@ set(DEFAULT_INTERNAL_MODULE FLYSKY CACHE STRING "Default internal module") set(TARGET_SRC ${TARGET_SRC} - audio_spi_driver.cpp + ../common/arm/stm32/audio_dac_driver.cpp ../common/arm/stm32/mixer_scheduler_driver.cpp ../common/arm/stm32/timers_driver.cpp ) diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 3ea434e056a..57804383fdf 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -99,7 +99,6 @@ void delay_self(int count) #define RCC_APB2PeriphOther (ADC_RCC_APB2Periph |\ HAPTIC_RCC_APB2Periph |\ AUX_SERIAL_RCC_APB2Periph |\ - AUDIO_RCC_APB2Periph |\ EXTMODULE_RCC_APB2Periph \ ) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index abc2c4c5bbd..d51a4c84ca6 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -481,7 +481,7 @@ bool audioChipReset(); #define audioDisableIrq() // interrupts must stay enabled on Horus #define audioEnableIrq() // interrupts must stay enabled on Horus -#if defined(PCBNV14) || defined(PCBPL18) +#if defined(PCBNV14) #define setSampleRate(freq) #else void setSampleRate(uint32_t frequency); diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index d03f51e6462..375a9989ed9 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -320,6 +320,7 @@ #define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 // Audio +/* #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH) #define AUDIO_RCC_APB2Periph RCC_APB2Periph_SPI1 #define AUDIO_SHUTDOWN_GPIO GPIOH @@ -343,6 +344,21 @@ #define AUDIO_SPI_MOSI_GPIO GPIOB #define AUDIO_SPI_MOSI_GPIO_PIN GPIO_Pin_5 // PB.05 #define AUDIO_SPI_MOSI_GPIO_PinSource GPIO_PinSource5 +*/ + +#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_AF GPIO_AF_DAC1 +#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 From 8fc832885ebf29f2780c7db8ae9edb330c7d3b42 Mon Sep 17 00:00:00 2001 From: rotorman Date: Thu, 6 Jan 2022 21:36:10 +0100 Subject: [PATCH 34/99] Analog audio is working, changed SD card to work with 1 bit mode (easier mod). Plus removed 2 obsolete files. --- radio/src/audio.h | 2 +- radio/src/targets/pl18/board.cpp | 2 +- radio/src/targets/pl18/board.h | 2 + radio/src/targets/pl18/hal.h | 10 - radio/src/targets/pl18/hallStick_driver.cpp | 615 -------------------- radio/src/targets/pl18/hallStick_driver.h | 220 ------- 6 files changed, 4 insertions(+), 847 deletions(-) delete mode 100644 radio/src/targets/pl18/hallStick_driver.cpp delete mode 100644 radio/src/targets/pl18/hallStick_driver.h diff --git a/radio/src/audio.h b/radio/src/audio.h index 0e18fda2b06..765d6f5daff 100644 --- a/radio/src/audio.h +++ b/radio/src/audio.h @@ -106,7 +106,7 @@ enum AudioBufferState #define AUDIO_DATA_MIN 0 #define AUDIO_DATA_MAX 0xffff #define AUDIO_BITS_PER_SAMPLE 16 -#elif defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) +#elif defined(PCBX12S) || defined(PCBNV14) typedef int16_t audio_data_t; #define AUDIO_DATA_SILENCE 0 #define AUDIO_DATA_MIN INT16_MIN diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 57804383fdf..cd07d2dff30 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -72,7 +72,6 @@ void delay_self(int count) AUX_SERIAL_RCC_AHB1Periph |\ TELEMETRY_RCC_AHB1Periph |\ TRAINER_RCC_AHB1Periph |\ - AUDIO_RCC_AHB1Periph |\ HAPTIC_RCC_AHB1Periph |\ INTMODULE_RCC_AHB1Periph |\ FLYSKY_HALL_RCC_AHB1Periph |\ @@ -86,6 +85,7 @@ void delay_self(int count) ) #define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ + AUDIO_RCC_APB1Periph |\ TRAINER_RCC_APB1Periph |\ INTMODULE_RCC_APB1Periph |\ FLYSKY_HALL_RCC_APB1Periph |\ diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index d51a4c84ca6..f984d7baa27 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -310,6 +310,8 @@ enum Analogs { NUM_ANALOGS }; +//TODO! #define HAS_TX_RTC_VOLTAGE + #define SLIDER_FIRST 0 #define SLIDER_LAST -1 diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 375a9989ed9..c72c535985d 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -223,8 +223,6 @@ #define TELEMETRY_GPIO_PinSource_RX GPIO_PinSource6 #define TELEMETRY_GPIO_AF GPIO_AF_USART2 #define TELEMETRY_USART USART2 -#define TELEMETRY_DMA_Stream_RX DMA1_Stream5 -#define TELEMETRY_DMA_Channel_RX DMA_Channel_4 #define TELEMETRY_DMA_Stream_TX DMA1_Stream6 #define TELEMETRY_DMA_Channel_TX DMA_Channel_4 #define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream6_IRQn @@ -350,7 +348,6 @@ #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_AF GPIO_AF_DAC1 #define AUDIO_GPIO_PinSource GPIO_PinSource4 #define AUDIO_DMA_Stream DMA1_Stream5 #define AUDIO_DMA_Stream_IRQn DMA1_Stream5_IRQn @@ -509,12 +506,6 @@ #define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) //TIMER -#define EXTMODULE_DMA_CHANNEL DMA_Channel_7 -#define EXTMODULE_DMA_STREAM DMA2_Stream1 -#define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn -#define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler -#define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 - #define EXTMODULE_TIMER_DMA_CHANNEL DMA_Channel_7 #define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 #define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream1_IRQn @@ -526,7 +517,6 @@ #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_TX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN diff --git a/radio/src/targets/pl18/hallStick_driver.cpp b/radio/src/targets/pl18/hallStick_driver.cpp deleted file mode 100644 index d6a872714de..00000000000 --- a/radio/src/targets/pl18/hallStick_driver.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/* - * 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" - -DMAFifo hallDMAFifo __DMA (HALL_DMA_Stream_RX); -Fifo hallStickTxFifo; -static uint8_t hallStickSendState = HALLSTICK_SEND_STATE_IDLE; -unsigned char HallCmd[264] __DMA; - -STRUCT_HALL HallProtocol = { 0 }; -STRUCT_HALL HallProtocolTx = { 0 }; -signed short hall_raw_values[FLYSKY_HALL_CHANNEL_COUNT]; -STRUCT_STICK_CALIBRATION hall_calibration[FLYSKY_HALL_CHANNEL_COUNT] = { {0, 0, 0} }; -unsigned short hall_adc_values[FLYSKY_HALL_CHANNEL_COUNT]; - -/* crc16 implementation according to CCITT standards */ -const unsigned short CRC16Table[256]= { - 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, - 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, - 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, - 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, - 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, - 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, - 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, - 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, - 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, - 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, - 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, - 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, - 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, - 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, - 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, - 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, - 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, - 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, - 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, - 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, - 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, - 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, - 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, - 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, - 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, - 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, - 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, - 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, - 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, - 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, - 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, - 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 -}; - -//const uint8_t sticks_mapping[4] = { 0 /*STICK1*/, 1/*STICK2*/, 2/*STICK3*/, 3 /*STICK4*/}; - -unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize) -{ - unsigned short crc16; - crc16 = 0xffff; - while (BufferSize) - { - crc16 = (crc16 << 8) ^ CRC16Table[((crc16>>8) ^ (*(unsigned char *)pBuffer)) & 0x00ff]; - pBuffer = (void *)((unsigned char *)pBuffer + 1); - BufferSize--; - } - return crc16; -} - -uint16_t get_hall_adc_value(uint8_t ch) -{ - if (ch >= FLYSKY_HALL_CHANNEL_COUNT) - { - return 0; - } - - if (ch < 2) - { - return MAX_ADC_CHANNEL_VALUE - hall_adc_values[ch]; - } - - return hall_adc_values[ch]; -} - - -void hall_stick_init(uint32_t baudrate) -{ - if (baudrate == 0) - { - USART_DeInit(HALL_SERIAL_USART); - return; - } - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = HALL_SERIAL_RX_DMA_Stream_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - USART_InitTypeDef USART_InitStructure; - GPIO_InitTypeDef GPIO_InitStructure; - - GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_RX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); - GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_TX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); - - GPIO_InitStructure.GPIO_Pin = HALL_SERIAL_TX_GPIO_PIN | HALL_SERIAL_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 = GPIO_Speed_2MHz; - GPIO_Init(HALL_SERIAL_GPIO, &GPIO_InitStructure); - - USART_InitStructure.USART_BaudRate = baudrate; - 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; - USART_Init(HALL_SERIAL_USART, &USART_InitStructure); - - DMA_Cmd(HALL_DMA_Stream_RX, DISABLE); - USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, DISABLE); - DMA_DeInit(HALL_DMA_Stream_RX); - - DMA_InitTypeDef DMA_InitStructure; - hallDMAFifo.clear(); - - USART_ITConfig(HALL_SERIAL_USART, USART_IT_RXNE, DISABLE); - USART_ITConfig(HALL_SERIAL_USART, USART_IT_TXE, DISABLE); - - DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; - DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); - DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(hallDMAFifo.buffer()); - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; - DMA_InitStructure.DMA_BufferSize = hallDMAFifo.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(HALL_DMA_Stream_RX, &DMA_InitStructure); - USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, ENABLE); - USART_Cmd(HALL_SERIAL_USART, ENABLE); - DMA_Cmd(HALL_DMA_Stream_RX, ENABLE); - - reset_hall_stick(); -} - -void HallSendBuffer(uint8_t * buffer, uint32_t count) -{ - for(uint32_t idx = 0; buffer != HallCmd && idx < count; idx++) - { - HallCmd[idx] = buffer[idx]; - } - DMA_InitTypeDef DMA_InitStructure; - DMA_DeInit(HALL_DMA_Stream_TX); - DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; - DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(HallCmd); - 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(HALL_DMA_Stream_TX, &DMA_InitStructure); - DMA_Cmd(HALL_DMA_Stream_TX, ENABLE); - USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Tx, ENABLE); - DMA_ITConfig(HALL_DMA_Stream_TX, DMA_IT_TC, ENABLE); - - /* enable interrupt and set it's priority */ - NVIC_EnableIRQ(HALL_SERIAL_TX_DMA_Stream_IRQn); - NVIC_SetPriority(HALL_SERIAL_TX_DMA_Stream_IRQn, 7); -} - -extern "C" void HALL_TX_DMA_Stream_IRQHandler(void) -{ - DEBUG_INTERRUPT(INT_TELEM_DMA); - - if (DMA_GetITStatus(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC)) - { - DMA_ClearITPendingBit(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC); - HALL_SERIAL_USART->CR1 |= USART_CR1_TCIE; - } -} - -uint8_t HallGetByte(uint8_t * byte) -{ - return hallDMAFifo.pop(*byte); -} - - -void reset_hall_stick( void ) -{ - unsigned short crc16 = 0xffff; - - HallCmd[0] = HALL_PROTOLO_HEAD; - HallCmd[1] = 0xD1; - HallCmd[2] = 0x01; - HallCmd[3] = 0x01; - - crc16 = calc_crc16(HallCmd, 4); - - HallCmd[4] = crc16 & 0xff; - HallCmd[5] = crc16 >>8 & 0xff; - - HallSendBuffer( HallCmd, 6); -} - -void get_hall_config( void ) -{ - unsigned short crc16 = 0xffff; - - HallCmd[0] = HALL_PROTOLO_HEAD; - HallCmd[1] = 0xD1; - HallCmd[2] = 0x01; - HallCmd[3] = 0x00; - - crc16 = calc_crc16(HallCmd, 4); // 2B 2C - - HallCmd[4] = crc16 & 0xff; - HallCmd[5] = crc16 >>8 & 0xff ; - - HallSendBuffer( HallCmd, 6); -} - -void get_hall_firmware_info() -{ - unsigned short crc16 = 0xffff; - - HallCmd[0] = HALL_PROTOLO_HEAD; - HallCmd[1] = 0xA2; - HallCmd[2] = 0x00; - - crc16 = calc_crc16(HallCmd, 3); // BE 02 - - HallCmd[3] = crc16 & 0xff; - HallCmd[4] = crc16 >>8 & 0xff ; - - HallSendBuffer( HallCmd, 5); -} - -void hallStickUpdatefwEnd( void ) -{ - unsigned short crc16 = 0xffff; - - HallCmd[0] = HALL_PROTOLO_HEAD; - HallCmd[1] = 0xA2; - HallCmd[2] = 0x01; - HallCmd[3] = 0x07; - - crc16 = calc_crc16(HallCmd, 4); - - HallCmd[4] = crc16 & 0xff; - HallCmd[5] = crc16 >>8 & 0xff ; - - HallSendBuffer( HallCmd, 6);// 94 DD -} - -static int parse_ps_state = 0; -void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch) -{ - if (parse_ps_state != 0) return; - parse_ps_state = 1; - - switch( hallBuffer->status ) - { - case GET_START: - { - if ( HALL_PROTOLO_HEAD == ch ) - { - hallBuffer->head = HALL_PROTOLO_HEAD; - hallBuffer->status = GET_ID; - hallBuffer->msg_OK = 0; - } - break; - } - case GET_ID: - { - hallBuffer->hallID.ID = ch; - hallBuffer->status = GET_LENGTH; - break; - } - case GET_LENGTH: - { - hallBuffer->length = ch; - hallBuffer->dataIndex = 0; - hallBuffer->status = GET_DATA; - if( 0 == hallBuffer->length ) - { - hallBuffer->status = GET_CHECKSUM; - hallBuffer->checkSum=0; - } - break; - } - case GET_DATA: - { - hallBuffer->data[hallBuffer->dataIndex++] = ch; - if( hallBuffer->dataIndex >= hallBuffer->length) - { - hallBuffer->checkSum = 0; - hallBuffer->dataIndex = 0; - hallBuffer->status = GET_STATE; - } - break; - } - case GET_STATE: - { - hallBuffer->checkSum = 0; - hallBuffer->dataIndex = 0; - hallBuffer->status = GET_CHECKSUM; - } - case GET_CHECKSUM: - { - hallBuffer->checkSum |= ch << ((hallBuffer->dataIndex++) * 8); - if( hallBuffer->dataIndex >= 2 ) - { - hallBuffer->dataIndex = 0; - hallBuffer->status = CHECKSUM; - } - else - { - break; - } - } - case CHECKSUM: - { - if(hallBuffer->checkSum == calc_crc16( (U8*)&hallBuffer->head, hallBuffer->length + 3 ) ) - { - hallBuffer->msg_OK = 1; - goto Label_restart; - } - else - { - goto Label_error; - } - } - } - - goto exit; - - Label_error: - Label_restart: - hallBuffer->status = GET_START; -exit: parse_ps_state = 0; - return ; -} - -#define ERROR_OFFSET 10 -void convert_hall_to_adcVaule( void ) -{ - uint16_t value; - - for ( uint8_t channel = 0; channel < 4; channel++ ) - { - if (hall_raw_values[channel] < hall_calibration[channel].mid) - { - value = hall_calibration[channel].mid - (hall_calibration[channel].min+ERROR_OFFSET); - value = ( MIDDLE_ADC_CHANNLE_VALUE * (hall_calibration[channel].mid - hall_raw_values[channel] ) ) / ( value ); - - if (value >= MIDDLE_ADC_CHANNLE_VALUE ) { - value = MIDDLE_ADC_CHANNLE_VALUE; - } - - hall_adc_values[channel] = MIDDLE_ADC_CHANNLE_VALUE - value; - } - else - { - value = (hall_calibration[channel].max - ERROR_OFFSET) - hall_calibration[channel].mid; - - value = ( MIDDLE_ADC_CHANNLE_VALUE * (hall_raw_values[channel] - hall_calibration[channel].mid ) ) / (value ); - - if (value >= MIDDLE_ADC_CHANNLE_VALUE ) - { - value = MIDDLE_ADC_CHANNLE_VALUE; - } - - hall_adc_values[channel] = MIDDLE_ADC_CHANNLE_VALUE + value + 1; - } - } -} - -uint8_t HallGetByteTx(uint8_t * byte) -{ - return hallStickTxFifo.pop(*byte); -} - -bool isHallStickUpdateFirmware( void ) -{ - return hallStickSendState == HALLSTICK_STATE_UPDATE_FW; -} - -void hallstick_send_by_state( void ) -{ - switch ( hallStickSendState ) - { - case HALLSTICK_STATE_SEND_RESET: - TRACE("HALLSTICK_STATE_SEND_RESET"); - reset_hall_stick(); - hallStickSendState = HALLSTICK_STATE_GET_FIRMWARE; - break; - - case HALLSTICK_STATE_GET_FIRMWARE: - TRACE("HALLSTICK_STATE_GET_FIRMWARE"); - get_hall_firmware_info(); - hallStickSendState = HALLSTICK_STATE_UPDATE_FW; - break; - - case HALLSTICK_STATE_UPDATE_FW: - return; - - default: break; - } -} - -void hallstick_wait_send_done(uint32_t timeOut) -{ - static unsigned int startTime = get_tmr10ms(); - - while ( hallStickSendState != HALLSTICK_SEND_STATE_IDLE ) - { - if ( (get_tmr10ms() - startTime) < timeOut ) - { - break; - } - } -} - -static uint32_t HallProtocolCount = 0; -bool isHallProtocolTxMsgOK( void ) -{ - bool isMsgOK = HallProtocolCount != 0; - HallProtocolCount = 0; - return isMsgOK; -} - -/* HallStick send main program */ -void hallStick_GetTxDataFromUSB( void ) -{ - unsigned char abyte; - uint8_t *pt = (uint8_t *)&HallProtocolTx; - - while( HallGetByteTx(&abyte) ) - { - Parse_Character(&HallProtocolTx, abyte ); - - if ( HallProtocolTx.msg_OK ) - { - HallProtocolTx.msg_OK = 0; - - pt[HallProtocolTx.length + 3] = HallProtocolTx.checkSum & 0xFF; - pt[HallProtocolTx.length + 4] = HallProtocolTx.checkSum >> 8; - - //TRACE("USB: %02X %02X %02X ...%04X; CRC:%04X", pt[0], pt[1], pt[2], - // HallProtocolTx.checkSum, calc_crc16(pt, HallProtocolTx.length+3)); - - switch ( HallProtocolTx.hallID.hall_Id.receiverID ) - { - case TRANSFER_DIR_TXMCU: - break; - - case TRANSFER_DIR_HALLSTICK: - onFlySkyUsbDownloadStart(TRANSFER_DIR_HALLSTICK); - - if ( 0xA2 == HallProtocolTx.hallID.ID ) - { - if ( 0 == HallProtocolTx.length ) // 55 A2 00 BE 02 - { - hallStickSendState = HALLSTICK_STATE_SEND_RESET; - break; - } - - else if ( 0x01 == HallProtocolTx.length && 0x07 == HallProtocol.data[0] ) - { - hallStickSendState = HALLSTICK_SEND_STATE_IDLE; - } - } - HallSendBuffer( pt, HallProtocolTx.length + 3 + 2 ); - break; - - case TRANSFER_DIR_RFMODULE: - onFlySkyUsbDownloadStart(TRANSFER_DIR_RFMODULE); - - if ( 0xAE == HallProtocolTx.hallID.ID && HallProtocolTx.length == 0 ) - { - setFlyskyState(INTERNAL_MODULE, FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE); - break; - } - - if ( 0x0D == HallProtocolTx.hallID.hall_Id.packetID && HallProtocolTx.data[0] == 1 ) - { - onFlySkyGetVersionInfoStart(INTERNAL_MODULE, 1); - break; - } - - //if ( isFlySkyUsbDownload() ) - { - intmoduleSendBuffer( pt, HallProtocolTx.length + 3 + 2 ); - } - break; - } - } - } - - if ( !usbPlugged() ) - { - onFlySkyUsbDownloadStart(0); - } -} - - -/* Run it in 1ms timer routine */ -void hall_stick_loop(void) -{ - static uint8_t count = 0; - static tmr10ms_t lastConfigTime = get_tmr10ms(); - bool log = 0; - - hallStick_GetTxDataFromUSB(); - - if(count>10) - { - count = 0; - hallstick_send_by_state(); - } - count++; - uint8_t byte; - while(HallGetByte(&byte)) - { - HallProtocol.index++; - - Parse_Character(&HallProtocol, byte); - - if ( HallProtocol.msg_OK ) - { - HallProtocol.msg_OK = 0; - HallProtocol.stickState = HallProtocol.data[HallProtocol.length - 1]; - - switch ( HallProtocol.hallID.hall_Id.receiverID ) - { - case TRANSFER_DIR_TXMCU: - if(HallProtocol.hallID.hall_Id.packetID == HALL_RESP_TYPE_CALIB) { - memcpy(&hall_calibration, HallProtocol.data, sizeof(hall_calibration)); - } - else if(HallProtocol.hallID.hall_Id.packetID == HALL_RESP_TYPE_VALUES) { - memcpy(hall_raw_values, HallProtocol.data, sizeof(hall_raw_values)); - convert_hall_to_adcVaule(); - } - break; - case TRANSFER_DIR_HOSTPC: - if (HallProtocol.length == 0x01 && (HallProtocol.data[0] == 0x05 || HallProtocol.data[0] == 0x06) ) - { - hallStickSendState = HALLSTICK_SEND_STATE_IDLE; - } - case TRANSFER_DIR_HALLSTICK: - HallProtocolCount++; - uint8_t *pt = (uint8_t*)&HallProtocol; - //HallProtocol.head = HALL_PROTOLO_HEAD; - //TRACE("HALL: %02X %02X %02X ...%04X", pt[0], pt[1], pt[2], HallProtocol.checkSum); - pt[HallProtocol.length + 3] = HallProtocol.checkSum & 0xFF; - pt[HallProtocol.length + 4] = HallProtocol.checkSum >> 8; - usbDownloadTransmit( pt, HallProtocol.length + 5 ); - break; - } - } - } - //check periodically if calibration is correct - if (get_tmr10ms() - lastConfigTime > 200) - { - //invalid calibration - if(hall_calibration[0].max - hall_calibration[0].min < 1024) { - TRACE("GET HALL CONFIG"); - get_hall_config(); - lastConfigTime = get_tmr10ms(); - } - - if (log) - { - TRACE_NOCRLF("Hall(%0d):", FLYSKY_HALL_BAUDRATE); - for (int idx = 0; idx < HallProtocol.length + 5; idx++) - { - TRACE_NOCRLF(" %02X", *((uint8_t*)&HallProtocol + idx)); - } - TRACE(";"); - } - } -} diff --git a/radio/src/targets/pl18/hallStick_driver.h b/radio/src/targets/pl18/hallStick_driver.h deleted file mode 100644 index a3359fe7bc7..00000000000 --- a/radio/src/targets/pl18/hallStick_driver.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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 __HALLSTICK_DRIVER_H__ - #define __HALLSTICK_DRIVER_H__ -/*************************************************************************************************** - -***************************************************************************************************/ - #ifdef EXTERN - #undef EXTERN - #endif - - #ifdef __HALLSTICK_DRIVER_C__ - #define EXTERN - #else - #define EXTERN extern - #endif -/*************************************************************************************************** - -***************************************************************************************************/ -#define HALLSTICK_BUFF_SIZE ( 512 ) -#define FLYSKY_HALL_BAUDRATE ( 921600 ) -#define FLYSKY_HALL_CHANNEL_COUNT ( 4 ) - -#define MAX_ADC_CHANNEL_VALUE ( 4095 ) -#define MIN_ADC_CHANNLE_VALUE ( 0 ) -#define MIDDLE_ADC_CHANNLE_VALUE ( 2047 ) - -typedef struct -{ - signed short min; - signed short mid; - signed short max; -} STRUCT_STICK_CALIBRATION; - -typedef struct -{ - STRUCT_STICK_CALIBRATION sticksCalibration[4]; - unsigned char reststate; - unsigned short CRC16; -} STRUCT_STICK_CALIBRATION_PACK; - -typedef struct -{ - signed short channel[4]; - unsigned char stickState; - unsigned short CRC16; -} STRUCT_CHANNEL_PACK; - -typedef union -{ - STRUCT_STICK_CALIBRATION_PACK channelPack; - STRUCT_CHANNEL_PACK sticksCalibrationPack; -} UNION_DATA; - -typedef struct -{ - unsigned char start; - unsigned char senderID:2; - unsigned char receiverID:2; - unsigned char packetID:4; - unsigned char length; - UNION_DATA payload; -} STRUCT_HALLDATA; - -typedef struct -{ - unsigned char senderID:2; - unsigned char receiverID:2; - unsigned char packetID:4; -} STRUCT_HALLID; - -typedef union -{ - STRUCT_HALLID hall_Id; - unsigned char ID; -} STRUCT_ID; - - -typedef union -{ - STRUCT_HALLDATA halldat; - unsigned char buffer[30]; -} UNION_HALLDATA; - - -typedef struct -{ - unsigned char head; - STRUCT_ID hallID; - unsigned char length; - unsigned char data[HALLSTICK_BUFF_SIZE]; - unsigned char reserved[15]; - unsigned short checkSum; - unsigned char stickState; - unsigned char startIndex; - unsigned char endIndex; - unsigned char index; - unsigned char dataIndex; - unsigned char deindex; - unsigned char completeFlg; - unsigned char status; - unsigned char recevied; - unsigned char msg_OK; -} STRUCT_HALL; - -enum -{ - GET_START = 0, - GET_ID, - GET_LENGTH, - GET_DATA, - GET_STATE, - GET_CHECKSUM, - CHECKSUM, -}; - -enum HALLSTICK_SEND_STATE_E { - HALLSTICK_SEND_STATE_IDLE, - HALLSTICK_STATE_SEND_RESET, - HALLSTICK_STATE_GET_CONFIG, - HALLSTICK_STATE_GET_FIRMWARE, - HALLSTICK_STATE_UPDATE_FW -}; - -enum TRANSFER_DIR_E { - TRANSFER_DIR_HALLSTICK, - TRANSFER_DIR_TXMCU, - TRANSFER_DIR_HOSTPC, - TRANSFER_DIR_RFMODULE, -}; - -#define HALL_PROTOLO_HEAD 0x55 -#define HALL_RESP_TYPE_CALIB 0x0e -#define HALL_RESP_TYPE_VALUES 0x0c - -#define HALL_SERIAL_USART UART4 -#define HALL_SERIAL_GPIO GPIOA -#define HALL_DMA_Channel DMA_Channel_4 -#define HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 -#define HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 -#define HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 -#define HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 -#define HALL_SERIAL_GPIO_AF GPIO_AF_UART4 - -#define HALL_SERIAL_RCC_APB2Periph RCC_APB2Periph_USART6 -#define HALL_RCC_AHB1Periph RCC_AHB1Periph_DMA1 -#define HALL_RCC_APB1Periph RCC_APB1Periph_UART4 - -#define HALL_SERIAL_USART_IRQHandler UART4_IRQHandler -#define HALL_SERIAL_USART_IRQn UART4_IRQn -#define HALL_SERIAL_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn -#define HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn -#define HALL_DMA_Stream_RX DMA1_Stream2 -#define HALL_DMA_Stream_TX DMA1_Stream4 -#define HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 - -#define HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler -#define HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler - -//#include "fifo.h" -//extern Fifo hallStickTxFifo; - - -/*************************************************************************************************** - interface function -***************************************************************************************************/ -extern unsigned short hall_adc_values[FLYSKY_HALL_CHANNEL_COUNT]; -extern signed short hall_raw_values[FLYSKY_HALL_CHANNEL_COUNT]; -extern STRUCT_STICK_CALIBRATION hall_calibration[FLYSKY_HALL_CHANNEL_COUNT]; - - -extern void reset_hall_stick( void ); -extern void get_hall_config( void ); -extern void hall_stick_init(uint32_t baudrate); -extern void hall_stick_loop( void ); -extern uint16_t get_hall_adc_value(uint8_t ch); -extern void hallSerialPutc(char c); -unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize); -void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch); -extern bool isFlySkyUsbDownload(void); -extern void onFlySkyUsbDownloadStart(uint8_t fw_state); -#endif - - - - - - - - - - - - - - - - From 32b97fcbc5df892948f3b83ae5599ab8dd11931b Mon Sep 17 00:00:00 2001 From: rotorman Date: Sat, 8 Jan 2022 18:58:16 +0100 Subject: [PATCH 35/99] Main battery and RTC coin cell voltage sampling works. TODO from the analogs are the VRC and both side sliders. --- radio/src/gui/colorlcd/radio_diagkeys.cpp | 2 +- radio/src/targets/pl18/battery_driver.cpp | 2 +- radio/src/targets/pl18/board.h | 21 +++--- radio/src/targets/pl18/hal.h | 89 ++++++++--------------- 4 files changed, 42 insertions(+), 72 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index ba7013db7da..24fea4964a1 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) // TODO! Check && !defined(PCBPL18) +#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 diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 2f92e415b24..bd64242930f 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -192,5 +192,5 @@ void handle_battery_charge(uint32_t last_press_time) uint16_t getBatteryVoltage() { int32_t instant_vbat = anaIn(TX_VOLTAGE); // using filtered ADC value on purpose - return (uint16_t)((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) / 2942); + return (uint16_t)((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) / 962); // = 2048*1000/(100*(3.3*(120+22)/22)) } diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index f984d7baa27..23685790535 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -293,11 +293,9 @@ enum Analogs { POT_FIRST, POT1 = POT_FIRST, POT2, - POT_LAST = POT2, + POT_LAST = POT_FIRST + NUM_POTS - 1, SWITCH_FIRST, - SWA = SWITCH_FIRST, - SWB, - SWC, + SWB = SWITCH_FIRST, SWD, SWE, SWF, @@ -310,12 +308,14 @@ enum Analogs { NUM_ANALOGS }; -//TODO! #define HAS_TX_RTC_VOLTAGE +//#define SLIDER1 SLIDER_FRONT_LEFT +//#define SLIDER2 SLIDER_FRONT_RIGHT #define SLIDER_FIRST 0 #define SLIDER_LAST -1 #define DEFAULT_POTS_CONFIG (POT_WITHOUT_DETENT << 0) + (POT_WITHOUT_DETENT << 2) // 2 pots without detent +//#define DEFAULT_SLIDERS_CONFIG (SLIDER_WITH_DETENT << 1) + (SLIDER_WITH_DETENT << 0) enum CalibratedAnalogs { CALIBRATED_STICK1, @@ -324,9 +324,7 @@ enum CalibratedAnalogs { CALIBRATED_STICK4, CALIBRATED_POT1, CALIBRATED_POT2, - CALIBRATED_SWA, CALIBRATED_SWB, - CALIBRATED_SWC, CALIBRATED_SWD, CALIBRATED_SWE, CALIBRATED_SWF, @@ -335,15 +333,16 @@ enum CalibratedAnalogs { NUM_CALIBRATED_ANALOGS }; -#define IS_POT(x) ((x)>=POT_FIRST && (x)<=POT_LAST) +#define IS_POT(x) ((x)>=POT_FIRST && (x)<=POT_LAST) #define IS_SLIDER(x) (false) +//#define IS_SLIDER(x) ((x)>=SLIDER_FIRST && (x)<=SLIDER_LAST) extern uint16_t adcValues[NUM_ANALOGS]; -#define BATTERY_WARN 36 // 3.6V -#define BATTERY_MIN 35 // 3.5V -#define BATTERY_MAX 42 // 4.2V +#define BATTERY_WARN 37 // 3.7V +#define BATTERY_MIN 35 // 3.4V +#define BATTERY_MAX 43 // 4.3V enum EnumPowerupState { diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index c72c535985d..da3117b7e73 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -97,7 +97,7 @@ // 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_0 | GPIO_Pin_1 | GPIO_Pin_14) -#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_13 ) /* TODO! PC8-PC11, currently allocated for SDIO */ +#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_13 ) /* TODO! PC8 currently allocated for SDIO D0 */ #define KEYS_GPIOD_PINS (GPIO_Pin_7) #define KEYS_GPIOF_PINS (GPIO_Pin_10) #define KEYS_GPIOH_PINS (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11) @@ -108,61 +108,59 @@ // 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) -// FLYSKY_HALL_STICKS -#define ADC_GPIO_PIN_STICK_LH 0 -#define ADC_GPIO_PIN_STICK_LV 0 -#define ADC_GPIO_PIN_STICK_RH 0 -#define ADC_GPIO_PIN_STICK_RV 0 - #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_SWA GPIO_Pin_1 // PB.01 -#define ADC_GPIO_PIN_SWB GPIO_Pin_8 // PF.08 -#define ADC_GPIO_PIN_SWC GPIO_Pin_0 // PB.00 -#define ADC_GPIO_PIN_SWD GPIO_Pin_10 // PF.10 +#define ADC_GPIO_PIN_POT3 GPIO_Pin_8 // PF.08 VRC +#define ADC_GPIO_PIN_SLIDER1 GPIO_Pin_9 // PF.09 LS +#define ADC_GPIO_PIN_SLIDER2 GPIO_Pin_7 // PA.07 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_7 // PA.07 -#define ADC_GPIO_PIN_SWG GPIO_Pin_0 // PC.00 -#define ADC_GPIO_PIN_SWH GPIO_Pin_1 // PC.01 +#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_BATT GPIO_Pin_5 // PC.05 -// FLYSKY_HALL_STICKS -#define ADC_GPIOA_PINS_FS (GPIO_Pin_6 | GPIO_Pin_7) +#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_8 | GPIO_Pin_10) +#define ADC_GPIOF_PINS (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_SWA ADC_Channel_9 // ADC12_IN9 -> ADC1_IN9 -#define ADC_CHANNEL_SWB ADC_Channel_6 // ADC3_IN6 -> ADC3_IN6 -#define ADC_CHANNEL_SWC ADC_Channel_8 // ADC12_IN8 -> ADC1_IN8 -#define ADC_CHANNEL_SWD ADC_Channel_8 // ADC3_IN8 -> ADC3_IN8 -#define ADC_CHANNEL_SWE ADC_Channel_10 // ADC123_IN10-> ADC1_IN10 -#define ADC_CHANNEL_SWF ADC_Channel_11 // ADC123_IN11-> ADC1_IN11 -#define ADC_CHANNEL_SWG ADC_Channel_7 // ADC12_IN7 -> ADC1_IN7 -#define ADC_CHANNEL_SWH ADC_Channel_12 // ADC123_IN12-> ADC1_IN12 - -#define ADC_CHANNEL_BATT ADC_Channel_15 // ADC12_IN15 -> ADC1_IN15 +#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_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_BATT ADC_Channel_15 // ADC12_IN15 -> ADC1_IN15 #define ADC_MAIN ADC1 #define ADC_EXT ADC3 -#define ADC_SAMPTIME 2 +#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 @@ -318,32 +316,6 @@ #define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 // Audio -/* -#define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH) -#define AUDIO_RCC_APB2Periph RCC_APB2Periph_SPI1 -#define AUDIO_SHUTDOWN_GPIO GPIOH -#define AUDIO_SHUTDOWN_GPIO_PIN GPIO_Pin_8 // PH.08 audio amp control pin -#define AUDIO_XDCS_GPIO GPIOH -#define AUDIO_XDCS_GPIO_PIN GPIO_Pin_14 // PH.14 -#define AUDIO_CS_GPIO GPIOH -#define AUDIO_CS_GPIO_PIN GPIO_Pin_13 // PH.13 -#define AUDIO_DREQ_GPIO GPIOH -#define AUDIO_DREQ_GPIO_PIN GPIO_Pin_15 // PH.15 -#define AUDIO_RST_GPIO GPIOD -#define AUDIO_RST_GPIO_PIN GPIO_Pin_4 // PD.4 -#define AUDIO_SPI SPI1 -#define AUDIO_SPI_GPIO_AF GPIO_AF_SPI1 -#define AUDIO_SPI_SCK_GPIO GPIOB -#define AUDIO_SPI_SCK_GPIO_PIN GPIO_Pin_3 // PB.03 -#define AUDIO_SPI_SCK_GPIO_PinSource GPIO_PinSource3 -#define AUDIO_SPI_MISO_GPIO GPIOB -#define AUDIO_SPI_MISO_GPIO_PIN GPIO_Pin_4 // PB.04 -#define AUDIO_SPI_MISO_GPIO_PinSource GPIO_PinSource4 -#define AUDIO_SPI_MOSI_GPIO GPIOB -#define AUDIO_SPI_MOSI_GPIO_PIN GPIO_Pin_5 // PB.05 -#define AUDIO_SPI_MOSI_GPIO_PinSource GPIO_PinSource5 -*/ - #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 @@ -423,8 +395,8 @@ // Internal Module #define HARDWARE_INTERNAL_MODULE #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) -#define INTMODULE_PWR_GPIO GPIOH -#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#define INTMODULE_PWR_GPIO GPIOD +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_3 // PD.03 Bogus pin #define INTMODULE_GPIO GPIOF //#define INTMODULE_TX_GPIO GPIOF #define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 @@ -456,7 +428,6 @@ #define INTMODULE_TIMER_IRQHandler TIM3_IRQHandler #define INTMODULE_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) - // External Module #define EXTMODULE #define EXTMODULE_PULSES From 61afdfddbb910ce99817fb36d9221ce60b06da7d Mon Sep 17 00:00:00 2001 From: rotorman Date: Sun, 9 Jan 2022 12:19:50 +0100 Subject: [PATCH 36/99] Pots VRA to VRC function. --- companion/src/firmwares/boards.cpp | 1 + radio/src/storage/yaml/yaml_datastructs_pl18.cpp | 5 +++-- radio/src/targets/pl18/board.h | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 37eb5c16856..7903dc1242d 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -775,6 +775,7 @@ StringTagMappingTable Boards::getAnalogNamesLookupTable(Board::Type board, const tbl.insert(tbl.end(), { {tr("VRA").toStdString(), "POT1"}, {tr("VRB").toStdString(), "POT2"}, + {tr("VRC").toStdString(), "POT3"}, }); } else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) { if (version < adcVersion) { diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 71e0dc87973..f60c5bbd20e 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -123,8 +123,9 @@ const struct YamlIdStr enum_MixSources[] = { { MIXSRC_Ele, "Ele" }, { MIXSRC_Thr, "Thr" }, { MIXSRC_Ail, "Ail" }, - { MIXSRC_POT1, "POT1" }, - { MIXSRC_POT2, "POT2" }, + { MIXSRC_POT1, "VRA" }, + { MIXSRC_POT2, "VRB" }, + { MIXSRC_POT3, "VRC" }, { MIXSRC_MAX, "MAX" }, { MIXSRC_CYC1, "CYC1" }, { MIXSRC_CYC2, "CYC2" }, diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 23685790535..8a1bc766986 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -276,12 +276,12 @@ void watchdogInit(unsigned int duration); #endif // ADC driver -#define NUM_POTS 2 +#define NUM_POTS 3 #define NUM_XPOTS 0 // NUM_POTS #define NUM_SLIDERS 0 #define NUM_PWMSTICKS 0 #define NUM_MOUSE_ANALOGS 0 -#define STORAGE_NUM_POTS 5 +#define STORAGE_NUM_POTS 6 #define STORAGE_NUM_SLIDERS 0 #define STORAGE_NUM_MOUSE_ANALOGS 0 @@ -293,6 +293,7 @@ enum Analogs { POT_FIRST, POT1 = POT_FIRST, POT2, + POT3, POT_LAST = POT_FIRST + NUM_POTS - 1, SWITCH_FIRST, SWB = SWITCH_FIRST, @@ -308,13 +309,15 @@ enum Analogs { NUM_ANALOGS }; +#define HARDWARE_POT3 + //#define SLIDER1 SLIDER_FRONT_LEFT //#define SLIDER2 SLIDER_FRONT_RIGHT #define SLIDER_FIRST 0 #define SLIDER_LAST -1 -#define DEFAULT_POTS_CONFIG (POT_WITHOUT_DETENT << 0) + (POT_WITHOUT_DETENT << 2) // 2 pots without detent +#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 { @@ -324,6 +327,7 @@ enum CalibratedAnalogs { CALIBRATED_STICK4, CALIBRATED_POT1, CALIBRATED_POT2, + CALIBRATED_POT3, CALIBRATED_SWB, CALIBRATED_SWD, CALIBRATED_SWE, From aac9e208a86863ce2d07a65d902e92f64c460c09 Mon Sep 17 00:00:00 2001 From: rotorman Date: Sun, 9 Jan 2022 12:26:09 +0100 Subject: [PATCH 37/99] Matched PL18EV switch types. --- radio/src/targets/pl18/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 8a1bc766986..529fc2f6595 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -202,7 +202,7 @@ enum EnumSwitches }; #define STORAGE_NUM_SWITCHES NUM_SWITCHES -#define DEFAULT_SWITCH_CONFIG (SWITCH_TOGGLE << 14) + (SWITCH_3POS << 12) + (SWITCH_3POS << 10) + (SWITCH_TOGGLE << 8) + (SWITCH_2POS << 6) + (SWITCH_TOGGLE << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); +#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 { From 3d4e156f56533ca78af8fe5eb162e21a00c090bd Mon Sep 17 00:00:00 2001 From: rotorman Date: Sun, 9 Jan 2022 16:31:20 +0100 Subject: [PATCH 38/99] PL18 side sliders are now working, started work on trims. --- companion/src/firmwares/boards.cpp | 22 ++++++++++------- radio/src/dataconstants.h | 7 ++++++ .../storage/yaml/yaml_datastructs_pl18.cpp | 14 +++++++---- radio/src/targets/pl18/board.h | 24 ++++++++++--------- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 7903dc1242d..dca9c5f3e70 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -364,12 +364,12 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) const Board::SwitchInfo switches[] = { {SWITCH_2POS, "SA"}, {SWITCH_3POS, "SB"}, - {SWITCH_TOGGLE, "SC"}, - {SWITCH_2POS, "SD"}, - {SWITCH_TOGGLE, "SE"}, - {SWITCH_3POS, "SF"}, + {SWITCH_2POS, "SC"}, + {SWITCH_3POS, "SD"}, + {SWITCH_3POS, "SE"}, + {SWITCH_2POS, "SF"}, {SWITCH_3POS, "SG"}, - {SWITCH_TOGGLE, "SH"} + {SWITCH_3POS, "SH"} }; if (index < DIM(switches)) return switches[index]; @@ -455,7 +455,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else if (IS_FLYSKY_NV14(board)) return 2; else if (IS_FLYSKY_PL18(board)) - return 2; + return 3; else return 3; @@ -468,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; @@ -559,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) && !IS_FLYSKY_PL18(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; @@ -573,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); @@ -776,6 +778,8 @@ StringTagMappingTable Boards::getAnalogNamesLookupTable(Board::Type board, const {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) { diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index da8fa3f65cc..f2c012df152 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -426,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, diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index f60c5bbd20e..03e77451c8f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -126,14 +126,20 @@ const struct YamlIdStr enum_MixSources[] = { { MIXSRC_POT1, "VRA" }, { MIXSRC_POT2, "VRB" }, { MIXSRC_POT3, "VRC" }, + { 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_TrimTR1, "TrimT1" }, + { MIXSRC_TrimTR2, "TrimT2" }, + { MIXSRC_TrimTR3, "TrimT3" }, + { MIXSRC_TrimTR4, "TrimT4" }, + { MIXSRC_TrimTR5, "TrimT5" }, + { MIXSRC_TrimTR6, "TrimT6" }, + { MIXSRC_TrimTR7, "TrimT7" }, + { MIXSRC_TrimTR8, "TrimT8" }, { MIXSRC_SA, "SA" }, { MIXSRC_SB, "SB" }, { MIXSRC_SC, "SC" }, diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 529fc2f6595..04f3d2ae916 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -278,11 +278,11 @@ void watchdogInit(unsigned int duration); // ADC driver #define NUM_POTS 3 #define NUM_XPOTS 0 // NUM_POTS -#define NUM_SLIDERS 0 +#define NUM_SLIDERS 2 #define NUM_PWMSTICKS 0 #define NUM_MOUSE_ANALOGS 0 -#define STORAGE_NUM_POTS 6 -#define STORAGE_NUM_SLIDERS 0 +#define STORAGE_NUM_POTS 3 +#define STORAGE_NUM_SLIDERS 2 #define STORAGE_NUM_MOUSE_ANALOGS 0 enum Analogs { @@ -295,6 +295,10 @@ enum Analogs { 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, @@ -311,14 +315,11 @@ enum Analogs { #define HARDWARE_POT3 -//#define SLIDER1 SLIDER_FRONT_LEFT -//#define SLIDER2 SLIDER_FRONT_RIGHT - -#define SLIDER_FIRST 0 -#define SLIDER_LAST -1 +#define SLIDER1 SLIDER_FRONT_LEFT +#define SLIDER2 SLIDER_FRONT_RIGHT #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) +#define DEFAULT_SLIDERS_CONFIG (SLIDER_WITH_DETENT << 1) + (SLIDER_WITH_DETENT << 0) enum CalibratedAnalogs { CALIBRATED_STICK1, @@ -328,6 +329,8 @@ enum CalibratedAnalogs { CALIBRATED_POT1, CALIBRATED_POT2, CALIBRATED_POT3, + CALIBRATED_SLIDER_REAR_LEFT, + CALIBRATED_SLIDER_REAR_RIGHT, CALIBRATED_SWB, CALIBRATED_SWD, CALIBRATED_SWE, @@ -338,8 +341,7 @@ enum CalibratedAnalogs { }; #define IS_POT(x) ((x)>=POT_FIRST && (x)<=POT_LAST) -#define IS_SLIDER(x) (false) -//#define IS_SLIDER(x) ((x)>=SLIDER_FIRST && (x)<=SLIDER_LAST) +#define IS_SLIDER(x) ((x)>=SLIDER_FIRST && (x)<=SLIDER_LAST) extern uint16_t adcValues[NUM_ANALOGS]; From 271fd421764623fc578739541f3a8d8c9993238a Mon Sep 17 00:00:00 2001 From: rotorman Date: Sun, 9 Jan 2022 20:26:56 +0100 Subject: [PATCH 39/99] Switches A to H work now. --- radio/src/gui/colorlcd/radio_diagkeys.cpp | 2 +- radio/src/targets/pl18/hal.h | 11 +++++++++++ radio/src/targets/pl18/keys_driver.cpp | 23 +++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index 24fea4964a1..369d729a239 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -109,7 +109,7 @@ class RadioKeyDiagsWindow : public Window dc->drawNumber(70, y, rotaryEncoderGetValue(), COLOR_THEME_PRIMARY1); #endif #else // defined(PCBNV14) || defined(PCBPL18) - // KEYS + // KEYS (both radios do NOT have physical keys, only remapped trim keys) { coord_t y = 1; dc->drawText(KEY_COLUMN, y, keysGetLabel(KEY_ENTER), COLOR_THEME_PRIMARY1); diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index da3117b7e73..7d1b6fd85e0 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -79,10 +79,21 @@ // 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 diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 22787cb51e1..181300519c8 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -153,7 +153,27 @@ void readKeysAndTrims() #if !defined(BOOT) uint32_t switchState(uint8_t index) { - uint16_t value = getAnalogValue(SWITCH_FIRST + index / 3); + 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) @@ -162,7 +182,6 @@ uint32_t switchState(uint8_t index) position = 2; else position = 1; - return position == (index % 3); } #endif From a0e79109e37bfd23dce46aa67d4e9df8aad93958 Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 10 Jan 2022 13:40:05 +0100 Subject: [PATCH 40/99] Extension to 8 trims to support FlySky PL18/PL18EV. After these changes, the radio behaves awkwardly (e.g. start screen has no sliders & trims). I had to extend the memory size in radio/src/storage/yaml/yaml_datastructs_funcs.cpp to make it build, but apparently something is not yet correct with this commit/patch. @pafleraf could you please take a look. Thanks! --- radio/src/opentx.cpp | 1 - .../storage/yaml/yaml_datastructs_funcs.cpp | 2 +- .../storage/yaml/yaml_datastructs_pl18.cpp | 20 +++++--- radio/src/targets/pl18/board.h | 11 +++-- radio/src/targets/pl18/keys_driver.cpp | 47 +++++++++++++++++-- 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 8a1eb1d0874..627b558a2d6 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -940,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() { 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 index 03e77451c8f..d0ae5a72348 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -230,14 +230,22 @@ const struct YamlIdStr enum_SwitchSources[] = { { SWSRC_SH0, "SH0" }, { SWSRC_SH1, "SH1" }, { SWSRC_SH2, "SH2" }, - { SWSRC_TrimRudLeft, "TrimRudLeft" }, + { SWSRC_TrimRudLeft, "TrimRudLeft" }, { SWSRC_TrimRudRight, "TrimRudRight" }, - { SWSRC_TrimEleDown, "TrimEleDown" }, - { SWSRC_TrimEleUp, "TrimEleUp" }, - { SWSRC_TrimThrDown, "TrimThrDown" }, - { SWSRC_TrimThrUp, "TrimThrUp" }, - { SWSRC_TrimAilLeft, "TrimAilLeft" }, + { 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" }, diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 04f3d2ae916..21b08d906d7 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -162,8 +162,13 @@ enum EnumKeys TRM4_UP, TRM5_DWN, TRM5_UP, - TRM_LAST = TRM5_UP, - + TRM6_DWN, + TRM6_UP, + TRM7_LEFT, + TRM7_RIGHT, + TRM8_LEFT, + TRM8_RIGHT, + TRM_LAST = TRM8_RIGHT, NUM_KEYS }; @@ -245,7 +250,7 @@ uint8_t keyState(uint8_t index); uint32_t switchState(uint8_t index); uint32_t readKeys(); uint32_t readTrims(); -#define NUM_TRIMS NUM_STICKS +#define NUM_TRIMS 8 #define NUM_TRIMS_KEYS (NUM_TRIMS * 2) #define TRIMS_PRESSED() (readTrims()) #define KEYS_PRESSED() (readKeys()) diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 181300519c8..707ecc8c67a 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -102,18 +102,55 @@ uint32_t readTrims() } #endif if(!getTrim) return result; - if (TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) result |= 1 << (TRM1_UP - TRM_BASE); - if (TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) + if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) result |= 1 << (TRM1_DWN - TRM_BASE); - if (TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) + if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) result |= 1 << (TRM2_UP - TRM_BASE); - if (TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) + if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) result |= 1 << (TRM2_DWN - TRM_BASE); - // TODO! Extract the matrix trims + // Extract the matrix trims + 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 << (TRM7_LEFT - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << (TRM7_RIGHT - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << (TRM5_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << (TRM5_UP - TRM_BASE); + 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 << (TRM3_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << (TRM3_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << (TRM4_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << (TRM4_DWN - TRM_BASE); + + 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 << (TRM6_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) + result |= 1 << (TRM6_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) + result |= 1 << (TRM8_LEFT - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) + result |= 1 << (TRM8_RIGHT - TRM_BASE); + GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); return result; } From 115ede1f58ac9c6158b4d3c5b0b9b3cf67f9d5ec Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 10 Jan 2022 14:00:29 +0100 Subject: [PATCH 41/99] Generate YAML struct for PL18 --- .../storage/yaml/yaml_datastructs_pl18.cpp | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index d0ae5a72348..b3871d0878d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -123,23 +123,23 @@ const struct YamlIdStr enum_MixSources[] = { { MIXSRC_Ele, "Ele" }, { MIXSRC_Thr, "Thr" }, { MIXSRC_Ail, "Ail" }, - { MIXSRC_POT1, "VRA" }, - { MIXSRC_POT2, "VRB" }, - { MIXSRC_POT3, "VRC" }, + { 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_TrimTR1, "TrimT1" }, - { MIXSRC_TrimTR2, "TrimT2" }, - { MIXSRC_TrimTR3, "TrimT3" }, - { MIXSRC_TrimTR4, "TrimT4" }, - { MIXSRC_TrimTR5, "TrimT5" }, - { MIXSRC_TrimTR6, "TrimT6" }, - { MIXSRC_TrimTR7, "TrimT7" }, - { MIXSRC_TrimTR8, "TrimT8" }, + { MIXSRC_TrimTR1, "TrimTR1" }, + { MIXSRC_TrimTR2, "TrimTR2" }, + { MIXSRC_TrimTR3, "TrimTR3" }, + { MIXSRC_TrimTR4, "TrimTR4" }, + { MIXSRC_TrimTR5, "TrimTR5" }, + { MIXSRC_TrimTR6, "TrimTR6" }, + { MIXSRC_TrimTR7, "TrimTR7" }, + { MIXSRC_TrimTR8, "TrimTR8" }, { MIXSRC_SA, "SA" }, { MIXSRC_SB, "SB" }, { MIXSRC_SC, "SC" }, @@ -230,22 +230,22 @@ const struct YamlIdStr enum_SwitchSources[] = { { SWSRC_SH0, "SH0" }, { SWSRC_SH1, "SH1" }, { SWSRC_SH2, "SH2" }, - { SWSRC_TrimRudLeft, "TrimRudLeft" }, + { SWSRC_TrimRudLeft, "TrimRudLeft" }, { SWSRC_TrimRudRight, "TrimRudRight" }, - { SWSRC_TrimEleDown, "TrimEleDown" }, - { SWSRC_TrimEleUp, "TrimEleUp" }, - { SWSRC_TrimThrDown, "TrimThrDown" }, - { SWSRC_TrimThrUp, "TrimThrUp" }, - { SWSRC_TrimAilLeft, "TrimAilLeft" }, + { 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_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" }, @@ -577,7 +577,7 @@ static const struct YamlNode struct_trim_t[] = { }; static const struct YamlNode struct_FlightModeData[] = { YAML_IDX, - YAML_ARRAY("trim", 16, 4, struct_trim_t, NULL), + 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 ), @@ -855,7 +855,7 @@ static const struct YamlNode struct_ModelData[] = { 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", 320, 9, struct_FlightModeData, fmd_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 ), From 1a94c9f029f92365af7ee7307a2920a00cc489a9 Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 10 Jan 2022 17:20:52 +0100 Subject: [PATCH 42/99] Correct trims mapping. --- radio/src/dataconstants.h | 7 +- radio/src/gui/colorlcd/radio_diagkeys.cpp | 5 ++ .../storage/yaml/yaml_datastructs_pl18.cpp | 16 ++-- radio/src/targets/pl18/board.h | 34 ++++----- radio/src/targets/pl18/keys_driver.cpp | 75 ++++++++++++------- 5 files changed, 80 insertions(+), 57 deletions(-) diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index f2c012df152..04b6bb1e047 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -522,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/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index 369d729a239..19925b90959 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -133,8 +133,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/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index b3871d0878d..0cca87481c0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -132,14 +132,14 @@ const struct YamlIdStr enum_MixSources[] = { { MIXSRC_CYC1, "CYC1" }, { MIXSRC_CYC2, "CYC2" }, { MIXSRC_CYC3, "CYC3" }, - { MIXSRC_TrimTR1, "TrimTR1" }, - { MIXSRC_TrimTR2, "TrimTR2" }, - { MIXSRC_TrimTR3, "TrimTR3" }, - { MIXSRC_TrimTR4, "TrimTR4" }, - { MIXSRC_TrimTR5, "TrimTR5" }, - { MIXSRC_TrimTR6, "TrimTR6" }, - { MIXSRC_TrimTR7, "TrimTR7" }, - { MIXSRC_TrimTR8, "TrimTR8" }, + { 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" }, diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 21b08d906d7..ed975aeb90c 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -152,23 +152,23 @@ enum EnumKeys KEY_RADIO, KEY_TELEM, TRM_BASE, - TRM1_DWN = TRM_BASE, - TRM1_UP, - TRM2_DWN, - TRM2_UP, - TRM3_DWN, - TRM3_UP, - TRM4_DWN, - TRM4_UP, - TRM5_DWN, - TRM5_UP, - TRM6_DWN, - TRM6_UP, - TRM7_LEFT, - TRM7_RIGHT, - TRM8_LEFT, - TRM8_RIGHT, - TRM_LAST = TRM8_RIGHT, + 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 }; diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 707ecc8c67a..93869a59ba4 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -102,15 +102,34 @@ uint32_t readTrims() } #endif if(!getTrim) return result; + /* The 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 << (TRM1_UP - TRM_BASE); + result |= 1 << (TRM_LS_UP - TRM_BASE); if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) - result |= 1 << (TRM1_DWN - TRM_BASE); + result |= 1 << (TRM_LS_DWN - TRM_BASE); if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) - result |= 1 << (TRM2_UP - TRM_BASE); + result |= 1 << (TRM_RS_UP - TRM_BASE); if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) - result |= 1 << (TRM2_DWN - TRM_BASE); + result |= 1 << (TRM_RS_DWN - TRM_BASE); // Extract the matrix trims GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); @@ -118,38 +137,38 @@ uint32_t readTrims() 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 << (TRM7_LEFT - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << (TRM7_RIGHT - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) - result |= 1 << (TRM5_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) - result |= 1 << (TRM5_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) // TR7 left + result |= 1 << (TRM_LH_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR7 right + result |= 1 << (TRM_LH_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR5 down + result |= 1 << (TRM_LV_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR5 up + result |= 1 << (TRM_LV_UP - TRM_BASE); 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 << (TRM3_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << (TRM3_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) - result |= 1 << (TRM4_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) - result |= 1 << (TRM4_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) // TR3 down + result |= 1 << (TRM_EX1_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR3 up + result |= 1 << (TRM_EX1_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR4 up + result |= 1 << (TRM_EX2_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR4 down + result |= 1 << (TRM_EX2_DWN - TRM_BASE); 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 << (TRM6_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << (TRM6_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) - result |= 1 << (TRM8_LEFT - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) - result |= 1 << (TRM8_RIGHT - TRM_BASE); + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) // TR6 up + result |= 1 << (TRM_RV_UP - TRM_BASE); + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR6 down + result |= 1 << (TRM_RV_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR8 left + result |= 1 << (TRM_RH_DWN - TRM_BASE); + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR8 right + result |= 1 << (TRM_RH_UP - TRM_BASE); GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); return result; } From 427a30db4ba9e3f9875ba6adc419c03b357229db Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 10 Jan 2022 21:19:51 +0100 Subject: [PATCH 43/99] Adds trims TR1 and TR4 as mixer sources (TR5 to TR8 are for the sticks). --- radio/src/myeeprom.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 From 5dfbb25475269f23007fd89b38799d516cad9155 Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 10 Jan 2022 23:00:55 +0100 Subject: [PATCH 44/99] Avoid concurrent key matrix agitation from multiple tasks. PL18/PL18EV key remap improvements, matching key diagnose values. --- radio/src/gui/colorlcd/radio_diagkeys.cpp | 5 +- radio/src/targets/pl18/board.h | 14 +- radio/src/targets/pl18/hal.h | 7 +- radio/src/targets/pl18/keys_driver.cpp | 186 ++++++++++++---------- 4 files changed, 115 insertions(+), 97 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index 19925b90959..a1eea38900d 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -94,7 +94,7 @@ class RadioKeyDiagsWindow : public Window dc->drawText(TRIM_MINUS_COLUMN, 1, "-", COLOR_THEME_PRIMARY1); dc->drawText(TRIM_PLUS_COLUMN, 1, "+", COLOR_THEME_PRIMARY1); -#if !defined(PCBNV14) && !defined(PCBPL18) // TODO! Check if can be removed !defined(PCBPL18) here +#if !defined(PCBNV14) // KEYS coord_t y = 1; for (uint8_t i = 0; i < keysGetMaxKeys(); i++) { @@ -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) || defined(PCBPL18) - // KEYS (both radios do NOT have physical keys, only remapped trim 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); diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index ed975aeb90c..9dec80c814a 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -140,17 +140,17 @@ 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_ENTER, KEY_MODEL, - KEY_RADIO, + KEY_UP = KEY_MODEL, + KEY_EXIT, + KEY_DOWN = KEY_EXIT, KEY_TELEM, + KEY_RIGHT = KEY_TELEM, + KEY_RADIO, + KEY_LEFT = KEY_RADIO, TRM_BASE, TRM_LH_DWN = TRM_BASE, TRM_LH_UP, diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 7d1b6fd85e0..bac8e2c4e6a 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -37,10 +37,15 @@ /* DMA Allocation: DMA/Stream/Channel 1/5/7 DAC/Audio - 2/0/0 ADC1 + 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 diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 93869a59ba4..eff04818d42 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -22,72 +22,112 @@ #include "opentx.h" #include "hal/adc_driver.h" -uint32_t readKeys() +uint32_t readKeyMatrix() { - uint32_t result = 0; - bool getKeys = true; + // 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 + */ -/* TODO! Uncomment, only for testing -#if defined(LUA) - if (!isLuaStandaloneRunning()) { - getKeys = false; - } -#endif -*/ + volatile static struct + { + uint32_t oldResult = 0; + uint8_t ui8ReadInProgress = 0; + } syncelem; - if (getKeys) { + 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 << KEY_RADIO; // TR7 left - - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << KEY_MODEL; // TR7 right - - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) - result |= 1 << KEY_TELEM; // TR5 down - - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) - result |= 1 << KEY_PGUP; // TR5 up + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) // TR7 left + result |= 1 << 8; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR7 right + result |= 1 << 9; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR5 down + result |= 1 << 4; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR5 up + result |= 1 << 5; 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 << KEY_DOWN; // TR3 down - - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << KEY_UP; // TR3 up - - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) - result |= 1 << KEY_LEFT; // TR4 up - - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) - result |= 1 << KEY_RIGHT; // TR4 down + if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) // TR3 down + result |= 1 << 0; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR3 up + result |= 1 << 1; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR4 up + result |= 1 << 3; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR4 down + result |= 1 << 2; 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) // TR6 up + result |= 1 << 7; + if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR6 down + result |= 1 << 6; + if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR8 left + result |= 1 << 10; + if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR8 right + result |= 1 << 11; + GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + syncelem.oldResult = result; + syncelem.ui8ReadInProgress = 0; + return result; +} + +uint32_t readKeys() +{ + uint32_t result = 0; + bool getKeys = true; + +#if defined(LUA) + if (!isLuaStandaloneRunning()) { + getKeys = false; } +#endif - // Enter and Exit are always supported - GPIO_SetBits(TRIMS_GPIO_OUT1, TRIMS_GPIO_OUT1_PIN); - GPIO_SetBits(TRIMS_GPIO_OUT2, TRIMS_GPIO_OUT2_PIN); - GPIO_SetBits(TRIMS_GPIO_OUT4, TRIMS_GPIO_OUT4_PIN); - GPIO_ResetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); - delay_us(10); + uint32_t mkeys = readKeyMatrix(); + if (getKeys) { + if (mkeys & (1 << 0)) result |= 1 << KEY_TELEM; // TR3 down - if (~TRIMS_GPIO_REG_IN1 & TRIMS_GPIO_PIN_IN1) - result |= 1 << KEY_ENTER; // TR6 up + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) // TR1 up + result |= 1 << KEY_RADIO; + if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) // TR1 down + result |= 1 << KEY_MODEL; - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) - result |= 1 << KEY_EXIT; // TR6 down + if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) // TR2 up + result |= 1 << KEY_PGUP; + if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) // TR2 down + result |= 1 << KEY_PGDN; + } - GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + // Enter and Exit are always supported + if (mkeys & (1 << 2)) result |= 1 << KEY_ENTER; // TR4 down + if (mkeys & (1 << 3)) result |= 1 << KEY_EXIT; // TR4 up return result; } @@ -102,7 +142,7 @@ uint32_t readTrims() } #endif if(!getTrim) return result; - /* The bit-order has to be: + /* The output bit-order has to be: 0 LHL TR7L (Left equals down) 1 LHR TR7R 2 LVD TR5D @@ -131,45 +171,19 @@ uint32_t readTrims() if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) result |= 1 << (TRM_RS_DWN - TRM_BASE); - // Extract the matrix trims - 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) // TR7 left - result |= 1 << (TRM_LH_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR7 right - result |= 1 << (TRM_LH_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR5 down - result |= 1 << (TRM_LV_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR5 up - result |= 1 << (TRM_LV_UP - TRM_BASE); - - 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) // TR3 down - result |= 1 << (TRM_EX1_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR3 up - result |= 1 << (TRM_EX1_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR4 up - result |= 1 << (TRM_EX2_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR4 down - result |= 1 << (TRM_EX2_DWN - TRM_BASE); - - 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) // TR6 up - result |= 1 << (TRM_RV_UP - TRM_BASE); - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR6 down - result |= 1 << (TRM_RV_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR8 left - result |= 1 << (TRM_RH_DWN - TRM_BASE); - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR8 right - result |= 1 << (TRM_RH_UP - TRM_BASE); - GPIO_SetBits(TRIMS_GPIO_OUT3, TRIMS_GPIO_OUT3_PIN); + uint32_t mkeys = readKeyMatrix(); + if (mkeys & (1 << 0)) result |= (1 << (TRM_EX1_DWN - TRM_BASE)); // TR3 down + if (mkeys & (1 << 1)) result |= (1 << (TRM_EX1_UP - TRM_BASE)); // TR3 up + if (mkeys & (1 << 2)) result |= (1 << (TRM_EX2_DWN - TRM_BASE)); // TR4 down + if (mkeys & (1 << 3)) result |= (1 << (TRM_EX2_UP - TRM_BASE)); // TR4 up + if (mkeys & (1 << 4)) result |= (1 << (TRM_LV_DWN - TRM_BASE)); // TR5 down + if (mkeys & (1 << 5)) result |= (1 << (TRM_LV_UP - TRM_BASE)); // TR5 up + if (mkeys & (1 << 6)) result |= (1 << (TRM_RV_DWN - TRM_BASE)); // TR6 down + if (mkeys & (1 << 7)) result |= (1 << (TRM_RV_UP - TRM_BASE)); // TR6 up + if (mkeys & (1 << 8)) result |= (1 << (TRM_LH_DWN - TRM_BASE)); // TR7 left + if (mkeys & (1 << 9)) result |= (1 << (TRM_LH_UP - TRM_BASE)); // TR7 right + if (mkeys & (1 << 10)) result |= (1 << (TRM_RH_DWN - TRM_BASE)); // TR8 left + if (mkeys & (1 << 11)) result |= (1 << (TRM_RH_UP - TRM_BASE)); // TR8 right return result; } From edc419e9d7d8db10ea7cf8a15c1726cf75a1c2bd Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 11 Jan 2022 09:14:57 +0100 Subject: [PATCH 45/99] TR3 and TR4 trims as buttons for bootloader. --- .../common/arm/stm32/bootloader/boot.cpp | 16 ++- .../src/targets/pl18/bootloader/boot_menu.cpp | 13 +++ radio/src/targets/pl18/keys_driver.cpp | 109 ++++++++++-------- 3 files changed, 88 insertions(+), 50 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index 52dc904db3d..a9d605d73b3 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -296,7 +296,7 @@ void bootloaderInitApp() // wait a bit for the inputs to stabilize... if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) { -#if !defined(PCBHORUS) && !defined(PCBNV14) +#if !defined(PCBHORUS) && !defined(PCBNV14) && !defined(PCBPL18) for (uint32_t i = 0; i < 150000; i++) { __ASM volatile ("nop"); } @@ -421,12 +421,16 @@ int bootloaderMain() if (state == ST_START) { bootloaderDrawScreen(state, vpos); - +#if defined(PCBPL18) + if (event == EVT_KEY_FIRST(KEY_PGDN)) { +#else if (IS_NEXT_EVENT(event)) { +#endif if (vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN) - 1) { vpos++; } continue; } else if (IS_PREVIOUS_EVENT(event)) { +#endif if (vpos > 0) { vpos--; } continue; } @@ -534,8 +538,11 @@ int bootloaderMain() if (nameCount < limit) { limit = nameCount; } - +#if defined(PCBPL18) + if (event == EVT_KEY_REPT(KEY_PGDN) || event == EVT_KEY_FIRST(KEY_PGUP)) { +#else if (IS_NEXT_EVENT(event)) { +#endif if (vpos < limit - 1) { vpos += 1; } @@ -547,7 +554,8 @@ int bootloaderMain() } } else if (IS_PREVIOUS_EVENT(event)) { - if (vpos > 0) { +#endif + if (vpos > 0) { vpos -= 1; } else { diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp index af6d49cee99..cdfebdb1999 100644 --- a/radio/src/targets/pl18/bootloader/boot_menu.cpp +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -211,10 +211,19 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, LBM_FLASH, BL_FOREGROUND); if (st == ST_FILE_LIST) { +#if defined(PCBPL18) + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[TR4 down] to select file", BL_FOREGROUND); +#else lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[R TRIM] to select file", BL_FOREGROUND); +#endif } else if (st == ST_FLASH_CHECK && opt == FC_OK) { +#if defined(PCBPL18) + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [TR4 down] long to flash", BL_FOREGROUND); +#else lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to flash", BL_FOREGROUND); +#endif + } else if (st == ST_FLASHING) { lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Writing Firmware ...", BL_FOREGROUND); @@ -226,7 +235,11 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) if (st != ST_FLASHING) { lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING - 2, LBM_EXIT, BL_FOREGROUND); +#if defined(PCBPL18) + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[TR4 up] to exit", BL_FOREGROUND); +#else lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit", BL_FOREGROUND); +#endif } } } diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index eff04818d42..5746fb11042 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -22,6 +22,22 @@ #include "opentx.h" #include "hal/adc_driver.h" +enum PhysicalTrims +{ + TR3D, + TR3U, + TR4D, + TR4U, + TR5D, + TR5U, + TR6D, + TR6U, + TR7L, + TR7R, + TR8L, + TR8R +}; + uint32_t readKeyMatrix() { // This function avoids concurrent matrix agitation @@ -61,38 +77,38 @@ uint32_t readKeyMatrix() 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) // TR7 left - result |= 1 << 8; - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR7 right - result |= 1 << 9; - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR5 down - result |= 1 << 4; - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR5 up - result |= 1 << 5; + 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) // TR3 down - result |= 1 << 0; - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR3 up - result |= 1 << 1; - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR4 up - result |= 1 << 3; - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR4 down - result |= 1 << 2; + 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) // TR6 up - result |= 1 << 7; - if (~TRIMS_GPIO_REG_IN2 & TRIMS_GPIO_PIN_IN2) // TR6 down - result |= 1 << 6; - if (~TRIMS_GPIO_REG_IN3 & TRIMS_GPIO_PIN_IN3) // TR8 left - result |= 1 << 10; - if (~TRIMS_GPIO_REG_IN4 & TRIMS_GPIO_PIN_IN4) // TR8 right - result |= 1 << 11; + 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; @@ -104,30 +120,31 @@ uint32_t readKeys() uint32_t result = 0; bool getKeys = true; +/* #if defined(LUA) if (!isLuaStandaloneRunning()) { getKeys = false; } #endif +*/ uint32_t mkeys = readKeyMatrix(); if (getKeys) { - if (mkeys & (1 << 0)) result |= 1 << KEY_TELEM; // TR3 down - - if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) // TR1 up + if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) result |= 1 << KEY_RADIO; - if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) // TR1 down + 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 (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) // TR2 up - result |= 1 << KEY_PGUP; - if (~TRIMS_GPIO_REG_TR2D & TRIMS_GPIO_PIN_TR2D) // TR2 down - result |= 1 << KEY_PGDN; + if (mkeys & (1 << TR3U)) result |= 1 << KEY_PGUP; + if (mkeys & (1 << TR3D)) result |= 1 << KEY_PGDN; } // Enter and Exit are always supported - if (mkeys & (1 << 2)) result |= 1 << KEY_ENTER; // TR4 down - if (mkeys & (1 << 3)) result |= 1 << KEY_EXIT; // TR4 up + if (mkeys & (1 << TR4D)) result |= 1 << KEY_ENTER; + if (mkeys & (1 << TR4U)) result |= 1 << KEY_EXIT; + return result; } @@ -172,18 +189,18 @@ uint32_t readTrims() result |= 1 << (TRM_RS_DWN - TRM_BASE); uint32_t mkeys = readKeyMatrix(); - if (mkeys & (1 << 0)) result |= (1 << (TRM_EX1_DWN - TRM_BASE)); // TR3 down - if (mkeys & (1 << 1)) result |= (1 << (TRM_EX1_UP - TRM_BASE)); // TR3 up - if (mkeys & (1 << 2)) result |= (1 << (TRM_EX2_DWN - TRM_BASE)); // TR4 down - if (mkeys & (1 << 3)) result |= (1 << (TRM_EX2_UP - TRM_BASE)); // TR4 up - if (mkeys & (1 << 4)) result |= (1 << (TRM_LV_DWN - TRM_BASE)); // TR5 down - if (mkeys & (1 << 5)) result |= (1 << (TRM_LV_UP - TRM_BASE)); // TR5 up - if (mkeys & (1 << 6)) result |= (1 << (TRM_RV_DWN - TRM_BASE)); // TR6 down - if (mkeys & (1 << 7)) result |= (1 << (TRM_RV_UP - TRM_BASE)); // TR6 up - if (mkeys & (1 << 8)) result |= (1 << (TRM_LH_DWN - TRM_BASE)); // TR7 left - if (mkeys & (1 << 9)) result |= (1 << (TRM_LH_UP - TRM_BASE)); // TR7 right - if (mkeys & (1 << 10)) result |= (1 << (TRM_RH_DWN - TRM_BASE)); // TR8 left - if (mkeys & (1 << 11)) result |= (1 << (TRM_RH_UP - TRM_BASE)); // TR8 right + 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; } From b0ca8cf7baf27f76c1f8f3ac54aa244c8b1409fc Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 11 Jan 2022 12:59:34 +0100 Subject: [PATCH 46/99] Removed PL18 internal module (remnants of NV14 code) and freeing up UART7. There were further changes necessary, as PL18 is the first color radio w/o an internal RF module. --- radio/src/gui/colorlcd/model_setup.cpp | 2 +- radio/src/gui/colorlcd/radio_version.cpp | 7 +++- radio/src/pulses/flysky.cpp | 16 ++++++--- radio/src/pulses/modules_helpers.h | 15 -------- .../targets/common/arm/stm32/pwr_driver.cpp | 2 ++ radio/src/targets/pl18/CMakeLists.txt | 17 ++------- radio/src/targets/pl18/board.cpp | 3 -- radio/src/targets/pl18/board.h | 4 --- radio/src/targets/pl18/hal.h | 36 ------------------- radio/src/telemetry/flysky_pl18.cpp | 6 ++-- 10 files changed, 27 insertions(+), 81 deletions(-) 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/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp index 740858cb807..ad9c9f8f985 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], diff --git a/radio/src/pulses/flysky.cpp b/radio/src/pulses/flysky.cpp index 8a572a39b1f..e13f2ab8319 100644 --- a/radio/src/pulses/flysky.cpp +++ b/radio/src/pulses/flysky.cpp @@ -135,7 +135,6 @@ static uint8_t _flysky_timeout; static uint8_t _esc_state; uint32_t NV14internalModuleFwVersion = 0; -uint32_t PL18internalModuleFwVersion = 0; static rf_info_t rf_info = { .bind_power = BIND_LOW_POWER, @@ -212,6 +211,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; @@ -221,6 +221,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) @@ -249,6 +250,7 @@ inline void putFlySkyFrameHeader(uint8_t*& p_buf) initFlySkyCRC(); *p_buf++ = END; putFlySkyFrameByte(p_buf, _flysky_frame_index); +#endif } inline void putFlySkyFrameFooter(uint8_t*& p_buf) @@ -258,6 +260,7 @@ inline void putFlySkyFrameFooter(uint8_t*& p_buf) } putFlySkyByte(p_buf, _flysky_crc ^ 0xff); *p_buf++ = END; +#endif } void afhds2Command(uint8_t*& p_buf, uint8_t type, uint8_t cmd) @@ -358,6 +361,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; @@ -467,9 +471,6 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) #if defined(PCBNV14) memcpy(&NV14internalModuleFwVersion, &resp->value + 1, sizeof(NV14internalModuleFwVersion)); -#else - memcpy(&PL18internalModuleFwVersion, &resp->value + 1, - sizeof(PL18internalModuleFwVersion)); #endif setFlyskyState(STATE_SET_RECEIVER_ID); break; @@ -487,10 +488,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; @@ -512,12 +515,13 @@ void processInternalFlySkyTelemetryData(uint8_t byte, uint8_t* buffer, uint8_t* } } } +#endif } void resetPulsesAFHDS2() { +#if defined(HARDWARE_INTERNAL_MODULE) NV14internalModuleFwVersion = 0; - PL18internalModuleFwVersion = 0; intmodulePulsesData.flysky.frame_index = 1; _flysky_frame_index = 1; setFlyskyState(STATE_SET_TX_POWER); @@ -528,6 +532,7 @@ void resetPulsesAFHDS2() if (50 > rx_freq || 400 < rx_freq) { gRomData.rx_freq[0] = 50; } +#endif } void setupPulsesAFHDS2(uint8_t*& p_buf) @@ -660,6 +665,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/modules_helpers.h b/radio/src/pulses/modules_helpers.h index f9e98f2056a..e5c918e918f 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -36,9 +36,6 @@ #if defined(PCBNV14) extern uint32_t NV14internalModuleFwVersion; #endif -#if defined(PCBPL18) -extern uint32_t PL18internalModuleFwVersion; -#endif #if defined(AFHDS3) #include "pulses/afhds3_module.h" @@ -651,24 +648,12 @@ inline uint32_t getNV14RfFwVersion() #endif } -inline uint32_t getPL18RfFwVersion() -{ -#if defined(PCBPL18) - return PL18internalModuleFwVersion; -#else - return 0; -#endif -} - inline bool isModuleRangeAvailable(uint8_t moduleIdx) { bool ret = isModuleBindRangeAvailable(moduleIdx) && !IS_RX_MULTI(moduleIdx); #if defined(PCBNV14) ret = ret && (!isModuleFlySky(moduleIdx) || NV14internalModuleFwVersion >= 0x1000E); -#elif defined(PCBPL18) - ret = ret && - (!isModuleFlySky(moduleIdx) || PL18internalModuleFwVersion >= 0x1000E); #else ret = ret && (!isModuleFlySky(moduleIdx)); #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/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 1ad023d4386..c010741c2d5 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -1,7 +1,7 @@ option(DISK_CACHE "Enable SD card disk cache" ON) option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) -option(AFHDS2 "Support for AFHDS2" 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) @@ -40,20 +40,13 @@ 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 -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) -add_definitions(-DPCBPL18 -DPCBFLYSKY -DINTERNAL_MODULE_SERIAL -DAFHDS2_BAUDRATE=${PCB_RF_BAUD}) +add_definitions(-DPCBPL18 -DPCBFLYSKY) add_definitions(-DBATTERY_CHARGE) add_definitions(-DSOFTWARE_VOLUME) -add_definitions(-DINTERNAL_MODULE_AFHDS2A) # defines existing internal modules -set(INTERNAL_MODULES AFHDS2A CACHE STRING "Internal modules") -set(DEFAULT_INTERNAL_MODULE FLYSKY CACHE STRING "Default internal module") +set(DEFAULT_INTERNAL_MODULE NONE CACHE STRING "No internal module") set(TARGET_SRC ${TARGET_SRC} @@ -87,7 +80,6 @@ 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(-DAFHDS2) add_definitions(-DHARDWARE_EXTERNAL_MODULE) #add_definitions(-DAUX_SERIAL) #add_definitions(-DFLYSKY_AUTO_POWER_DOWN) @@ -100,8 +92,6 @@ if(NOT UNEXPECTED_SHUTDOWN) add_definitions(-DNO_UNEXPECTED_SHUTDOWN) endif() -set(AFHDS2 ON) - # disable for now as it crashes on radio #set(AFHDS3 ON) @@ -130,7 +120,6 @@ set(TARGET_SRC extmodule_helper.cpp ../horus/extmodule_driver.cpp trainer_driver.cpp - ../common/arm/stm32/intmodule_serial_driver.cpp ) set(FIRMWARE_TARGET_SRC diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index cd07d2dff30..fa2b99ab2d4 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -73,7 +73,6 @@ void delay_self(int count) TELEMETRY_RCC_AHB1Periph |\ TRAINER_RCC_AHB1Periph |\ HAPTIC_RCC_AHB1Periph |\ - INTMODULE_RCC_AHB1Periph |\ FLYSKY_HALL_RCC_AHB1Periph |\ EXTMODULE_RCC_AHB1Periph\ ) @@ -87,10 +86,8 @@ void delay_self(int count) #define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ AUDIO_RCC_APB1Periph |\ TRAINER_RCC_APB1Periph |\ - INTMODULE_RCC_APB1Periph |\ FLYSKY_HALL_RCC_APB1Periph |\ EXTMODULE_RCC_APB1Periph |\ - INTMODULE_RCC_APB1Periph |\ AUX_SERIAL_RCC_APB1Periph |\ MIXER_SCHEDULER_TIMER_RCC_APB1Periph \ ) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 9dec80c814a..e5781f88625 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -111,16 +111,12 @@ uint32_t isBootloaderStart(const uint8_t * buffer); void SDRAM_Init(); // Pulses driver -#define INTERNAL_MODULE_OFF() GPIO_SetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) -#define INTERNAL_MODULE_ON() GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) void EXTERNAL_MODULE_ON(); void EXTERNAL_MODULE_OFF(); #define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF #define BLUETOOTH_MODULE_ON() GPIO_ResetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) #define BLUETOOTH_MODULE_OFF() GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) -#define IS_INTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) == Bit_SET) #define IS_EXTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) == Bit_SET) -#define IS_UART_MODULE(port) (port == INTERNAL_MODULE) #define IS_PXX2_INTERNAL_ENABLED() (false) void init_intmodule_heartbeat(); diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index bac8e2c4e6a..de1428bc2bb 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -408,42 +408,6 @@ #define FLYSKY_HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler #define FLYSKY_HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler -// Internal Module -#define HARDWARE_INTERNAL_MODULE -#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) -#define INTMODULE_PWR_GPIO GPIOD -#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_3 // PD.03 Bogus pin -#define INTMODULE_GPIO GPIOF -//#define INTMODULE_TX_GPIO GPIOF -#define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 -//#define INTMODULE_RX_GPIO GPIOF -#define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 -#define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 -#define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 -#define INTMODULE_USART UART7 -#define INTMODULE_GPIO_AF GPIO_AF_UART7 -#define INTMODULE_USART_IRQn UART7_IRQn -#define INTMODULE_USART_IRQHandler UART7_IRQHandler -#define INTMODULE_DMA_STREAM DMA1_Stream1 -#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn -#define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 -#define INTMODULE_DMA_CHANNEL DMA_Channel_5 - -/*#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 -#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn -#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream3_IRQHandler -#define INTMODULE_TX_DMA_STREAM DMA1_Stream1 -#define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn -#define INTMODULE_TX_DMA_Stream_IRQHandler DMA1_Stream1_IRQHandler -#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1 */ - -#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) -#define INTMODULE_RCC_APB2Periph 0 -#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 diff --git a/radio/src/telemetry/flysky_pl18.cpp b/radio/src/telemetry/flysky_pl18.cpp index 4c03dac6348..e12e1d740bd 100644 --- a/radio/src/telemetry/flysky_pl18.cpp +++ b/radio/src/telemetry/flysky_pl18.cpp @@ -83,8 +83,6 @@ const FlyskyPl18Sensor Pl18Sensor[]= }; -extern uint32_t PL18internalModuleFwVersion; - extern int32_t getALT(uint32_t value); signed short CalculateAltitude(unsigned int pressure) @@ -134,6 +132,7 @@ int32_t GetSensorValueFlySkyPl18(const FlyskyPl18Sensor* sensor, // 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; @@ -148,6 +147,7 @@ int32_t GetSensorValueFlySkyPl18(const FlyskyPl18Sensor* sensor, } else if (sensor->id == FLYSKY_SENSOR_RX_SIGNAL) { telemetryData.rssi.set(value); } +*/ if (sensor->id == FLYSKY_SENSOR_PRESSURE) { switch(sensor->subId) @@ -221,6 +221,7 @@ void flySkyPl18ProcessTelemetryPacket(const uint8_t* ptr, uint8_t size) 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])); @@ -244,6 +245,7 @@ void flySkyPl18ProcessTelemetryPacket(const uint8_t* ptr, uint8_t size) } } } +*/ if (sensorCount) { telemetryStreaming = TELEMETRY_TIMEOUT10ms; } From 2d3941eb711bc3d8d7a201e5e5d31bc919fda887 Mon Sep 17 00:00:00 2001 From: rotorman Date: Tue, 11 Jan 2022 13:21:22 +0100 Subject: [PATCH 47/99] Side sliders of PL18 are called VRD and VRE not LS and RS. SYS button of T16 Family is actually "RADIO" button. Changed labels accordingly. --- radio/src/translations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 053e9577370..d9ce70ba9be 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -955,7 +955,7 @@ const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; const char STR_RACING_MODE[] = TR_RACING_MODE; -#if defined(PCBNV14) || defined(PCBPL18) +#if defined(PCBNV14) ISTR(RFPOWER_AFHDS2); #endif From 97bcc2fdf5bc8b8fb7eb29fa240666691228e72c Mon Sep 17 00:00:00 2001 From: rotorman Date: Wed, 12 Jan 2022 23:26:55 +0100 Subject: [PATCH 48/99] Initial steps towards stick pot controls and battery charger(s). I/O definitions and setting outputs init state. --- radio/src/targets/pl18/battery_driver.cpp | 31 ++++- radio/src/targets/pl18/battery_driver.h | 14 +- radio/src/targets/pl18/board.h | 4 +- radio/src/targets/pl18/extmodule_helper.cpp | 8 -- radio/src/targets/pl18/hal.h | 135 ++++++++++++-------- 5 files changed, 112 insertions(+), 80 deletions(-) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index bd64242930f..2518aaf4b10 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -35,13 +35,34 @@ void battery_charge_init() { GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN; + GPIO_InitStructure.GPIO_Pin = UCHARGER_STDBY_GPIO_PIN; 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_UP; - GPIO_Init(PWR_CHARGING_GPIO, &GPIO_InitStructure); - GPIO_SetBits(PWR_CHARGING_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN); + GPIO_Init(UCHARGER_STDBY_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = UCHARGER_CHARGE_GPIO_PIN; + GPIO_Init(UCHARGER_CHARGE_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = WCHARGER_STDBY_GPIO_PIN; + GPIO_Init(WCHARGER_STDBY_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = WCHARGER_CHARGE_GPIO_PIN; + GPIO_Init(WCHARGER_CHARGE_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = WCHARGER_EN_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(WCHARGER_EN_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(WCHARGER_EN_GPIO, WCHARGER_EN_GPIO_PIN); + + GPIO_InitStructure.GPIO_Pin = WCHARGER_VBUS_EN_GPIO_PIN; + GPIO_Init(WCHARGER_VBUS_EN_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(WCHARGER_VBUS_EN_GPIO, WCHARGER_VBUS_EN_GPIO_PIN); + + GPIO_InitStructure.GPIO_Pin = WCHARGER_I_CONTROL_GPIO_PIN; + GPIO_Init(WCHARGER_I_CONTROL_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(WCHARGER_I_CONTROL_GPIO, WCHARGER_I_CONTROL_GPIO_PIN); } #define CHARGE_SAMPLES 10 @@ -53,8 +74,8 @@ uint16_t get_battery_charge_state() uint16_t chargeState = CHARGE_UNKNOWN; int maxSamples = CHARGE_SAMPLES; #if !defined(SIMU) - bool isFinished = !READ_CHARGE_FINISHED_STATE(); - bool isCharging = !READ_CHARGING_STATE(); + bool isFinished = !READ_UCHARGE_FINISHED_STATE(); + bool isCharging = !READ_UCHARGING_STATE(); //maxSamples = boardState == BOARD_POWER_OFF ? CHARGE_SAMPLES/2 : CHARGE_SAMPLES; if(chargeSampleIndex >= maxSamples) chargeSampleIndex = 0; uint16_t currentChargeState = isFinished ? CHARGE_FINISHED : isCharging ? CHARGE_STARTED : CHARGE_NONE; diff --git a/radio/src/targets/pl18/battery_driver.h b/radio/src/targets/pl18/battery_driver.h index 1eace4d8cb9..9e2c9823cf4 100644 --- a/radio/src/targets/pl18/battery_driver.h +++ b/radio/src/targets/pl18/battery_driver.h @@ -29,6 +29,7 @@ ***************************************************************************************************/ #include "board.h" +#include "hal.h" enum ChargeState { @@ -38,17 +39,8 @@ enum ChargeState CHARGE_FINISHED }; - -#define PWR_CHARGE_FINISHED_GPIO GPIOB -#define PWR_CHARGE_FINISHED_GPIO_REG PWR_CHARGE_FINISHED_GPIO->IDR -#define PWR_CHARGE_FINISHED_GPIO_PIN GPIO_Pin_13 // PB.13 - -#define PWR_CHARGING_GPIO GPIOB -#define PWR_CHARGING_GPIO_REG PWR_CHARGING_GPIO->IDR -#define PWR_CHARGING_GPIO_PIN GPIO_Pin_14 // PB.14 - -#define READ_CHARGE_FINISHED_STATE() GPIO_ReadInputDataBit( PWR_CHARGE_FINISHED_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN ) -#define READ_CHARGING_STATE() GPIO_ReadInputDataBit( PWR_CHARGING_GPIO, PWR_CHARGING_GPIO_PIN ) +#define READ_UCHARGE_FINISHED_STATE() GPIO_ReadInputDataBit( UCHARGER_STDBY_GPIO, UCHARGER_STDBY_GPIO_PIN ) +#define READ_UCHARGING_STATE() GPIO_ReadInputDataBit( UCHARGER_CHARGE_GPIO, UCHARGER_CHARGE_GPIO_PIN ) extern void battery_charge_init(); extern void handle_battery_charge(uint32_t last_press_time); diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index e5781f88625..af3ec08af82 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -114,8 +114,8 @@ void SDRAM_Init(); void EXTERNAL_MODULE_ON(); void EXTERNAL_MODULE_OFF(); #define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF -#define BLUETOOTH_MODULE_ON() GPIO_ResetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) -#define BLUETOOTH_MODULE_OFF() GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) +#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_EXTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) == Bit_SET) #define IS_PXX2_INTERNAL_ENABLED() (false) diff --git a/radio/src/targets/pl18/extmodule_helper.cpp b/radio/src/targets/pl18/extmodule_helper.cpp index 40ff0f9c2e0..7d9b2df0ec3 100644 --- a/radio/src/targets/pl18/extmodule_helper.cpp +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -24,13 +24,11 @@ void EXTERNAL_MODULE_ON() { GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); - GPIO_ResetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); } void EXTERNAL_MODULE_OFF() { GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); - GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); } void extModuleInit() @@ -44,14 +42,8 @@ void extModuleInit() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); - //for additional transistor to ensuring module is completely disabled GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - //pin must be pulled to V+ (voltage of board - VCC is not enough to fully close transistor) GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_FIX_GPIO_PIN; - GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); - GPIO_Init(EXTMODULE_PWR_FIX_GPIO, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index de1428bc2bb..abb1dd3b338 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -63,7 +63,7 @@ #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 +// 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 @@ -113,7 +113,7 @@ // 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_0 | GPIO_Pin_1 | GPIO_Pin_14) -#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_13 ) /* TODO! PC8 currently allocated for SDIO D0 */ +#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10 | 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_GPIOF_PINS (GPIO_Pin_10) #define KEYS_GPIOH_PINS (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11) @@ -127,14 +127,18 @@ #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_SLIDER1 GPIO_Pin_9 // PF.09 LS -#define ADC_GPIO_PIN_SLIDER2 GPIO_Pin_7 // PA.07 RS +#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) @@ -142,7 +146,7 @@ #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_8 | GPIO_Pin_9 | GPIO_Pin_10) +#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 @@ -152,6 +156,8 @@ #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 @@ -160,6 +166,8 @@ #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 @@ -184,6 +192,34 @@ #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_STDBY_GPIO GPIOB +#define UCHARGER_STDBY_GPIO_REG UCHARGER_STDBY_GPIO->IDR +#define UCHARGER_STDBY_GPIO_PIN GPIO_Pin_13 // PB.13 input + +#define UCHARGER_CHARGE_GPIO GPIOB +#define UCHARGER_CHARGE_GPIO_REG UCHARGER_CHARGE_GPIO->IDR +#define UCHARGER_CHARGE_GPIO_PIN GPIO_Pin_14 // PB.14 input + +#define WCHARGER_EN_GPIO GPIOH +#define WCHARGER_EN_GPIO_PIN GPIO_Pin_4 // PH.04 output + +#define WCHARGER_VBUS_EN_GPIO GPIOG +#define WCHARGER_VBUS_EN_GPIO_PIN GPIO_Pin_3 // PG.03 output + +#define WCHARGER_I_CONTROL_GPIO GPIOH +#define WCHARGER_I_CONTROL_GPIO_PIN GPIO_Pin_13 // PH.13 output + +#define WCHARGER_STDBY_GPIO GPIOI +#define WCHARGER_STDBY_GPIO_PIN GPIO_Pin_10 // PI.10 input + +#define WCHARGER_CHARGE_GPIO GPIOI +#define WCHARGER_CHARGE_GPIO_PIN GPIO_Pin_9 // PI.09 input + +// TODO! Check IOLL1 to PI.01 connectivity! + // S.Port update connector #define SPORT_MAX_BAUDRATE 400000 #define SPORT_UPDATE_RCC_AHB1Periph 0 @@ -251,6 +287,7 @@ #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 @@ -332,44 +369,44 @@ #define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 // 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 +#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 GPIO_Pin_7 // PB.07 -#define I2C_B1_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 -#define I2C_B1_GPIO_AF GPIO_AF_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_RST_RCC_AHB1Periph RCC_AHB1Periph_GPIOB -#define TOUCH_RST_GPIO GPIOB -#define TOUCH_RST_GPIO_PIN GPIO_Pin_12 // PB.12 - -#define TOUCH_INT_RCC_AHB1Periph RCC_AHB1Periph_GPIOB -#define TOUCH_INT_GPIO GPIOB -#define TOUCH_INT_GPIO_PIN GPIO_Pin_9 // PB.09 -#define TOUCH_INT_EXTI_LINE1 EXTI_Line9 -#define TOUCH_INT_EXTI_IRQn1 EXTI9_5_IRQn -#define TOUCH_INT_EXTI_IRQHandler1 EXTI9_5_IRQHandler -#define TOUCH_INT_EXTI_PortSource EXTI_PortSourceGPIOB -#define TOUCH_INT_EXTI_PinSource1 EXTI_PinSource9 -#define TOUCH_INT_STATUS() (GPIO_ReadInputDataBit(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN)) +#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 GPIO_Pin_7 // PB.07 +#define I2C_B1_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 +#define I2C_B1_GPIO_AF GPIO_AF_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_RST_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_RST_GPIO GPIOB +#define TOUCH_RST_GPIO_PIN GPIO_Pin_12 // PB.12 + +#define TOUCH_INT_RCC_AHB1Periph RCC_AHB1Periph_GPIOB +#define TOUCH_INT_GPIO GPIOB +#define TOUCH_INT_GPIO_PIN GPIO_Pin_9 // PB.09 +#define TOUCH_INT_EXTI_LINE1 EXTI_Line9 +#define TOUCH_INT_EXTI_IRQn1 EXTI9_5_IRQn +#define TOUCH_INT_EXTI_IRQHandler1 EXTI9_5_IRQHandler +#define TOUCH_INT_EXTI_PortSource EXTI_PortSourceGPIOB +#define TOUCH_INT_EXTI_PinSource1 EXTI_PinSource9 +#define TOUCH_INT_STATUS() (GPIO_ReadInputDataBit(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN)) // Haptic: TIM1_CH1 #define HAPTIC_PWM @@ -413,8 +450,6 @@ #define EXTMODULE_PULSES #define EXTMODULE_PWR_GPIO GPIOD #define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_11 -#define EXTMODULE_PWR_FIX_GPIO GPIOA -#define EXTMODULE_PWR_FIX_GPIO_PIN GPIO_Pin_2 // PA.02 #define EXTMODULE_RCC_AHB1Periph \ (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) @@ -473,11 +508,6 @@ #define EXTMODULE_RX_NORMAL() EXTMODULE_TX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN #define EXTMODULE_RX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_RX_INVERT_GPIO_PIN -// Heartbeat (not used) -#define HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD -#define HEARTBEAT_GPIO GPIOD -#define HEARTBEAT_GPIO_PIN GPIO_Pin_12 // PD.12 - // Trainer Port #define TRAINERMODULE #define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD) @@ -510,8 +540,8 @@ //BLUETOOTH #define BLUETOOTH_ON_RCC_AHB1Periph RCC_AHB1Periph_GPIOI -#define BLUETOOTH_ON_GPIO GPIOI -#define BLUETOOTH_ON_GPIO_PIN GPIO_Pin_8 // PI.8 +#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) @@ -526,12 +556,9 @@ #define BT_TX_GPIO_PinSource GPIO_PinSource10 #define BT_RX_GPIO_PinSource GPIO_PinSource11 #define BT_USART_IRQHandler USART3_IRQHandler - -#define BT_EN_GPIO GPIOI -#define BT_EN_GPIO_PIN GPIO_Pin_8 // PI.08 #define BT_CONNECTED_GPIO GPIOJ -#define BT_CONNECTED_GPIO_PIN GPIO_Pin_1 // PJ.10 +#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 From 757a125d0bb16ce846d0fa183ae3610e18254361 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 11 Feb 2022 21:57:26 +0100 Subject: [PATCH 49/99] USB detection workaround for PL18 USB charging working for PL18 display charge animation on PL18 --- radio/src/targets/common/arm/stm32/usb_conf.h | 2 ++ radio/src/targets/common/arm/stm32/usb_driver.cpp | 2 ++ radio/src/targets/pl18/battery_driver.cpp | 13 +++++++------ radio/src/targets/pl18/board.cpp | 6 ++++++ radio/src/targets/pl18/hal.h | 6 +++--- 5 files changed, 20 insertions(+), 9 deletions(-) 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/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 2518aaf4b10..f651ffe6a6f 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -44,6 +44,10 @@ void battery_charge_init() GPIO_InitStructure.GPIO_Pin = UCHARGER_CHARGE_GPIO_PIN; GPIO_Init(UCHARGER_CHARGE_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = UCHARGER_EN_GPIO_PIN; + GPIO_Init(UCHARGER_EN_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(UCHARGER_EN_GPIO, UCHARGER_EN_GPIO_PIN); + GPIO_InitStructure.GPIO_Pin = WCHARGER_STDBY_GPIO_PIN; GPIO_Init(WCHARGER_STDBY_GPIO, &GPIO_InitStructure); @@ -56,10 +60,6 @@ void battery_charge_init() GPIO_Init(WCHARGER_EN_GPIO, &GPIO_InitStructure); GPIO_ResetBits(WCHARGER_EN_GPIO, WCHARGER_EN_GPIO_PIN); - GPIO_InitStructure.GPIO_Pin = WCHARGER_VBUS_EN_GPIO_PIN; - GPIO_Init(WCHARGER_VBUS_EN_GPIO, &GPIO_InitStructure); - GPIO_ResetBits(WCHARGER_VBUS_EN_GPIO, WCHARGER_VBUS_EN_GPIO_PIN); - GPIO_InitStructure.GPIO_Pin = WCHARGER_I_CONTROL_GPIO_PIN; GPIO_Init(WCHARGER_I_CONTROL_GPIO, &GPIO_InitStructure); GPIO_ResetBits(WCHARGER_I_CONTROL_GPIO, WCHARGER_I_CONTROL_GPIO_PIN); @@ -74,8 +74,8 @@ uint16_t get_battery_charge_state() uint16_t chargeState = CHARGE_UNKNOWN; int maxSamples = CHARGE_SAMPLES; #if !defined(SIMU) - bool isFinished = !READ_UCHARGE_FINISHED_STATE(); - bool isCharging = !READ_UCHARGING_STATE(); + bool isFinished = READ_UCHARGE_FINISHED_STATE(); + bool isCharging = READ_UCHARGING_STATE(); //maxSamples = boardState == BOARD_POWER_OFF ? CHARGE_SAMPLES/2 : CHARGE_SAMPLES; if(chargeSampleIndex >= maxSamples) chargeSampleIndex = 0; uint16_t currentChargeState = isFinished ? CHARGE_FINISHED : isCharging ? CHARGE_STARTED : CHARGE_NONE; @@ -198,6 +198,7 @@ void handle_battery_charge(uint32_t last_press_time) backlightInit(); lcdInit(); lcdInited = true; + touchPanelInit(); } else { lcdOn(); diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index fa2b99ab2d4..4ff98190da1 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -236,3 +236,9 @@ void boardOff() } } + +int usbPlugged() +{ + static PinDebounce debounce; + return debounce.debounce(UCHARGER_CHARGE_GPIO, UCHARGER_CHARGE_GPIO_PIN); +} diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index abb1dd3b338..09ef1b57a45 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -203,12 +203,12 @@ #define UCHARGER_CHARGE_GPIO_REG UCHARGER_CHARGE_GPIO->IDR #define UCHARGER_CHARGE_GPIO_PIN GPIO_Pin_14 // PB.14 input +#define UCHARGER_EN_GPIO GPIOG +#define UCHARGER_EN_GPIO_PIN GPIO_Pin_3 // PG.03 output + #define WCHARGER_EN_GPIO GPIOH #define WCHARGER_EN_GPIO_PIN GPIO_Pin_4 // PH.04 output -#define WCHARGER_VBUS_EN_GPIO GPIOG -#define WCHARGER_VBUS_EN_GPIO_PIN GPIO_Pin_3 // PG.03 output - #define WCHARGER_I_CONTROL_GPIO GPIOH #define WCHARGER_I_CONTROL_GPIO_PIN GPIO_Pin_13 // PH.13 output From 2d061942eb1727f2023eea6c7380aeda2ae80a5b Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 11 Feb 2022 22:23:31 +0100 Subject: [PATCH 50/99] PL18 reset battery charge screen timeout only on touch end --- radio/src/targets/pl18/board.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 4ff98190da1..54f6d81c498 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -157,8 +157,9 @@ void boardInit() } else { uint32_t press_end_touch = press_end; if (touchPanelEventOccured()) { - touchPanelRead(); - press_end_touch = get_tmr10ms(); + TouchState ts = touchPanelRead(); + if(ts.event == TE_UP) + press_end_touch = get_tmr10ms(); } press_start = 0; handle_battery_charge(press_end_touch); From af0b9e27dba22e765241c3bb33a987dee84e06aa Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 13 Feb 2022 13:27:35 +0100 Subject: [PATCH 51/99] rebase fs_pl18 branch to lvgl branch fix compilation of PL18 target untested --- radio/src/targets/pl18/board.h | 1 + radio/src/targets/pl18/lcd_driver.cpp | 77 ++++++++++++++++++++++----- radio/src/targets/pl18/tp_cst340.cpp | 6 +++ 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index af3ec08af82..c3e1b796725 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -562,5 +562,6 @@ extern AuxSerialRxFifo auxSerialRxFifo; // Touch panel driver bool touchPanelEventOccured(); struct TouchState touchPanelRead(); +struct TouchState getInternalTouchState(); #endif // _BOARD_H_ diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 035790ba34e..309a3ed5d37 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -21,6 +21,9 @@ #include "opentx.h" +#define LCD_PHYS_H LCD_H +#define LCD_PHYS_W LCD_W + #define LCD_FIRST_LAYER 0 #define LCD_SECOND_LAYER 1 @@ -30,6 +33,7 @@ uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t currentLayer = LCD_FIRST_LAYER; +BitmapBuffer lcdBackup(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_BACKUP_FRAME_BUFFER); BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); @@ -684,7 +688,7 @@ void LCD_ILI9486_Init(void) { lcdWriteData(0x00); lcdWriteData(0x08); lcdWriteCommand(0x36); - lcdWriteData(0x18); + lcdWriteData(0xF8); lcdWriteCommand(0x3a); lcdWriteData(0x65); lcdWriteCommand(0xc0); @@ -743,6 +747,10 @@ void LCD_ILI9486_Init(void) { lcdWriteData(0x1D); lcdWriteData(0x00); + lcdWriteCommand(0x36); + lcdWriteData(0x38); + + lcdWriteCommand(0x21); lcdWriteCommand(0x11); delay_ms(120); @@ -992,7 +1000,8 @@ void LCD_ST7796S_Init(void) { lcdWriteData( 0x96 ); lcdWriteCommand( 0x36 ); - lcdWriteData( 0x88 ); +// lcdWriteData( 0x88 ); + lcdWriteData( 0xA8 ); lcdWriteCommand( 0x3A ); lcdWriteData( 0x66 ); @@ -1171,13 +1180,13 @@ void LCD_Init_LTDC() { /* Configure accumulated vertical back porch */ LTDC_InitStruct.LTDC_AccumulatedVBP = VBP; /* Configure accumulated active width */ - LTDC_InitStruct.LTDC_AccumulatedActiveW = LCD_W + HBP; + LTDC_InitStruct.LTDC_AccumulatedActiveW = LCD_PHYS_W + HBP; /* Configure accumulated active height */ - LTDC_InitStruct.LTDC_AccumulatedActiveH = LCD_H + VBP; + LTDC_InitStruct.LTDC_AccumulatedActiveH = LCD_PHYS_H + VBP; /* Configure total width */ - LTDC_InitStruct.LTDC_TotalWidth = LCD_W + HBP + HFP; + LTDC_InitStruct.LTDC_TotalWidth = LCD_PHYS_W + HBP + HFP; /* Configure total height */ - LTDC_InitStruct.LTDC_TotalHeigh = LCD_H + VBP + VFP; + LTDC_InitStruct.LTDC_TotalHeigh = LCD_PHYS_H + VBP + VFP; LTDC_Init(<DC_InitStruct); @@ -1197,7 +1206,7 @@ void LCD_Init_LTDC() { 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); - LTDC_LIPConfig(LCD_H); + LTDC_LIPConfig(LCD_PHYS_H); #if 0 DMA2D_ITConfig(DMA2D_CR_TCIE, ENABLE); @@ -1220,9 +1229,9 @@ void LCD_LayerInit() { 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_W + HBP); + LTDC_Layer_InitStruct.LTDC_HorizontalStop = (LCD_PHYS_W + HBP); LTDC_Layer_InitStruct.LTDC_VerticalStart = VBP + 1; - LTDC_Layer_InitStruct.LTDC_VerticalStop = (LCD_H + VBP); + LTDC_Layer_InitStruct.LTDC_VerticalStop = (LCD_PHYS_H + VBP); /* Pixel Format configuration*/ LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565; @@ -1243,14 +1252,14 @@ void LCD_LayerInit() { Active high width = LCD_W number of bytes per pixel = 2 (pixel_format : RGB565) */ - LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((LCD_W * 2) + 3); + 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_W * 2); + LTDC_Layer_InitStruct.LTDC_CFBPitch = (LCD_PHYS_W * 2); /* Configure the number of lines */ - LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_H; + LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_PHYS_H; /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_FIRST_FRAME_BUFFER; @@ -1645,8 +1654,50 @@ static void lcdSwitchLayers() // TODO: replace through some smarter mechanism without busy wait while(_frameBufferAddressReloaded == 0); } +static bool once = false; +void newLcdRefresh(uint16_t *buffer, const rect_t& copy_area) +{ +#if 0 +#if defined(LCD_VERTICAL_INVERT) + auto total = copy_area.w * copy_area.h; + auto src = buffer + total - 1; +#else + auto src = buffer; +#endif + auto dst = (uint16_t*)(LCD_SCRATCH_FRAME_BUFFER) + copy_area.y * LCD_PHYS_W + copy_area.x; + + auto y2 = copy_area.y + copy_area.h; + for (auto line = copy_area.y; line < y2; line++) { + + auto line_end = dst + copy_area.w; + while (dst != line_end) { +#if defined(LCD_VERTICAL_INVERT) + *(dst++) = *(src--); +#else + *(dst++) = *(src++); +#endif + } + + dst += LCD_W - copy_area.w; + } +#endif + DMACopyBitmap((uint16_t*)LCD_BACKUP_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, copy_area.x, copy_area.y, buffer, copy_area.w, copy_area.h, 0, 0, copy_area.w, copy_area.h); + + if(!once) + { + LTDC_Layer1->CFBAR = (intptr_t)LCD_BACKUP_FRAME_BUFFER; + _frameBufferAddressReloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + while(_frameBufferAddressReloaded == 0); + once=true; + } +// LTDC_Layer1->CFBAR = (intptr_t)buffer; +// _frameBufferAddressReloaded = 0; +// LTDC->SRCR = LTDC_SRCR_VBR; +// while(_frameBufferAddressReloaded == 0); +} void lcdRefresh() { - lcdSwitchLayers(); +// lcdSwitchLayers(); } diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index 7a8b8279633..543a0da5330 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -349,3 +349,9 @@ bool touchPanelEventOccured() { return touchEventOccured; } + +TouchState getInternalTouchState() +{ + return internalTouchState; +} + From 9a471838bae5abe3ea48d0c9661f8a788f9bbb28 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 13 Feb 2022 14:13:31 +0100 Subject: [PATCH 52/99] fix PL18 display orientation --- radio/src/targets/pl18/board.h | 6 ++-- radio/src/targets/pl18/lcd_driver.cpp | 42 +++------------------------ 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index c3e1b796725..e4833b72d2b 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -417,8 +417,10 @@ uint32_t pwrPressedDuration();; #define AUX_SERIAL_POWER_OFF() // LCD driver -#define LCD_W 320 /* TODO! should be 480 */ -#define LCD_H 480 /* TODO! should be 320 */ +#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(); diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 309a3ed5d37..356ce913d31 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -21,9 +21,6 @@ #include "opentx.h" -#define LCD_PHYS_H LCD_H -#define LCD_PHYS_W LCD_W - #define LCD_FIRST_LAYER 0 #define LCD_SECOND_LAYER 1 @@ -688,7 +685,7 @@ void LCD_ILI9486_Init(void) { lcdWriteData(0x00); lcdWriteData(0x08); lcdWriteCommand(0x36); - lcdWriteData(0xF8); + lcdWriteData(0x18); lcdWriteCommand(0x3a); lcdWriteData(0x65); lcdWriteCommand(0xc0); @@ -747,10 +744,6 @@ void LCD_ILI9486_Init(void) { lcdWriteData(0x1D); lcdWriteData(0x00); - lcdWriteCommand(0x36); - lcdWriteData(0x38); - - lcdWriteCommand(0x21); lcdWriteCommand(0x11); delay_ms(120); @@ -1000,8 +993,7 @@ void LCD_ST7796S_Init(void) { lcdWriteData( 0x96 ); lcdWriteCommand( 0x36 ); -// lcdWriteData( 0x88 ); - lcdWriteData( 0xA8 ); + lcdWriteData( 0x88 ); lcdWriteCommand( 0x3A ); lcdWriteData( 0x66 ); @@ -1654,33 +1646,11 @@ static void lcdSwitchLayers() // TODO: replace through some smarter mechanism without busy wait while(_frameBufferAddressReloaded == 0); } + static bool once = false; + void newLcdRefresh(uint16_t *buffer, const rect_t& copy_area) { -#if 0 -#if defined(LCD_VERTICAL_INVERT) - auto total = copy_area.w * copy_area.h; - auto src = buffer + total - 1; -#else - auto src = buffer; -#endif - auto dst = (uint16_t*)(LCD_SCRATCH_FRAME_BUFFER) + copy_area.y * LCD_PHYS_W + copy_area.x; - - auto y2 = copy_area.y + copy_area.h; - for (auto line = copy_area.y; line < y2; line++) { - - auto line_end = dst + copy_area.w; - while (dst != line_end) { -#if defined(LCD_VERTICAL_INVERT) - *(dst++) = *(src--); -#else - *(dst++) = *(src++); -#endif - } - - dst += LCD_W - copy_area.w; - } -#endif DMACopyBitmap((uint16_t*)LCD_BACKUP_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, copy_area.x, copy_area.y, buffer, copy_area.w, copy_area.h, 0, 0, copy_area.w, copy_area.h); if(!once) @@ -1691,10 +1661,6 @@ void newLcdRefresh(uint16_t *buffer, const rect_t& copy_area) while(_frameBufferAddressReloaded == 0); once=true; } -// LTDC_Layer1->CFBAR = (intptr_t)buffer; -// _frameBufferAddressReloaded = 0; -// LTDC->SRCR = LTDC_SRCR_VBR; -// while(_frameBufferAddressReloaded == 0); } void lcdRefresh() From 01264453288df6d8ea51c48a228932926b3313cf Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 13 Feb 2022 19:32:59 +0100 Subject: [PATCH 53/99] fix touch calculcation --- radio/src/targets/pl18/tp_cst340.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index 543a0da5330..91e94786b02 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -294,8 +294,8 @@ struct TouchState touchPanelRead() if( touchData[0] == 0x06 ) { - x = LCD_W - ((touchData[1]<<4) + ((touchData[3]>>4)&0x0f)); - y = LCD_H - ((touchData[2]<<4) + ((touchData[3])&0x0f)); + x = LCD_PHYS_W - ((touchData[1]<<4) + ((touchData[3]>>4)&0x0f)); + y = LCD_PHYS_H - ((touchData[2]<<4) + ((touchData[3])&0x0f)); if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { internalTouchState.event = TE_DOWN; internalTouchState.startX = internalTouchState.x = x; From 79a9b77079800259c1ae87870518a38992f363c9 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Sat, 19 Feb 2022 21:16:58 +0100 Subject: [PATCH 54/99] charging display adapted to horizontal screen --- radio/src/targets/pl18/battery_driver.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index f651ffe6a6f..e955f6b6048 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -24,7 +24,7 @@ #define BATTERY_W 140 #define BATTERY_H 320 -#define BATTERY_TOP ((LCD_H - BATTERY_H)/2) +#define BATTERY_TOP ((LCD_PHYS_H - BATTERY_H)/2) #define BATTERY_CONNECTOR_W 32 #define BATTERY_CONNECTOR_H 10 #define BATTERY_BORDER 4 @@ -146,13 +146,13 @@ void drawChargingInfo(uint16_t chargeState){ } BACKLIGHT_ENABLE(); - lcd->drawSizedText(LCD_W/2, LCD_H-50, text, strlen(text), CENTERED|COLOR_THEME_PRIMARY2); + lcd->drawSizedText(LCD_PHYS_W/2, LCD_PHYS_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_PHYS_W - BATTERY_W)/2, BATTERY_TOP, BATTERY_W, BATTERY_H, SOLID, COLOR_THEME_PRIMARY2); + lcd->drawFilledRect((LCD_PHYS_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); + lcd->drawFilledRect((LCD_PHYS_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER + BATTERY_H_INNER - h , BATTERY_W_INNER, h, SOLID, color); + lcd->drawFilledRect((LCD_PHYS_W - BATTERY_CONNECTOR_W)/2, BATTERY_TOP-BATTERY_CONNECTOR_H , BATTERY_CONNECTOR_W, BATTERY_CONNECTOR_H, SOLID, COLOR_THEME_PRIMARY2); } #define CHARGE_INFO_DURATION 500 //this method should be called by timer interrupt or by GPIO interrupt From 61e6a046a93cd1b27872da052e3d6675e5843c2e Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Mon, 21 Feb 2022 19:35:35 +0100 Subject: [PATCH 55/99] fix PL18 compilation after rebase untested --- radio/src/gui/colorlcd/lcd.cpp | 13 +- radio/src/targets/pl18/backlight_driver.cpp | 3 +- radio/src/targets/pl18/battery_driver.cpp | 5 - radio/src/targets/pl18/board.cpp | 12 +- radio/src/targets/pl18/board.h | 1 + .../src/targets/pl18/bootloader/boot_menu.cpp | 224 +++++++------- radio/src/targets/pl18/diskio.cpp | 10 +- radio/src/targets/pl18/extmodule_helper.cpp | 2 +- radio/src/targets/pl18/haptic_driver.cpp | 2 +- radio/src/targets/pl18/keys_driver.cpp | 12 +- radio/src/targets/pl18/lcd_driver.cpp | 280 ++++-------------- 11 files changed, 208 insertions(+), 356 deletions(-) diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index ef656cf4bcb..a733c74809a 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,8 +159,8 @@ 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.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 = 0; #if !defined(LCD_VERTICAL_INVERT) @@ -169,6 +169,11 @@ void lcdInitDisplayDriver() disp_drv.direct_mode = 0; #endif +#if defined (PCBPL18) + disp_drv.rotated = LV_DISP_ROT_90; + disp_drv.sw_rotate = 1; +#endif + // Register the driver and save the created display object disp = lv_disp_drv_register(&disp_drv); diff --git a/radio/src/targets/pl18/backlight_driver.cpp b/radio/src/targets/pl18/backlight_driver.cpp index 2308939f93a..7a324fd43d6 100644 --- a/radio/src/targets/pl18/backlight_driver.cpp +++ b/radio/src/targets/pl18/backlight_driver.cpp @@ -19,7 +19,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "opentx_types.h" +#include "board.h" void backlightInit() { diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index e955f6b6048..0118da0b803 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -211,8 +211,3 @@ void handle_battery_charge(uint32_t last_press_time) #endif } -uint16_t getBatteryVoltage() -{ - int32_t instant_vbat = anaIn(TX_VOLTAGE); // using filtered ADC value on purpose - return (uint16_t)((instant_vbat * (1000 + g_eeGeneral.txVoltageCalibration)) / 962); // = 2048*1000/(100*(3.3*(120+22)/22)) -} diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 54f6d81c498..c434822d9bd 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -19,11 +19,21 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "board.h" +#include "globals.h" +#include "sdcard.h" #include "touch.h" +#include "debug.h" #include "hal/adc_driver.h" #include "../common/arm/stm32/stm32_hal_adc.h" +#include "../../timers.h" +#include "../../debounce.h" + +#include "bitmapbuffer.h" +#include "colors.h" + +#include #if defined(__cplusplus) && !defined(SIMU) extern "C" { diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index e4833b72d2b..6f5076d624a 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -350,6 +350,7 @@ 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 { diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp index cdfebdb1999..f5e8b623a3f 100644 --- a/radio/src/targets/pl18/bootloader/boot_menu.cpp +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -19,10 +19,15 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "board.h" +#include "fw_version.h" +#include "lcd.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 @@ -43,26 +48,10 @@ const uint8_t __bmp_background_rle[] { }; RLEBitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background_rle); -const uint8_t LBM_FLASH[] = { -#include "icon_flash.lbm" -}; - -const uint8_t LBM_EXIT[] = { -#include "icon_exit.lbm" -}; - -const uint8_t LBM_SD[] = { -#include "icon_sd.lbm" -}; - const uint8_t LBM_FILE[] = { #include "icon_file.lbm" }; -const uint8_t LBM_ERROR[] = { -#include "icon_error.lbm" -}; - const uint8_t LBM_OK[] = { #include "icon_ok.lbm" }; @@ -73,14 +62,12 @@ const uint8_t LBM_OK[] = { #define BL_FOREGROUND COLOR2FLAGS(WHITE) #define BL_SELECTED COLOR2FLAGS(RGB(11, 65, 244)) // deep blue +extern BitmapBuffer * lcd; + void bootloaderInitScreen() { backlightEnable(BACKLIGHT_LEVEL_MAX); - - // TODO: load/decompress bitmaps - extern void loadFonts(); - loadFonts(); - + lcdInitDisplayDriver(); } static void bootloaderDrawTitle(const char* text) @@ -121,7 +108,7 @@ static void bootloaderDrawBackground() void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { - // clear screen + lcdInitDirectDrawing(); bootloaderDrawBackground(); int center = LCD_W/2; @@ -129,118 +116,117 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) bootloaderDrawTitle(BOOTLOADER_TITLE); - lcd->drawBitmapPattern(50, 72, LBM_FLASH, BL_FOREGROUND); - lcd->drawText(84, 75, "Write Firmware", BL_FOREGROUND); + lcd->drawText(62, 75, LV_SYMBOL_CHARGE, BL_FOREGROUND); + coord_t pos = lcd->drawText(84, 75, "Write Firmware", BL_FOREGROUND); + pos += 8; - lcd->drawBitmapPattern(50, 107, LBM_EXIT, BL_FOREGROUND); + lcd->drawText(60, 110, LV_SYMBOL_NEW_LINE, BL_FOREGROUND); lcd->drawText(84, 110, "Exit", BL_FOREGROUND); - lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, LCD_W - 79 - 28, 26, 2, BL_SELECTED); + pos -= 79; + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); lcd->drawBitmap(center - 55, 165, &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); - } - else if (st == ST_USB) { - lcd->drawBitmap(center - 26, 98, &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("SD>FIRMWARE"); - lcd->drawBitmapPattern(DEFAULT_PADDING, 16, LBM_SD, BL_FOREGROUND); - - 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); + lcd->drawText(center, LCD_H - DOUBLE_PADDING, + "Current Firmware:", CENTERED | BL_FOREGROUND); + lcd->drawText(center, LCD_H - DEFAULT_PADDING, + getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); + } else if (st == ST_USB) { + lcd->drawBitmap(center - 26, 98, &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% } - else if (st == ST_DIR_CHECK) { - if (opt == FR_NO_PATH) { - lcd->drawText(20, MESSAGE_TOP, "Directory is missing", BL_FOREGROUND); - } - else { - lcd->drawText(20, MESSAGE_TOP, "Directory is empty", BL_FOREGROUND); - } - - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); + 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, STR_INVALID_FIRMWARE, BL_FOREGROUND); - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); - } - 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->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_OK, BL_GREEN); - } + } else if (st == ST_FLASH_CHECK) { + bootloaderDrawFilename(str, 0, true); + + if (opt == FC_ERROR) { + lcd->drawText(20, MESSAGE_TOP, + LV_SYMBOL_CLOSE " " STR_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->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP - 10, + LBM_OK, BL_GREEN); } - - bootloaderDrawFooter(); + } - if ( st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { - - lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, LBM_FLASH, BL_FOREGROUND); - - if (st == ST_FILE_LIST) { -#if defined(PCBPL18) - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[TR4 down] to select file", BL_FOREGROUND); -#else - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[R TRIM] to select file", BL_FOREGROUND); -#endif - } - else if (st == ST_FLASH_CHECK && opt == FC_OK) { -#if defined(PCBPL18) - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [TR4 down] long to flash", BL_FOREGROUND); -#else - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to flash", BL_FOREGROUND); -#endif - - } - 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); - } + 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->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING - 2, LBM_EXIT, BL_FOREGROUND); -#if defined(PCBPL18) - lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[TR4 up] to exit", BL_FOREGROUND); -#else - lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit", BL_FOREGROUND); -#endif - } + if (st != ST_FLASHING) { + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, + LV_SYMBOL_NEW_LINE " [L TRIM] to exit", BL_FOREGROUND); + } } } diff --git a/radio/src/targets/pl18/diskio.cpp b/radio/src/targets/pl18/diskio.cpp index c44c85a974c..8de94e47895 100644 --- a/radio/src/targets/pl18/diskio.cpp +++ b/radio/src/targets/pl18/diskio.cpp @@ -26,10 +26,11 @@ /*-----------------------------------------------------------------------*/ #include "diskio.h" -#include -#include "opentx.h" +#include "debug.h" #include "targets/common/arm/stm32/sdio_sd.h" +#include + // TODO share this with Horus (and perhaps other STM32) /*-----------------------------------------------------------------------*/ @@ -325,6 +326,11 @@ void sdInit(void) } } #else + +#include "audio.h" +#include "sdcard.h" +#include "disk_cache.h" + void sdInit() { TRACE("sdInit"); diff --git a/radio/src/targets/pl18/extmodule_helper.cpp b/radio/src/targets/pl18/extmodule_helper.cpp index 7d9b2df0ec3..006d316b5b1 100644 --- a/radio/src/targets/pl18/extmodule_helper.cpp +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -19,7 +19,7 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "board.h" void EXTERNAL_MODULE_ON() { diff --git a/radio/src/targets/pl18/haptic_driver.cpp b/radio/src/targets/pl18/haptic_driver.cpp index b4a45357f90..d8fc8f809da 100644 --- a/radio/src/targets/pl18/haptic_driver.cpp +++ b/radio/src/targets/pl18/haptic_driver.cpp @@ -19,7 +19,7 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "board.h" void hapticOff(void) { diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 5746fb11042..759c027a7f5 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -19,9 +19,16 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#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, @@ -230,11 +237,12 @@ void readKeysAndTrims() 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) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 356ce913d31..14bd118da90 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -19,23 +19,26 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#include "opentx_types.h" +#include "libopenui_config.h" +#include "lcd.h" -#define LCD_FIRST_LAYER 0 -#define LCD_SECOND_LAYER 1 +static pixel_t LCD_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; -uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -uint8_t currentLayer = LCD_FIRST_LAYER; +static uint16_t* next_frame_buffer; +static rect_t next_frame_area; -BitmapBuffer lcdBackup(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_BACKUP_FRAME_BUFFER); -BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); -BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); +static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, + const rect_t ©_area) +{ + (void)disp_drv; -BitmapBuffer * lcdFront = &lcdBuffer1; -BitmapBuffer * lcd = &lcdBuffer2; + next_frame_buffer = buffer; + next_frame_area = copy_area; + + // Enable line IRQ + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); +} lcdSpiInitFucPtr lcdInitFunction; lcdSpiInitFucPtr lcdOffFunction; @@ -1182,22 +1185,15 @@ void LCD_Init_LTDC() { LTDC_Init(<DC_InitStruct); - //Configure IRQ - // LTDC_ITConfig(LTDC_IER_RRIE, ENABLE); - // 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); - - 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; /* Not used as 4 bits are used for the pre-emption priority. */; + 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 @@ -1254,70 +1250,42 @@ void LCD_LayerInit() { LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_PHYS_H; /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ - LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_FIRST_FRAME_BUFFER; + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_FRAME_BUFFER; /* Initialize LTDC layer 1 */ LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); -#if defined(LTDC_DOUBLELAYER) - /* Configure Layer 2 */ - LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA; - LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA; - - /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ - LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_SECOND_FRAME_BUFFER; - - /* Initialize LTDC layer 2 */ - LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct); -#endif /* LTDC configuration reload */ LTDC_ReloadConfig(LTDC_IMReload); LTDC_LayerCmd(LTDC_Layer1, ENABLE); LTDC_LayerAlpha(LTDC_Layer1, 255); -#if defined(LTDC_DOUBLELAYER) - LTDC_LayerCmd(LTDC_Layer2, ENABLE); - LTDC_LayerAlpha(LTDC_Layer2, 0); -#endif - LTDC_ReloadConfig(LTDC_IMReload); + /* dithering activation */ LTDC_DitherCmd(ENABLE); } -extern void loadFonts(); const char* boardLcdType = ""; -void LCD_SetLayer(uint32_t layer) +void lcdInit(void) { - if (layer == LCD_FIRST_LAYER) { - lcdFront = &lcdBuffer1; - lcd = &lcdBuffer2; - } - else { - lcdFront = &lcdBuffer2; - lcd = &lcdBuffer1; - } - currentLayer = layer; -} - -void lcdInit(void) { // Clear buffers first - memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); - memset(LCD_SECOND_FRAME_BUFFER, 0, sizeof(LCD_SECOND_FRAME_BUFFER)); + memset(LCD_FRAME_BUFFER, 0, sizeof(LCD_FRAME_BUFFER)); - loadFonts(); /* Configure the LCD SPI+RESET pins */ lcdSpiConfig(); + // TODO: init lVGL + /* Reset the LCD --------------------------------------------------------*/ lcdReset(); /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); - /* Send LCD initializaiton commands */ + /* Send LCD initialization commands */ if (LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { TRACE("LCD INIT: ILI9481"); boardLcdType = "ILI9481"; @@ -1359,38 +1327,21 @@ void lcdInit(void) { LCD_LayerInit(); LTDC_Cmd(ENABLE); LTDC_ReloadConfig(LTDC_IMReload); + + lcdSetFlushCb(startLcdRefresh); } -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 DMAWait() { - DMA2D_DeInit(); - - DMA2D_InitTypeDef DMA2D_InitStruct; - DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M; - DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; - DMA2D_InitStruct.DMA2D_OutputGreen = (0x07E0 & color) >> 5; - DMA2D_InitStruct.DMA2D_OutputBlue = 0x001F & color; - DMA2D_InitStruct.DMA2D_OutputRed = (0xF800 & color) >> 11; - DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F; - DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest) + 2*(destw*y + x); - DMA2D_InitStruct.DMA2D_OutputOffset = (destw - w); - DMA2D_InitStruct.DMA2D_NumberOfLine = h; - DMA2D_InitStruct.DMA2D_PixelPerLine = w; - DMA2D_Init(&DMA2D_InitStruct); - - /* Start Transfer */ - DMA2D_StartTransfer(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); + 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) + +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; @@ -1417,17 +1368,14 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, /* Start Transfer */ DMA2D_StartTransfer(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } -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 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; @@ -1463,18 +1411,15 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ /* Start Transfer */ DMA2D_StartTransfer(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } // 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) +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; @@ -1514,17 +1459,11 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } 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; @@ -1551,119 +1490,20 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); -} - -void lcdCopy(void * dest, void * src) -{ - 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); - 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 = LCD_H; - DMA2D_InitStruct.DMA2D_PixelPerLine = LCD_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_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(); - - /* Check configuration error */ - if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) - return; // Exit if configuration or transfer error - - /* Wait for CTC Flag activation */ - while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); -} - -void lcdStoreBackupBuffer() -{ - lcdCopy(LCD_BACKUP_FRAME_BUFFER, lcd->getData()); -} - -int lcdRestoreBackupBuffer() -{ - lcdCopy(lcd->getData(), LCD_BACKUP_FRAME_BUFFER); - return 1; } -uint16_t* lcdGetBackupBuffer() -{ - return (uint16_t*)LCD_BACKUP_FRAME_BUFFER; -} - -uint16_t* lcdGetScratchBuffer() -{ - return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; -} - -//static volatile uint8_t refreshRequested = 0; -static volatile uint8_t _frameBufferAddressReloaded = 0; - extern "C" void LTDC_IRQHandler(void) { - LTDC_ClearFlag(LTDC_ICR_CLIF); - _frameBufferAddressReloaded = 1; -} - -static void lcdSwitchLayers() -{ - if (currentLayer == LCD_FIRST_LAYER) { - LTDC_Layer1->CFBAR = (uint32_t)LCD_SECOND_FRAME_BUFFER; - LCD_SetLayer(LCD_SECOND_LAYER); - } - else { - LTDC_Layer1->CFBAR = (uint32_t)LCD_FIRST_FRAME_BUFFER; - LCD_SetLayer(LCD_FIRST_LAYER); - } - - // reload shadow registers on vertical blank - _frameBufferAddressReloaded = 0; - LTDC->SRCR = LTDC_SRCR_VBR; - - // wait for reload - // TODO: replace through some smarter mechanism without busy wait - while(_frameBufferAddressReloaded == 0); -} - -static bool once = false; - -void newLcdRefresh(uint16_t *buffer, const rect_t& copy_area) -{ - DMACopyBitmap((uint16_t*)LCD_BACKUP_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, copy_area.x, copy_area.y, buffer, copy_area.w, copy_area.h, 0, 0, copy_area.w, copy_area.h); - - if(!once) - { - LTDC_Layer1->CFBAR = (intptr_t)LCD_BACKUP_FRAME_BUFFER; - _frameBufferAddressReloaded = 0; - LTDC->SRCR = LTDC_SRCR_VBR; - while(_frameBufferAddressReloaded == 0); - once=true; - } -} - -void lcdRefresh() -{ -// lcdSwitchLayers(); + // clear interrupt flag + LTDC->ICR = LTDC_ICR_CLIF; + LTDC_ITConfig(LTDC_IER_LIE, DISABLE); + + // TODO: use modified version with "Transfer Complete" IRQ + DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, + next_frame_area.x, next_frame_area.y, next_frame_buffer, + next_frame_area.w, next_frame_area.h, 0, 0, + next_frame_area.w, next_frame_area.h); + + // TODO: call on "Transfer Complete" IRQ + lcdFlushed(); } From c7c35b43b8519bb1b3a3b4435808147ae2116dce Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Tue, 22 Feb 2022 10:58:05 +0100 Subject: [PATCH 56/99] fix simu crash for PL18 simu screen needs to be rotated --- radio/src/simu.cpp | 20 ++++++++++---------- radio/src/targets/simu/simulcd.cpp | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 249e267ab61..a1ebbdb2df3 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -37,14 +37,14 @@ #include "hal/adc_driver.h" #include "hal/rotary_encoder.h" -#if LCD_W > 212 +#if LCD_PHYS_W > 212 #define LCD_ZOOM 1 #else #define LCD_ZOOM 2 #endif -#define W2 LCD_W*LCD_ZOOM -#define H2 LCD_H*LCD_ZOOM +#define W2 LCD_PHYS_W*LCD_ZOOM +#define H2 LCD_PHYS_H*LCD_ZOOM #if defined(HARDWARE_TOUCH) #define TAP_TIME 25 @@ -195,7 +195,7 @@ void OpenTxSim::createBitmap(int index, uint16_t *data, int x, int y, int w, int for (int i=0; i>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f); snapshot.setPixel(i, j, color); } @@ -520,7 +520,7 @@ long OpenTxSim::onTimeout(FXObject*, FXSelector, void*) return 0; } -#if LCD_W >= 212 +#if LCD_PHYS_W >= 212 #define BL_COLOR FXRGB(47, 123, 227) #else #define BL_COLOR FXRGB(150, 200, 152) @@ -559,10 +559,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 +570,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_PHYS_W + x]; uint8_t z = (y & 1) ? (*p >> 4) : (*p & 0x0F); if (z) { FXColor color; @@ -583,7 +583,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_PHYS_W] & (1<<(y%8))) { setPixel(x, y, onColor); } #endif diff --git a/radio/src/targets/simu/simulcd.cpp b/radio/src/targets/simu/simulcd.cpp index 0fc69390ce5..dc1320ac949 100644 --- a/radio/src/targets/simu/simulcd.cpp +++ b/radio/src/targets/simu/simulcd.cpp @@ -61,8 +61,8 @@ 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; @@ -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 dst = simuLcdBuf + 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; } } @@ -142,7 +142,7 @@ static void simuRefreshLcd(lv_disp_drv_t * disp_drv, uint16_t *buffer, const rec { #if !defined(LCD_VERTICAL_INVERT) // rename into "Use direct mode" ??? // Direct mode: driver flush is called on final LVGL flush - + dst += LCD_PHYS_W - copy_area.w; // simply set LVGL's buffer as our current frame buffer simuLcdBuf = buffer; From d7228db326bef60177615c96292b430151a86478 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Tue, 22 Feb 2022 13:04:27 +0100 Subject: [PATCH 57/99] remove old truetype fonts reference --- radio/src/targets/pl18/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index c010741c2d5..988571b295b 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -68,7 +68,7 @@ set(FONTS_TARGET x12_fonts) set(LCD_DRIVER lcd_driver.cpp) set(LUA_EXPORT lua_export_pl18) set(TOUCH_DRIVER tp_cst340.cpp ../horus/i2c_driver.cpp) -set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET} truetype_fonts) +set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) set(FIRMWARE_DEPENDENCIES datacopy) set(HARDWARE_TOUCH ON) From cf5e49289dcdc159ad7321bad6d6cd3d8936a098 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Thu, 24 Feb 2022 13:20:29 +0100 Subject: [PATCH 58/99] work around for garbled screen usb charging display fix --- radio/src/gui/colorlcd/lcd.cpp | 4 +- radio/src/targets/pl18/battery_driver.cpp | 18 ++++---- radio/src/targets/pl18/lcd_driver.cpp | 51 ++++++++++++++++++++--- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index a733c74809a..e407c6f75ec 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -161,7 +161,7 @@ void lcdInitDisplayDriver() 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 = 0; + disp_drv.full_refresh = 1; #if !defined(LCD_VERTICAL_INVERT) disp_drv.direct_mode = 1; @@ -171,7 +171,7 @@ void lcdInitDisplayDriver() #if defined (PCBPL18) disp_drv.rotated = LV_DISP_ROT_90; - disp_drv.sw_rotate = 1; + #endif // Register the driver and save the created display object diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 0118da0b803..0c56a9c3b03 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -23,8 +23,8 @@ #define __BATTERY_DRIVER_C__ #define BATTERY_W 140 -#define BATTERY_H 320 -#define BATTERY_TOP ((LCD_PHYS_H - BATTERY_H)/2) +#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 @@ -146,13 +146,13 @@ void drawChargingInfo(uint16_t chargeState){ } BACKLIGHT_ENABLE(); - lcd->drawSizedText(LCD_PHYS_W/2, LCD_PHYS_H-50, text, strlen(text), CENTERED|COLOR_THEME_PRIMARY2); + lcd->drawSizedText(LCD_W/2, LCD_H-50, text, strlen(text), CENTERED|COLOR_THEME_PRIMARY2); - lcd->drawFilledRect((LCD_PHYS_W - BATTERY_W)/2, BATTERY_TOP, BATTERY_W, BATTERY_H, SOLID, COLOR_THEME_PRIMARY2); - lcd->drawFilledRect((LCD_PHYS_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER, BATTERY_W_INNER, BATTERY_H_INNER, SOLID, COLOR_THEME_PRIMARY1); + 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_PHYS_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER + BATTERY_H_INNER - h , BATTERY_W_INNER, h, SOLID, color); - lcd->drawFilledRect((LCD_PHYS_W - BATTERY_CONNECTOR_W)/2, BATTERY_TOP-BATTERY_CONNECTOR_H , BATTERY_CONNECTOR_W, BATTERY_CONNECTOR_H, SOLID, COLOR_THEME_PRIMARY2); + lcd->drawFilledRect((LCD_W - BATTERY_W_INNER)/2, BATTERY_TOP_INNER + BATTERY_H_INNER - h , BATTERY_W_INNER, h, SOLID, color); + lcd->drawFilledRect((LCD_W - BATTERY_CONNECTOR_W)/2, BATTERY_TOP-BATTERY_CONNECTOR_H , BATTERY_CONNECTOR_W, BATTERY_CONNECTOR_H, SOLID, COLOR_THEME_PRIMARY2); } #define CHARGE_INFO_DURATION 500 //this method should be called by timer interrupt or by GPIO interrupt @@ -197,13 +197,15 @@ void handle_battery_charge(uint32_t last_press_time) if(!lcdInited) { backlightInit(); lcdInit(); + lcdInitDisplayDriver(); lcdInited = true; touchPanelInit(); } else { lcdOn(); } - updateTime = get_tmr10ms(); + updateTime = get_tmr10ms(); + lcdInitDirectDrawing(); lcd->clear(); drawChargingInfo(chargeState); lcdRefresh(); diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 14bd118da90..ee1166213d5 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -35,9 +35,32 @@ static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, next_frame_buffer = buffer; next_frame_area = copy_area; - +// DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, +// next_frame_area.x, next_frame_area.y, next_frame_buffer, +// next_frame_area.w, next_frame_area.h, 0, 0, +// next_frame_area.w, next_frame_area.h); +// lcdFlushed(); // Enable line IRQ - LTDC_ITConfig(LTDC_IER_LIE, ENABLE); +//memcpy(LCD_FRAME_BUFFER, buffer, LCD_PHYS_W*LCD_PHYS_H); +// for(int i=0; i< LCD_PHYS_W*LCD_PHYS_H;++i) +// { +// if(i%LCD_PHYS_H==0 || i%(LCD_PHYS_H-1)==0) +// ((uint16_t*)LCD_FRAME_BUFFER)[i]=0xFFFF; +//// ((uint16_t*)LCD_FRAME_BUFFER)[i/2]=i&0xFFFF; +// } + lcdFlushed(); +// LTDC_ITConfig(LTDC_IER_LIE, ENABLE); + LTDC_Layer1->CFBAR = (uint32_t)buffer; + + + // reload shadow registers on vertical blank + //_frameBufferAddressReloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait +// while(_frameBufferAddressReloaded == 0); + } lcdSpiInitFucPtr lcdInitFunction; @@ -996,7 +1019,20 @@ void LCD_ST7796S_Init(void) { lcdWriteData( 0x96 ); lcdWriteCommand( 0x36 ); - lcdWriteData( 0x88 ); + lcdWriteData( 0x28 ); + // lcdWriteData( 0x88 ); + //lcdWriteData( 0xB8 ); + lcdWriteCommand( 0x2A ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0xDF ); + lcdWriteCommand( 0x2B ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x01 ); + lcdWriteData( 0x3F ); + lcdWriteCommand( 0x3A ); lcdWriteData( 0x66 ); @@ -1009,9 +1045,12 @@ void LCD_ST7796S_Init(void) { lcdWriteData( 0x01 ); lcdWriteCommand( 0xB6 ); - lcdWriteData( 0x20 ); - lcdWriteData( 0x02 ); - lcdWriteData( 0x3B ); + // lcdWriteData( 0x20 ); + // lcdWriteData( 0x02 ); + // lcdWriteData( 0x3B ); + lcdWriteData( 0x20 ); + lcdWriteData( 0x02 ); + lcdWriteData( 0x3B ); //SET RGB END lcdWriteCommand( 0xB7); From 3c291942a03db53da0c6e8f5a3da7a331a06a5e3 Mon Sep 17 00:00:00 2001 From: rotorman Date: Thu, 24 Feb 2022 20:41:44 +0100 Subject: [PATCH 59/99] ran the YAML generator on PL18 --- .../storage/yaml/yaml_datastructs_pl18.cpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 0cca87481c0..54e9eb83d47 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -372,6 +372,7 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { }; static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "version", 8 ), + YAML_CUSTOM("board",nullptr,w_board), YAML_PADDING( 16 ), YAML_ARRAY("calib", 48, 9, struct_CalibData, NULL), YAML_PADDING( 16 ), @@ -405,12 +406,10 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "templateSetup", 8 ), YAML_SIGNED( "PPM_Multiplier", 8 ), YAML_SIGNED_CUST( "hapticLength", 8, r_5pos, w_5pos ), - YAML_PADDING( 8 ), - YAML_UNSIGNED( "stickReverse", 8 ), YAML_SIGNED_CUST( "beepLength", 3, r_5pos, w_5pos ), YAML_SIGNED_CUST( "hapticStrength", 3, r_5pos, w_5pos ), YAML_UNSIGNED( "gpsFormat", 1 ), - YAML_UNSIGNED( "unexpectedShutdown", 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 ), @@ -481,7 +480,7 @@ static const struct YamlNode struct_TimerData[] = { }; static const struct YamlNode struct_CurveRef[] = { YAML_UNSIGNED( "type", 8 ), - YAML_SIGNED( "value", 8 ), + YAML_SIGNED_CUST( "value", 8, in_read_weight, in_write_weight ), YAML_END }; static const struct YamlNode struct_MixData[] = { @@ -492,7 +491,7 @@ static const struct YamlNode struct_MixData[] = { YAML_UNSIGNED( "mixWarn", 2 ), YAML_ENUM("mltpx", 2, enum_MixerMultiplex), YAML_PADDING( 1 ), - YAML_SIGNED( "offset", 14 ), + 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), @@ -505,10 +504,10 @@ static const struct YamlNode struct_MixData[] = { }; static const struct YamlNode struct_LimitData[] = { YAML_IDX, - YAML_SIGNED( "min", 11 ), - YAML_SIGNED( "max", 11 ), + 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( "offset", 11 ), + YAML_SIGNED_CUST( "offset", 11, in_read_weight, in_write_weight ), YAML_UNSIGNED( "symetrical", 1 ), YAML_UNSIGNED( "revert", 1 ), YAML_PADDING( 3 ), @@ -527,7 +526,7 @@ static const struct YamlNode struct_ExpoData[] = { YAML_SIGNED_CUST( "weight", 8, in_read_weight, in_write_weight ), YAML_PADDING( 1 ), YAML_STRING("name", 6), - YAML_SIGNED( "offset", 8 ), + YAML_SIGNED_CUST( "offset", 8, in_read_weight, in_write_weight ), YAML_STRUCT("curve", 16, struct_CurveRef, NULL), YAML_END }; @@ -828,7 +827,7 @@ static const struct YamlNode struct_CustomScreenData[] = { YAML_END }; static const struct YamlNode struct_TopBarPersistentData[] = { - YAML_ARRAY("zones", 576, 2, struct_ZonePersistentData, NULL), + YAML_ARRAY("zones", 576, 4, struct_ZonePersistentData, NULL), YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -875,7 +874,7 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("potsWarnPosition", 8, 5, struct_signed_8, NULL), YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), YAML_ARRAY("screenData", 6800, 5, struct_CustomScreenData, NULL), - YAML_STRUCT("topbarData", 1248, struct_TopBarPersistentData, NULL), + YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), YAML_END From 6bebe30d8ba953212a82610b52196100b237f43b Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Thu, 24 Feb 2022 20:46:44 +0100 Subject: [PATCH 60/99] fix PL18 screen flickering --- radio/src/gui/colorlcd/lcd.cpp | 1 - radio/src/targets/pl18/lcd_driver.cpp | 40 ++++++++------------------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index e407c6f75ec..3eeae7f8bfe 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -171,7 +171,6 @@ void lcdInitDisplayDriver() #if defined (PCBPL18) disp_drv.rotated = LV_DISP_ROT_90; - #endif // Register the driver and save the created display object diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index ee1166213d5..36a133f2d82 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -35,32 +35,16 @@ static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, next_frame_buffer = buffer; next_frame_area = copy_area; -// DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, -// next_frame_area.x, next_frame_area.y, next_frame_buffer, -// next_frame_area.w, next_frame_area.h, 0, 0, -// next_frame_area.w, next_frame_area.h); -// lcdFlushed(); - // Enable line IRQ -//memcpy(LCD_FRAME_BUFFER, buffer, LCD_PHYS_W*LCD_PHYS_H); -// for(int i=0; i< LCD_PHYS_W*LCD_PHYS_H;++i) -// { -// if(i%LCD_PHYS_H==0 || i%(LCD_PHYS_H-1)==0) -// ((uint16_t*)LCD_FRAME_BUFFER)[i]=0xFFFF; -//// ((uint16_t*)LCD_FRAME_BUFFER)[i/2]=i&0xFFFF; -// } +#if 1 + DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, + next_frame_area.y, next_frame_area.x, next_frame_buffer, + next_frame_area.h, next_frame_area.w, 0, 0, + next_frame_area.h, next_frame_area.w); lcdFlushed(); -// LTDC_ITConfig(LTDC_IER_LIE, ENABLE); - LTDC_Layer1->CFBAR = (uint32_t)buffer; - - - // reload shadow registers on vertical blank - //_frameBufferAddressReloaded = 0; - LTDC->SRCR = LTDC_SRCR_VBR; - - // wait for reload - // TODO: replace through some smarter mechanism without busy wait -// while(_frameBufferAddressReloaded == 0); - +#else + // Enable line IRQ + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); +#endif } lcdSpiInitFucPtr lcdInitFunction; @@ -1539,9 +1523,9 @@ extern "C" void LTDC_IRQHandler(void) // TODO: use modified version with "Transfer Complete" IRQ DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, - next_frame_area.x, next_frame_area.y, next_frame_buffer, - next_frame_area.w, next_frame_area.h, 0, 0, - next_frame_area.w, next_frame_area.h); + next_frame_area.y, next_frame_area.x, next_frame_buffer, + next_frame_area.h, next_frame_area.w, 0, 0, + next_frame_area.h, next_frame_area.w); // TODO: call on "Transfer Complete" IRQ lcdFlushed(); From 39242e8521899d219d39d8377927bfe73fb11467 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Thu, 24 Feb 2022 21:25:00 +0100 Subject: [PATCH 61/99] workaround for lvgl button problem in radio_calibration From a36540d85460dc513f15bfff76668b557083d871 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sat, 5 Mar 2022 21:41:55 +0100 Subject: [PATCH 62/99] workaround for PL18, disable hardware keys in ghost --- radio/src/gui/colorlcd/radio_ghost_module_config.cpp | 2 +- radio/src/gui/colorlcd/radio_ghost_module_config.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index a328687ef3a..e0e8c95ee65 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -144,7 +144,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; From 4c5131142b5f7c209dd18f96028bcb89ae40780c Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 7 Mar 2022 21:28:26 +0100 Subject: [PATCH 63/99] 480x320 pixel splash screens for PL18/PL18EV --- radio/src/bitmaps/480x272/splash_480x320.png | Bin 0 -> 51772 bytes .../src/bitmaps/480x272/splash_chr_480x320.png | Bin 0 -> 46524 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 radio/src/bitmaps/480x272/splash_480x320.png create mode 100644 radio/src/bitmaps/480x272/splash_chr_480x320.png diff --git a/radio/src/bitmaps/480x272/splash_480x320.png b/radio/src/bitmaps/480x272/splash_480x320.png new file mode 100644 index 0000000000000000000000000000000000000000..43e59de80d46ccbc32677de7ab94ccf9e7344ee9 GIT binary patch literal 51772 zcmZs?V{|9W7cLyzwr$(CZQFJxww;MJv28oQBoo`VC%$ve{h#}Oc|Y{(wR%-|@7nv> zc&chgDJe+8!{ERG0Rh2FONprf0Riv+y@a4Z{yw>=rLY;G#IVZ(%BZyFod5W%rJ zwEb8QwT_keZu|D`w7}Yb0s*l}xU*}V#oW~}X3wP6%-IcePobS|cSVzto-CvOwA^JT zF)WtjO2TsA`%-!Bul|ze*c zFLCcKPVYe(DkO+)Q#wk-TN=Vkzp7+s+J}RL`%t4#e%V`1`_RhF@t|*U?%te@2m8Ti}IaZ za>(HzbqOI_FeL~wq(la`Z$nyW;wlx$QzicmgI;uHJyimIsRKt-#Lj}VBJ2;lhDE^d zZ=e-JyQ8CB^b(f+I}@G1QlvwlBjK=l`fz!Ie{!@+Fk#QR7Gpz`FyU0QF=;Z9#*8~5 zsaz=g?$DP1-|2%XMHtN$W(;pmkMkkLU+|Xl*=4z!Qd3dStiU*Bw&_?`kXw>A6VUvB z!=Hs!y$IAoO(c!Ph`rqk$UPfg=cIrwzM$z-S@y@D<6i69NJ2~h6-Gm84CmYlE}M5W zzOp@1OzZ}Hz6u))VN=yHiUU(8nN+mQWKz%JF!_~Jra|q0Dl|oO=YqJZ>c=sq`csHt zhD@3QghxC5=AnfrDi*bz)v7X>jQA}>24m*>=4gXlq3ILMxl%udiA7;-tFH4s)3((9G@2!KDlIJye}Oml%Ba%Nz(4_|9;cZlxMV((im(bD zpU8B%f&4!+;R>_Et(PeT_k@ja5%X*mxV2<{E2nDt^F>ooQpk%RA1^Jbm*+xFDT6b* z+G!9tO~-AWd7QVr9WIs(baXe`2{e<)4kTAtk22&1XF!I+ZI+9Q{13zK??Q3L!1L-L z&Ohq4bJYT?C#4dy=f~lfC!iN(el1*k@aDiSYra8`A__AQlK_0S@~jKzt>kVpN84IU z_!)@T49$3SSxzE^k40c6Lr)Tf2xObc?ogC>?^OU}$=?^dRBg-zCpk z$p153Cw#%^vB=GItGrIsuIaBC#QdC`ob868NaT=%$1~O*15COfR5L2^!4!t`m!#_^ z>EU>*y4JCv)aErBteP#n_|!|&)A9JMtTPr^F4gLQD{QN;k-%VQOO3QmRIhYC6~)BtZ{6gvwIxcGRo>DqA4$FqPd;b|B0jj$j=dkqbRRn!v7q319sCI5%6B<%R&l!m(W6yax zIUH~Eas~P$Y7}?`lGC~WB07iCf&cq6F~{nH$)0ppOI$aTpnjK~bWCcyuY~b@BEwP? zF=K{S{uYpUuxJ=+?(gmL_D097IXnR4=~HL;;L@{Aj{zr0rGaC!4(hZtk0Y1T(SHW! zFBo-VD(WdPr5Ps!GujYD@yO#9sNJ(Tx|kQfir4v=ozNm`Fk}328Q|qo3(wgQ3S%oS zHA+ItQy{Apnwbv&NM?XON)ky7rex$O9LK@c@V{`{2D|9xY&<~H`1&X(kK#GSBnfUH z^CGK>*x;|(tvfb>fLVp|Ha4%1aKf^_Ny?!F{tSLKl<=!M}(hr;})BV6G)*Kx2-7^qUKregK}3XAdyMqv!-i{eCX9&TDR$&Th; z{L*<9AZat&3@aF_TJh`7d4Ki)+^{tW#}$}cz-9Cb-QlW8Ao3z4u%r*8HIGi`J6ItM2t&4lXbc`3LF>Py?m$}J5M10)z*PPt2?e1q6aIA} zYh&CC;~rq})_s54kd*j}MxF#g+0}x1iBi#z8A|QBclsa91(ZwyE=M`-ot`mB`H~EE z9I`i^`bL4;I`%EtWu*|ZBCb|ha7fo4!xtZb|K6ccGF2PlejBou^{NfF#kHzg1SfA7 z;#+h)O!;Aoox7g^Ibr2n39N;XJ&aZLzm$3f%Dx`@a&9@w}WV=f|`S{jH6ty)dy)<_YTTLRsL@@ zeZlA+ZckZkw$V=g$Tb|~kw@(861T8XU!Aq0Ht|zKQ%V?OlPfdpcWbe(16{KFxK{@3w#> zAV3z$Z)C%!DvP-Z`TQ60Aj=#2=s)n6+s(nlP*4#-$0@>ZT>fA2vH@;GVDW2(7Bf0e z80PmGIZ{$sKG@BFV*GWsm4N1v5*9@=Q^3H%1Vl8bZ(*I9(yR(ic`+6GqH-$Cxx|Qs zbEw>BQDm`6gFrS_d=Bll3{C8J+06BS`uH1L{9laRpuV;n8xhWX6S!c?Va}0CgL$G= zgz?2I3LMn#nRpFJ&liU@+=OFnx1x)JN0fxkJyI&X?qUr6hTu8%TxZyQpD(lW^XpbF zLN>?Wi5!#|JTlf*g!%JL*3aE|X89hbQxpZf6R16R9{NEItIC$FCanr<4?=piz~MmO zVs0KZcZ`lqgnPc;E)1V4HG4je7&-mCOW@VbH>ZY&Lz6ZoLIXG2{C|4CpB5(sT$&QT zA5sduJ{=g=l`Xwy(1&E7>{GGzk!yr_wsm2qK4+gaM!q0-+ZS&8T+|5Op$a-5jwAc| zHnfCS#%goBoz5YOy#CJUeS{3Wg+%24qx9#hHCJQjkCJuL+);XOd2%%7*P+TpO|pYx zxdimv*Uk5rX77EPfBp(-PjQ(}(u^xr0r+e)a|&zekI8Sp4+Kk9SkWXwt#YNgFau|^q8|Uw^>2E zpp5Tor;H4{w;w!kk*{kJ^ZH&GwKv=xqTLk6A9C{=TX&#RtId)W-CEtb==}3LF3WwU ztUCowz{po1LPK@_{0lo^gs;mY&}GdomHF-v#qmXp?6S1Zck80}fVcM`Bj7B9!2d8Q z=iZ*RW)#Zi((yTGyAMOeZUADkpxpynM#}9j`U?2T-qCjhcju49A#pgn;_%y?*;~K# ztL+Qhjeee2Gp68gYli4f^@Jjvtply*Pk!S4@T_sR^b9#KK=24QmhNgHZ8W4kv4xl6=S2c8$2ro@U1k%*&s0O-42@52oXxKtuyg# z7m-L_pAKJ)+m@6@FWs1tN_E+~?TWhWWX7J7&n?dS&xfb6S*T(q+?1+$tL_W^8=u*~ zNQ?M7c5Gd0#$haPsH>_QrKiLiCp7^bbe)Z7rD=s+Z0q~Ha`YO)#VURc@M5v`T-bT_ zyYeJheU)-dpIQ24`S@Yb!r9ub6~9<`szf_#^{gUp8h(suTsI4^`rrW zmcj%+$=kHXR8RLFu>7X=jNZTihJxb^bR)cJ_V^PVq{3{L@XDEz^U8>A;WyYq{D{dm zo@QJSNaF~S)kG-vFMCQL-3UJfjwJ=7_(w*jeqCU9<`o$R6L8ZnHfu|&wb9htpSAkZ zYnbUzn0d;9K87cVH~InZobHZY0yb5{RtpXIm>-e>kCK`inu5;_3HB-0>>f(45Iqb{ zeFb7qn1ZxQx3?hp1ktRJpZXg>=Zs(bjJ;n^b(a0vr~?f7=U-c-ml=J{C$d$pasGEp zU213>t{WYy!02W|W~Z^~dRc@UvDfY;mzC>cXp|xl3};VTK*TFf;SCFt+^Ca{7-r6& z*%XZoo|b@)iTf3_Dn`-P$dBJm$wC}XL_xfaV9kv5K>*x?4z2zK7v3M)OWBLQQ^ovq z@XKmRyr|i|0?vJ0^}BIC)B1G$vxidr+@@?W7~pm|UUZOLqhMri=oTd z&A}=WzVi{ev}}lpR*X!fGi9+IjUf-*^XfIT{2M+8JI@C0JHP9`E}q%n&kcFWl@8vs z@?W!jdcO7$1KS@LZ14(4pS5A)Sz*>uvH)_TNk}}=C0WY?@i&*PyJgW;s=5;-w^n)^ z&7U$(AEC9O)Lr-mz0WOm{#zFYzlgDANqR!sDE-(TBer3XvM{G08W)$hl?i#Bh~J7CxjPY9YJ5G zO=2TfKL+M`8azXE4Ih*ej=uz1%w=!TP9<-rFRL8;LxkyPu4}~}T zjiUTMhvWF3n?Y%dr2frIJqJxsJAc-bvWA|fo+sqa=)~#aD1MC{216Se1QXOa`>>#E zdpcRFuas0OLUHMGwUZk`cDF|cF($1M`5yPntomHWVvz7BQ1}m8&ulU-1RUElXQy&0MJVum0 zc#s75(w7G6ktu_hM36!7DuBwFlY;&;l4dZ*LM;OGa}0gw1GHT(D20Np<}Q>E1A*Q_ zti{1;ZIe+UC)VupSBK2C!KrcCj`8v%h0xy4^`o~ zx-QET_G$kGGmApDWB-233CkD8?wf(7B{Fr+A9S0DYhAM8?npN&>;7U`M96}mx)px< z3@qZ?p@Mk$i!$35>(;!@d#PoF^36vrUC5Po4Qv?{`6LV|WiE6au6A3_!>P>){+D$= zpb_RV^V)jTx{ja{d?hj3a_J9CTfpOEwVs~c=WcG7r(&MptFixg1s5`%bTZ*vA>rH2 zO@Jr$ucbdCang0lB_!AbM&~T(YcgR{aojbjD+EI95BGa~XYPj}z|J`Z239z6@B>M= z4)`nSWkH4p<#mCd7j-?)Iu}}tm-iEo{)o}vwlG9$>1O5to9F2$RYjVhSrInYFRV1X3dLX7X{b(I&esf-eip@OL&YmMlA;@8}kQ+3A;m})7|_OW?Hv zzb9o@;rr3@H)6oaB(<{W&vVPbYs8|0eTWu0ouUhGVB1Y&Xj=6(d0{LEtzJl|-z@B8pSV)rf6jt;ppIE<| zg`|AVz`o8k+Ei{19Tbl%-dsQb{1UB2UU3M}@-q)lXUT|Vs9LPZNHB4k^#<%L|9mga zOY#MKbwbBRkU|xc2aEl1fAr$7E}MqpgGl|~(I{QukS)T3W!BMR*|NX*S*yGsQXeSO z$uFcI+AS`WX*uo_I7u0zTF!HH{P1tDK)iPHf$gq}K+`neYqTfxF3v4CXc`vRp%7x% z=E`S-N9~6a||Pmlq- zkoDpGPnV{KmVLf!g`eYe#g50XBF{wxQ~yo=F7=Uf6}m9-F@@Lo1yPZxZ>?OzQ@~vw?qpF5Gmw8rygd=E#m^O3jEpiQOCavE4O(#>{Y?3x~c<8GhZr zd_TR*{;mD-Z7$(@^dx7^FvgsVx0!A*GSb<6IN&)3;)u&i5MMVBxkSFpgp$D-NFKPN zvu8AF3^;F6#Sy1PAuEMyH&T(#0k5xrITdoH@k7hyry`(wl(#msDaKo8HxC~8oK+Sq zu(H_K+T;CbexugO$lzpQP|s6ogcYQwM3wggLr^@ z3&D&e-f}A#eaYIz^a9_qMbSpb!}9V>*UHZgQrh1VTA<|F<&6mT9i^?o}?><XM5coXzw0N;xZ?Wu;}gXv*++J0^in-5g*JS1+O+c9&QUo$Kvs62byVs)!I^! zD#wBxOdnGfnyNLbW&i+sItI6O{GqRbPb#L);Zrc|D_DYNr#6Ul*hR~-DVL^D%U$Ud<<7hLP<_h9+%#)rH7_zx^;m2)|+4h4zb;=juYno*Dk8UhTUz=ueI)| zl;P8l8jXd>(zG=51+XE1=TxDl{M$y6p8Luwq~C6-{&#KWn{{g~EnEBJuL2Pf>o~!t-jU>e0D{ zsT1Pq2j_Ax^w?Ou+B0^!T-+u4EyXi0STadnoPUM=shVv#xk;zw)yTu=_gVB)5B>*> zK@e9qlHrcE3Jc8}X9{u(Kqjq?jgxj|kfzJ)VPj-6LX?_XgK0ij93{@0Oz9hlx^$JpzW4F;;_Lhj%ky6 zRb1nOOuiHp6*;rvo$n7PCt*$+8mB=u{L*g z>btwU8@oY@W=0K0oWY({$zNpxy$f+VY=#@NL}{sgJ25HUtCimIJllHjte&)2_-Ca$Cqn&tulW%w&e^dPc3(HC zVBRB=ZPRM(7ROCi*Q6xoIvwo}3$@1W-;=VQQ4?nsF`y7E2Ee8o426(!T2jxbj4>E1 zFfeNk&dh>;{$-c>FYv??(?!?W`l5EZq2xTsb50%(u*^4eWY@f0tYAp^iZT>dsMz)Pb&mQ@57C9004juXGW*Sp;A|0`|8ODTS8wJyOz&q76DO1 zBo5lKoE>FI$)Q$y!Oh#cW8-2_X;92 z9n3f+b^gctUBdRm>7v?$9_CwVEMBiZY^;{q<+j|&!#ly{)fVpnSD9e@YZAUitcwZL1w0HfhH7wj+_N=cq-1f*Unu+!1I7v z89G}Jg8upy2N_eL;v$0T1TOEjpM$4qnS^1LUIp#4C_vk3p%Ud^;5M;^+01=4#0n)H zfV<>K9AYp#F3|CN-*ba*QGKYYrm(WIhOc=RW@479n0-i5x*m&ep_7Isu6uO#{B^v} zMiZjp#FkmBI|y`iE>xWOiGRCYm$D(Wwux^JPvOZ7Kane)im(pVC zVh;f#nIkifR?`&1EMR8Q9I=@hv-;=8drrh2#54-AEYrUT6Mc=Gr&Tzcr=P;4X{_Kc zk^<7BhG%JQZZ>QFteBY4>2U~+MP`R9tyS3$t|tLOxpT&BH4)&1=_X#CgcL1AVn@~% zo}VESnq)8zK5PwLgHIjEL6k7%F7$!NnXCQ@g{sK6Zbaj#^i0U3=XIa75DDwZvOwhop&+Kr1<@BGLAlOMVy5ZHEWY#~e z(X;g6<6(0@1>XNvc4NkQ_587Ns)MyG*z(eEeu>WO2^dTDqoI`Lg|yn{akXhy`_ht* zmnIJNPSmzRGG5H^zYfOxwV9ibn`{(7iiq>_Pp)zER1rB6SRaR*VQOIlf={= zTQ^l4!64y#Zq5_O|MCC9Ap7I2$2rvO9@Hz)+x?R;@uCZuLm?>`Hx$SktYtef1^TUkhn5$r= zAVVq9S8F{*3^GmT)WmH6F36lW_%~HIC~vv$a80?pm_08eLx-arj*N%`UuGO@!uYh; zfXGk<*9(wot;5HcJ;8}%=%}ZQ5#=IxtmguQext|n_vLRXCjJ(2$6@=}Z+2?3z&HJ4 zVxua;Jb_OO3y>7A(iIBfYNP%IBpT{ug;8G&h<=jhpolddO`QVKlr1{xe2bl@PSoI0 zsh?|AJsgi<);w514pA*_@xbnr;$8%B5XSQ@Liy*Qm4}Hwh^%xX+J=rzd2j2_X=Y}^ zRgEzlxzB$zV+Q{~SC)T%u;MCIEkJBbX@r~oNf#SR(r59d+b^VVLPlC*rBWYx4wxEr zm`#B2;5@=*5?Zv&0&t!(!IEr!v&g$SC0S;4|i zA{M%z-)zZz>eo=Ii9}o5535R)>A*%2{>Uxv9JEmNN)&_8dxXg^N&xF4;89N6iz5@_|Q zckjG=y(RrRstwow<+5e(5Zew5vH_P=OAyJl=Ol4)dtbfUe1jhAX%hQ-1_Rbf=96wE zZj$isn_DkbIGn?1!aCd$*88-5|AYWzq=RWqoU1)FD17J|r}BfNma=-`VKTRXX?dX% zOvyA#nDSPU$7!$>3K-fnl`g1Wj&!oqw)#W3g%Zm%+m|M(e{OpTfvZ;y29p}w)r@%s zdw^rioqfzjWTbL#^TY`XiiVHD%*6f(6oU^6aVVH7m-mD?0pQO#PsdP!n?HKo6TWM!@_%|=p&A9DkU=kaB>Ys zRm5vGig?&e80ZP8_|y~4yU;u7@VoPCAk6aUL*Ax%o&n>@q{w&PTdUdI{h`-|iXY-g z)kNp1TKHojjIiCfx(Cr`^Y22NXdFLPU7>3r2vVw^sO9up&vKN*#_qM2wV)Y0J+kfc z3hp{H5v^nFKmoq>w(gB#wXDr*AbF^cS9!1n$LvYwk(h}p4n=vtMua+^%Au{o!)Ecc zyC`0{6GlqYdPn?jH;`@g$a3GjL2}##mjl$c0grbXD_XkwL~>-YtdP*Ysb0h*1`%PN zm?vrCewUjUCFF@40}*KkoMc_x2AmXe^_Q2}ul~*e6EZutjs|JyUzFERr+X8{zetsY zfC@wgNrPn;F{LRtU{T^=lIM6ZX`Q44_BEU+C7feFn{fimFTir%-iI(xtD)yJS3DL< z49%dzCGtZpki4NErEZ@RZ;c(e(MQI)E9jZkXs8OknJ&0Z!6}EGN@=wzk7VmV$WM16 zj)SS&Nq6vnoTq~~wBRErfVY#hKdGWBCS*>!d1^-wL%7^qo!Vn&1BzTG;^20Y3?jC3 z-hTFlM*G#D-HvZMHy^e*RoardY1(jY#vDW|2dEln&U0Eww2>@VJ9A}gq=cCMiC6_( zcCcG^>hO-F0w8Fh>JVTS-?11F5zTY(o*1sB4^xW}F{eEr(SZ-?vYINcv zf6p+s2EL}y`dZe6HE<90rv`eOUd(8676kxmXLa%+AZB`B>Jk$B5N_7W;g3jw{l(QVaYQ@@B3{DwAVH*u(jniA(d|2g zJClx^n!A*#$rnX}a>3w4z}O&{w0#prQ=>8!;W}+Q`^ZEry(1aN$oX=5(_vRpzulOL z&*#Vqli)n;9Yw$fCK^37v^_|P5O`j;;B+%E5no03WW*YD^sN|brrA6YzQ8=>o%E+M zx}c>bLh__Ivbu`XoNn=rE!&x|@o+l`hsPd#r-i6B`?SF}J%rltjKM~RV5p+Dn}PMCoFsxp;maqnrVL#)kGXL48&dkYW;;{#5pC=p5ulAaHnb@xmRDBr5iZ z9!Ar3w{TT<5s33WJ?Q2dNd|F%pH@3L1ZO!4+GH~2{A)XRE>Ny& z*?xf7oL(r)cQet6Lj9poP|vn^F_Tl+aB>DNX68O~D2~mX2ZS^ACeElh9opqVXH=1M z(3PEnd$_R{fnRr-I&^_UvM?-hVw%_JU=I9c3;4_MJQvYUyruYM z^}+7Pl$S94Hq$S*g;ps3Ob^zg-(wk8u`tJYzlYE_t`ft3_;KG-q1ED>d=4>$bw2Sy zV)U$udS>t=q4jg=5+lX`y!IX7OYl&)uj$wCBb-~xW4#Xk`KD+7?~f6BLQC{DkX^D@ z>q)~JBoFE6VaO$!oN>en{?uSq9Dpyy;lq(3;k3~$U=2Ry&;uo7Iy#2szqVzQw1!us zzU&H}6DpNr^TQJPxLTMMY}xRUI;8Niw^PrhalAgx=kNZujENyK2`NCkDk|iT$UTFB z6zurI_RnxFZc2E)%hotRaA&ps@aX|jo$|vsswO7&6L9mFATc#FdW_ zFq_)tLaV*qE;K{T@{$le=x-7rZ5adPY!H)~;PPyT)68(B$;zSGqF<>p_>12B^zzup z6*6qdY)~{d6P0}E8Rd->)^srGvUf{v?0wgr?MLgP`ckCbD8G-~p;@cA(Iv(3M^Ux< zy5rNb4-kBI-0VL@UtVH@=7ftfIPMe~M-`lx9gG|qn#}HRIYm<%1S1|1uccJ5)EQv} zuR`E^{1~E`YquQ1#jP}yWIMmO8C}_Cg3i(n^rtY5s7&S0DJVS{xy`>B$JAX zhMX}*38xadk5xb2q+ zGcUHLfR}}|!0G&exmvVjd1|rT3rSr3-QmHhXhkN49AO;C#QAWkmr6h%fRRr7?tcfmEX9Z9*W-uE@V@hXLis&) z6A+G{LkuK_5tjr)_N_weD>JlNSV6i1kfWrT@|;TSolcY=YFK1&CU*V`Vz;t6#|_X) z13IU`Zhl5gd6k?RG#<=p0em=4+F!Y))Ud3Kf!K@}IIr%~?0GZ1&l>nNdta1B`~oAG zB$~S_TOlvME!5vg#*SB-l3-5h6GKnqm3MEdk8Z#z9PGL#2yR*tNAUUSe%AfLP$G6v zT{krH_-LdBYMDZFB1wT}3_YCDIXAaN${zd|E(6l&2d|%TFztp%6j; zSPwJ`75r|ebjcd8(#q8{(MkAk8Kq?D(eUS#VUhZ5Z@(ndUNf6HRHy{H1wfL>$)XfJ zF*nL<0kvVJH}ZhG(>{p35jlZicN)`QxA7}P7%O-eY1wjG7zmVT72Qs6qUcA=Z{uUT z-ZUUd_+nDtWa~1;VEIDeapWFPMmQ%;}K5AZI6*^QrSA_$ZaL>qsxOgPTpC zQ>{S{Sl{jyQi|s#q6J}R>(IHdqZNB=d#OJ+>f-aVT)wGr3-wU>cc$vnyGwYL*amBd zcde?o`lhu957yx$45KoZ5Xz{~e?Et+KDbhdMuL3k z?^R3fDSvAzJLx4%&t#QHco}CGQoD|JvS%9%$>wc6ZyuxmvJ=t?=BW$a^7HBI2d04E zC@e{s#%#T^%4H7BMa|U=nwx{@-$t3vDCJ$IvU}Lk_!`+;`BMD7)JU~=r}&xq(Fp;k z#PY7){z|^-EQ7-U>hNm>OpwqFYGGnexJ~>)hn{KeMQ-dzFZrt5r%aKp?wBFXoETEW zaX8va_>;D6_g?dyf?&f$SOTk_^1~-vSzOHh%OIE|4^drGt>iWhZUR}BAJvCOgDKXv zExaA1MySI95H8w0s=Pbv@PrYYq2jltIIHm4T{-T5vdYrK%f(J!IhUi3l=j33nia0{ zfF%{iMKA=N$o&%(dHc!{-s%;H z2r-jmJ7X&`J&W9ks>M{(HoYR?p#+Q5>~V|N)bFq8F*rEcINj*_Ha2bVY@#?PoX}c^ z7b8`?V=JEo3&4(2JE5@yRCcG%ZB`L^=;G{7G`d>iv+D^B)7i^ru?P$>877#*0H9%J zCgc#bER&r!P=hf$QpibCMAb=xL}WxK{0%<%48wv$WK+{=aE%T`l2?@&AhK52#hvEX zv|F*MA*;xOub=FCprQy0=7N}e6*7rpcGZcA1`;6iT~d=o4>xfhv+v>mS&DQ|fy)+A z+?sYl8Jj`$Ok2pIQrBunX)!mCEntZlqxhk*PA!AORXKwRJS9Sdn<@;78#;8^9wTs2 zFAoMS){@Eb0`1ah<79T*D=w{c@&?#$l)*U@@-zD ziIvO459^wyOV75tEJ>o0@hg!d%d+~`A-UmTc7F5Ma0So}nOAhEAPnomyH^^*$0Vw# zM&VSW;G%4th+4d2_i_I+VD$SLQI{J`r}d}sU8*A{9OuULZ#|83UI5oQ8z=AHkh2<` z5y$Fj$1uc>8$V`V0M2hR!4|I+6B7z2Z&M3qC4~{*!6Xv5R7OD%Da>i&>P>Pk{=?~}lXW_m{W_EN@_ZVxGa#NF%eEVX>)?z<)=VD+<=X)+-*IQWc_nH2W=?Z`=t z_}*&d-Q6sXQaMOMTEUq!QNF=){=%Vdg_p9Y-obKrrHG1I-c3_|D{}|>{uD3IJLOqr z>jGZoR|nq=9Aj)N;TY6b{D9+2_pcGISvo}+QN z*|K~$9BImlzWnc_6gir<-3k2V+a(Ab1LL(s438_5f*8VSdH6>a~AlJBhIDDl^AZv^>O~L7KwvZ3}j9?o(lvr?#}f{P|#WmFA}}WHgyJY z7y6$`42_B+wz|ZGk!>Ea*2*Dv9#r!r_oA}ze>(X&lpyk2lPsB0*&jL4$BHyd_ZsZ+ z{tW7G5>2Wt3ts!=~I>eU!Js!l*8FCE^eAgl*9<&mgas{Mm^#V@dFD3gj z+I_VAjF)C>=~ozz(~LNCuWWC%prgLxBxW*qQchUrMq>^uXVmem+hfrQ1G9cpheP`P z!QmW#H+l<#^;m%8wvuPO17=V_bVskKSzQuo6VC90RSmHfLNhy4z$;T$L2-t{#SOcCl?s3G#dS6Wou?}iX zYt3?E(55K)XUYGV&1iC2#-LnjKN%S4sw%h^9+gOQ^+Wbbr29?c^DpoEhvbAD2c3zv z4c#`d2T%M6S(`Or!s!pAxCF8^eLv9-nrq_`9`B1>hg( zFz4^7RnrIXj=(SKDE&!)P-^sBf=;u=m^PFkkSQ*cU@($xN0=&^rppWRo5Rm?N581o z(cQR+E2=6n&|iMSNWcxZjN)keHRlocwaMq!G%cg^meF$`ygfm-$cYpJAX#9BkKBjyqQN4x*PRlA(^+X$(6QvvR1=03$D9X4HofPHjnmw_%F-*OFTETLVG3nXz;+| zo%He{<@)t4tGmXhMX8NIvnmFW zb=S35%FAXV*+YNEOk0oRadIp5tg~9Lumw2*H!BD}b>%BVHsH8z?4Pa7tzbOB@o3Ki zS&J9#@feP2WLemwLu*vQLK}oPM@-&yT(IE@UTBjc+uuFMYdWyqUK{nxkz3bUWNNBn z8DYDHC42vvXmsj8 zu7@PxZDe6~WSTbAa;?65Ag9@YiGSiKP`BeUe<4f1dTcW(xU35<;_`=tAXJxgOdS83G_>t?KFT4`IfyrJEt?Et%Tm`-M`I-1wioU`6kpLSO8#eKDX$wmHvmP4( z7}*m?icp`8+~!p%a@Hpa{F83wT1dWjO|5QG0uywSm7qcwhppyQ3QMOZgoaKU*x(;t zb=7Q-rP#Swq@-1HtXkAGR^AcR$~2cdVuIFgo!7c3P+@;EV3KSeX~D%E&-!Bk;g#^X z_!$^s>d<(=lbSDH$DY`HOAX$o)@agKR{J-rP`jXM&X`_vngMg}BCeDRmfG?2R1rER z+4PD`LpcQxoxnn&s3t&ry>ep)Eg^Ku!ZP0(FN!tS&AW6FPolLRT;+LidxoqP=e@;;T1DMo?ze7pZ=IKswsrV1!|}{ZTrGGfo9UP} z`~>hWr`1!V-*n4rw%u5wTK{bv)E@USCh*Rd^E)y-*?4J-J6!cKlR!1x%SG)eQAkKE zLbKF~gVZRh<8Sg>D4h_IL@*-O%N(?CmxX-p-WBd++k_3lUMU5zcaJ&zqkkk`=!T-i z8A|EFOS-ht%aJ+9e;bg6_+$6EqTSjyqMZ5~Tarynt&=_nllkM7RT-EFk@&QBMWx#RqzgYFvj`tc1f};pw<%7XP}9lqV&s{+BBf!cP?UaM8nsgm zt)RHf%*)E{xgZX>TvXAsZEm9Hhu61m<+Rz^$^B#E=s+>i?lHfpvxM7H>UB;?5V#NS zi`zr6Ap%j;RcQ)JqMy<{LFieLEYshEnr+WC{(q~Wf7_wa+Qo}DBka*`xbkvxnq`N) z>_q1qM%KdrI|~qTlun=HH0Z=kO!2EMJtO^+F5;F{H42*(^Zc@|AzVj~tb`+0PCWo1 zg}riqcl{aobX&|R$G}lEYEssATw-Nx>7~fdPT>I6>a!s}=pgy>yT)QpBCAg6rsK%_y$O`Sx$ZI=*i;({mtQWh-{k zXn`SQ&zru(z|Ds_+dLjutAPDGa&&uWx~YOD20KO?uYBfix=11@2;IN)^!aNo8^I8S zl^@RHV*Q_|trke2wpxDwza85azuYKi?2wXYUjfV$W(;$%oQ`VFSp~-<)N^Q66FFlm zWhyBddX3-P2k(I|0}+mWBBeA2i9h8J1?7p$%b1$4nlB40OYB+*6Lc`V z%X&L{FJicw4*5TXy#sfqLDw#r4m!4NyJOo%$F}V!=-9Sx+qT`YZCfY(zVpqTnKiTK z7u;2K*RH*5Unm6Y@o-~KrglsHC)94oX`Zhe6PDx!*@osmm|y*!$xmlI^#fQj3fCUNqOgG(r?r3cr1*S3)Ha*?Dhf@Q&W@a z4311%dNKQiN0g9g@i7`1rlU=o2sG`ImIiqt*ww(o)ie1TzlB2$6Z=l@XLtJNk)ySh zk74g^ekTcH&O{Mvkjen-E)4ap_)-_HF#A0a(5JP`$ZvHmuJM>(4T;wDHSo zaJ%iK$)St!2S9;c4I4nW@^!p>=Y8Ni{b6N#L)va#FkBk(gn}Pym`i!zaqObX+1>7N z?K9C~wp0-EP1VaMYMi5?RD+Q0l2YpWQGMMjQhC&e8U%WU{5@( zu}j!E&C8+a;Iza3!{|G+1hPOZ0fa-iZcwCaFEKp-KPBx`~1lUwhh)zV%c&wI@as#WRdcqMBOFA<>= zHlh?&^60ZxQIHrvwa;iB`|~?3wU&)!o9G)VOfz2z1jdC>yIG+$5%d!k8A;KV9#&*O zx?T^S&O13$x0=7(MqU4&yKo7KdOR+q z`8z8akwtH0nPMqTH!SkGkafps*>AneI4U8-t|Z~C3&`WJ5*sMtEz$e*LgV|`o#H<6 z-p}EVvrV4mD|I9SQB5*+e(qtH470io{S;SfBu?C=WM4hVdf*Ad`f(bNC6H|R9YE)d845cO0li(&d#V7u7-aq?|u#cd?T@1?=s1>Db^{M zla;~B7^fb`X`){hGyu0|5a>+iBT9!E^&c-)=rZ^Y*|(~Y}Sls)0VAE zr+5y{7O)oV>ROn+#1yyBA#FM2ODq6%Qe;muQs~fDCGi&1Mt9>}Rjq7N*34dR+>#|@ z`bz9hAQpa;`xSQVfjAfxJXVP-a4y)PR8m89gZj+Xb<#6k~S z=e)J@6KT-l79^x|=(@6X*XuMxqVlcnBb*9J!RHRoG}BO4|9Lgld;3 z>1&g3rKP2kgtAPDfzrIIxWvcPk2RD}?))87j-9s3+bz~bc^E3RC=A%o+%oZ{m@W_D z04nL}9rf_H2_^#r=ABxav`fWLx`6Uf%U|M@Ljy1aA*|#g86_3zeq4zPn~bQ(t!CD3 z#ed=iMzd0-M+X+FLQk~cCN$rs@$3U1_UP{9O9c^2PX@FKQ0I&-B`xGAB5#S)YF3~N z$`;g5kz5>Z`EI)^Us0S5yXEJKa8MN-J*qf(Y>{iR-Z<)6nbpAjmJ1xA9VOW$QzPYD zUC$TD%N=JZki^^JSmcPyAd`+^rbS64?K_tae=Nl)sRS$Fn1m$h{X0Ndj5?UsSWE3X z(f|LfGfe4XfK~T4B_aK5RLWD!S2Lq9!JWrMR--SE2UO>ncKUtVa}tq25ox# zF++k<>z#V29$CI~y|&uWVPnzR54xyPE`F9NN=Uvum_=o(vg3yV;-^}pJ>uwDusa=N z+H+CxzF}FEqDnEthvYj>2>5D6{8F*3BEx(ZuBT<9hb16c7l%($RQ4_usDh3 z{V8NawpNAYqRzlTF#l(NQ+q2Cj>SB_{2BiR^{rRRK=>(?sMlmqqqON12nO z*-fC~VL73SXo|Gl9dkb<45nKk>n*q(v1$+~vD}Abj-m=r|A`0N?tFA62_>(-8?yoQ zTc1om{Hc*xLX4{lwL7Mv`Z@-DYQtPtQh3P`F2Z4;jyC!zR2TdzDZnMLKzG4eAwRr7 zN5T5_PL9t}97z1AKnT~6lmlD-?7+1g!a4pIyE#~QPk!T>j8li2SMrG`gk}lrQE*N1 z4SxwIsaai4YbMZ-&H^4JQI50FjhL3o)7U8b%Z@qdb*5AD6M_c ziYIirwI;NO{Q1t1Q#*+OBDba-{V1G#UWK4QXvs--Y3}@q->~-Dakt4l^WSrp3v(=U zD9ZvxiQCcO-$_W{h|UsYH&p$2_q=-+dNE}%+u=Uy<$S{(oLa-;wcSMs*xT4&Rx8V! zGTR-;3sk0w^7P`5`w43K(@ylhtg!ImdCv3=M1CStQx_vFjpV4g3(}2@zY+#=tT)Z4 zIluXYj$I5a@M>r;8>|*w?$q{yuYx*yxUVDvM9v$RnWB8!s_fWePyKBWF2E>MAvg{G zid7N_u;orz&aAp-9DO+LgS*%eIDuWV1SzHU@b4xJJPJkM&WO%QNr7 zls*H)6+Ije?8%XN`kz_iOe#7;sMoXB&f*JGGiRD67cTX`7{I5WG82VV0EL~4hgmu_)w4$_c= zsc;#x^-N6vq10eqtu@2f_GY0$%Z*JO#x$spo!61lD(?PK2TV@O6WhtHPZ@Id}W_46MN=p1%Mh^{ONPEw{O1N(9svt3<}p+mffe3beD z?8503261mPr+%fWJe;)X87ToiWc)@@(;{NuD$DF`y3r!7ojTU<hqtHj=IUFCQD5d@W$ zVVNAqj3aJZ0RcJt8J2$lfkYDj(*vm*$`Ek?0hpquIY(42h>BGCBD)&ETeFJ1FBy_I zoXz>M`mv$wz4`g6@cZ-@0AIlO@p8#BueTijJaD1}u$23}ERTw+8hk@rVB1>B3UQ}3 z7ztu|jr;bcaM|4N*q4&O|BDuhRVKP3GNnI7ps^8=0iJE{vsJjk_xaZ=pLjTd9IftY zvwpSY$Wooa=}$5;YAfcjo@X|PSKEK%YA_HCrS)&9U$Z58(;f|Nn{Jy&$VGdf;85R4 z>`wxaFZR~2ZDu&waLi|2LlX-IPt-n!G`@jCVLika2vk#|Wb`toPI;+=7RJJEy*Z)!O4qMxxvw>;2dV0APhnb}cA<`!x@UO&>8dQEr;d7% zhNnI|PAdj{Hg9ozd{M=a_S?25QQg`PVxlIuQyFk9Vxj@m4}W~l*17OQa&7*6o+Taz z?`@g-YZip6?d5ezRq4032mioYLv@Du@jhG7nUi3Mr>(qW+k3EJf8+kIE&^jGy z<3q0|q_GURv3S&Zti$6wN!;$(PqgJFb+j?|pUR1-7T2WX@Wc)iJ-9(*&CJe+Y@!0F z3BWwc)r>rgoNo8zU$<4$x?bV~`I2)^!_JNH+?1-QmU@2~eI*;lfxzJr{1^QM_HTY& zE!v~+NntQ2#P+_j;k(&97y~@2y?S#I6?_jso@h7SeLS}HgpP*&?5^vt*YA+{ukuYQ z{_V8Y^#v?o3d}*^-d|j$PD%DTKsA;LQ!EDRJfQD@%;;yYt$t@{M&O6Y44X(DyxbPu zQh{msNfA+M^#2$bjl(YH@6`aqBUV<4kngdr@@w7hGOM;%Gfq}@+@8{ZT&VKh4+eLg zC6$XUY=5HptW+Hf$r-C^kf!ZYr#N!9*N8*}j{Y{!EN+$GGY9*pW6cac|?IBFMGY4?a zjnMw8boh_Pz#QYoGaiI;Z5umQ{rkVa!93h}H`Qg~=@MWY2jJFc^eoqdSV0l6@8E>? zG8BgF$Ck?jJ_-lEy9-!XsJ^N&HiH7+U$oVe=j2N4EW2=sE3SbZJZ!1CUgGLMUj{M-lK9}qv8Bt z4uT!KR1e43+j5_SDSEH3@vPw>?q|2_+jv#7D<7hMSVkS$KgRKQEqYz~m(tRy?Z*aR zpgXfv`?KU+xBPYQ^Ym?2!Xvew@3oJ7Jaq7QA(L*J#I$x3@c}=&!?FF%Y?2NB4R!+T zDZ|%-s5V!70o>l}I{JR8kF^P_ue#$7M;QU5cNH)G+Z@aSF}bCaP$*GQXvzcY^0K>r zN)l?3(Ttlc^oE7l1mJMg115)-hkLC4ZKg0w&-1$dcIC0{OyA`qEYodN+~Q8>VW)m! z+cSi&6GHmLt#KrV*#jz)`##+Br(876)|Kj}NRlt;WJEh4C%GLl&a`&~0=y-N%C};O$8o zN#%&>Xr$&xj#O8%uWj&Kp&DfIqMKl(5FNkPM!QJJG;y*jwrX7{onA>Sg{F0od1-H4 z&9Ex~l2U_@$=&TRP6&-`HuyY}swcxm7-vi#wda?OyECy|m}kn|a&S_>1&( z3`7%((`4SpvGhnzRZO)bECGAYbTI)NDItDI^_g_3pTzYa#Cq9< zaonYoTFx5)UZ1OrYHtM}okeI>@6)mU$?S_ua+-TjPx;|AKP%2B680gPZ<2j}iP!4| zeHuQlo>JGf(bK$KJqQDpZ0L~8FSpY|rPr8-nE!T#EGqh){kK1_AC4iK0?as5 zfRIAW)0K26#YrY8aly#3?XmeAozcCpaNwJfYrZV8PiLpVtrNK~<{5HVQVPYQbUp)z z@jR}`FWv9PZE+C)sOlo&W}*};@+70cljJ^w2gfHIbv1j(BFyPrKkt|(eJzd0r^8*~ zA!^Wc-=>l#4X{L!*D^K#G|~01l3r$65{MAU4RVkvXAH7X?fKAC6zC1JD;_@{G2svQ z1G0{CrwBFcrED)Wl;YO)ro>nFu-P9_L{Korj>Y+?+TUM};$*JSd%(RNyE(6>3CEty zRMmOQ=Dnq__UJvRpC?v#EQfqd8qfQ3vTdp#5{wKsG>P=KA5&*w^VfraleI^5M*G^h zGZpilPlo5KdG-yimwH-gR6XfSl)mH3`xu9*myLOqUQ15JZun1GsR5xdXE{~^`3S3d zK4m9wbHr6cJ*C~@8k)=b6Ia^CjTG4DuM3@xZ~0Y^Qn@fbuhVf=d-rpeoac*mwfks@ z##d^2+!HUIT%i>i8GSgM7A9fv->Ibz7we_v!QtdKwuaSa92{%bL|3oc$y3;TUa5(ZpE&a^k}_qzZVmfs6wsqv7TW?IJdN z#?6ukIO>>_TY=IYi-l@q4thZpezo7vg zN(y`)DQSWS;y{nJq7w97mL}foPL8u}F)Kc(Z{3s*!bxe=P8B#aQHyArfEIu#Gp8|= z@;bk@k1F6^lFApU=gW2K3)%Jh7&v8DUtL*?ACoDX@m2&ycqzW72=3M znfir*u&!0e-~Ry3J7v+G)v#zX-TE}t&3e0Q$7}o8TK`2v(5#!-an+(Ft4{2< z(d`sPvokXoEHuist>o{STl`d0xCR%=%J_xvRXL0mnWA8YYc_UyEMn)tpj^^86hiR3 zVcO~BA20Dx-^HWTA@>-w03p}`X{0jQTqc4lSjnbDza+@zQu^5-ooaTymBr1Q3fc40 zFCW%lCC~dhDBb67KKr#cS$jSWLxO#nl=gG=JYm$3zUpY3d)kI5}oBG4^IhFAaLxdpSFduI7&*Ynor_g3T*Q;%2i&O4>%2s_*6 zgxjcTn!5rO$5W=e8xp$&hE%LH2g6gX1@#Cws1WDqIwIGVGDZ78k&+P>A>A+ko2j7A z+w+xOz0zpTPZ$j**9JB(u(eKN+>G-!FR3o!OV0SZBg^y!g4W$2^o}!pzW1x{i~XHW zF0tA7Z>{jvx8msw`Sz5Otdkz9U$;?n$O##0j%j%!FPgjdSqVI!r+z;FPr$+aFxQt| zgtj-Nd$u~=SWZ2^Z4g|43}+-EgWWxEY@3#wZ$LAIEPY0bKs z@BNUA3Y?JHrGM#`tFCi6!WGF*Gmkdy+ybUx=ijfkmLkd0SnoOB8?te)^|V1YEo54i z!@G!8s?44B$I*l)`_CQfYkjzS{-RfBY+FcOi%8_|CWfH~RLnSW=ZdNlQGGj5nECX< z+zIHnb@Ta}N)7JiPq*J}xNf}6H)pMOI@+L^Riz`+XGQb6E8^Lht2y)+|L)^Y(^F7Pp#x-V ztw(;aQswi&TbJ}Xjz>7fzAv(=wQkFgQjq=3RQ1CXdWc+s*3_9i5W`%+72)J^@{is z$aKn&+$0Zv<_DIe#a+A5YAp8JCj>6~17ut*+L`YoqQ%3uqQD@q9|=-^#L~PQ{vJ0% zXS~3A7kxU{tHDXVXt6~o(klssCSAx!ABz5cv=GTTl#WLk_Z>=DsSBqp%(-DxR)c@V_5s80Y zNMJe(S@wqy6~3~Q{dN=k1$nIH-0x0Y=*M-=3`6VaoK71L21QkE*NZE!+kV@RB11N9 z`x6TJPOIh?Z*ZTsa@R#D%%6mmRCE}(#VT{y(+@g`$eWC93fz9jK^qdH(IE7WPY?Xe}yK0AMaB9v*lP=-a6;#^q~z{_Jd6ju)jb` zh&kRxE%ZM%bZVGS3)j^=n8CP`VD6*2+Da*;YcP5dRH_F zIjI)w&q40p^q_bxr8fbbYE>yH)0Ri<70YFM9!1VinmD2W<&@}tYBg_K{AI^W10{xi zVT8A$Ga#Y+f$x?#%N%TJO~*RPxLdXTJuE61Q2YPk0{mx)ON**|Nyrhhj3lfC+)wBG zO!c1MzJQTu%$C5c(jt&Y;}?%}w+`1U1jd!G=H$5s9SYS;@zC9p8AT@+?Zg9Z*Dc`N z%#qIYLhYJ&6zWrX0}BVNo!Q1OWA)^c9{c;)XZ9XJi!rsqj@89-r zwZA*-v)b8lqS2&MIeU_I0E49YW?18<(XO8-%1(n*;;R`qzN_JDD-};*@pdR^AFrMG zdXe!zBUgNIX#Z1er7;~lr+$EumUUly7CpubknFksAqv@~Aza81pw@V=Cz+n`4uM^3=cRyTl)6(~i461`6 zbeDC{o`McAI&JD4bv^fAf{Q&pAaNh*s81^ivFCnmFtRy*-G91zZddWWZHXdHDKJUi z?;Ml!CSRIEV1mY{Y}w=RIlM1KpR3d><#V~of32c(DOAE~BcF4)c=e4gW~{C0^Mj+W z9>|Sl5?Dn3?7zX=2(Af+hMN_NaHdA8yjjvuXKk zWPfddd!JthcRd7+(?ghh9W1`(`ByqI#s{fL3PKsHhi0Di(RZ2!9BFiZS&rkg4_RFY zt*us?%ei)W9-w^!uk~dD%C*^zZ1TQ!834hk#1V*uqEIMkN=ph zq%{fPrSx9tpf5s;qbVZ{674am-be>!sjxJbqu!Np+8VKzy|=$!#zqMtk1LIzl>isf zIPmx>B(e8nGqaYNt+nr4cy_L~SO(_hUvG^)r3_!(vg=qC{Ult(5HqsocY>t(?wg!Ls4E>>mW}LaQnc-7mAv@@R_VnmS+XJ^PB`y)L6?x$O+Bh<$4vDRkZMa{=ek z+97Cyip|0yjDMPt>DJx83?KB@WoI0dHGCXl&ogr!5hH-8n}|V<+-9-~ae+n_XB?QX zF9OdQg;IlrAkBaZ03Hj$zklSc%1;R$VCRSVHi=*xL=vHhA`Uo+N-9g}ErPI)R2w8S z>8(Yjc+94+Cg?+(H%GN4SHLPrmq1FaAzpt_#T==iTk>q68cNtwCQkR(2;YtskQ5Oob>Z=`j}E0^G0eizdSDON=2H~$WNl^)*3~Dp*Ty}1q`;fh zX!_~%c9qak+2=JyXXG1O_4oz_9T3%?ODo2k?`fP< z4k&_CJwbf5WAv8+$y?tD#*8dansT$&aaXV3aCKb~4?w>nm`o32fHa;1DbiNO$JlKV zKtu7s>zD4gWQ`lJi0B9(xTBoSiUe6enGtFhJmgyoQY=lv7(5LCKtH?ZH5DsZHS&5Gg`Qb%15G%8OJ`u+iuX_#|moZQgBnf8AmW zA(Bd-_pKUX#m2Y6xT%aV*_9pk+5V zgNrq@sg-mXvX7x2qhOhiPFW^^RCH!96exCFv=W@mn~LMt+eR*+T?>bdfRS9@dDNY5 zw=n{p=;LbSxZMLKWE_#<-8pC`_Mjx;2xQA~s}uiYzdReT)A+>eV3eP6FdMAKQbEyh zUVf+AbwPongZOAl-}HI+a=o2lSrLx)EA1;D#%ToFy__TXk{?rvo?g0^qTk{^ch%!p ziLMrkSd%rNO01ifmL3^VmcNz|Lj(vKfQL}G*-O{w5u=xCPU14rRN>I7=%7BsXL9Tg zlqBgxyYf8^YXK!=3uhoGpwerH0=4wy(k0WO<9+vHWW!q-{tX5KHqM!{O*EoI2*2d2 z_=6}D>rRrhV}uakzkkqgBZ!A-WpLPUr+Gha0r;GtGuwqnU09w^CU1M_eZ($%!%;Bh z*95qW_})zyiQ{kswafJ~AymP@3BlP{M=B=u%J)`Pk%?K!LVW}j(RvCM@~prA=>G;P zVMSnV3tQTwrnblUnRWELe32r|lP|;`CNO0?mP43kxWe)bPLVJmw|jwO|RPOkRgfW1>}mj71J0C=7?!b(A{Yd6T$z)P1tq39?Hf!dKgU zPd=^l+S=TbUKE7deX~$FWm^3wxM@X;f*%zr9s#rsye`{q1qBfv#KU+!j_?5@gc)RY zP$A;qv@FO8ZFM^A(ei^OFhmL!%{@F!?9FTEFXeBhF9Tvi=mbE;Izm=fTLUd%m62_u z%Mdtqzu~uHb4zc8TUPz98A>|kPF^A1WkthU)EF!W$sadDIDPDg25IPI2BU;bgDN2_ zv?|1XJhMX6$FYXYhP=chx`kRi5M}6qpwWz{dWY@d+&vE^@XNPrWZkd#PQXvbv&p%) zra$-`7+>hYL5ie*kY6tLyem%;4-otvBK;_sBCDV6OMxI4ur$IQP7o&nQ}R~zjGi-t z7%E_Y#;D7H^ahSXrc=h;pu(xbLSI1}`!p|9a751W)@wx$x@5LkheB_X?F}e-2e%yd zB2!$b{H(;%g{(g%3#Ato>sNA`fryMCoaN30d~+?t@0Sh*_s%`*wTR-Alo~52$4>%M zx+DyQ1Yym?!?1IJ)e}KTrgH^6hTZt%&d@LsDrr7E9`{k{nX(HUEVKp-?Nd#v^ZuEu=S7V~;-8Zy;h$Lj z7);fyb~M3j`)V;JY7OPBs3ZohEKa@k)@uh2ttyqRS!*`U9|Sp(H>ti2wfjXi6OJ@Q z$`7KLS#gu^vZ`2>mMs@V%u0`kRGTDJB8VrT3P#Ag*Q9I}xHE^h`c3^0Nb~;9gp*v= z@AVo_+sm5uLTIfRLoFfIw z>(%$O0jiHP4`-7m$b#6!1A<>_&4eMQP>k06dA}-;iQCxBvf*5#G#fpvJuK2#XUv$> zB8%+$6ydDab}xiT)k92bOXL$xlE3De#+w3(cMUyFYVA8WvK~4tMpJ1LsTj@X-4U4z ze|P~VW=G-=t|7KrB((Fm6NM6HJzr_ZMajV%U5THjTG;Y3_Bu@xHL#5bdDen4@!9RJ zE#5OEja->#zQTyERq=donBVGtI-9M!j*dl>hIeuQwSxI{ zLS3q{LS3=@r8lTDc}ta)vqGarq0b&hv*v|MIhebsMCOu8L1pk5vvMa48fsZf{(1v2 zk^_PR4#v>)x?;bdS)U`T>e#%pG%^Zk3-ZuH+K?w&g1tKbtdJrm@O`? z@A_@90~j@Fhw9*>vlb+NNgk$0ERtq`uzl(I-zS{U8S*Ty$DXd83sOwx*snb`WKPng zmZ+;KGJZ5x^3eUn0RVeCGym^$t`k28#8|HwV_o4hh*67_i3v0VYIjUxo(d?S#N=P` zdV|mbyC!|^eJaC*r_bkO7LAJKOFYw-uAG%B;hX&XMo1YLDRpfS6w^_Y-zZRdL#$%x z)JncedS9LikqNYMSS~{ZyKbJVgbb*jBqk~hRSYmze1~5E>tO$pmQ?UlzTNLYyw1zK8R=ZT5nRs&=ih^Ksv*DQyHV>-w2II zm5!a9S|lP0o_1ed)mrF|Y#HQ!DLEmWk&vgN zPcFf=VhC1j6MRX~z+h*=2zl19?%!5syfK+FlY#v^mn`N_9OmHIT2ef=m6Uz6#)8)=MFU{RS-a9L)xMY9S;-$E;+SGr|fVCNft0;9NXp_*yFH#*CIC_ zIKzHyh$AxOaO;S~9?C9yXz*qBV55?BC6y${>S(?<4hWlOZ+DYZS}W<)?Wp@b5H1ux zq<&KJC_rG0$0B`EZM39jjn6Y&(SaZfentl(l5=apu>gEh+lAHAPcB1EC1R|W!V_XK zpC`-ekM(9g2LKroc7ZP6l%PBgzd> z{f8s52(+4sJoeRpCD{c9%px|b4?bbP7!<+QCT2p^Myx-`dbOAoB|Xwr3uG~S_&{cq_iuJU>MZz zsx>Mu&R8qTybwbR0B1-74T`>lekdAOR;!mRNS6vFMU}PZ$MXfb7*TtXZv;@A z1f!KevVKk6gB&2_BGskjT@xdcpoOEcXIp_38Ynx{U-VECC_8(})gxJKVWPBhm7qeE zN67$IDx>7SvR&8G%MKuiO9_W)b7>9Db)ljkX!A;j!gXLAS4h}om zM=!Ve!yPosPO;W5E2UcFHiSDoZ7twd1Y*m0V3MI1)mnib0wn)3>XLr$tuKy@q;@w2 zpsxrs4t;_`{FhVmZIL;9(Es1XDMv*^B>qJNd9pIpg$JCFDrQ=qCxQTVUej{1?ROvV zu~S}2fUj5x&w1vi`b8{=GB1&|5iU{zXH!@Y>S(yJBmt#~a$@Ia$5D_6!Kf2&*yF|8 zU81Va>mvE*WAWdC=9rhD#28-d9cUSyOu{rK z@M5ptty%+tM1+7~i1a?^?tgEEuOdRVz`@F42vzzJMB+VpY`qiO{rAxe6B4?FqpPiY z448?Wu;H&EwQ2>RW#cs~=(2dhMVC>U;w9S2nxHO`{Q(Wg1eXpAx62ZsxC86+s+yHx zTiN&P=F^$Z;{-LU`XRXd7Jt+}pxV4k%8hABLp`pAFM?u#)y20P@P)!LN(2YC+l(44 zB9Gr^t$GjvPyX;9!ADu2!CQ-ED%59Yc5cF4Lwr89N>l(E`%I)NX|x)Cpg6ia2^bjV130AXqSINN#LS%XJq;y@OxiNfc_1==#~N5 z^6w&H%tTwwsZ1&bvGH)8zfJAlR$d%uUu8~w{Q^6~n0agR{X%=RzDH80!`tpT@2KbG zI{?Ret?MBbo!tAOp8kC-y0dueFi2QV+syl1%hPOXSN$ZS^d~uGN}x=ok31fMGu-)U zp(2AoXIlxm=`YOSQ~$DJR6R9RAPb@#$Khyi(_Kj~GI>!hltS#dlVl5-y6U9p-yUQJ z3>B>x$vc9Ai`}C>2UPIqq;<-T^(-5)*`wa}D^P@uMgBwYlY#n)pD_%I4#q5ieiQ~0 zj+Q!e+Tsfn<36p&0|9sTP-;>qU`yXlc)nL~dcK$O%VO*n;;UNs$$bSS3@+@0 zgP)7)Pnkjt*~xF&(VMCx0uww8$}3EA7q}oEz{&`Gxk9i-`qF(HxJvAwHo}#p6bM3! zdjTllU7Txvlqniht@>&=9qau=`J0>~8Z!=8(>q0)q(FyG9AFlvf8F_Uy1?AT{wZH^4q z?7!5{qm8p+Eg&M?T4L~-$W^WvRhd5wL`G>$l6ih+)Dk1F1gv4IqTAR-g(7vgBAm%H zfczy@jc|i219CKyg919&?$I-raL{#TZb$D$RHM=r-f$M^C0N23>J~`o_WTL%{Q~_B zPB-K8))?M#^20@qbOQ=uai@R2xL`>yG<$ZOxYBaMBej-zV9W|nS`Xku?8{zB_kGet z?$%HNEi^)z0mT)92+hS~+(JkvwZIg3rdha*2`U!!XEITD7v3hqWQf99yr$^1k6uWY zmODCD`lDL?hEQRt`zeA+F185?Dc#|NLKpt zIg-BfH5R`;9{6>h#vZrBbgGZUSn~&qjGqG>A&!azaIU()Mbrd!zCb04k`kTxJ*yBV zXQe-u3Nk-2+rLnP|B-1fCG1rzJ9Yaud%v|71 zUi*`+I(ydgbsE@!P>w0>f}bZxkTT_xqHI@yNq3WISV^(B|beRJ1y>DSJC zBEZEO_n2o%@Yx4YimQ2mL|}hqYCO;>HQC22AO&7(^aOwS0Ez}n8|HUx$3)triaMjW z5VTk#bfL))GQZ0dLJx97XRz!cTop(bPkG{lkD)v0OQk>Jqfe9;fAI22vIxag$91r; zW*kSM+Jm(D(%ZE#tsyaH{E!+s#E_AaHJJYR@i+%KWd^sb&<(SP_%fb` zufo)4f5@C9hpZY5`sK$U5*KX^ge@1IGFCC3 zt^C!(ND#TfVQ7{&to3WZsoaL_kB|4nzr*x#AM>m`m#{fSn-#$NkF(B`6nYNI!o~<$ zA94^HDU|%gplwl-`z&y&xUk44K!1zQ#SFev#3?5j^9&s|aD!EBQYy>}<1*MS`L7AH zL<9Z{8Q_~gzXCTILDIH716e4lQtNX4E!D{rjD-||o14i&O*7W0wwPP%q%ZmE-u$=h z-)&XqX!@{ZBKHVY8VzaO($wMU&5WOX6qwAP3wH9UjpuH>ii<7o0bw7^*Sq=) zIb9)m>pI~T6v4W}w#hM_G1yEYsxr+{r_75VKN5dS3@$^irIAd*E~dZMHAfqsL?bz1h87`(#Gq~lvi@?#StAL9wmNRIlZP{^eQ++NQRv8m=Yycy9x$=Oj| zi5>>R_TuI%jBV^e^kU<7;M080_^!1Hy79OW=_nBI+GbE(P@_SH-bz;H0_9(m>1Uat zg_dwo&eSU*<#lLV7?v_Dzah?O7k6U*!=WC@bLy$N%TV{=fr0`EH7G;X{cEMUtq#SY zEiUbezF#j5ZF{{i87B;{RB<ySG9HP@)bt@_0O!FHlAM*S9& zZ7jj1VWnfAp>$OJu?EPbjc;y@IETNq_xULBDUEDSQlQWpJthBL6$WyIkDxxdm2c#f zN3v)HZPq&B!J+I3n|D=9=FY6~ui$>$EHUtAZH)0Arlch5hS4P1pD`1V(VQVbeXQ7~ zl$X~{d~F@hx_amG6R7Mzh(RTEf<~tLpwfaH7@fKD&C=2SA1**rJ?1Y%cxE;eoIc5= zQ#opGaGeox4ue1;{JXfeYe8aS^i!xU3X zQ3rA(55?IJBk@x@(V zBEC!A$3+QP$SPWR>FYQwgF2PL^lBO0T<@n6$%k|{1l9<$6u@mHjleA|C`i`U6l$Ja z;&qU3M~6JHhYij@W3fLX?`afXeK0hwjz?R&C|5;mN@BAWJeV@TNMInhvb|B(q}mxL;rf#%;#8GbG< zDE}_iQql?hF%KcYBB6&Ii!EOX??QWwZBm*bp+3>kbu(>aq%kiPjon$P{fb ze6r>i;0l*18|>rOf2x>nPztehHFSMsNa>J=36-8A3*mGVb6^c|%}3dIvuapSnOu5< zZn5te_Fpp4<`+to<(d%-|CdbI$vkQNbhE#{!GP zi}w<^JKUh8{t}lpBimJZdE6@9t!;}l==*-vhiwE=GMg=~<*Tb&)e06I=6c&vdN8$0 z@bY+3UAU%?+%S`7q>o!PM?>!i(H(<3{5Z~WWw7s~osZQXL>@+%B3!l#MWBHSSygHs z1ZQZ7MiD?1$MHl(4-l$zANic$C$!%mJZAD6KlPE1=?ps6HYifF=iW4vQ1+OYL2!`tLoYtDxE(%AdqF#--ojB#umFOw?Th$BF zTh7&_Uk%9PYaf8AaHJz*emjkNUpc;!;dND}vbxO%N3Gy-qfLQRH~7ce7l!vzR<<@i zhx}0R1At(cGIHVLXbeAg6;ZxwYKjJ5h73!Q?;7OGUD zXre~sbuHn)=FJ9{fjVAsLe&tqvDMQSKy3MbHZVZ4eJ4XO89fLrOrKmza`QB`s@tkb zL(QC+hZWQ7h}$+1_AA98oEX!sNsEk(-Be5YGL4p%SFYm$@h@xx_I~M|ljT90NZMyW zkW=ZFDc7gYL)-~Y+HrP^mK}2DkGP9fdQvyU25MYq7v+n-{JVm9sLjkO*hj`+D$*NA zihsCe5hEjHGySycLCv8r_ZcOFB_|!l9V3_>;g^2NnP*+lG0f|Ftclmx`7w)wPcBca z5pc~lorrknlF?=7P`Y=OmXSf7*k8zYP59>~(q>j-=IzlqW%*H!w2|7;r1m4q{0OI= zrLy^=cmC<34$p(RZG~WcOs;~=jDoO>Vp}OMcd=i6bUfE{>XeFi(uLYX8DJmkSV~dP z@vlSXQ8iw!nOq{!A<#czA$YZ`*G9w?iY*GE+E+~TPr8P<)NbUDRsn&;g-#w)fHH#B zeizh*D}l8c@+;R_RXrjlXGzJ-$66M~LT722MGl?yXj_`Yow?nCjHAruEUwDxK_|s# z;Su$Y@BG1AilE2R`aBk|h2R-*Tn9F}ZsCCr0I~NF68)_7!L1&#q$~;+ngRP&oPeps zv1o6*V63MapM+Egi14Nne`+v)mw3MAoG>Fdn~9a+JZ8#(Hyjv^K)S^wniYHz`2CDp zH6g#uC2J-MFb>EeMQx~wj@TlSE?P9UnFgwJ_KEokBqaew*V-iQtD~c(W!~H@D{XyC zI>TVjNkBagv%ATVJhF&=N3KMT)kM4tUC8$POv^pyF_6`|$;&?aVG7uW@J@Zky;+^N zcKGvo@R<)~NWuhjCz1UF;k5e}vg`yUxsR)uigyByWk8nF6H{#xoLk_ZopWk1R~df4 zJS*wF%KRoo3lPBk5@Y^eXy}_=0Z(f3yV<34BX??86q*u%ubr(82Fsee(qyY-XzPa# z?96<5#e@!kg*8RQ-g32eyUs7{lvS-^YbuCc4t`n?{jLh}yQT45B0`i~n`#`P75gM> zo!6J@g|-2rh6jI{N~FJL(mS@BJiS_Z$W_fk)z`_h*7N$ z@w#A}Zl}U7VS486XF1X{aReRHX^M0S5qklDYZ5Hazo!%UnKg*uG2Oi+ts?z_byd3r zkYi92k-m@16)CJ5%!rOt_RLX*D#I*9cXZ2W?1cqFecFeVZj;mAyj@qanZtGuj zg6BCgGEAz$mL2w(F7{x9Ccg1e>1A#fAS)crdXd7&6DKl6IH)1UfTfhc?6ck~u5if- zd6e>L|C~<03cdYw?5})x|BifG_!KlbevG2%w!@?~{_9AK?s$jayDT37su0YD zzY(J&#J46PnXtF}WKNfKn~b|1f8TVk!9LowdaA!H^CQ8&uL3{vdd+%UF3mYWrofJh z#Ycd`A2RU9t!&D1(^TcwG^9a_CqezdbEN&()}Rc+lg=-S9QYeY(`9b%27Fx1;;VBuB#Y-2&%rA7 zdgA$bl?hetG(wsj=yeh-F?Xwh975oVrB*;r)|OxI0~ z_GI27Ca3CT$9Dpv*#9d~y_>r7wkYQkyw3iJ^gVG=s~JgnyT#sBv9rrLD0R;Are@3& zO}^WXlK8Goe$@41w=eiHU(|W>aegW@mmTi&OmqX!lOu(uP$>&G12i-u=+`>CbZB~F z_E3>}ZOxB(q?K{;xyU|&zZb~H-?k)5NJ1GM9=ON{ZK0u}%%O^TGgOHZ=t0!JmT=UV zRG>bFhtr`1##~uhX*y~xD9o5iR)K(rr~P4j=rW2jFY|7?qiM z7q#)64_tc4yV|nubU<>uLETd1@0_1Rs7G^dzkq2Jxo;;EDOLM~Oe3ilBlyR?ziJG; z1%=qeS~pOIg4vEV}2hiU9&D@2anW2???e}HQmoPSu?STOg$hl5+c@@803 zM}3nMvKgbs5-`3U17`Q^VI$3)J`rKSZ2TwItklP(G`;{9^c`#aK17M?KH8Xl0=KzB zqfrDa53N~)W~j~x%mGVhvG84bkoJxbHw;sJAT)r{qszo_n~E<<6net5A*7Ky}7 zEwo~4IZ~(?#A_vb=`F|E)K@`*5ho|{P`WcDmy(0el|hhJ7`yi>zZsE zgLJbWkT779EZ8qA5NCo2ofhmG`Tl(jQA(%7FB5UW#^0`>mG+)RUc@Cak5#N_r0kR| zo`3v+J*WJL;l*_gi53Fk{{wyxn{^~DB@5$wAmImBxc2Ssg5h6& z9dX1qb78;NUJ|uEri}uOd4Zsmp@_)eQ>EKZGrP#%(h8N#Wa}}W0-0*@a6@EPn$ppH zUVIx{Hzf@Ua#OpHzH0tD*V+j64bK^K7ITT7nGUt#5l`*4w*m%GZ`FV{!06|n!(`e# zu}rt|#{0+Jy3o?Z%to{FU1)@pNxddR zWR79+$=5fK=DUX#m<8-M+8W_Oc(MW2^pV480P)plCuADQ4^!&;pH${X`LTx!6+*KX zFCBN$Wu>KMWTX?vQO2I<<_PbikT6h$ZRpbR%SgP^^8^aJ!)NX{gauMgSIW)sDJrmXoF(@uTW zZ{}!VXvmk7zdL91KK5R@tZ3Nj9=DyKMX$+f^)(a9H9zkm6xD7Zli3R!#<3;4#d=j( z21yUZQC&PVK)a)cMhXi6LH*-dJ){bKmoc9aFmdN3Dc*Or{*IC(r$|t#Uo~$m^_69V zl`=S>OY|OPwXmmd13Uwz-` z1so3=4+jnR>~4#k14AY6=)5I`1mQ%3FZef7NgbIZq{bY{&A1xiAJ?NB==L_m162OS z%Dur_O#et!Udq~5C5wfyi`>t7rrq0=W2;k&s={*NT50_BA0a{ux4dft#d{9Xocgog z2MnF+?Bw7)Sau$fOhdKzqv_J9h-FT8$7QJ86w^vl6-CO;ZovB4ML{=|&ga3eD{S*; zOc3_v+hgI+7%0myeaFv>;gdbv^z?iB<$Iu(7A~srxQw=zZ|?_Kcs!>`9k&4N($>8& zMZ8^FK%CL<<+7d^dtcm0Gr-*louhD90^Ls7Q*TC7(v%%>_vHchNMY)QHO0b{8%s(Y z$?zq!7y!^XGpP6X_m2Ne;k5@1=?1~|8XUXdpmj0UxSma>R4 z3BjoE`1+V);1Mxc?-Jv6iR~F5!lqF7w7z%=s>`qZ;u&A<-S>mqg(zwnGpAXl24tq!>0IJ2e0j!m z`+kOu$8|T}`)%xP%Zv9;5_Tb42rK>ZPj|=mYpH4es=_JS9(F98MoYB#VMK;0j0P*8}U%@{esfJJ+R(ng|zaVg$hO&mCv$>cJG}{qdE3O>NKrK-7jUEQ9@9e%$KE( z><|o<;4ySA@Pqyh3f{NFiy!_up&Wc50)ZVCd}LN=#$bPel$=)>VB7q6i~vf4=tFt z2F`0({rLdP$OdKqQq#Ar1~-1DO%rnOXFu1641x-_TvMtuL|UZWwfw&=?)XW_nJb7D zVu0&2`5F2$L(b6f)bkizM>2wJG2zqIU<+N1TOf6AMcAy?TfIrbuv<8hiC+L#R6l&Z%Q{^$exc2G+2AZb}ws z$3AfZxKIPovQxX;W;A#jera;wrWxb8gNU44X`6{$RlMb|<-Ra)1R0iKHh^0DtERaWlHBH&C8f0I!a!a;^8qzy6oz}catf+Vy&n(@HhHXH12HL>r*+JoqqKRt z4eg!Q7nq{*dcvuf)4Odif?UqN6caaSEv3{NdQLB!Hbe%#Zj~%>L`Qv@w zQ)PFWqBYj6c}33wpS2SKdO>?!(cbPQXLPsxfLjuiDp+nC&k0yNGcC90kVWM)bRws zSF^CdgvX^QcVz)kF^Q(9Or+)*bths_>BkMbJ#s1stZh};4`o29pm_QXx9U=3vq?hv z*PK*yyaFGSMe-$7gSw;=?HIbl$r@8D#M;Dua<`AQ!J@1B59%JyRajgHH1GEwqZQaZ ze)sHO*=4dlrZUbvo{Q^@gRMpLvE)1+$Jg`TFKebcE_=XNZcJL%-`jUj2@2pc!Ph?i z%b9C42CyvVu{NC8WgLEhnSSiPg<&*ZM6PKp6d|>};1buuCLIRGcd#B2Y~n!?DRl>M z_+hRZPv5Q9TACby+Nyd7s6Zi)^Gt!~WvWb`N_dP~lh`w9-+2EVW_WSF3fJInqKk|H zh@>~CcLHconwz^lnHOQI5DTZ337py5MCRaZ4$X1MO#=e!#2BvA92TX%dJ^?8+OJMU#B8AOo5w9A*IDZ1oiaONi(;g~Cq9k9KH)5d5XeOJ>*VkL$DqM$E zu92?{n_lFD=$T!G=^5Aw=PeNBD;c3FK3fnh(0of$OiDR$Q>*HbERT;u2RLmkJQ zCO58@t)@R-hwS4rKEkHSz}-ZvQpzj7E+W}@%$vMBKU!;O+^^o(WsgypBYFZm;J)ma zK0`}3x)wmbwSqvncFZ!5JfCTJeh#dCLjZ#}#wN6BdPJ;Be zWud=28yIbla8?SSyomK(HMg@9blwx4a!+3?o>w@`m}c_fhi_U<1ofI8=kxPo*_kVZ zXwfy9osxZL=w>j_?Y`vpLyydKou+;?4bxenB#fdd5?xlt_lYipRo1aIL2bqP zol(((cp&Grr>vi+gsVVIMfD4Ldt(2>A~yka@=>gI zzscXxP7W~gIm}rmeN)mSO<`EoC;m$@%$*}+jaAcT8;C*s>u`O zee`Z$k(G+`Y{FD5XGbCjRW0D@V*P8bP%+!{Vnx-F#y=Va79o!bVn0o+4WUYH@zUuJ zqE(fiH4!AKSHSkH$-PxDNJJYi%`{Zu^eck%?)kAis1aSib#QoM_e=zD0nGQ(!rnMWJWw5 zaF$#NFHeJ8<0;3UX}F0DmcR>DT|nmg>(n)Wp@TNn>agOdLTaS$J^cclr4etN-T74` zT!VUt55|l#s@Z+^sETv?4T+OV%_Y0OE_qNz9sKd?>m5z@eMqtTi}kAONNF9fA}I&% zz%u@QW#K(O{D6*?YW@6Ys=_!%qF6f0*%r`Ac-4BrB|s5P00-clE+y&75Ej*Y4JJwn z3rMIPZDE^JxEMQK!8<;hItX}#DsC9noWflyi0}K9eSknC5l?C#3KAtf1Ug4YI=@bb86ZZ4GYV^l(|9D6bKISn42|zQz#%WWx_wCRT=I zdqyBL{qyZ{aN9fQ{nR5|hjrzE{k6|2xi4Cqigz|`UEMIk{3}0gxeT$Y52@4ptF}7p zYh0DK^J^!wdizHuKb=Y!ZoYG3RcABD$KcXFPdiTWYiP}Q%ge;knr#~hL0?{m&aKeF zD;RH#md>+2Go_qW{ZsmKdUo*ePxvkRE}u~q=VQ?(h@eyk#9Adn4M3=1IsIJ3c!p*$ ziu5!wynDA`4%Vvxrhe*LK77P=*f5}Qk_154SY6`n&Mjagd+AG(mvit1kIRkcUL1Kc zQY&7}hPzZyn}*#fUFs_zl4;URt?f`J206%!r18fj&+do0M~~esv#Y(xxgZy7t0V3{ki5hyu#+T{&0^L)8{@ z$#X{x+;yKjKUbfoA43UJMDh@df!>l$;ZTju9!W(9dab*+7z#3d_#u5BHaD;2Lezq^ zF~1fWo|vrb=V%h;q4=Dd^{dK7GW{&e9XjtWTMMr?4l`P2dkf0j9)?0#TE=g_p%UF8 zM@{atr%Q0#&NbdwS;bShU-M{mpBsklIyRtYGa~n?J#GuF@1M65Hcd`AO>6YCBt1Ef zx*+Is^R9h>`WiQ-zSF!T{u+E}hTpX>v+^R)X0i-=h{SMWw(BuJ;=H0WfRk~uL`5~1 z)&Jq@kz7~IDzi~MMyGNV5L>e%f!;)4H86#V(PNY}d5&{c(_j#77>E2xjfX~m1( z@?yz&xB6vf(QVTTdr~3%8c$v)ae1HBuiNrI2S-gyWDq#((@yTmr~1>Q`S+)y*&Cvr zIdwhQ^mBi+R?RsND!{yT-tfecc*w@q29A+RjXraooE6RC6-0liD|w!5OoS8x-v@O3 ztQ#6Xb5OYaO*}uJ|85+a$4zRuVGIfsF#3SS$RNKP1;&V<(_~1oP?`~^*P5!8dT-?) z@MhX>L!{?d=_VGol|QCqbEFHe<&Wp_w#%?&6DiX|t1{-kVvM4bl`dCZAJbe8k95(~ z-dCQQGPFU!y3HLwDg=*j!878!F>tH=6Tp1rf0$^?nb`fd-x4y46`{>Ern^w_7{pc~ zf>;DQN#>t($jJSfMmpF8&}#tes~|0BZ*%|TC&0X|rj;)BUo3#GIjAW0)jh<<$N+5h zCwz#$EhU;LdLBQN8KD;T*(6$?k#IA-f9qpCM{6Vvvd-J($xxvQg^vGQuC8B1tML2?p33Ve9zpK7+Rrc7+8`U-;k zpB37^G(k#Kh{aoiGe7g47;ou3XUHBAQJSH@-n)T8eF%u0bxdT#&Jm%oR?%P!m#%&= z!rXk~d9bs;fziiR&EV1podjP&=n?j1;pDbDuzi>X;et@di#`=8W3WY^>Y@Adbr;t~ zUmuFgTP!%4WNycRdU5=_U=`z#b=2p}3 zT1-!su;8$Ff%x5fCvBQ!x&v^tNJ4x=AS9o-v?W}Fjy-91@<>rL%WeDd3bkewqI~`} z89x{wZol{$j1VUv@<9PFODoJ41N&Sul!*AB*hWRx;P;)s&%g<~`%F(a?-TC4+8Hk+ zp&%IE{Pgr9s$_C_TK1RcHT@?bIT^s8f7*z=#dYR*gEeb z%5Hy8bhc7&(xV6g-!_~Eyh`^^&ZfEYz?ifO)u?cn;+#a8hcJ*7YyB4^<99!JfdDiF`_^{qnk4$-yO3DJ|?9$n4 z1|6U}e`WBP!A~5HuPIr+;B@l3*iGu@@_h9KH%IIDt+*>10WXQd+6KfK{(0oJ& z${J+JOLsQF;iERK4?5ZH!B4b~8OS|wQ{;T|zTF>eo@}&i--pu|@d{OfW!B*}$MJMBIf|kT zdD$9V5D0|$LflqLo8Y%-c$iTGx+m1^;Pt%0kr(}Cz=Jv7lt7hr{A|;@+WALr@TY1% z4m5wG7q0tO0v%%AX`=teiS##~k>*&ZVa}#(80(01S`L>X;tGA$sP%(rvA%tC>cj*C z#^ne<-lX2;gx%t8xh&YEc4M)8d7YsmrH>ZLp$|J+zQJ{TX3rFdq=jQZ15FiPHf_>9;NzkauJP>yfNC3ggGF zfV8yt^GGmPifHH=ZdRWow8b8NZi>|dY+tX&Oht;qM7>94^hCfBSRwX|y@|N2)t@;> zj~hqxOT-DF19;*UQ*7KjYS3 zpZOeyHn6c^RGrU5s$ib>tK7+4sPtv58M{1I+qz1DYDChlx$m2V{)|;nTEHp^s>D@6 zXLEQ2hB^n_pf;#-09SFfx(xveDCG-8u2>TCj&Gm}FzO(J0am><&dFygPzRw)oW1PX zutgYWqg0q>&4L#io1*Uy$u<};tfvXs3ASa&`En)p*V|47khHW zUKbbEN4vhb?A!uae;*Sb^Hp)vm!T zxiBWUEpYf+F`$g#tsuRyEQ?2nR}yB`>LrO&DcVU=)8KTYQHiDrwrhCX5zd;rA}^hN{Fx z2OwjaF9pa{^55_?wE%$b9GGA!t5L$b$DleQ8^HM|6bo=gCg!1rP$Y{xt6pJZFL;m^ zo-Eyf?+ljbBJK6IJJ5)&u{}>eL)ez%hWf+f@p_NJ^=rR1ztSEsoKo5rxB{+yPQNfi z(KIV;+Wn#0cKO&k6gR3TB=aAFO^Ru+v^(sYm-RMZe_BM?evAr~3Np`|bT1C$gNMd* z-tg-hC*h+dp@qE|VtG1&OB8$c$4-B(1Jbp1-H=yFzuP6x=EVf z(gA0pIE>p|bu(fSwJ!$gqnYe_kUwyL|y9YEEvPsKiXLda94?n%onv}vY z_m+NR)cSIcW7hrtoo_%xD|$31ypQ3F8q)Af{0P2=3!E-w0TorK>TH5;J0xlt{OO8z zI%!Q`LZo_7wO529DrQf{a{VnbkQLWzJAegh`QGt0AAXJTqTI&w#V~v8UU!(KdOpD` zgmN(2u{g}jRKyPXue749a(%49WgX-2T-*d^DE_1>O$ix`GD`jk6%fA2DWVE|Kg>P@ z>jb2}^;Hkl5t$wgiIIN=xinE0$?y<>V5m$T&1*Hu*k9$XvQiZromqe(V&zEM=!U)K zP57!ra`FgSMXgH7x)C+1bFItytM~PB%zh&Xf%W;}utS-QV%jdwEbt8!AeJAtf5YEgIvG4Q7$PEhw2Jk zLMe5xQCEzyU>&uWqKQ#$E|kSk7-ym_fl01SUR-lj;8sd(V%Q%Pzo@qyJ7jfW)R_%G z8LH$gy5iz$RPtcHW%>rO4V>_49LOoH-&{kz_ysO7JF+GrpqNvf8`D38n-9XxM#OF**G78j$eXcLEv18!?W<8%$#NO zbiQRp!EC1N582gE{-LK}t}=Y+@!Y1H_#5%VqL`4$^to0EtW83+-$;2Kpr*1*xEu#snmowY9P4ar`OjF`w`=^i4pU1(LS)s!G&uO z`wcOWM;%wnnu9?QO^#`Y_pw0CmqY&dwA4SWF z(Mt|!TE~0iNyfqGH~@KT6&`4Hme(Gi`*f6l`nnE!Gf~Yhs`CYF%q4Y%X;HE4cs^a{$EM~wGM7X*^kY;A=!MBStyXUnSya>d#XnZ9 z1Jnle>U-FRZTu1QrLv5|d&VeNx6`U7mQM&NSq=k=Wvh{K&yTc85+@@E3JNCzZbwivVQpL zdyTA9P)^aJAhFFH_ZKF_h!l4>z{?mp;K^~w9R~IA`f#Sex8B&g^pYAl&-!>?CWvPU z6ww%LE#8cJ18!I5j-Xr{#UfKEKhZP{BgG&z0lt}OOaHQTCo%ViQ+k9+1m1vprRn0! zvyNZ1^v7ot3;D^>@yz(?j|Eb6lC9yvkpxD4EDX2f$tg)U3N}yMbh%5W6Tsa&3p-uZ z0LbuseLWg04tLjmWWLd75i|XDikU>Eg4ONoNhev?{VwOWrp0d<&^0*jKeSrp%+}8i z?E|%ig(MLA|N2$>SP6yLt->h5)=;fNn8W1Ih2@GucA9{d073Qm6d}klYOI3&jb^OI zY68FA4V#ieCCEsph2dz?*z5wbQMt-wk*|F(cLXU71|76_?Cu(@UvfHA)Mn^#a(u%| zd`J~g^(3+daAV(qn`<+KgsarAbI;1XyTV^!p8aMHmxv9zX`TQrD4c4Cv8Dz^u~AWo z+Tpw&AUUd~8)jP`FN1%sx!=Ye4rJD+$zkn^%m^SIz|Zw_<5V~6&Q=oNn(26r{bkq! zGCPr9>sTF*U$(pJ_HqNiH{2D&Ju#y^Se1-6`^yxyA|I_m7A%;@{^P$U=|p{}JUwWn zi5#S_uQo}Uv(p|(4bbC-$_`A!`RXQ_O(Zs|N~6z=1d2dZ6m zyIXItswSOLS5ITmaalJtT(jRYqN8S9?oF2qz%R$)EKCu$;Hw0(BVFG2_iPW>aqxZ! zc31>qeR8^9vKsyMUvzBrI_039bLzLv3KqRkoFI>A@jdjU zoN)EswZj&TH6Pap-K)zkR=t@HdxeWSCBOQr!_r~{iI5w!*FLRsSru=pQ}0mwpHWa7 zQ&FM}iE|W6XOiC@&~$n1r+cN{i!r6~L{}Qx9at%1*aD(hYdyD1%Y&b^JSOW81^4d* z!o*E*>e9LA_FUFWt#}_pGr_wrZgby?eAPwM95 zbpGr&U&vkCW&KWa*3Ny>Nnc-8@(%_{VY$vyAAMMlrdP-Ue~X%E00r(~qtv`{jDl<4yi;Ul0 zV<%o9Zj-h_R`DH9vF48VZ}!_9F5P#jqw`0p!q$sgjhYl9E7gecpjPqo`w6%{`?J^I zc%Q!x?6#`}TJ5+3nV?gqcXB^{MJsz8=Yx8qXyAj=owQTCmW^|NbJK?dZEa@E3|}T~ zz+UTme<&eeq;4`J^}z?P0v&00{gN8o-52uI9|o3_MRHsF_Q zez{Pj4xc1d;M42_B4lH^KB6L~aFF#S<@yPMV)UVfDpAsp8wyRk3QLgkt@;DW1p9Q+M4 zTY-lbAC0Km@Seuas~oh{3Jwhx=_9%SoG| z#e-+p64bK8p?hSHpzh9it4jg;*g~(jOp^BG@oahhE(X(`lQ^PjK9!`iSU=OD!as*) zh6uW^Op+5~Fy?5Vds9TGYcC-Xt^z6jGyf=zy=i7?isbAM6KHM?=q-sz_D0Ei25bRw zX1M^F%48QUqy75Fa1Vi{J$P2zj54f`?vkZisgh5A>#1TagUM$3MGv1IH`>;8iH{-> z(&C+oQ%fBjcQGSQXr;9BRobIinG**4@GW6qZTn4Sl@XOxc>4HO<4!M&=)`JlG z9QSK09+ejMMx8TR?hz7c*j^4LlifSZDSA`%Q2;Fy<19vyb@(%QL!m%GAic|0Z z5o#H;oOB0wnZU6}Z)#RnQRNGN$N^77mSQ}RU?JVnO08iu_ak~IwO^BNs^|R!uUeHQ z_tVsqY?9AGGvqmuKJP?U63ed>S$PPbTFVmHVw>Iz;Vfx3Dl0`6R(VG6IENu~p@cy) zYlwJ|F9i_jWL)O=18%AI7Ay`qR7Z(ic*`t&E&q*EqnWqS0|^z^*8rG9#8$^o0qoch z(I@#{%oqKl_z+~Z(K%0SKsOe=5^O47j2lLv2$PbFA7Y%!nGwX&q0wTj$pc|2;gwQ@ z43bdaF7sOcRHYfuV!|_61-{Sl9Wm-Cknddt%k(1{2vpHuDO zWkY}fYQG3u^P~iWYQ)2};zBM^C{SPsd5e`n3CuuQm=2WICAPKN+*J*NfpTnCGfT8P z-2ySypSV0$Dp$P9<2k_dXgy7~gxOaGDKjPY`Utr4%c5dQz{X?n?_M62@HTdBrU~Fd zL_hacn>(*(r#5u-*!x=TZuiBLu(X=g8vfqzV4yV`X+d~)M*TjbbSstF4Q4Ex0H7Bl z>F>;eITH6+*inrrt@2PSatRT}D|4Z@H+Xf-oQeIOj=%cf&1q?jsXVmK!G z(oIFfwl*3$QsTT^la1-r-ei2gNhc@fYU)N7Pc83I4a>=rybtF4O^AyP6t20*Iqdng zRH{horLw!$mdzSEY8@|Y(8F-Njx&{QnIhn2F6wo2uPDt<1o2Zj*74%tL7`i5aC2&k z2RdgS%@H?jdP;>#cx5ToodyMHR)3eb6(J z6Lw^%4`%~|J}UR)*9Vh&$lOQ-gIsT)RyFu(WWrl9v&Di3**QCy-^AHKb>$G7I)APEf z(@l{gkgl!AtRz2#lu`zl-6V#qpMerMtu_xzuUoE6L`$~meBYpY;ba#Cgd$`PH1 z@?teeSe>)ym`3hy;{&2Z##TG|G|#+I;%kWB`dfIU623!_*MF@&V$U@qBNYMeoXl6q z@ER+Mo0^)c->9A2$=2As(fY9B@a45WXj*;lc{De1Ik+kqS;{WZ*a>yO;E&A5vFqpK zfgy$wnkfGrpf$oW$~*Kfk3VI3>ZVN!?Ldk!>SAIH5?DuTpMe)Diq%gH4bGINe+ysU zAIgR#NVFq`qqt4~g`(gPpbMD@Dd#6<9P5K2 z=04>>r^|l*C+qc4;yC=3lpRy3zrfi)9WZ6jfKjQ;x1=0jY!D;ugjtONKD-<86Wwp5 zvy01aH}Q6Hk8!w*&GiMv+g#J#9@YROSfKDuoiwvoo1sDkwk*={!ENwUYb%$a`4v(( zu%NDEub!%WQKC}kiK}|^q(Otm4LCMl+}1#E?3D0^UkVAN|C_#5iYzQ&F0KC}MTlR} z#+V5t#D@t@ncxHAa$V*3x*JZ1%i(BtG?lr_?7SiV`jwPkec5rVwO+cNw$;q$oTI*d znq0iMCN%~Fm{>8p$1<%EQ1`Vz2KgfHG43X+^y&EvoK&WBL3=LOz4IzBKfYZ6-VvGt zFmt6CQf3l=_AIh*Ex}N_8v{Q90K~0^cnvt*$KeH-$zNrFeRt6Cw}r!0tQXhrCGlu$ zgx;A+`(5Do!F&rCCxoa%pkcl&DBAInuVjbG&|fmfv@VW;Z_GNyBtDo|pd`93Gm2Wo z_(bB@OOX4S)Wa>-!Arc%AJN6p!z4kehG7QpofF04@8f9udeEX7e`7wI#2UhCd#;(N zla*2a{};jQkbNQ!wv3B&Mh)^ecCfu9GuQ$4^&D2@K@i@vBRU`MDv=A4?}pG{WWi$IcIT@i7oqiwG3t+OzZ7k*&flNLZq#+$dRF+sf?Gn~i;8WNHw{Qk zG!+ijy8WKZf^y;ZY|ra_#`@5%D`!9GiRYV(=y)Ugo)^*84blrC6?Ds^1g*RGm=|Ul z)_>x;9_O>=)bEd!3nQfy)tdPpz}^oA4nm_%(b3WrrnhPaEkb;^@MprN=qJA#hmK2> zqGX$}&{Lb&2UwsC%%@u?3p2BDVCQtGS572UFWobU`RgCbJPk41Z=xVpq)7Q{!S|lp z7_D)GOu~9{GQf1`j7;Er0!d8ZOt26b_#HiGeX&|^dbU&{d3i)g;*ziY32E?f-kN)PRe-l#o>rfr^6-rTcP1=PL}JTFk=`Z9LGg^i_o+} zt2QoxZH=vHM6FHT_;}W+G5WhSPJal;Su5;7J~tC0nkf%W;Qw;91P7dC6JrLE1Mxyj z&8Rj0c+|lpxQV3K!CfkpAP*gkC;wXh&S%~5lp;v$jNOG{CMIFn09hq%2gBbg&+NU^ z7pTcyHc$_qun>v#Z=_z`={?78F+qfyv0w%d3p6 zgd&uoCM<6l#_!T5iE=lboswajLM6;cRk)DxQWYR^Gow#BXC|K*zQ9tvC~6wyCaBGs z3Rm-HVPAfq5p7iLQ$CAsep#vFFJTNWi3cBeA!BN1cQX`;rL-Fnyg0(&s4k#aPe-1v z1>xS8EA0a%&5p#~H3>@@@ zaEaY1l3?E%ox7z{V!%;EYRV-PT8t&yVHwTF5Tv13LbRX?4Vn0s^D=nPOUwyp6TS^5 zsM8y=Y*9;NZjCgCw!>&WuuKH1+8_)l&o`?fd`7;Cv??-Btb*-8Cl#jeN|0$VpdtF} zj_-~=h~uB)#>&@dh~U`w6^_Ej?RPlx@IuD3x@YGLKbwUV(>>!;CWUFZky4LDNIpOZ z2jPwbAoj4r3qWHA<-$nWMpW*Sf_`WrrRTkM4`eQ2S zZ^Ip{qgU|p0Q^@m-T(6?RNQaU&I9P?flVej|x}eQQ}52Vly8N)6q# zI!C_cgJ=4Ge|Ak5m8qU)z?qc6n{yo44}pKKH6PsNy(tl`Zmr-5ABO&~*n9-eizP&sXC& zpv%DRN(s!qZBI01(Coi)BoMSWi6MydPoY6ycp!Eoa~$ zWhGn|2>c}2fSD$P1KzXvU%eY^CzOJ!$*O_(E|NCcvg@@%jMFU*3Sh)QOzIXE)=zUqE zlv&FES`6y{-Qvn0b~m6UbQ(MGy^V7+w;{IIA&fb8k8;2MSDoKNxz-bYEM0)3F!K^Z zlUP1k48&5~fSCXL6CB}^56EMwk|z5)q`hdpsgC3{>7PrH=0~1HSJ6ZN8#hrL^KC5U ziRmKLILeSl>l&i&64M#h+lP7d)1Tk70G%m_;|C?!!b3ZoV}B^e9I+>h0usFEHSFB~ z&*zxZC0Ix^9bkDQJ~V2(SR1(t3UlLC+UI|P0{`n64S1A>W4y45z5naW!N4{zyasaXl%G)dXoRO; zp}L{uKc}v2)KwyGE}SnS{+l?23yYwRq##1geON%De`Vdky#6J4LMMXcxS(~^=)$RT z=YZeM{->D7<~-{23mOLt3isLcYwRn6?X(i`Q4;d}(p&S%0xY48Ljp&RH-bBe`EMyT zese3z#Y;4i3;SC8_$5xM@;5pd100Kd_Rw!ILmEI|6ZH_|(P0_GjQ?xt_ZJ@d=Pk&s zXJh0z&p9(;#iC)m<2h1sjYu$0yhHFKN;wRs!EXB6`r-8do6o%>C<0xo0ZudnTc(R9z#}3Q zk)Xd3IxN(z{GVa^);>d+Sp|d#9h)}qGr;s~U%->Lim;8xVeNQ1TNc5n!HDQQV5>!( zr4$d~g025I4BwjGVHtth7>Yxj;oSE@c`0(bjVl?Zc|LC7OXXV2^}3KNXL5K{&5%`s z|0zxXTP$D;{|G&6FxC4EBLm>lF}Iquzrong=)-=3NR25p*DwiM>^CIxZdU)OJ%Rb? z^b@Cl5!qqGl|3&1H(!d}HlA_$Bpq3@}m)zA|$RpIW;|1EG0%MzAt8KMS=eRne-@`pZelgEy;wmir! zuG&n*K%G*hzMln^`v9+B>zwO@BFgwb{0$Hl?-so~zo0GWr6Y8Ex)_1e_Rm{^g01Qn z$F3uIT~YxTx@v&%UBgg$+T9!!dOwB5R7%^)6y;xZ01F}qEu=b~oN zqUYf(D+&94x8W)cBZaI&R0Ho%p8h(fE6TyeiWuo#^TvnAOzq0Th96a`6GV$wuZ$MNBC_`F;YJKXMu zw(1mVdNdZMh=rdf4hZsNQs2S|MO9QqRn!%rI$Ss>v#Ci^gn8{Wer(e;$I}Ti0*>$R zpJC`Z0w~96v?4UijOLru#O&b?@2x~|;Jx2$Hn{51b>4^b`E)v;>v|8ze}Au(9cSIK zJjIzz#B7P1QB*}$R7G76s^ghNZpqr2w5qC2wb|{q`~4o2KxyyywtZlLADJUWFCr5Y zNm7(&r_KcvJLp}8UFpfM&>Vr=?`Dc@gs;G*ps4GErho%ubu;D7Hy}|D| zhgMM^g@i&Dlo-Phv{n!fr2M#f`pSWW&B|oO)5wGT5X#c!bUA$?Qa~$1Kh$--t9P`E z=A(gL!RwJcAH``-5b+IFR7F)(MO_W5`}+D4Hr~@hc-~JB=mmaynzl@nl(Ks!NQwNo z)GR}@E}6Hufs5yaicNlcsu*mbLsLY{?=KjJ9v-%jcDvm|ptjrHe*a;z(#?TGGOnnK zs;G*(4phf4Mx+Zm7??aSz^4B^?>x`_c;w1qwiBPMXK&?WQ?QU^i7w!$`^lSgX}JkG zT2K_>jVU1=d`m<^&@#{~*A|Ef94`SZpoyzG`S z*$cTe)%V#>>h^P)(>hr@@>-SW{J5mc`Mr$v>=9b1!XJ7r2~AX5BYeQX$lvmWCPxH$ zg-TBBD5|0=s-mt0)&2hd;Z8qkhy0+jv(e)KBPAN8P-Gq zXpSzrp4g0h5?7?(3{;EJlKimIq097?leLamlTN P00000NkvXXu0mjflbqc( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7cc10bc0b21c24226f44c783a137be92ec1dd1b8 GIT binary patch literal 46524 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 literal 0 HcmV?d00001 From fb657b85603b35553db48770dfdd27122f56f23c Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 3 Apr 2022 10:56:32 +0200 Subject: [PATCH 64/99] fix main view with three pots From 73b93ce64cccd6195b34279bf75fcda2a611d93e Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 3 Apr 2022 18:29:03 +0200 Subject: [PATCH 65/99] fix compilation after rebase --- .../storage/yaml/yaml_datastructs_pl18.cpp | 290 ++++++++++++++---- radio/src/targets/pl18/CMakeLists.txt | 80 ++--- radio/src/targets/pl18/board.cpp | 16 +- radio/src/targets/pl18/board.h | 7 +- radio/src/targets/pl18/hal.h | 48 +-- radio/src/targets/pl18/telemetry_driver.cpp | 2 +- 6 files changed, 312 insertions(+), 131 deletions(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 54e9eb83d47..5d61f5615d9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -36,6 +36,7 @@ const struct YamlIdStr enum_ModuleType[] = { { 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[] = { @@ -85,14 +86,6 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, { 0, NULL } }; -const struct YamlIdStr enum_UartModes[] = { - { UART_MODE_NONE, "MODE_NONE" }, - { UART_MODE_TELEMETRY_MIRROR, "MODE_TELEMETRY_MIRROR" }, - { UART_MODE_TELEMETRY, "MODE_TELEMETRY" }, - { UART_MODE_SBUS_TRAINER, "MODE_SBUS_TRAINER" }, - { UART_MODE_LUA, "MODE_LUA" }, - { 0, NULL } -}; const struct YamlIdStr enum_ZoneOptionValueEnum[] = { { ZOV_Unsigned, "Unsigned" }, { ZOV_Signed, "Signed" }, @@ -261,6 +254,12 @@ const struct YamlIdStr enum_PotsWarnMode[] = { { 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" }, @@ -320,21 +319,40 @@ static const struct YamlNode struct_anonymous_1[] = { YAML_END }; static const struct YamlNode struct_anonymous_2[] = { + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_anonymous_3[] = { 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[] = { +static const struct YamlNode struct_anonymous_4[] = { + YAML_SIGNED( "val", 16 ), + YAML_UNSIGNED( "mode", 8 ), + YAML_UNSIGNED( "param", 8 ), + YAML_SIGNED( "spare", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_5[] = { + YAML_SIGNED( "val1", 32 ), + YAML_SIGNED( "val2", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_6[] = { 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_STRUCT("", 48, struct_anonymous_1, NULL), + YAML_STRUCT("play", 48, struct_anonymous_2, NULL), + YAML_STRUCT("", 48, struct_anonymous_3, NULL), + YAML_STRUCT("all", 48, struct_anonymous_4, NULL), + YAML_STRUCT("", 48, struct_anonymous_5, NULL), + YAML_STRUCT("clear", 48, struct_anonymous_6, NULL), YAML_END }; static const struct YamlNode struct_CustomFunctionData[] = { @@ -371,9 +389,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { YAML_END }; static const struct YamlNode struct_RadioData[] = { - YAML_UNSIGNED( "version", 8 ), + YAML_CUSTOM("semver",nullptr,w_semver), YAML_CUSTOM("board",nullptr,w_board), - YAML_PADDING( 16 ), YAML_ARRAY("calib", 48, 9, struct_CalibData, NULL), YAML_PADDING( 16 ), YAML_SIGNED( "currModel", 8 ), @@ -384,13 +401,13 @@ static const struct YamlNode struct_RadioData[] = { YAML_ENUM("antennaMode", 2, enum_AntennaModes), YAML_UNSIGNED( "disableRtcWarning", 1 ), YAML_UNSIGNED( "keysBacklight", 1 ), - YAML_PADDING( 1 ), + YAML_SIGNED( "rotEncDirection", 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_ENUM("beepMode", 2, enum_BeeperMode), YAML_UNSIGNED( "alarmsFlash", 1 ), YAML_UNSIGNED( "disableMemoryWarning", 1 ), YAML_UNSIGNED( "disableAlarmWarning", 1 ), @@ -398,7 +415,8 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED( "timezone", 5 ), YAML_UNSIGNED( "adjustRTC", 1 ), YAML_UNSIGNED( "inactivityTimer", 8 ), - YAML_UNSIGNED( "telemetryBaudrate", 3 ), + 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 ), @@ -421,8 +439,9 @@ static const struct YamlNode struct_RadioData[] = { 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( "jitterFilter", 1 ), YAML_UNSIGNED( "disableRssiPoweroffAlarm", 1 ), YAML_UNSIGNED( "USBMode", 2 ), YAML_UNSIGNED( "jackMode", 2 ), @@ -436,8 +455,9 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED_CUST( "varioRange", 8, r_vPitch, w_vPitch ), YAML_SIGNED( "varioRepeat", 8 ), YAML_ARRAY("customFn", 72, 64, struct_CustomFunctionData, cfn_is_active), - YAML_ENUM("auxSerialMode", 4, enum_UartModes), - YAML_ENUM("aux2SerialMode", 4, enum_UartModes), + YAML_CUSTOM("auxSerialMode",r_serialMode,nullptr), + YAML_CUSTOM("aux2SerialMode",r_serialMode,nullptr), + YAML_ARRAY("serialPort", 4, 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), @@ -518,7 +538,7 @@ static const struct YamlNode struct_LimitData[] = { static const struct YamlNode struct_ExpoData[] = { YAML_UNSIGNED( "mode", 2 ), YAML_UNSIGNED( "scale", 14 ), - YAML_UNSIGNED_CUST( "srcRaw", 10, r_mixSrcRaw, w_mixSrcRaw ), + YAML_ENUM("srcRaw", 10, enum_MixSources), YAML_SIGNED( "carryTrim", 6 ), YAML_UNSIGNED( "chn", 5 ), YAML_SIGNED_CUST( "swtch", 9, r_swtchSrc, w_swtchSrc ), @@ -578,7 +598,7 @@ 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_ENUM("swtch", 9, enum_SwitchSources), YAML_PADDING( 7 ), YAML_UNSIGNED( "fadeIn", 8 ), YAML_UNSIGNED( "fadeOut", 8 ), @@ -613,27 +633,45 @@ static const struct YamlNode struct_RssiAlarmData[] = { YAML_SIGNED( "critical", 6 ), YAML_END }; -static const struct YamlNode struct_anonymous_5[] = { +static const struct YamlNode struct_anonymous_8[] = { YAML_SIGNED( "delay", 6 ), YAML_UNSIGNED( "pulsePol", 1 ), YAML_UNSIGNED( "outputType", 1 ), YAML_SIGNED( "frameLength", 8 ), YAML_END }; -static const struct YamlNode struct_anonymous_6[] = { - YAML_PADDING( 3 ), +static const struct YamlNode struct_anonymous_9[] = { + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_UNSIGNED( "outputType", 1 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_10[] = { + YAML_PADDING( 8 ), YAML_UNSIGNED( "disableTelemetry", 1 ), YAML_UNSIGNED( "disableMapping", 1 ), - YAML_PADDING( 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_11[] = { + 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( 6 ), + YAML_PADDING( 2 ), + YAML_SIGNED( "optionValue", 8 ), YAML_END }; -static const struct YamlNode struct_anonymous_7[] = { +static const struct YamlNode struct_anonymous_12[] = { YAML_UNSIGNED( "power", 2 ), YAML_PADDING( 2 ), YAML_UNSIGNED( "receiverTelemetryOff", 1 ), @@ -642,7 +680,23 @@ static const struct YamlNode struct_anonymous_7[] = { YAML_PADDING( 8 ), YAML_END }; -static const struct YamlNode struct_anonymous_8[] = { +static const struct YamlNode struct_anonymous_13[] = { + 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_14[] = { + YAML_PADDING( 6 ), + YAML_UNSIGNED( "noninverted", 1 ), + YAML_PADDING( 1 ), + YAML_SIGNED( "refreshRate", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_15[] = { YAML_PADDING( 6 ), YAML_UNSIGNED( "noninverted", 1 ), YAML_PADDING( 1 ), @@ -654,13 +708,19 @@ static const struct YamlNode struct_string_64[] = { YAML_STRING("val", 8), YAML_END }; -static const struct YamlNode struct_anonymous_9[] = { +static const struct YamlNode struct_anonymous_16[] = { 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_10[] = { +static const struct YamlNode struct_anonymous_17[] = { + 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_18[] = { YAML_ARRAY("rx_id", 8, 4, struct_unsigned_8, NULL), YAML_UNSIGNED( "mode", 3 ), YAML_UNSIGNED( "rfPower", 1 ), @@ -668,7 +728,15 @@ static const struct YamlNode struct_anonymous_10[] = { YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), YAML_END }; -static const struct YamlNode struct_anonymous_11[] = { +static const struct YamlNode struct_anonymous_19[] = { + 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_20[] = { YAML_UNSIGNED( "bindPower", 3 ), YAML_UNSIGNED( "runPower", 3 ), YAML_UNSIGNED( "emi", 1 ), @@ -679,28 +747,78 @@ static const struct YamlNode struct_anonymous_11[] = { YAML_UNSIGNED( "reserved", 6 ), YAML_END }; -static const struct YamlNode union_anonymous_4_elmts[] = { +static const struct YamlNode struct_anonymous_21[] = { + YAML_UNSIGNED( "bindPower", 3 ), + YAML_UNSIGNED( "runPower", 3 ), + YAML_UNSIGNED( "emi", 1 ), + YAML_UNSIGNED( "telemetry", 1 ), + YAML_UNSIGNED( "failsafeTimeout", 16 ), + YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), + YAML_UNSIGNED( "mode", 2 ), + YAML_UNSIGNED( "reserved", 6 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_22[] = { + YAML_UNSIGNED( "raw12bits", 1 ), + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_23[] = { + YAML_UNSIGNED( "raw12bits", 1 ), + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_24[] = { + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_25[] = { + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_26[] = { + YAML_UNSIGNED( "flags", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_27[] = { + YAML_UNSIGNED( "flags", 8 ), + YAML_END +}; +static const struct YamlNode union_anonymous_7_elmts[] = { YAML_ARRAY("raw", 8, 25, struct_unsigned_8, NULL), - YAML_STRUCT("ppm", 16, struct_anonymous_5, NULL), - YAML_STRUCT("multi", 24, struct_anonymous_6, NULL), - YAML_STRUCT("pxx", 16, struct_anonymous_7, NULL), - YAML_STRUCT("sbus", 16, struct_anonymous_8, NULL), - YAML_STRUCT("pxx2", 200, struct_anonymous_9, NULL), - YAML_STRUCT("flysky", 56, struct_anonymous_10, NULL), - YAML_STRUCT("afhds3", 64, struct_anonymous_11, NULL), + YAML_STRUCT("", 16, struct_anonymous_8, NULL), + YAML_STRUCT("ppm", 16, struct_anonymous_9, NULL), + YAML_STRUCT("", 24, struct_anonymous_10, NULL), + YAML_STRUCT("multi", 24, struct_anonymous_11, NULL), + YAML_STRUCT("", 16, struct_anonymous_12, NULL), + YAML_STRUCT("pxx", 16, struct_anonymous_13, NULL), + YAML_STRUCT("", 16, struct_anonymous_14, NULL), + YAML_STRUCT("sbus", 16, struct_anonymous_15, NULL), + YAML_STRUCT("", 200, struct_anonymous_16, NULL), + YAML_STRUCT("pxx2", 200, struct_anonymous_17, NULL), + YAML_STRUCT("", 56, struct_anonymous_18, NULL), + YAML_STRUCT("flysky", 56, struct_anonymous_19, NULL), + YAML_STRUCT("", 64, struct_anonymous_20, NULL), + YAML_STRUCT("afhds3", 64, struct_anonymous_21, NULL), + YAML_STRUCT("", 8, struct_anonymous_22, NULL), + YAML_STRUCT("ghost", 8, struct_anonymous_23, NULL), + YAML_STRUCT("", 8, struct_anonymous_24, NULL), + YAML_STRUCT("crsf", 8, struct_anonymous_25, NULL), + YAML_STRUCT("", 8, struct_anonymous_26, NULL), + YAML_STRUCT("dsmp", 8, struct_anonymous_27, NULL), YAML_END }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_ENUM("type", 4, enum_ModuleType), - YAML_PADDING( 4 ), + 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( 3 ), - YAML_PADDING( 1 ), - YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), + YAML_PADDING( 4 ), + YAML_UNION("mod", 200, union_anonymous_7_elmts, select_mod_type), YAML_END }; static const struct YamlNode struct_TrainerModuleData[] = { @@ -735,61 +853,98 @@ static const struct YamlNode struct_string_32[] = { YAML_STRING("val", 4), YAML_END }; -static const struct YamlNode union_anonymous_12_elmts[] = { +static const struct YamlNode union_anonymous_28_elmts[] = { YAML_UNSIGNED( "id", 16 ), YAML_UNSIGNED( "persistentValue", 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_14[] = { +static const struct YamlNode struct_anonymous_30[] = { + YAML_UNSIGNED( "physID", 5 ), + YAML_UNSIGNED( "rxIndex", 3 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_31[] = { YAML_UNSIGNED( "physID", 5 ), YAML_UNSIGNED( "rxIndex", 3 ), YAML_END }; -static const struct YamlNode union_anonymous_13_elmts[] = { - YAML_STRUCT("frskyInstance", 8, struct_anonymous_14, NULL), +static const struct YamlNode union_anonymous_29_elmts[] = { + YAML_STRUCT("", 8, struct_anonymous_30, NULL), + YAML_STRUCT("frskyInstance", 8, struct_anonymous_31, NULL), YAML_UNSIGNED( "instance", 8 ), YAML_ENUM("formula", 8, enum_TelemetrySensorFormula), YAML_END }; -static const struct YamlNode struct_anonymous_16[] = { +static const struct YamlNode struct_anonymous_33[] = { YAML_UNSIGNED( "ratio", 16 ), YAML_SIGNED( "offset", 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_17[] = { +static const struct YamlNode struct_anonymous_34[] = { + YAML_UNSIGNED( "ratio", 16 ), + YAML_SIGNED( "offset", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_35[] = { YAML_UNSIGNED( "source", 8 ), YAML_UNSIGNED( "index", 8 ), YAML_PADDING( 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_18[] = { +static const struct YamlNode struct_anonymous_36[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_UNSIGNED( "index", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_37[] = { YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), YAML_END }; -static const struct YamlNode struct_anonymous_19[] = { +static const struct YamlNode struct_anonymous_38[] = { + YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_39[] = { YAML_UNSIGNED( "source", 8 ), YAML_PADDING( 24 ), YAML_END }; -static const struct YamlNode struct_anonymous_20[] = { +static const struct YamlNode struct_anonymous_40[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_PADDING( 24 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_41[] = { YAML_UNSIGNED( "gps", 8 ), YAML_UNSIGNED( "alt", 8 ), YAML_PADDING( 16 ), YAML_END }; -static const struct YamlNode union_anonymous_15_elmts[] = { - YAML_STRUCT("custom", 32, struct_anonymous_16, NULL), - YAML_STRUCT("cell", 32, struct_anonymous_17, NULL), - YAML_STRUCT("calc", 32, struct_anonymous_18, NULL), - YAML_STRUCT("consumption", 32, struct_anonymous_19, NULL), - YAML_STRUCT("dist", 32, struct_anonymous_20, NULL), +static const struct YamlNode struct_anonymous_42[] = { + YAML_UNSIGNED( "gps", 8 ), + YAML_UNSIGNED( "alt", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode union_anonymous_32_elmts[] = { + YAML_STRUCT("", 32, struct_anonymous_33, NULL), + YAML_STRUCT("custom", 32, struct_anonymous_34, NULL), + YAML_STRUCT("", 32, struct_anonymous_35, NULL), + YAML_STRUCT("cell", 32, struct_anonymous_36, NULL), + YAML_STRUCT("", 32, struct_anonymous_37, NULL), + YAML_STRUCT("calc", 32, struct_anonymous_38, NULL), + YAML_STRUCT("", 32, struct_anonymous_39, NULL), + YAML_STRUCT("consumption", 32, struct_anonymous_40, NULL), + YAML_STRUCT("", 32, struct_anonymous_41, NULL), + YAML_STRUCT("dist", 32, struct_anonymous_42, NULL), YAML_UNSIGNED( "param", 32 ), YAML_END }; static const struct YamlNode struct_TelemetrySensor[] = { YAML_IDX, - YAML_UNION("id1", 16, union_anonymous_12_elmts, select_id1), - YAML_UNION("id2", 8, union_anonymous_13_elmts, select_id2), + YAML_UNION("id1", 16, union_anonymous_28_elmts, select_id1), + YAML_UNION("id2", 8, union_anonymous_29_elmts, select_id2), YAML_STRING("label", 4), YAML_UNSIGNED( "subId", 8 ), YAML_ENUM("type", 1, enum_TelemetrySensorType), @@ -802,7 +957,7 @@ static const struct YamlNode struct_TelemetrySensor[] = { YAML_UNSIGNED( "persistent", 1 ), YAML_UNSIGNED( "onlyPositive", 1 ), YAML_PADDING( 1 ), - YAML_UNION("cfg", 32, union_anonymous_15_elmts, select_sensor_cfg), + YAML_UNION("cfg", 32, union_anonymous_32_elmts, select_sensor_cfg), YAML_END }; static const struct YamlNode struct_WidgetPersistentData[] = { @@ -832,6 +987,7 @@ static const struct YamlNode struct_TopBarPersistentData[] = { YAML_END }; static const struct YamlNode struct_ModelData[] = { + YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 248, struct_ModelHeader, NULL), YAML_ARRAY("timers", 128, 3, struct_TimerData, NULL), YAML_UNSIGNED( "telemetryProtocol", 3 ), @@ -845,6 +1001,9 @@ static const struct YamlNode struct_ModelData[] = { YAML_UNSIGNED( "extendedLimits", 1 ), YAML_UNSIGNED( "extendedTrims", 1 ), YAML_UNSIGNED( "throttleReversed", 1 ), + YAML_UNSIGNED( "enableCustomThrottleWarning", 1 ), + YAML_PADDING( 7 ), + YAML_SIGNED( "customThrottleWarningPosition", 8 ), YAML_UNSIGNED( "beepANACenter", 16 ), YAML_ARRAY("mixData", 160, 64, struct_MixData, NULL), YAML_ARRAY("limitData", 104, 32, struct_LimitData, NULL), @@ -862,9 +1021,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_STRUCT("varioData", 40, struct_VarioData, NULL), YAML_UNSIGNED_CUST( "rssiSource", 8, r_tele_sensor, w_tele_sensor ), YAML_STRUCT("rssiAlarms", 16, struct_RssiAlarmData, NULL), - YAML_PADDING( 3 ), 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), @@ -885,7 +1045,7 @@ static const struct YamlNode struct_PartialModel[] = { YAML_END }; -#define MAX_RADIODATA_MODELDATA_PARTIALMODEL_STR_LEN 24 +#define MAX_RADIODATA_MODELDATA_PARTIALMODEL_STR_LEN 29 static const struct YamlNode __RadioData_root_node = YAML_ROOT( struct_RadioData ); diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 988571b295b..13b4de9ad25 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -40,6 +40,10 @@ 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) @@ -48,34 +52,24 @@ add_definitions(-DSOFTWARE_VOLUME) # defines existing internal modules set(DEFAULT_INTERNAL_MODULE NONE CACHE STRING "No internal module") -set(TARGET_SRC - ${TARGET_SRC} - ../common/arm/stm32/audio_dac_driver.cpp - ../common/arm/stm32/mixer_scheduler_driver.cpp - ../common/arm/stm32/timers_driver.cpp - ) - -set(FIRMWARE_TARGET_SRC - ${FIRMWARE_TARGET_SRC} - battery_driver.cpp - ../horus/flyskyHallStick_driver.cpp - ../common/arm/stm32/stm32_hal_adc.cpp - ../../hal/adc_driver.cpp - ) - 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 ../horus/i2c_driver.cpp) +set(HARDWARE_TOUCH YES) set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) set(FIRMWARE_DEPENDENCIES datacopy) set(HARDWARE_TOUCH ON) set(SOFTWARE_KEYBOARD ON) -add_definitions(-DSTM32F429_439xx -DSDRAM -DCOLORLCD -DLIBOPENUI - -DHARDWARE_TOUCH -DHARDWARE_KEYS -DSOFTWARE_KEYBOARD) +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}) @@ -95,6 +89,10 @@ endif() # disable for now as it crashes on radio #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) @@ -115,25 +113,6 @@ set(SRC set(GVAR_SCREEN model_gvars.cpp) -set(TARGET_SRC - ${TARGET_SRC} - extmodule_helper.cpp - ../horus/extmodule_driver.cpp - trainer_driver.cpp - ) - -set(FIRMWARE_TARGET_SRC - ${FIRMWARE_TARGET_SRC} - ${LCD_DRIVER} - ${TOUCH_DRIVER} - board.cpp - backlight_driver.cpp - sdram_driver.c - startup_stm32f42_43xxx.s - ../common/arm/stm32/pwr_driver.cpp - ../common/arm/stm32/sdio_sd.c - ) - if(BOOTLOADER) set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} @@ -154,6 +133,35 @@ if (MULTIMODULE) ) endif() +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + board.cpp + startup_stm32f42_43xxx.s + ${LCD_DRIVER} + ${TOUCH_DRIVER} + sdram_driver.c + trainer_driver.cpp + battery_driver.cpp + backlight_driver.cpp + extmodule_helper.cpp + ../horus/flyskyHallStick_driver.cpp + ) + +set(FIRMWARE_SRC + ${FIRMWARE_SRC} + hal/adc_driver.cpp + targets/common/arm/stm32/audio_dac_driver.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/extmodule_serial_driver.cpp + targets/common/arm/stm32/stm32_usart_driver.cpp + targets/common/arm/stm32/pwr_driver.cpp + targets/common/arm/stm32/sdio_sd.c + ) + # Make malloc() thread-safe add_definitions(-DTHREADSAFE_MALLOC) diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index c434822d9bd..57fd69cf668 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -26,8 +26,8 @@ #include "debug.h" #include "hal/adc_driver.h" -#include "../common/arm/stm32/stm32_hal_adc.h" -#include "../../timers.h" +#include "stm32_hal_adc.h" +#include "timers_driver.h" #include "../../debounce.h" #include "bitmapbuffer.h" @@ -125,11 +125,8 @@ void boardInit() __enable_irq(); #endif -#if defined(DEBUG) && defined(AUX_SERIAL) - auxSerialInit(UART_MODE_DEBUG, 0); // default serial mode (None if DEBUG not defined) -#endif -#if defined(DEBUG) && defined(AUX2_SERIAL) - aux2SerialInit(UART_MODE_DEBUG, 0); // default serial mode (None if DEBUG not defined) +#if defined(DEBUG) + serialInit(SP_AUX1, UART_MODE_DEBUG); #endif TRACE("\nPL18 board started :)"); @@ -248,6 +245,11 @@ void boardOff() } } +const etx_serial_port_t* auxSerialGetPort(int port_nr) +{ + return nullptr; +} + int usbPlugged() { static PinDebounce debounce; diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 6f5076d624a..4d36178b9eb 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -111,11 +111,14 @@ uint32_t isBootloaderStart(const uint8_t * buffer); void SDRAM_Init(); // Pulses driver +#define INTERNAL_MODULE_OFF() +#define INTERNAL_MODULE_ON() void EXTERNAL_MODULE_ON(); void EXTERNAL_MODULE_OFF(); #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) @@ -414,6 +417,7 @@ bool pwrPressed(); #endif uint32_t pwrPressedDuration();; +const etx_serial_port_t* auxSerialGetPort(int port_nr); #define AUX_SERIAL_POWER_ON() #define AUX_SERIAL_POWER_OFF() @@ -504,12 +508,13 @@ int32_t getVolume(); #define VOLUME_LEVEL_DEF 12 // Telemetry driver +#define INTMODULE_FIFO_SIZE 512 #define TELEMETRY_FIFO_SIZE 512 void telemetryPortInit(uint32_t baudrate, uint8_t mode); void telemetryPortSetDirectionOutput(); void telemetryPortSetDirectionInput(); void sportSendBuffer(const uint8_t * buffer, uint32_t count); -bool telemetryGetByte(uint8_t * byte); +bool sportGetByte(uint8_t * byte); void telemetryClearFifo(); void sportSendByte(uint8_t byte); extern uint32_t telemetryErrors; diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 09ef1b57a45..c6898f5b4a7 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -454,29 +454,33 @@ (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) #define EXTMODULE_RCC_APB1Periph 0 -#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) -#define EXTMODULE_TX_GPIO GPIOC -#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 -#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 -#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 -#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 -#define EXTMODULE_RX_GPIO GPIOC -#define EXTMODULE_RX_GPIO_PIN GPIO_Pin_7 // PC.07 -#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 -#define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 -#define EXTMODULE_TIMER TIM8 -#define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn -#define EXTMODULE_TIMER_CC_IRQn TIM8_CC_IRQn -#define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler -#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#define EXTMODULE_TIMER_TX_GPIO_AF GPIO_AF_TIM8 +#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) +#define EXTMODULE_TX_GPIO GPIOC +#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 +#define EXTMODULE_TX_GPIO_PIN_LL LL_GPIO_PIN_6 +#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 +#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 +#define EXTMODULE_TX_GPIO_AF_USART GPIO_AF_USART6 +#define EXTMODULE_RX_GPIO GPIOC +#define EXTMODULE_RX_GPIO_PIN GPIO_Pin_7 // PC.07 +#define EXTMODULE_RX_GPIO_PIN_LL LL_GPIO_PIN_7 +#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 +#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 GPIO_AF_TIM8 //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 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_TX_DMA_IRQn DMA2_Stream7_IRQn #define EXTMODULE_USART_TX_DMA_IRQHandler DMA2_Stream7_IRQHandler #define EXTMODULE_USART_TX_DMA_FLAG_TC DMA_IT_TCIF7 @@ -494,6 +498,8 @@ //TIMER #define EXTMODULE_TIMER_DMA_CHANNEL 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_TIMER_DMA_FLAG_TC DMA_IT_TCIF1 @@ -574,10 +580,10 @@ #define TIMER_2MHz_TIMER TIM7 // Mixer scheduler timer -#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13 -#define MIXER_SCHEDULER_TIMER TIM13 +#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_UP_TIM13_IRQn -#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler +#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/telemetry_driver.cpp b/radio/src/targets/pl18/telemetry_driver.cpp index 53936f9148f..b94ce7abc17 100644 --- a/radio/src/targets/pl18/telemetry_driver.cpp +++ b/radio/src/targets/pl18/telemetry_driver.cpp @@ -215,7 +215,7 @@ extern "C" void TELEMETRY_USART_IRQHandler(void) } // TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp -bool telemetryGetByte(uint8_t * byte) +bool sportGetByte(uint8_t * byte) { #if defined(PCBX12S) if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) From a629da512f553c224e52fa3dd4e532de82ea0913 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Wed, 13 Apr 2022 19:01:54 +0200 Subject: [PATCH 66/99] add missing typedef --- radio/src/opentx_types.h | 1 + 1 file changed, 1 insertion(+) 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; From 876d463c1b842928029057962d9791bc3067e2ad Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Wed, 13 Apr 2022 19:16:52 +0200 Subject: [PATCH 67/99] fix pl18 simu compilation --- radio/src/targets/pl18/board.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 4d36178b9eb..1e254e25d90 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -111,6 +111,8 @@ uint32_t isBootloaderStart(const uint8_t * buffer); void SDRAM_Init(); // Pulses driver +#if !defined(SIMU) + #define INTERNAL_MODULE_OFF() #define INTERNAL_MODULE_ON() void EXTERNAL_MODULE_ON(); @@ -122,6 +124,19 @@ void EXTERNAL_MODULE_OFF(); #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(); From 5288feb11d14b06bb10118442d06a7c97fd279e9 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Wed, 13 Apr 2022 19:30:36 +0200 Subject: [PATCH 68/99] fix PL18 simu lcd --- radio/src/simu.cpp | 45 ++++++++++++++++++++++-------- radio/src/targets/simu/simulcd.cpp | 20 ++++++------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index a1ebbdb2df3..a31133bd8d8 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -37,14 +37,14 @@ #include "hal/adc_driver.h" #include "hal/rotary_encoder.h" -#if LCD_PHYS_W > 212 +#if LCD_W > 212 #define LCD_ZOOM 1 #else #define LCD_ZOOM 2 #endif -#define W2 LCD_PHYS_W*LCD_ZOOM -#define H2 LCD_PHYS_H*LCD_ZOOM +#define W2 LCD_W*LCD_ZOOM +#define H2 LCD_H*LCD_ZOOM #if defined(HARDWARE_TOUCH) #define TAP_TIME 25 @@ -195,7 +195,7 @@ void OpenTxSim::createBitmap(int index, uint16_t *data, int x, int y, int w, int for (int i=0; i>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f); snapshot.setPixel(i, j, color); } @@ -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 @@ -520,7 +541,7 @@ long OpenTxSim::onTimeout(FXObject*, FXSelector, void*) return 0; } -#if LCD_PHYS_W >= 212 +#if LCD_W >= 212 #define BL_COLOR FXRGB(47, 123, 227) #else #define BL_COLOR FXRGB(150, 200, 152) @@ -562,7 +583,7 @@ void OpenTxSim::refreshDisplay() 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_PHYS_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_PHYS_W] & (1<<(y%8))) { + if (simuLcdBuf[x+(y/8)*LCD_W] & (1<<(y%8))) { setPixel(x, y, onColor); } #endif diff --git a/radio/src/targets/simu/simulcd.cpp b/radio/src/targets/simu/simulcd.cpp index dc1320ac949..757f07bb44c 100644 --- a/radio/src/targets/simu/simulcd.cpp +++ b/radio/src/targets/simu/simulcd.cpp @@ -67,7 +67,7 @@ static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_ar 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,7 +124,7 @@ 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 dst = simuLcdBuf + y1 * LCD_PHYS_W + x1; + auto dst = simuLcdBuf + y1 * LCD_W + x1; auto px_src = src; auto px_dst = dst + offset; From ccd2c5d2d6dd72ff7064bce9961cea5c6c879cb3 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 12 Jun 2022 15:10:27 +0200 Subject: [PATCH 69/99] adapt to changes from main --- radio/src/cli.cpp | 2 ++ .../src/targets/pl18/bootloader/boot_menu.cpp | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 7d421f2ba29..67cfd2c9299 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1038,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) @@ -1058,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*); diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp index f5e8b623a3f..7a00d985b3a 100644 --- a/radio/src/targets/pl18/bootloader/boot_menu.cpp +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -33,20 +33,20 @@ #define DOUBLE_PADDING 56 #define MESSAGE_TOP (LCD_H - (2*DOUBLE_PADDING)) -const uint8_t __bmp_plug_usb_rle[] { +const uint8_t __bmp_plug_usb[] { #include "bmp_plug_usb.lbm" }; -RLEBitmap BMP_PLUG_USB(BMP_ARGB4444, __bmp_plug_usb_rle); +LZ4Bitmap BMP_PLUG_USB(BMP_ARGB4444, __bmp_plug_usb); -const uint8_t __bmp_usb_plugged_rle[] { +const uint8_t __bmp_usb_plugged[] { #include "bmp_usb_plugged.lbm" }; -RLEBitmap BMP_USB_PLUGGED(BMP_ARGB4444, __bmp_usb_plugged_rle); +LZ4Bitmap BMP_USB_PLUGGED(BMP_ARGB4444, __bmp_usb_plugged); -const uint8_t __bmp_background_rle[] { +const uint8_t __bmp_background[] { #include "bmp_background.lbm" }; -RLEBitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background_rle); +LZ4Bitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background); const uint8_t LBM_FILE[] = { #include "icon_file.lbm" @@ -91,7 +91,8 @@ static void bootloaderDrawBackground() for (int i=0; idrawBitmap(i, j, &BMP_BACKGROUND); + BitmapBuffer* bg_bmp = &BMP_BACKGROUND; + _background->drawBitmap(i, j, bg_bmp); } } _background->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, @@ -126,7 +127,7 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) pos -= 79; lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, pos, 26, 2, BL_SELECTED); - lcd->drawBitmap(center - 55, 165, &BMP_PLUG_USB); + 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); @@ -136,7 +137,7 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); } else if (st == ST_USB) { - lcd->drawBitmap(center - 26, 98, &BMP_USB_PLUGGED); + 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 || From 25f881c27f55175f422e6654f60e6675b114f00c Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 12 Jun 2022 17:03:44 +0200 Subject: [PATCH 70/99] repair simu after rebase --- radio/src/targets/simu/simulcd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/simu/simulcd.cpp b/radio/src/targets/simu/simulcd.cpp index 757f07bb44c..57e1f76823b 100644 --- a/radio/src/targets/simu/simulcd.cpp +++ b/radio/src/targets/simu/simulcd.cpp @@ -124,7 +124,7 @@ 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 dst = simuLcdBuf + y1 * LCD_W + x1; + auto offset = y1 * LCD_PHYS_W + x1; auto px_src = src; auto px_dst = dst + offset; @@ -142,7 +142,7 @@ static void simuRefreshLcd(lv_disp_drv_t * disp_drv, uint16_t *buffer, const rec { #if !defined(LCD_VERTICAL_INVERT) // rename into "Use direct mode" ??? // Direct mode: driver flush is called on final LVGL flush - dst += LCD_PHYS_W - copy_area.w; + // simply set LVGL's buffer as our current frame buffer simuLcdBuf = buffer; From abb803dd1bcbc13d2d4df29e98786caf75c0225e Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 26 Jun 2022 23:23:55 +0200 Subject: [PATCH 71/99] merge fixes --- radio/src/gui/colorlcd/radio_version.cpp | 2 +- .../storage/yaml/yaml_datastructs_pl18.cpp | 237 ++++-------------- radio/src/targets/pl18/CMakeLists.txt | 2 +- 3 files changed, 51 insertions(+), 190 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp index ad9c9f8f985..3cfa04f73b2 100644 --- a/radio/src/gui/colorlcd/radio_version.cpp +++ b/radio/src/gui/colorlcd/radio_version.cpp @@ -331,7 +331,7 @@ RadioVersionPage::RadioVersionPage(): { } -#if defined(PCBNV14) +#if defined(PCBNV14) || defined(PCBPL18) extern const char* boardLcdType; #endif diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 5d61f5615d9..6c7683009b9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -319,40 +319,21 @@ static const struct YamlNode struct_anonymous_1[] = { YAML_END }; static const struct YamlNode struct_anonymous_2[] = { - YAML_STRING("name", 6), - YAML_END -}; -static const struct YamlNode struct_anonymous_3[] = { YAML_SIGNED( "val", 16 ), YAML_UNSIGNED( "mode", 8 ), YAML_UNSIGNED( "param", 8 ), YAML_SIGNED( "spare", 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_4[] = { - YAML_SIGNED( "val", 16 ), - YAML_UNSIGNED( "mode", 8 ), - YAML_UNSIGNED( "param", 8 ), - YAML_SIGNED( "spare", 16 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_5[] = { - YAML_SIGNED( "val1", 32 ), - YAML_SIGNED( "val2", 16 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_6[] = { +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("", 48, struct_anonymous_1, NULL), - YAML_STRUCT("play", 48, struct_anonymous_2, NULL), - YAML_STRUCT("", 48, struct_anonymous_3, NULL), - YAML_STRUCT("all", 48, struct_anonymous_4, NULL), - YAML_STRUCT("", 48, struct_anonymous_5, NULL), - YAML_STRUCT("clear", 48, struct_anonymous_6, NULL), + 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[] = { @@ -407,7 +388,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "view", 8 ), YAML_PADDING( 2 ), YAML_UNSIGNED( "fai", 1 ), - YAML_ENUM("beepMode", 2, enum_BeeperMode), + YAML_SIGNED_CUST( "beepMode", 2, r_beeperMode, w_beeperMode ), YAML_UNSIGNED( "alarmsFlash", 1 ), YAML_UNSIGNED( "disableMemoryWarning", 1 ), YAML_UNSIGNED( "disableAlarmWarning", 1 ), @@ -457,7 +438,7 @@ static const struct YamlNode struct_RadioData[] = { 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", 4, 4, struct_serialConfig, 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), @@ -472,6 +453,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_STRUCT("themeData", 480, struct_OpenTxTheme__PersistentData, NULL), YAML_STRING("ownerRegistrationID", 8), YAML_SIGNED( "uartSampleMode", 2 ), + YAML_PADDING( 6 ), YAML_END }; static const struct YamlNode struct_unsigned_8[] = { @@ -538,7 +520,7 @@ static const struct YamlNode struct_LimitData[] = { static const struct YamlNode struct_ExpoData[] = { YAML_UNSIGNED( "mode", 2 ), YAML_UNSIGNED( "scale", 14 ), - YAML_ENUM("srcRaw", 10, enum_MixSources), + YAML_UNSIGNED_CUST( "srcRaw", 10, r_mixSrcRaw, w_mixSrcRaw ), YAML_SIGNED( "carryTrim", 6 ), YAML_UNSIGNED( "chn", 5 ), YAML_SIGNED_CUST( "swtch", 9, r_swtchSrc, w_swtchSrc ), @@ -598,7 +580,7 @@ static const struct YamlNode struct_FlightModeData[] = { YAML_IDX, YAML_ARRAY("trim", 16, 8, struct_trim_t, NULL), YAML_STRING("name", 10), - YAML_ENUM("swtch", 9, enum_SwitchSources), + YAML_SIGNED_CUST( "swtch", 9, r_swtchSrc, w_swtchSrc ), YAML_PADDING( 7 ), YAML_UNSIGNED( "fadeIn", 8 ), YAML_UNSIGNED( "fadeOut", 8 ), @@ -633,33 +615,14 @@ static const struct YamlNode struct_RssiAlarmData[] = { YAML_SIGNED( "critical", 6 ), YAML_END }; -static const struct YamlNode struct_anonymous_8[] = { +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_9[] = { - YAML_SIGNED( "delay", 6 ), - YAML_UNSIGNED( "pulsePol", 1 ), - YAML_UNSIGNED( "outputType", 1 ), - YAML_SIGNED( "frameLength", 8 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_10[] = { - 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_11[] = { +static const struct YamlNode struct_anonymous_5[] = { YAML_PADDING( 8 ), YAML_UNSIGNED( "disableTelemetry", 1 ), YAML_UNSIGNED( "disableMapping", 1 ), @@ -671,16 +634,7 @@ static const struct YamlNode struct_anonymous_11[] = { YAML_SIGNED( "optionValue", 8 ), YAML_END }; -static const struct YamlNode struct_anonymous_12[] = { - 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_13[] = { +static const struct YamlNode struct_anonymous_6[] = { YAML_UNSIGNED( "power", 2 ), YAML_PADDING( 2 ), YAML_UNSIGNED( "receiverTelemetryOff", 1 ), @@ -689,14 +643,7 @@ static const struct YamlNode struct_anonymous_13[] = { YAML_PADDING( 8 ), YAML_END }; -static const struct YamlNode struct_anonymous_14[] = { - YAML_PADDING( 6 ), - YAML_UNSIGNED( "noninverted", 1 ), - YAML_PADDING( 1 ), - YAML_SIGNED( "refreshRate", 8 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_15[] = { +static const struct YamlNode struct_anonymous_7[] = { YAML_PADDING( 6 ), YAML_UNSIGNED( "noninverted", 1 ), YAML_PADDING( 1 ), @@ -708,27 +655,13 @@ static const struct YamlNode struct_string_64[] = { YAML_STRING("val", 8), YAML_END }; -static const struct YamlNode struct_anonymous_16[] = { - 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_17[] = { +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_18[] = { - 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_19[] = { +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 ), @@ -736,18 +669,7 @@ static const struct YamlNode struct_anonymous_19[] = { YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), YAML_END }; -static const struct YamlNode struct_anonymous_20[] = { - YAML_UNSIGNED( "bindPower", 3 ), - YAML_UNSIGNED( "runPower", 3 ), - YAML_UNSIGNED( "emi", 1 ), - YAML_UNSIGNED( "telemetry", 1 ), - YAML_UNSIGNED( "failsafeTimeout", 16 ), - YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), - YAML_UNSIGNED( "mode", 2 ), - YAML_UNSIGNED( "reserved", 6 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_21[] = { +static const struct YamlNode struct_anonymous_10[] = { YAML_UNSIGNED( "bindPower", 3 ), YAML_UNSIGNED( "runPower", 3 ), YAML_UNSIGNED( "emi", 1 ), @@ -758,56 +680,32 @@ static const struct YamlNode struct_anonymous_21[] = { YAML_UNSIGNED( "reserved", 6 ), YAML_END }; -static const struct YamlNode struct_anonymous_22[] = { - YAML_UNSIGNED( "raw12bits", 1 ), - YAML_UNSIGNED( "telemetryBaudrate", 3 ), - YAML_PADDING( 4 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_23[] = { +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_24[] = { - YAML_UNSIGNED( "telemetryBaudrate", 3 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_25[] = { +static const struct YamlNode struct_anonymous_12[] = { YAML_UNSIGNED( "telemetryBaudrate", 3 ), YAML_END }; -static const struct YamlNode struct_anonymous_26[] = { - YAML_UNSIGNED( "flags", 8 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_27[] = { +static const struct YamlNode struct_anonymous_13[] = { YAML_UNSIGNED( "flags", 8 ), YAML_END }; -static const struct YamlNode union_anonymous_7_elmts[] = { +static const struct YamlNode union_anonymous_4_elmts[] = { YAML_ARRAY("raw", 8, 25, struct_unsigned_8, NULL), - YAML_STRUCT("", 16, struct_anonymous_8, NULL), - YAML_STRUCT("ppm", 16, struct_anonymous_9, NULL), - YAML_STRUCT("", 24, struct_anonymous_10, NULL), - YAML_STRUCT("multi", 24, struct_anonymous_11, NULL), - YAML_STRUCT("", 16, struct_anonymous_12, NULL), - YAML_STRUCT("pxx", 16, struct_anonymous_13, NULL), - YAML_STRUCT("", 16, struct_anonymous_14, NULL), - YAML_STRUCT("sbus", 16, struct_anonymous_15, NULL), - YAML_STRUCT("", 200, struct_anonymous_16, NULL), - YAML_STRUCT("pxx2", 200, struct_anonymous_17, NULL), - YAML_STRUCT("", 56, struct_anonymous_18, NULL), - YAML_STRUCT("flysky", 56, struct_anonymous_19, NULL), - YAML_STRUCT("", 64, struct_anonymous_20, NULL), - YAML_STRUCT("afhds3", 64, struct_anonymous_21, NULL), - YAML_STRUCT("", 8, struct_anonymous_22, NULL), - YAML_STRUCT("ghost", 8, struct_anonymous_23, NULL), - YAML_STRUCT("", 8, struct_anonymous_24, NULL), - YAML_STRUCT("crsf", 8, struct_anonymous_25, NULL), - YAML_STRUCT("", 8, struct_anonymous_26, NULL), - YAML_STRUCT("dsmp", 8, struct_anonymous_27, 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", 64, 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[] = { @@ -818,7 +716,7 @@ static const struct YamlNode struct_ModuleData[] = { YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), YAML_ENUM("failsafeMode", 4, enum_FailsafeModes), YAML_PADDING( 4 ), - YAML_UNION("mod", 200, union_anonymous_7_elmts, select_mod_type), + YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), YAML_END }; static const struct YamlNode struct_TrainerModuleData[] = { @@ -853,98 +751,61 @@ static const struct YamlNode struct_string_32[] = { YAML_STRING("val", 4), YAML_END }; -static const struct YamlNode union_anonymous_28_elmts[] = { +static const struct YamlNode union_anonymous_14_elmts[] = { YAML_UNSIGNED( "id", 16 ), YAML_UNSIGNED( "persistentValue", 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_30[] = { - YAML_UNSIGNED( "physID", 5 ), - YAML_UNSIGNED( "rxIndex", 3 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_31[] = { +static const struct YamlNode struct_anonymous_16[] = { YAML_UNSIGNED( "physID", 5 ), YAML_UNSIGNED( "rxIndex", 3 ), YAML_END }; -static const struct YamlNode union_anonymous_29_elmts[] = { - YAML_STRUCT("", 8, struct_anonymous_30, NULL), - YAML_STRUCT("frskyInstance", 8, struct_anonymous_31, NULL), +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_33[] = { - YAML_UNSIGNED( "ratio", 16 ), - YAML_SIGNED( "offset", 16 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_34[] = { +static const struct YamlNode struct_anonymous_18[] = { YAML_UNSIGNED( "ratio", 16 ), YAML_SIGNED( "offset", 16 ), YAML_END }; -static const struct YamlNode struct_anonymous_35[] = { - YAML_UNSIGNED( "source", 8 ), - YAML_UNSIGNED( "index", 8 ), - YAML_PADDING( 16 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_36[] = { +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_37[] = { - YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), - YAML_END -}; -static const struct YamlNode struct_anonymous_38[] = { +static const struct YamlNode struct_anonymous_20[] = { YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), YAML_END }; -static const struct YamlNode struct_anonymous_39[] = { - YAML_UNSIGNED( "source", 8 ), - YAML_PADDING( 24 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_40[] = { +static const struct YamlNode struct_anonymous_21[] = { YAML_UNSIGNED( "source", 8 ), YAML_PADDING( 24 ), YAML_END }; -static const struct YamlNode struct_anonymous_41[] = { - YAML_UNSIGNED( "gps", 8 ), - YAML_UNSIGNED( "alt", 8 ), - YAML_PADDING( 16 ), - YAML_END -}; -static const struct YamlNode struct_anonymous_42[] = { +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_32_elmts[] = { - YAML_STRUCT("", 32, struct_anonymous_33, NULL), - YAML_STRUCT("custom", 32, struct_anonymous_34, NULL), - YAML_STRUCT("", 32, struct_anonymous_35, NULL), - YAML_STRUCT("cell", 32, struct_anonymous_36, NULL), - YAML_STRUCT("", 32, struct_anonymous_37, NULL), - YAML_STRUCT("calc", 32, struct_anonymous_38, NULL), - YAML_STRUCT("", 32, struct_anonymous_39, NULL), - YAML_STRUCT("consumption", 32, struct_anonymous_40, NULL), - YAML_STRUCT("", 32, struct_anonymous_41, NULL), - YAML_STRUCT("dist", 32, struct_anonymous_42, NULL), +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_28_elmts, select_id1), - YAML_UNION("id2", 8, union_anonymous_29_elmts, select_id2), + 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), @@ -957,7 +818,7 @@ static const struct YamlNode struct_TelemetrySensor[] = { YAML_UNSIGNED( "persistent", 1 ), YAML_UNSIGNED( "onlyPositive", 1 ), YAML_PADDING( 1 ), - YAML_UNION("cfg", 32, union_anonymous_32_elmts, select_sensor_cfg), + YAML_UNION("cfg", 32, union_anonymous_17_elmts, select_sensor_cfg), YAML_END }; static const struct YamlNode struct_WidgetPersistentData[] = { diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 13b4de9ad25..b5349ffe3e5 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -56,7 +56,7 @@ 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 ../horus/i2c_driver.cpp) +set(TOUCH_DRIVER tp_cst340.cpp) set(HARDWARE_TOUCH YES) set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) set(FIRMWARE_DEPENDENCIES datacopy) From ad22d5f7c1d040861f006ca6e1085f49b849ebb5 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 26 Jun 2022 23:25:12 +0200 Subject: [PATCH 72/99] support another touch controller on POL18, CST340 broken for now --- radio/src/targets/pl18/battery_driver.cpp | 3 +- radio/src/targets/pl18/board.cpp | 2 + radio/src/targets/pl18/hal.h | 33 +- radio/src/targets/pl18/tp_cst340.cpp | 781 ++++++++++++++-------- radio/src/targets/pl18/tp_cst340.h | 222 +++++- radio/src/tasks.cpp | 2 +- 6 files changed, 760 insertions(+), 283 deletions(-) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 0c56a9c3b03..db786bf7eba 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -155,6 +155,7 @@ void drawChargingInfo(uint16_t chargeState){ 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) { @@ -199,7 +200,7 @@ void handle_battery_charge(uint32_t last_press_time) lcdInit(); lcdInitDisplayDriver(); lcdInited = true; - touchPanelInit(); + TouchInit(); } else { lcdOn(); diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 57fd69cf668..d18ba1d2dea 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -20,6 +20,7 @@ */ #include "board.h" +#include "tp_cst340.h" #include "globals.h" #include "sdcard.h" #include "touch.h" @@ -140,6 +141,7 @@ void boardInit() flysky_hall_stick_init(); init2MhzTimer(); init1msTimer(); + TouchInit(); usbInit(); uint32_t press_start = 0; diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index c6898f5b4a7..defa48b3e8a 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -387,26 +387,31 @@ #define I2C_B1_RCC_APB1Periph RCC_APB1Periph_I2C1 #define I2C_B1 I2C1 #define I2C_B1_GPIO GPIOB -#define I2C_B1_SDA_GPIO_PIN GPIO_Pin_7 // PB.07 -#define I2C_B1_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 -#define I2C_B1_GPIO_AF GPIO_AF_I2C1 -#define I2C_B1_SDA_GPIO_PinSource GPIO_PinSource7 -#define I2C_B1_SCL_GPIO_PinSource GPIO_PinSource8 -#define I2C_B1_CLK_RATE 100000 +#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 GPIO_Pin_12 // PB.12 +#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 GPIO_Pin_9 // PB.09 -#define TOUCH_INT_EXTI_LINE1 EXTI_Line9 -#define TOUCH_INT_EXTI_IRQn1 EXTI9_5_IRQn -#define TOUCH_INT_EXTI_IRQHandler1 EXTI9_5_IRQHandler -#define TOUCH_INT_EXTI_PortSource EXTI_PortSourceGPIOB -#define TOUCH_INT_EXTI_PinSource1 EXTI_PinSource9 -#define TOUCH_INT_STATUS() (GPIO_ReadInputDataBit(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN)) +#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 diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index 91e94786b02..a7b303f064d 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -19,329 +19,536 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "../horus/i2c_driver.h" +#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" -bool touchCST340Flag = false; -volatile static bool touchEventOccured = false; -struct TouchData touchData; -uint16_t touchICfwver = 0; -uint32_t touchI2Chiccups = 0; +#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_CST836U_I2C_ADDRESS (0x15) + +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 -I2C_HandleTypeDef hi2c1; +struct TouchControllerDescriptor +{ + void (*read)(uint16_t * X, uint16_t * Y, uint32_t * event); + uint8_t (*detectTouch)(); + void (*printDebugInfo)(); + uint32_t contactEvent; +}; + +static const TouchControllerDescriptor *tc = nullptr; static TouchState internalTouchState = {}; -static void TOUCH_AF_ExtiStop(void) +void I2C_FreeBus() { - SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); - - EXTI_InitTypeDef EXTI_InitStructure; - EXTI_StructInit(&EXTI_InitStructure); - EXTI_InitStructure.EXTI_Line = TOUCH_INT_EXTI_LINE1; - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; - EXTI_InitStructure.EXTI_LineCmd = DISABLE; - EXTI_Init(&EXTI_InitStructure); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = TOUCH_INT_EXTI_IRQn1; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // Not used as 4 bits are used for the pre-emption priority - NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; - NVIC_Init(&NVIC_InitStructure); + 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"); } -static void TOUCH_AF_ExtiConfig(void) +// 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) { - SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); - - EXTI_InitTypeDef EXTI_InitStructure; - EXTI_StructInit(&EXTI_InitStructure); - EXTI_InitStructure.EXTI_Line = TOUCH_INT_EXTI_LINE1; - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; - EXTI_InitStructure.EXTI_LineCmd = ENABLE; - EXTI_Init(&EXTI_InitStructure); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = TOUCH_INT_EXTI_IRQn1; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; - 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); + if (GPIOx == GPIOB) + __HAL_RCC_GPIOB_CLK_ENABLE(); + else + return -1; + + return 0; } -bool I2C_CST340_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) +void I2C_Init() { - uint8_t uAddrAndBuf[6]; - uAddrAndBuf[0] = (uint8_t)((reg & 0xFF00) >> 8); - uAddrAndBuf[1] = (uint8_t)(reg & 0x00FF); - - if (len > 0) - { - for (int i = 0;i < len;i++) - { - uAddrAndBuf[i + 2] = buf[i]; - } - } + stm32_i2c_deinit(TOUCH_I2C_BUS); - if (HAL_I2C_Master_Transmit(&hi2c1, CST340_I2C_ADDR << 1, uAddrAndBuf, len + 2, 100) != HAL_OK) - { - TRACE("I2C B1 ERROR: WriteRegister failed"); - return false; - } - return true; + __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); } -bool I2C_CST340_ReadRegister(uint16_t reg, uint8_t * buf, uint8_t len) +#define I2C_TIMEOUT_MAX 5 // 5 ms + +bool touch_i2c_read(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) { - uint8_t uRegAddr[2]; - uRegAddr[0] = (uint8_t)((reg & 0xFF00) >> 8); - uRegAddr[1] = (uint8_t)(reg & 0x00FF); - - if (HAL_I2C_Master_Transmit(&hi2c1, CST340_I2C_ADDR << 1, uRegAddr, 2, 10) != HAL_OK) - { - TRACE("I2C B1 ERROR: ReadRegister write reg address failed"); - return false; - } +// 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; +// } - if (HAL_I2C_Master_Receive(&hi2c1, CST340_I2C_ADDR << 1, buf, len, 100) != HAL_OK) - { - TRACE("I2C B1 ERROR: ReadRegister read reg address failed"); - return false; - } - return true; + return true; } -void touchPanelDeInit(void) +#if 0 +static bool touch_i2c_write(uint8_t addr, uint8_t reg, uint8_t * data, uint8_t len) { - TOUCH_AF_ExtiStop(); - touchCST340Flag = false; + if (stm32_i2c_write(TOUCH_I2C_BUS, addr, reg, 1, data, len, I2C_TIMEOUT_MAX) < 0) + return false; + + return true; } -bool touchPanelInit(void) +static void TS_IO_Write(uint8_t addr, uint8_t reg, uint8_t data) { - uint8_t tmp[4] = {0}; + uint8_t tryCount = 3; + while (!touch_i2c_write(addr, reg, &data, 1)) { + if (--tryCount == 0) break; + I2C_Init(); + } +} +#endif - if (touchCST340Flag) { - TOUCH_AF_ExtiConfig(); - return true; +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(); } - else { - TRACE("Touchpanel init start ..."); - - delay_ms(1); - // TOUCH RST - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = TOUCH_RST_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(TOUCH_RST_GPIO, &GPIO_InitStructure); - - // TOUCH INT - GPIO_InitStructure.GPIO_Pin = TOUCH_INT_GPIO_PIN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_Init(TOUCH_INT_GPIO, &GPIO_InitStructure); - - // I2C configuration - hi2c1.Instance = I2C_B1; - hi2c1.Init.ClockSpeed = I2C_B1_CLK_RATE; - hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_16_9; - hi2c1.Init.OwnAddress1 = 0; - hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; - hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; - hi2c1.Init.OwnAddress2 = 0; - hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; - hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; - if (HAL_I2C_Init(&hi2c1) != HAL_OK) - { - TRACE("I2C B1 ERROR: HAL_I2C_Init() failed"); - return false; - } - // Configure analog filter - if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) - { - TRACE("I2C B1 ERROR: HAL_I2CEx_ConfigAnalogFilter() failed"); - return false; - } - // Configure digital filter - if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) - { - TRACE("I2C B1 ERROR: HAL_I2CEx_ConfigDigitalFilter() failed"); - return false; - } + return retult; +} - TPRST_LOW(); - delay_ms(1); // FlySky example uses 10ms, datasheet says 0.1us minimally - TPRST_HIGH(); - delay_ms(400); // FlySky example uses 20ms only, datasheet says 300ms +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; +} - TOUCH_AF_ExtiConfig(); +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 +} - // Enter info mode - if (!I2C_CST340_WriteRegister(CST340_MODE_DEBUG_INFO, tmp, 0)) { - TRACE("CST340 chip NOT FOUND"); - return false; - } - - if (!I2C_CST340_ReadRegister(CST340_CHIPTYPE_REG, tmp, 4)) { - TRACE("Error reading CST340 chip type!"); - return false; - } +/** + * @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; - // Check the value, expected ChipID - if ((tmp[0] << 8) + tmp[1] != CST340_CHIP_ID) { - TRACE("Error identifying CST340 touch controller chip!"); - return false; - } + /* Reset current active touch index on which to work on */ + tc_handle.currActiveTouchIdx = 0; + return (nbTouch); +} - TRACE("CST340 chip detected"); +#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 (!I2C_CST340_ReadRegister(CST340_FWVER_REG, tmp, 4)) { - TRACE("Error reading CST340 firmware version!"); - return false; - } + if (touchIdx < ft6x06_handle.currActiveTouchNb) { + switch (touchIdx) { + case 0 : + regAddress = FT6206_P1_WEIGHT_REG; + break; - // Enter normal mode - if (!I2C_CST340_WriteRegister(CST340_MODE_NORMAL, tmp, 0)) { - TRACE("ERROR chaning CST340 mode back to normal!"); - return false; - } + case 1 : + regAddress = FT6206_P2_WEIGHT_REG; + break; + + default : + break; + + } /* end switch(touchIdx) */ - touchICfwver = (tmp[0] << 8) + tmp[1]; - TRACE("CST340 FW version: %u", touchICfwver); - touchCST340Flag = true; + /* 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 - // Read once the finger register - if (!I2C_CST340_ReadRegister(CST340_FINGER1_REG, tmp, 4)) { - TRACE("Initial CST340 finger1 data register read failed!"); - return false; +/** + * @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++; } - return true; } -bool I2C_ReInit(void) +static void touch_cst340_debug_info(void) { - TRACE("I2C B1 ReInit"); - touchPanelDeInit(); - if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) - TRACE("I2C B1 ReInit - I2C DeInit failed"); - - // If DeInit fails, try to re-init anyway - if (!touchPanelInit()) - { - TRACE("I2C B1 ReInit - touchPanelInit failed"); - return false; - } - return true; +#if defined(DEBUG) + TRACE("cst836u: fw ver 0x%02X %02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_FW_VERSION_H_REG), TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_FW_VERSION_L_REG)); + TRACE("cst836u: module version 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_MODULE_VERSION_REG)); + TRACE("cst836u: project name 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_PROJECT_NAME_REG)); + TRACE("cst836u: chip type 0x%02X 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_CHIP_TYPE_H_REG), TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_CHIP_TYPE_L_REG)); +#endif } -#if defined(SIMU) || defined(SEMIHOSTING) || defined(DEBUG) -static const char* event2str(uint8_t ev) +/** + * @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 cst340_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) { - switch(ev){ - case TE_NONE: - return "NONE"; - case TE_UP: - return "UP"; - case TE_DOWN: - return "DOWN"; - case TE_SLIDE_END: - return "SLIDE_END"; - case TE_SLIDE: - return "SLIDE"; - default: - return "UNKNOWN"; + uint8_t regAddress = 0; + uint8_t dataxy[4]; + + if (tc_handle.currActiveTouchIdx < tc_handle.currActiveTouchNb) { + switch (tc_handle.currActiveTouchIdx) { + case 0 : +// regAddress = CST836U_TOUCH1_XH_REG; + break; + case 1 : +// regAddress = CST836U_TOUCH2_XH_REG; + break; + default : + break; + } + + /* Read X and Y positions */ +// TS_IO_ReadMultiple(TOUCH_CST340_I2C_ADDRESS, regAddress, dataxy, sizeof(dataxy)); + /* Send back ready X position to caller */ +// *X = ((dataxy[0] & CST836U_MSB_MASK) << 8) | dataxy[1]; + /* Send back ready Y position to caller */ +/// *Y = ((dataxy[2] & CST836U_MSB_MASK) << 8) | dataxy[3]; + +// *event = (dataxy[0] & CST836U_TOUCH_EVT_FLAG_MASK) >> CST836U_TOUCH_EVT_FLAG_SHIFT; + /* + uint32_t weight; + uint32_t area; + */ + tc_handle.currActiveTouchIdx++; } } -#endif -struct TouchState touchPanelRead() +/** + * @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 cst340_TS_DetectTouch() +{ + volatile uint8_t nbTouch = 0; + + /* Read register CST836U_TOUCH_NUM_REG to check number of touches detection */ +// nbTouch = TS_IO_Read(CST340_I2C_ADDR, CST836U_TOUCH_NUM_REG); +// if (nbTouch > CST836U_MAX_DETECTABLE_TOUCH) { +// /* If invalid number of touch detected, set it to zero */ +// 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 = 0 //CST340_TOUCH_EVT_FLAG_CONTACT +}; + +void detectTouchController() +{ + touchController = TC_CST340; + tc = &CST340; + touchController = TC_FT6236; + tc = &FT6236; +} + +void TouchInit() +{ + I2C_Init(); + TouchReset(); + detectTouchController(); + tc->printDebugInfo(); +} + +void handleTouch() { - static uint8_t touchData[4]; - static uint16_t x = 0; - static uint16_t y = 0; - - if (!touchEventOccured) - return internalTouchState; - - touchEventOccured = false; - - internalTouchState.deltaX = 0; - internalTouchState.deltaY = 0; - tmr10ms_t now = get_tmr10ms(); - internalTouchState.tapCount = 0; - - if (!I2C_CST340_ReadRegister(CST340_FINGER1_REG, touchData, 4)) { - touchI2Chiccups++; - TRACE("CST340 I2C read XY error"); - if (!I2C_ReInit()) - TRACE("I2C B1 ReInit failed"); - return internalTouchState; + unsigned short touchX; + unsigned short touchY; + uint32_t tEvent = 0; + tc->read(&touchX, &touchY, &tEvent); +#if 0 + unsigned short tmp = /*(480 - 1) - */touchY; + touchY = touchX; + touchX = tmp; +#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; } - - if( touchData[0] == 0x06 ) - { - x = LCD_PHYS_W - ((touchData[1]<<4) + ((touchData[3]>>4)&0x0f)); - y = LCD_PHYS_H - ((touchData[2]<<4) + ((touchData[3])&0x0f)); - if (internalTouchState.event == TE_NONE || internalTouchState.event == TE_UP || internalTouchState.event == TE_SLIDE_END) { - internalTouchState.event = TE_DOWN; - internalTouchState.startX = internalTouchState.x = x; - internalTouchState.startY = internalTouchState.y = y; - downTime = now; - } else { - internalTouchState.deltaX = x - internalTouchState.x; - internalTouchState.deltaY = y - internalTouchState.y; - if (internalTouchState.event == TE_SLIDE || - abs(internalTouchState.deltaX) >= SLIDE_RANGE || - abs(internalTouchState.deltaY) >= SLIDE_RANGE) - { - internalTouchState.event = TE_SLIDE; - internalTouchState.x = x; - internalTouchState.y = y; - } + 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 - { - if (internalTouchState.event == TE_SLIDE) { - internalTouchState.event = TE_SLIDE_END; - } 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 if (internalTouchState.event != TE_SLIDE_END) { - internalTouchState.event = TE_NONE; + else { + internalTouchState.event = TE_DOWN; + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; } - } - - TRACE("touch event = %s", event2str(internalTouchState.event)); - return internalTouchState; + } + else if (internalTouchState.event == TE_SLIDE) { + internalTouchState.event = TE_SLIDE; //no change + internalTouchState.deltaX = (short) dx; + internalTouchState.deltaY = (short) dy; + } + + } } -extern "C" void TOUCH_INT_EXTI_IRQHandler1(void) +#if !defined(TOUCH_INT_EXTI_IRQHandler) + #error "TOUCH_INT_EXTI_IRQHandler is not defined" +#endif +extern "C" void TOUCH_INT_EXTI_IRQHandler(void) { - if (EXTI_GetITStatus(TOUCH_INT_EXTI_LINE1) != RESET) { + if (LL_EXTI_IsEnabledIT_0_31(TOUCH_INT_EXTI_Line) && + LL_EXTI_IsActiveFlag_0_31(TOUCH_INT_EXTI_Line)) { touchEventOccured = true; - EXTI_ClearITPendingBit(TOUCH_INT_EXTI_LINE1); + LL_EXTI_ClearFlag_0_31(TOUCH_INT_EXTI_Line); } } @@ -350,8 +557,50 @@ 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 index 053641588c0..a009b9725b9 100644 --- a/radio/src/targets/pl18/tp_cst340.h +++ b/radio/src/targets/pl18/tp_cst340.h @@ -23,6 +23,226 @@ #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 @@ -43,7 +263,7 @@ extern bool touchCST340Flag; extern uint32_t touchI2Chiccups; extern uint16_t touchICfwver; -extern bool touchPanelInit(); +void TouchInit(); struct TouchState touchPanelRead(); bool touchPanelEventOccured(); diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 4309034b674..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(PCBNV14) && !defined(SIMU) +#if defined(HARDWARE_TOUCH) && !defined(PCBNV14) && !defined(PCBPL18) && !defined(SIMU) touchPanelInit(); #endif From dbd17c3d6e51bf4dd8e6f40bda1b320c2a9f97eb Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 26 Jun 2022 23:25:38 +0200 Subject: [PATCH 73/99] add support for another LCD version for PL18 --- radio/src/targets/pl18/lcd_driver.cpp | 1571 ++++++++++++++++++++++++- 1 file changed, 1555 insertions(+), 16 deletions(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 36a133f2d82..6c790b928ee 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -56,7 +56,7 @@ volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; static void LCD_Delay(void) { volatile unsigned int i; - for (i = 0; i < 20; i++) { + for (i = 0; i < 10000; i++) { ; } } @@ -181,7 +181,7 @@ static void lcdSpiConfig(void) { GPIO_Init(LCD_NRST_GPIO, &GPIO_InitStructure); /* Set the chip select pin aways low */ - CLR_LCD_CS(); + SET_LCD_CS(); } void lcdDelay() { @@ -216,6 +216,8 @@ unsigned char LCD_ReadByteOnFallingEdge(void) { } static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { + CLR_LCD_CS(); + LCD_SCK_LOW(); lcdDelay(); @@ -245,6 +247,7 @@ static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { } LCD_SCK_LOW(); + SET_LCD_CS(); } unsigned char LCD_ReadByte(void) { @@ -267,6 +270,7 @@ unsigned char LCD_ReadByte(void) { } CLR_LCD_CLK(); SET_LCD_DATA_OUTPUT(); + return (ReceiveData); } @@ -277,6 +281,7 @@ unsigned char LCD_ReadRegister(unsigned char Register) { LCD_DELAY(); LCD_DELAY(); ReadData = LCD_ReadByte(); + SET_LCD_CS(); return (ReadData); } @@ -385,11 +390,21 @@ void LCD_HX8357D_Init(void) { lcdWriteData(0x66); lcdWriteCommand(0x36); - lcdWriteData(0x08); + 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); -#else +#elif 0 delay_ms(50); lcdWriteCommand(0xB9); //EXTC lcdWriteData(0xFF); //EXTC @@ -512,7 +527,7 @@ void LCD_HX8357D_Init(void) { lcdWriteData(0x00); lcdWriteData(0x01); lcdWriteCommand(0x36); - lcdWriteData(0x18); + lcdWriteData(0x38); lcdWriteCommand(0x11); // SLPOUT delay_ms(200); @@ -520,14 +535,135 @@ void LCD_HX8357D_Init(void) { 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); #endif } void LCD_HX8357D_On(void) { + lcdWriteCommand(0x28); lcdWriteCommand(0x29); - lcdWriteCommand(0x22); } void LCD_HX8357D_Off(void) { @@ -1003,9 +1139,8 @@ void LCD_ST7796S_Init(void) { lcdWriteData( 0x96 ); lcdWriteCommand( 0x36 ); - lcdWriteData( 0x28 ); - // lcdWriteData( 0x88 ); - //lcdWriteData( 0xB8 ); + lcdWriteData( 0x28 ); + lcdWriteCommand( 0x2A ); lcdWriteData( 0x00 ); lcdWriteData( 0x00 ); @@ -1145,6 +1280,1403 @@ unsigned int LCD_ST7796S_ReadID(void) { 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 ); +} + static void lcdReset() { LCD_NRST_HIGH(); delay_ms(1); @@ -1182,7 +2714,8 @@ void LCD_Init_LTDC() { /* 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_IPC; + LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IIPC; /* Configure R,G,B component values for LCD background color */ LTDC_InitStruct.LTDC_BackgroundRedValue = 0; @@ -1309,25 +2842,25 @@ void lcdInit(void) LCD_AF_GPIOConfig(); /* Send LCD initialization commands */ - if (LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { + if (0 && LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { TRACE("LCD INIT: ILI9481"); boardLcdType = "ILI9481"; lcdInitFunction = LCD_ILI9481_Init; lcdOffFunction = LCD_ILI9481_Off; lcdOnFunction = LCD_ILI9481_On; - } else if (LCD_ILI9486_ReadID() == LCD_ILI9486_ID) { + } else if (0 && LCD_ILI9486_ReadID() == LCD_ILI9486_ID) { TRACE("LCD INIT: ILI9486"); boardLcdType = "ILI9486"; lcdInitFunction = LCD_ILI9486_Init; lcdOffFunction = LCD_ILI9486_Off; lcdOnFunction = LCD_ILI9486_On; - } else if (LCD_ILI9488_ReadID() == LCD_ILI9488_ID) { + } else if (0 && LCD_ILI9488_ReadID() == LCD_ILI9488_ID) { TRACE("LCD INIT: ILI9488"); boardLcdType = "ILI9488"; lcdInitFunction = LCD_ILI9488_Init; lcdOffFunction = LCD_ILI9488_Off; lcdOnFunction = LCD_ILI9488_On; - } else if (LCD_HX8357D_ReadID() == LCD_HX8357D_ID) { + } else if (LCD_HX8357D_ReadID() == LCD_HX8357D_ID || 1) { TRACE("LCD INIT: HX8357D"); boardLcdType = "HX8357D"; lcdInitFunction = LCD_HX8357D_Init; @@ -1339,9 +2872,15 @@ void lcdInit(void) lcdInitFunction = LCD_ST7796S_Init; lcdOffFunction = LCD_ST7796S_Off; lcdOnFunction = LCD_ST7796S_On; - } else { + } 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; +/* } else { TRACE("LCD INIT: unknown LCD controller"); - boardLcdType = "unknown"; + boardLcdType = "unknown";*/ } lcdInitFunction(); From 45489b64ce221d53057110a3d8d30316c7af244c Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Wed, 29 Jun 2022 22:25:26 +0200 Subject: [PATCH 74/99] fix(PL18): black screen after wake up --- radio/src/targets/pl18/lcd_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 6c790b928ee..b2097620145 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -667,7 +667,7 @@ void LCD_HX8357D_On(void) { } void LCD_HX8357D_Off(void) { - lcdWriteCommand(0x22); +// lcdWriteCommand(0x22); lcdWriteCommand(0x28); } From 7f012e9c843f6447c1236b6a2e35ba87180ce411 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Wed, 29 Jun 2022 22:58:56 +0200 Subject: [PATCH 75/99] support for both known touch controllers in PL18, files need to be renamed --- radio/src/targets/pl18/tp_cst340.cpp | 92 ++++++++++++++-------------- radio/src/targets/pl18/tp_cst340.h | 1 + 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index a7b303f064d..0b65d8fb267 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -39,7 +39,7 @@ volatile static bool touchEventOccured; #define TOUCH_SDA_GPIO_PIN I2C_B1_SDA_GPIO_PIN // PB.09 #define TOUCH_FT6236_I2C_ADDRESS (0x70>>1) -#define TOUCH_CST836U_I2C_ADDRESS (0x15) +#define TOUCH_CST340_I2C_ADDRESS 0x1A enum TouchControllers {TC_NONE, TC_FT6236, TC_CST340}; TouchControllers touchController = TC_NONE; @@ -385,17 +385,28 @@ static void ft6x06_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) static void touch_cst340_debug_info(void) { #if defined(DEBUG) - TRACE("cst836u: fw ver 0x%02X %02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_FW_VERSION_H_REG), TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_FW_VERSION_L_REG)); - TRACE("cst836u: module version 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_MODULE_VERSION_REG)); - TRACE("cst836u: project name 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_PROJECT_NAME_REG)); - TRACE("cst836u: chip type 0x%02X 0x%02X", TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_CHIP_TYPE_H_REG), TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST836U_CHIP_TYPE_L_REG)); + 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 } /** * @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 @@ -403,35 +414,16 @@ static void touch_cst340_debug_info(void) */ static void cst340_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 = CST836U_TOUCH1_XH_REG; - break; - case 1 : -// regAddress = CST836U_TOUCH2_XH_REG; - break; - default : - break; - } + /* 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 */ - /* Read X and Y positions */ -// TS_IO_ReadMultiple(TOUCH_CST340_I2C_ADDRESS, regAddress, dataxy, sizeof(dataxy)); - /* Send back ready X position to caller */ -// *X = ((dataxy[0] & CST836U_MSB_MASK) << 8) | dataxy[1]; - /* Send back ready Y position to caller */ -/// *Y = ((dataxy[2] & CST836U_MSB_MASK) << 8) | dataxy[3]; - -// *event = (dataxy[0] & CST836U_TOUCH_EVT_FLAG_MASK) >> CST836U_TOUCH_EVT_FLAG_SHIFT; - /* - uint32_t weight; - uint32_t area; - */ - tc_handle.currActiveTouchIdx++; - } + *event = dataxy[0]; } /** @@ -439,18 +431,17 @@ static void cst340_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) * 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). + * @retval : Number of active touches detected */ static uint8_t cst340_TS_DetectTouch() { - volatile uint8_t nbTouch = 0; + uint8_t nbTouch; + uint8_t reg = TS_IO_Read(TOUCH_CST340_I2C_ADDRESS, CST340_FINGER1_REG); + if( reg == 0x06 ) + nbTouch = 1; + else + nbTouch = 0; - /* Read register CST836U_TOUCH_NUM_REG to check number of touches detection */ -// nbTouch = TS_IO_Read(CST340_I2C_ADDR, CST836U_TOUCH_NUM_REG); -// if (nbTouch > CST836U_MAX_DETECTABLE_TOUCH) { -// /* If invalid number of touch detected, set it to zero */ -// nbTouch = 0; -// } tc_handle.currActiveTouchNb = nbTouch; tc_handle.currActiveTouchIdx = 0; @@ -477,15 +468,19 @@ static const TouchControllerDescriptor CST340 = .read = cst340_TS_GetXY, .detectTouch = cst340_TS_DetectTouch, .printDebugInfo = touch_cst340_debug_info, - .contactEvent = 0 //CST340_TOUCH_EVT_FLAG_CONTACT + .contactEvent = CST340_TOUCH_EVT_FLAG_CONTACT }; void detectTouchController() { - touchController = TC_CST340; - tc = &CST340; - touchController = TC_FT6236; - tc = &FT6236; + 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() @@ -504,8 +499,11 @@ void handleTouch() tc->read(&touchX, &touchY, &tEvent); #if 0 unsigned short tmp = /*(480 - 1) - */touchY; - touchY = touchX; + touchY = 319 - touchX; touchX = tmp; +#else + touchX = 319 - touchX; + touchY = 479 - touchY; #endif if (tEvent == tc->contactEvent) { int dx = touchX - internalTouchState.x; diff --git a/radio/src/targets/pl18/tp_cst340.h b/radio/src/targets/pl18/tp_cst340.h index a009b9725b9..ec5f1610749 100644 --- a/radio/src/targets/pl18/tp_cst340.h +++ b/radio/src/targets/pl18/tp_cst340.h @@ -250,6 +250,7 @@ typedef struct #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 From 3760db482535541ca11afe8ba42a811a84cad2cf Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Wed, 29 Jun 2022 22:59:21 +0200 Subject: [PATCH 76/99] PL18: use correct LCD pixel clock, display detection is broken --- radio/src/targets/pl18/lcd_driver.cpp | 68 ++++++++++++++++++--------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index b2097620145..c0e1731746d 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -50,6 +50,7 @@ static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, lcdSpiInitFucPtr lcdInitFunction; lcdSpiInitFucPtr lcdOffFunction; lcdSpiInitFucPtr lcdOnFunction; +uint32_t lcdPixelClock; volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; @@ -188,6 +189,17 @@ 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; @@ -667,13 +679,27 @@ void LCD_HX8357D_On(void) { } void LCD_HX8357D_Off(void) { -// lcdWriteCommand(0x22); 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); } @@ -1252,6 +1278,7 @@ void LCD_ST7796S_Off(void) { } unsigned int LCD_ST7796S_ReadID(void) { + lcdReset(); unsigned int ID = 0; lcdWriteCommand( 0XF0 ); @@ -2677,26 +2704,16 @@ void LCD_NT35310_Off( void ) lcdWriteCommand( 0x28 ); } -static void lcdReset() { - LCD_NRST_HIGH(); - delay_ms(1); - - LCD_NRST_LOW(); // RESET(); - delay_ms(100); - - LCD_NRST_HIGH(); - delay_ms(100); -} - 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 * PLLSAI_N = 192 Mhz */ - /* PLLLCDCLK = PLLSAI_VCO Output/PLL_LTDC = 192/3 = 64 Mhz */ - /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = 64/4 = 16 Mhz */ - RCC_PLLSAIConfig(192 * 2 / 3, 6, 3); + /* 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 */ @@ -2842,49 +2859,54 @@ void lcdInit(void) LCD_AF_GPIOConfig(); /* Send LCD initialization commands */ - if (0 && LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { + 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; - } else if (0 && LCD_ILI9486_ReadID() == LCD_ILI9486_ID) { + 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; - } else if (0 && LCD_ILI9488_ReadID() == LCD_ILI9488_ID) { + 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; - } else if (LCD_HX8357D_ReadID() == LCD_HX8357D_ID || 1) { + 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; - } else if (LCD_ST7796S_ReadID() == LCD_ST7796S_ID) { + lcdPixelClock = 12000000; + } else if (1 || LCD_ST7796S_ReadID() == LCD_ST7796S_ID) { TRACE("LCD INIT (default): ST7796S"); boardLcdType = "ST7796S"; lcdInitFunction = LCD_ST7796S_Init; lcdOffFunction = LCD_ST7796S_Off; lcdOnFunction = LCD_ST7796S_On; - } else { // NT35310 can not be detected + 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); From 7950befddce1f51d2c67fa56d23adfe29fd98210 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 30 Jun 2022 21:34:47 +0200 Subject: [PATCH 77/99] detect HX.. display --- radio/src/targets/pl18/lcd_driver.cpp | 29 ++++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index c0e1731746d..58fb0aadd59 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -57,7 +57,7 @@ volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; static void LCD_Delay(void) { volatile unsigned int i; - for (i = 0; i < 10000; i++) { + for (i = 0; i < 100; i++) { ; } } @@ -228,7 +228,6 @@ unsigned char LCD_ReadByteOnFallingEdge(void) { } static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { - CLR_LCD_CS(); LCD_SCK_LOW(); lcdDelay(); @@ -259,7 +258,6 @@ static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { } LCD_SCK_LOW(); - SET_LCD_CS(); } unsigned char LCD_ReadByte(void) { @@ -270,12 +268,10 @@ unsigned char LCD_ReadByte(void) { SET_LCD_DATA_INPUT(); for (i = 0; i < 8; i++) { CLR_LCD_CLK(); - LCD_DELAY(); - LCD_DELAY(); + lcdDelay(); ReceiveData <<= 1; SET_LCD_CLK(); - LCD_DELAY(); - LCD_DELAY(); + lcdDelay(); if (READ_LCD_DATA_PIN()) { ReceiveData |= 0x01; } @@ -289,20 +285,25 @@ unsigned char LCD_ReadByte(void) { unsigned char LCD_ReadRegister(unsigned char Register) { unsigned char ReadData = 0; + CLR_LCD_CS(); lcdWriteByte(0, Register); - LCD_DELAY(); - LCD_DELAY(); + 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) { @@ -1294,11 +1295,11 @@ unsigned int LCD_ST7796S_ReadID(void) { SET_LCD_CLK_OUTPUT(); SET_LCD_DATA_INPUT(); CLR_LCD_CLK(); - LCD_DELAY(); - LCD_DELAY(); + lcdDelay(); + lcdDelay(); SET_LCD_CLK(); - LCD_DELAY(); - LCD_DELAY(); + lcdDelay(); + lcdDelay(); LCD_ReadByte(); ID += (uint16_t)(LCD_ReadByte())<<8; @@ -2887,7 +2888,7 @@ void lcdInit(void) lcdOffFunction = LCD_HX8357D_Off; lcdOnFunction = LCD_HX8357D_On; lcdPixelClock = 12000000; - } else if (1 || LCD_ST7796S_ReadID() == LCD_ST7796S_ID) { + } else if (LCD_ST7796S_ReadID() == LCD_ST7796S_ID || 1) { TRACE("LCD INIT (default): ST7796S"); boardLcdType = "ST7796S"; lcdInitFunction = LCD_ST7796S_Init; From 8875ef88821cd3272bfc38d8515ae9a57580e171 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Mon, 14 Nov 2022 20:01:27 +0100 Subject: [PATCH 78/99] fix(pl18): input edit screen --- radio/src/targets/pl18/libopenui_config.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/pl18/libopenui_config.h b/radio/src/targets/pl18/libopenui_config.h index 679c94bab94..2c216664d13 100644 --- a/radio/src/targets/pl18/libopenui_config.h +++ b/radio/src/targets/pl18/libopenui_config.h @@ -114,11 +114,11 @@ 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 = 80; -constexpr coord_t INPUT_EDIT_CURVE_WIDTH = LCD_W; -constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = 158; -constexpr coord_t INPUT_EDIT_CURVE_LEFT = 0; -constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT; +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; From 1a9c504d43a300d188a2d2b549819cfe344c9c39 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 1 Apr 2022 20:40:54 +0200 Subject: [PATCH 79/99] add PL18 to prepare script --- prepare.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 prepare.sh 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 ../ From c616b6dd3e1dc3890bf8105a182f9c6dd9665351 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 1 Jul 2022 09:23:19 +0200 Subject: [PATCH 80/99] PL18: fix LCD issues --- radio/src/targets/pl18/lcd_driver.cpp | 43 +++++++++------------------ 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 58fb0aadd59..9099950081a 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -23,28 +23,22 @@ #include "libopenui_config.h" #include "lcd.h" -static pixel_t LCD_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; - -static uint16_t* next_frame_buffer; -static rect_t next_frame_area; +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; - next_frame_buffer = buffer; - next_frame_area = copy_area; -#if 1 - DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, - next_frame_area.y, next_frame_area.x, next_frame_buffer, - next_frame_area.h, next_frame_area.w, 0, 0, - next_frame_area.h, next_frame_area.w); - lcdFlushed(); -#else - // Enable line IRQ - LTDC_ITConfig(LTDC_IER_LIE, ENABLE); -#endif + // 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; @@ -2758,6 +2752,7 @@ void LCD_Init_LTDC() { 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; @@ -2824,7 +2819,8 @@ void LCD_LayerInit() { LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_PHYS_H; /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ - LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_FRAME_BUFFER; + 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); @@ -2845,9 +2841,6 @@ const char* boardLcdType = ""; void lcdInit(void) { - // Clear buffers first - memset(LCD_FRAME_BUFFER, 0, sizeof(LCD_FRAME_BUFFER)); - /* Configure the LCD SPI+RESET pins */ lcdSpiConfig(); @@ -3081,14 +3074,6 @@ extern "C" void LTDC_IRQHandler(void) { // clear interrupt flag LTDC->ICR = LTDC_ICR_CLIF; - LTDC_ITConfig(LTDC_IER_LIE, DISABLE); - - // TODO: use modified version with "Transfer Complete" IRQ - DMACopyBitmap((uint16_t *)LCD_FRAME_BUFFER, LCD_PHYS_W, LCD_PHYS_H, - next_frame_area.y, next_frame_area.x, next_frame_buffer, - next_frame_area.h, next_frame_area.w, 0, 0, - next_frame_area.h, next_frame_area.w); - // TODO: call on "Transfer Complete" IRQ - lcdFlushed(); + _frame_addr_reloaded = 1; } From 8454520a9197d335ac32c8cbb90f95ef54ba7d58 Mon Sep 17 00:00:00 2001 From: Risto Date: Sun, 17 Jul 2022 14:33:51 +0200 Subject: [PATCH 81/99] Fix PL18/HX8357D bootloader init prob --- radio/src/targets/pl18/lcd_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/pl18/lcd_driver.cpp b/radio/src/targets/pl18/lcd_driver.cpp index 9099950081a..2cd0bd5848b 100644 --- a/radio/src/targets/pl18/lcd_driver.cpp +++ b/radio/src/targets/pl18/lcd_driver.cpp @@ -664,8 +664,8 @@ void LCD_HX8357D_Init(void) { lcdWriteData(0x00); lcdWriteData(0x00); delay_ms(5); + lcdWriteCommand(0x29); #endif - } void LCD_HX8357D_On(void) { From 6a88ab2d47305265c1ef1f4cfed922bb5b0ba572 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 1 Nov 2022 16:49:54 +0800 Subject: [PATCH 82/99] Fixed PL18 external module signal inversion. --- radio/src/targets/pl18/extmodule_helper.cpp | 8 ++++---- radio/src/targets/pl18/hal.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/pl18/extmodule_helper.cpp b/radio/src/targets/pl18/extmodule_helper.cpp index 006d316b5b1..4b02679ca79 100644 --- a/radio/src/targets/pl18/extmodule_helper.cpp +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -42,8 +42,8 @@ void extModuleInit() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); @@ -51,6 +51,6 @@ void extModuleInit() GPIO_InitStructure.GPIO_Pin = EXTMODULE_RX_INVERT_GPIO_PIN; GPIO_Init(EXTMODULE_RX_INVERT_GPIO, &GPIO_InitStructure); - GPIO_ResetBits(EXTMODULE_TX_INVERT_GPIO, EXTMODULE_TX_INVERT_GPIO_PIN); - GPIO_ResetBits(EXTMODULE_RX_INVERT_GPIO, EXTMODULE_RX_INVERT_GPIO_PIN); + EXTMODULE_TX_INVERTED(); + EXTMODULE_RX_INVERTED(); } diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index defa48b3e8a..9dcf26d3e57 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -516,8 +516,8 @@ #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_TX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN -#define EXTMODULE_RX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_RX_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 TRAINERMODULE From f389718adf4efda47b666b8cc4bec6cf90a41171 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 2 Nov 2022 09:14:10 +0800 Subject: [PATCH 83/99] Removed redundant init of PWR pin. --- radio/src/targets/pl18/extmodule_helper.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/radio/src/targets/pl18/extmodule_helper.cpp b/radio/src/targets/pl18/extmodule_helper.cpp index 4b02679ca79..dbb02e90be2 100644 --- a/radio/src/targets/pl18/extmodule_helper.cpp +++ b/radio/src/targets/pl18/extmodule_helper.cpp @@ -34,18 +34,11 @@ void EXTERNAL_MODULE_OFF() void extModuleInit() { GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_GPIO_PIN; + 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_UP; - GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); - GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = EXTMODULE_RX_INVERT_GPIO_PIN; From e45034f8aa267f246642f9f8607a8a60522b04b9 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sun, 20 Nov 2022 14:22:34 +0800 Subject: [PATCH 84/99] Fixed broken icons in PL18 bootloader. --- radio/src/targets/pl18/bootloader/boot_menu.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp index 7a00d985b3a..b80261d24a9 100644 --- a/radio/src/targets/pl18/bootloader/boot_menu.cpp +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -48,14 +48,6 @@ const uint8_t __bmp_background[] { }; LZ4Bitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background); -const uint8_t LBM_FILE[] = { -#include "icon_file.lbm" -}; - -const uint8_t LBM_OK[] = { -#include "icon_ok.lbm" -}; - #define BL_GREEN COLOR2FLAGS(RGB(73, 219, 62)) #define BL_RED COLOR2FLAGS(RGB(229, 32, 30)) #define BL_BACKGROUND COLOR2FLAGS(BLACK) @@ -197,8 +189,8 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) MESSAGE_TOP + DEFAULT_PADDING, tag.flavour, BL_FOREGROUND); - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP - 10, - LBM_OK, BL_GREEN); + lcd->drawText(LCD_W - DOUBLE_PADDING, MESSAGE_TOP - 10, + LV_SYMBOL_OK, BL_GREEN); } } @@ -233,7 +225,7 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) { - lcd->drawBitmapPattern(DEFAULT_PADDING, 76 + (line * 25), LBM_FILE, BL_FOREGROUND); + 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) { From 142a67eb1da1922b23d854409c966cfc1ec34a0d Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 2 Jan 2023 14:12:55 +0800 Subject: [PATCH 85/99] Implemented wireless charging. --- radio/src/targets/pl18/CMakeLists.txt | 2 + radio/src/targets/pl18/battery_driver.cpp | 431 ++++++++++++++++------ radio/src/targets/pl18/battery_driver.h | 14 +- radio/src/targets/pl18/board.cpp | 11 +- radio/src/targets/pl18/hal.h | 30 +- 5 files changed, 361 insertions(+), 127 deletions(-) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index b5349ffe3e5..9923e35a8cc 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -17,6 +17,7 @@ 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) @@ -75,6 +76,7 @@ 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) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index db786bf7eba..274d85a6bec 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -32,130 +32,332 @@ #define BATTERY_H_INNER (BATTERY_H - 2*BATTERY_BORDER) #define BATTERY_TOP_INNER (BATTERY_TOP + BATTERY_BORDER) -void battery_charge_init() -{ - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = UCHARGER_STDBY_GPIO_PIN; - 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_UP; - GPIO_Init(UCHARGER_STDBY_GPIO, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = UCHARGER_CHARGE_GPIO_PIN; - GPIO_Init(UCHARGER_CHARGE_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = UCHARGER_EN_GPIO_PIN; - GPIO_Init(UCHARGER_EN_GPIO, &GPIO_InitStructure); - GPIO_ResetBits(UCHARGER_EN_GPIO, UCHARGER_EN_GPIO_PIN); +#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 - GPIO_InitStructure.GPIO_Pin = WCHARGER_STDBY_GPIO_PIN; - GPIO_Init(WCHARGER_STDBY_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = WCHARGER_CHARGE_GPIO_PIN; - GPIO_Init(WCHARGER_CHARGE_GPIO, &GPIO_InitStructure); +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; - GPIO_InitStructure.GPIO_Pin = WCHARGER_EN_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(WCHARGER_EN_GPIO, &GPIO_InitStructure); - GPIO_ResetBits(WCHARGER_EN_GPIO, WCHARGER_EN_GPIO_PIN); +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 +char buffer[1024]; - GPIO_InitStructure.GPIO_Pin = WCHARGER_I_CONTROL_GPIO_PIN; - GPIO_Init(WCHARGER_I_CONTROL_GPIO, &GPIO_InitStructure); - GPIO_ResetBits(WCHARGER_I_CONTROL_GPIO, WCHARGER_I_CONTROL_GPIO_PIN); +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; + } + } } -#define CHARGE_SAMPLES 10 +void resetChargeEndDetection(STRUCT_BATTERY_CHARGER* charger) +{ + charger->isChargeEnd = false; + charger->isChargingDetectionReady = false; + charger->chargingSamplingCount = 0; + charger->isHighCurrent = false; +} -uint16_t get_battery_charge_state() +void chargeEndDetection(STRUCT_BATTERY_CHARGER* charger, uint8_t chargeEndPinActive, uint8_t samplingCountThreshold) { - static uint16_t chargeSamples[CHARGE_SAMPLES] = {0}; - static uint16_t chargeSampleIndex = 0; - uint16_t chargeState = CHARGE_UNKNOWN; - int maxSamples = CHARGE_SAMPLES; -#if !defined(SIMU) - bool isFinished = READ_UCHARGE_FINISHED_STATE(); - bool isCharging = READ_UCHARGING_STATE(); - //maxSamples = boardState == BOARD_POWER_OFF ? CHARGE_SAMPLES/2 : CHARGE_SAMPLES; - if(chargeSampleIndex >= maxSamples) chargeSampleIndex = 0; - uint16_t currentChargeState = isFinished ? CHARGE_FINISHED : isCharging ? CHARGE_STARTED : CHARGE_NONE; - chargeSamples[chargeSampleIndex++] = currentChargeState; - //TRACE("CHARGE sample %d value %d", chargeSampleIndex -1, currentChargeState); -#endif - uint8_t temp1 = 0, temp2 = 0, temp3 = 0; - for(int index = 0; index < maxSamples; index++) { - if(chargeSamples[index] == CHARGE_FINISHED) + 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 { - temp1++; + charger->chargeEndSamplingCount = 0; + charger->chargingSamplingCount++; + if (charger->chargingSamplingCount >= samplingCountThreshold) + { + charger->chargingSamplingCount = 0; + charger->isChargeEnd = false; + charger->isChargingDetectionReady = true; + } } - else if(chargeSamples[index] == CHARGE_STARTED) + } + else + { + if (!chargeEndPinActive) { - temp2++; + charger->chargeEndSamplingCount = 0; + if (charger->isChargingDetectionReady) + { + charger->chargingSamplingCount = 0; + } + else + { + charger->chargingSamplingCount++; + if (charger->chargingSamplingCount >= samplingCountThreshold) + { + charger->chargingSamplingCount = 0; + charger->isChargingDetectionReady = true; + } + } } else { - temp3++; + charger->chargingSamplingCount = 0; + charger->chargeEndSamplingCount++; + if (charger->chargeEndSamplingCount >= samplingCountThreshold) + { + charger->chargeEndSamplingCount = 0; + charger->isChargeEnd = true; + charger->isChargingDetectionReady = true; + } } + } +} - if(temp1>=temp2&& temp1>temp3) - { - chargeState = CHARGE_FINISHED; - } - else if(temp2>=temp1&& temp2>temp3) +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 { - chargeState = CHARGE_STARTED; + 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 { - chargeState = CHARGE_NONE; + resetChargeEndDetection(&uCharger); + + // Disable USB charger if it is not present, so that wireless charger can be detected properly + DISABLE_UCHARGER(); } } - return chargeState; -} -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 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 { - if(progress >= 100) + chargeEndDetection(&wCharger, IS_WCHARGER_CHARGE_END_ACTIVE(), WCHARGER_CHARGING_SAMPLING_CNT); + if (wCharger.isChargingDetectionReady) + { + if (wCharger.isChargeEnd) { - progress = 0; + state = CHARGE_FINISHED; } else { - progress+=25; + state = CHARGE_STARTED; } - text = STR_BATTERYCHARGING; - h = ((BATTERY_H_INNER * progress) / 100); - color = COLOR_THEME_EDIT; + } + + // 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++; + } } - else if(CHARGE_FINISHED == chargeState) + } + +#endif + + return state; +} + +bool isChargerActive() +{ +#if defined(WIRELESS_CHARGER) + if (uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady) + { + return uCharger.hasCharger || wCharger.hasCharger; + } +#else + if (uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady) + { + return uCharger.hasCharger || wCharger.hasCharger; + } + if (uCharger.isChargerDetectionReady) + { + return uCharger.hasCharger; + } +#endif + else + { + return true; + } +} + +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) { - text = STR_BATTERYFULL; - h = BATTERY_H_INNER; - color = COLOR_THEME_EDIT; + progress = 0; } else { - text = STR_BATTERYNONE; - h = BATTERY_H_INNER; - color = COLOR_THEME_PRIMARY1; + 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); + 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) / 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); + 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) { @@ -167,22 +369,23 @@ void handle_battery_charge(uint32_t last_press_time) uint32_t now = get_tmr10ms(); uint16_t chargeState = get_battery_charge_state(); - if(chargeState != CHARGE_UNKNOWN) { + if (chargeState != CHARGE_UNKNOWN) { - if(lastState != chargeState) { + if (lastState != chargeState) { //avoid positive check when none and unknown - if(lastState + chargeState > 1) { + 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) { + 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(); @@ -193,24 +396,42 @@ void handle_battery_charge(uint32_t last_press_time) return; } - if(updateTime == 0 || ((get_tmr10ms() - updateTime) >= 50)) + + 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); - lcdRefresh(); - } + if (!lcdInited) { + backlightInit(); + lcdInit(); + lcdInitDisplayDriver(); + lcdInited = true; + TouchInit(); + } + else { + lcdOn(); + } + updateTime = get_tmr10ms(); + lcdInitDirectDrawing(); + lcd->clear(); + drawChargingInfo(chargeState); + + +/* 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);*/ + + lcdRefresh(); + } #endif } diff --git a/radio/src/targets/pl18/battery_driver.h b/radio/src/targets/pl18/battery_driver.h index 9e2c9823cf4..6fdd651d51f 100644 --- a/radio/src/targets/pl18/battery_driver.h +++ b/radio/src/targets/pl18/battery_driver.h @@ -39,12 +39,22 @@ enum ChargeState CHARGE_FINISHED }; -#define READ_UCHARGE_FINISHED_STATE() GPIO_ReadInputDataBit( UCHARGER_STDBY_GPIO, UCHARGER_STDBY_GPIO_PIN ) -#define READ_UCHARGING_STATE() GPIO_ReadInputDataBit( UCHARGER_CHARGE_GPIO, UCHARGER_CHARGE_GPIO_PIN ) +#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 index d18ba1d2dea..f2448410c62 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -151,10 +151,8 @@ void boardInit() pwrOn(); } else { - // prime debounce state... - usbPlugged(); - - while (usbPlugged()) { + while (isChargerActive()) { +// while(1) { uint32_t now = get_tmr10ms(); if (pwrPressed()) { press_end = now; @@ -172,7 +170,7 @@ void boardInit() } press_start = 0; handle_battery_charge(press_end_touch); - delay_ms(20); + delay_ms(10); press_end = 0; } } @@ -255,5 +253,6 @@ const etx_serial_port_t* auxSerialGetPort(int port_nr) int usbPlugged() { static PinDebounce debounce; - return debounce.debounce(UCHARGER_CHARGE_GPIO, UCHARGER_CHARGE_GPIO_PIN); + return debounce.debounce(UCHARGER_GPIO, UCHARGER_GPIO_PIN); } + diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 9dcf26d3e57..d597d9718fc 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -195,28 +195,30 @@ // Chargers (USB and wireless) #define CHARGER_RCC_AHB1Periph ( RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOI ) -#define UCHARGER_STDBY_GPIO GPIOB -#define UCHARGER_STDBY_GPIO_REG UCHARGER_STDBY_GPIO->IDR -#define UCHARGER_STDBY_GPIO_PIN GPIO_Pin_13 // PB.13 input +#define UCHARGER_GPIO GPIOB +#define UCHARGER_GPIO_PIN GPIO_Pin_14 // PB.14 input -#define UCHARGER_CHARGE_GPIO GPIOB -#define UCHARGER_CHARGE_GPIO_REG UCHARGER_CHARGE_GPIO->IDR -#define UCHARGER_CHARGE_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 -#define WCHARGER_EN_GPIO GPIOH -#define WCHARGER_EN_GPIO_PIN GPIO_Pin_4 // PH.04 output +#if defined (WIRELESS_CHARGER) -#define WCHARGER_I_CONTROL_GPIO GPIOH -#define WCHARGER_I_CONTROL_GPIO_PIN GPIO_Pin_13 // PH.13 output + #define WCHARGER_GPIO GPIOI + #define WCHARGER_GPIO_PIN GPIO_Pin_9 // PI.09 input -#define WCHARGER_STDBY_GPIO GPIOI -#define WCHARGER_STDBY_GPIO_PIN GPIO_Pin_10 // PI.10 input + #define WCHARGER_CHARGE_END_GPIO GPIOI + #define WCHARGER_CHARGE_END_GPIO_PIN GPIO_Pin_10 // PI.10 input -#define WCHARGER_CHARGE_GPIO GPIOI -#define WCHARGER_CHARGE_GPIO_PIN GPIO_Pin_9 // PI.09 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! From f8138f8012990e2dbe6b4ff02839a3f05a8b18ba Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 6 Jan 2023 15:51:58 +0800 Subject: [PATCH 86/99] Updated telemetry driver, ELRS can now achieve 5.25M BAUD. --- radio/src/targets/pl18/battery_driver.cpp | 4 - radio/src/targets/pl18/hal.h | 23 +- radio/src/targets/pl18/telemetry_driver.cpp | 274 ++++++++++++++++++-- 3 files changed, 268 insertions(+), 33 deletions(-) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 274d85a6bec..e2d385ff605 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -245,10 +245,6 @@ bool isChargerActive() return uCharger.hasCharger || wCharger.hasCharger; } #else - if (uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady) - { - return uCharger.hasCharger || wCharger.hasCharger; - } if (uCharger.isChargerDetectionReady) { return uCharger.hasCharger; diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index d597d9718fc..aa6fb6b79dc 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -263,9 +263,6 @@ // 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_GPIO GPIOD @@ -275,6 +272,9 @@ #define TELEMETRY_GPIO_PinSource_RX GPIO_PinSource6 #define TELEMETRY_GPIO_AF GPIO_AF_USART2 #define TELEMETRY_USART USART2 + +#define TELEMETRY_DMA_Stream_RX DMA1_Stream5 +#define TELEMETRY_DMA_Channel_RX DMA_Channel_4 #define TELEMETRY_DMA_Stream_TX DMA1_Stream6 #define TELEMETRY_DMA_Channel_TX DMA_Channel_4 #define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream6_IRQn @@ -283,12 +283,17 @@ #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 +#define TELEMETRY_DIR_OUTPUT() GPIO_ResetBits(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN); +#define TELEMETRY_DIR_INPUT() GPIO_SetBits(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN); + +// Telemetry TX/RX Inversion Control +#define TELEMETRY_INV_GPIO GPIOJ +#define TELEMETRY_RX_INV_GPIO_PIN GPIO_Pin_8 // PJ.08 +#define TELEMETRY_TX_INV_GPIO_PIN GPIO_Pin_7 // PJ.07 +#define TELEMETRY_TX_NORM() GPIO_ResetBits(TELEMETRY_INV_GPIO, TELEMETRY_TX_INV_GPIO_PIN); +#define TELEMETRY_TX_INV() GPIO_SetBits(TELEMETRY_INV_GPIO, TELEMETRY_TX_INV_GPIO_PIN); +#define TELEMETRY_RX_NORM() GPIO_ResetBits(TELEMETRY_INV_GPIO, TELEMETRY_RX_INV_GPIO_PIN); +#define TELEMETRY_RX_INV() GPIO_SetBits(TELEMETRY_INV_GPIO, TELEMETRY_RX_INV_GPIO_PIN); // USB #define USB_RCC_AHB1Periph_GPIO RCC_AHB1Periph_GPIOA diff --git a/radio/src/targets/pl18/telemetry_driver.cpp b/radio/src/targets/pl18/telemetry_driver.cpp index b94ce7abc17..007c23c99bd 100644 --- a/radio/src/targets/pl18/telemetry_driver.cpp +++ b/radio/src/targets/pl18/telemetry_driver.cpp @@ -19,23 +19,48 @@ * 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) +#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; @@ -53,28 +78,30 @@ void telemetryPortInitCommon(uint32_t baudrate, uint8_t mode, uint8_t noinv = 0) 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 = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Speed = baudrate <= 400000 ? GPIO_Speed_2MHz : GPIO_Speed_25MHz; GPIO_Init(TELEMETRY_GPIO, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = TELEMETRY_DIR_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(TELEMETRY_DIR_GPIO, &GPIO_InitStructure); - TELEMETRY_DIR_INPUT(); - - GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_REV_GPIO_PIN | TELEMETRY_RX_REV_GPIO_PIN; + 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_Init(TELEMETRY_REV_GPIO, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(TELEMETRY_INV_GPIO, &GPIO_InitStructure); if (noinv != 0) { - TELEMETRY_TX_POL_NORM(); - TELEMETRY_RX_POL_NORM(); + TELEMETRY_TX_NORM(); + TELEMETRY_RX_NORM(); } else { - TELEMETRY_TX_POL_INV(); - TELEMETRY_RX_POL_INV(); + 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; @@ -88,37 +115,202 @@ void telemetryPortInitCommon(uint32_t baudrate, uint8_t mode, uint8_t noinv = 0) } 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) } -/*void extmoduleSendInvertedByte(uint8_t byte) +#if !(defined(PCBNV14) || defined(PCBPL18)) +void telemetryPortInvertedRxBit() { -#warning "TODO extmoduleSendInvertedByte"; -}*/ + 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 } @@ -131,6 +323,39 @@ void sportSendByte(uint8_t byte) 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(); @@ -156,6 +381,7 @@ void sportSendBuffer(const uint8_t * buffer, uint32_t count) 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) ; @@ -214,10 +440,18 @@ extern "C" void TELEMETRY_USART_IRQHandler(void) } } +#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) +#if defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) return telemetryNoDMAFifo.pop(*byte); else @@ -229,7 +463,7 @@ bool sportGetByte(uint8_t * byte) void telemetryClearFifo() { -#if defined(PCBX12S) +#if defined(PCBX12S) || defined(PCBNV14) || defined(PCBPL18) telemetryDMAFifo.clear(); #endif From b33c22897ae90437f6f3b837ffd8b3df8df5e99d Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 27 Apr 2023 23:43:39 +0800 Subject: [PATCH 87/99] Fixed YAML loading problem after rebase. --- .../storage/yaml/yaml_datastructs_pl18.cpp | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 6c7683009b9..94242181a5d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -84,6 +84,7 @@ const struct YamlIdStr enum_Functions[] = { { 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[] = { @@ -370,6 +371,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { 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), @@ -382,7 +385,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_ENUM("antennaMode", 2, enum_AntennaModes), YAML_UNSIGNED( "disableRtcWarning", 1 ), YAML_UNSIGNED( "keysBacklight", 1 ), - YAML_SIGNED( "rotEncDirection", 1 ), + YAML_PADDING( 1 ), YAML_ENUM("internalModule", 8, enum_ModuleType), YAML_STRUCT("trainer", 128, struct_TrainerData, NULL), YAML_UNSIGNED( "view", 8 ), @@ -446,14 +449,17 @@ static const struct YamlNode struct_RadioData[] = { YAML_PADDING( 192 ), YAML_PADDING( 216 ), YAML_STRING("currModelFilename", 17), - YAML_PADDING( 1 ), + 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_PADDING( 6 ), + YAML_UNSIGNED( "stickDeadZone", 3 ), + YAML_PADDING( 1 ), YAML_END }; static const struct YamlNode struct_unsigned_8[] = { @@ -465,6 +471,7 @@ 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[] = { @@ -477,6 +484,8 @@ static const struct YamlNode struct_TimerData[] = { YAML_UNSIGNED( "minuteBeep", 1 ), YAML_UNSIGNED( "persistent", 2 ), YAML_SIGNED( "countdownStart", 2 ), + YAML_UNSIGNED( "showElapsed", 1 ), + YAML_PADDING( 7 ), YAML_STRING("name", 8), YAML_END }; @@ -520,8 +529,9 @@ static const struct YamlNode struct_LimitData[] = { 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_SIGNED( "carryTrim", 6 ), YAML_UNSIGNED( "chn", 5 ), YAML_SIGNED_CUST( "swtch", 9, r_swtchSrc, w_swtchSrc ), YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), @@ -608,11 +618,14 @@ static const struct YamlNode struct_VarioData[] = { YAML_END }; static const struct YamlNode struct_RssiAlarmData[] = { - YAML_SIGNED( "disabled", 1 ), - YAML_UNSIGNED( "flysky_telemetry", 1 ), - YAML_SIGNED( "warning", 6 ), - YAML_PADDING( 2 ), - YAML_SIGNED( "critical", 6 ), + 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[] = { @@ -670,14 +683,10 @@ static const struct YamlNode struct_anonymous_9[] = { YAML_END }; static const struct YamlNode struct_anonymous_10[] = { - YAML_UNSIGNED( "bindPower", 3 ), - YAML_UNSIGNED( "runPower", 3 ), - YAML_UNSIGNED( "emi", 1 ), + YAML_UNSIGNED( "emi", 2 ), YAML_UNSIGNED( "telemetry", 1 ), - YAML_UNSIGNED( "failsafeTimeout", 16 ), - YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), - YAML_UNSIGNED( "mode", 2 ), - YAML_UNSIGNED( "reserved", 6 ), + YAML_UNSIGNED( "phyMode", 3 ), + YAML_UNSIGNED( "reserved", 2 ), YAML_END }; static const struct YamlNode struct_anonymous_11[] = { @@ -702,7 +711,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { 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", 64, struct_anonymous_10, NULL), + YAML_STRUCT("afhds3", 8, 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), @@ -849,8 +858,8 @@ static const struct YamlNode struct_TopBarPersistentData[] = { }; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), - YAML_STRUCT("header", 248, struct_ModelHeader, NULL), - YAML_ARRAY("timers", 128, 3, struct_TimerData, NULL), + 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 ), @@ -863,7 +872,8 @@ static const struct YamlNode struct_ModelData[] = { YAML_UNSIGNED( "extendedTrims", 1 ), YAML_UNSIGNED( "throttleReversed", 1 ), YAML_UNSIGNED( "enableCustomThrottleWarning", 1 ), - YAML_PADDING( 7 ), + YAML_UNSIGNED( "disableTelemetryWarning", 1 ), + YAML_PADDING( 6 ), YAML_SIGNED( "customThrottleWarningPosition", 8 ), YAML_UNSIGNED( "beepANACenter", 16 ), YAML_ARRAY("mixData", 160, 64, struct_MixData, NULL), @@ -881,7 +891,8 @@ static const struct YamlNode struct_ModelData[] = { 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", 16, struct_RssiAlarmData, NULL), + 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), @@ -901,8 +912,8 @@ static const struct YamlNode struct_ModelData[] = { YAML_END }; static const struct YamlNode struct_PartialModel[] = { - YAML_STRUCT("header", 248, struct_ModelHeader, NULL), - YAML_ARRAY("timers", 128, 3, struct_TimerData, NULL), + YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL), YAML_END }; From 725b1c5149df48875dd82620965b638aa28f58b1 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 16 Dec 2022 17:15:54 +0800 Subject: [PATCH 88/99] Added support for FRM303. --- radio/src/targets/pl18/CMakeLists.txt | 3 +-- radio/src/targets/pl18/hal.h | 18 +++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 9923e35a8cc..432d21b5741 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -88,8 +88,7 @@ if(NOT UNEXPECTED_SHUTDOWN) add_definitions(-DNO_UNEXPECTED_SHUTDOWN) endif() -# disable for now as it crashes on radio -#set(AFHDS3 ON) +set(AFHDS3 ON) # VCP CLI set(ENABLE_SERIAL_PASSTHROUGH ON CACHE BOOL "Enable serial passthrough") diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index aa6fb6b79dc..ecc2938d097 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -468,36 +468,32 @@ #define EXTMODULE_RCC_APB1Periph 0 #define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) #define EXTMODULE_TX_GPIO GPIOC -#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 -#define EXTMODULE_TX_GPIO_PIN_LL LL_GPIO_PIN_6 -#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 -#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 // TIM8_CH1 +#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 GPIO_Pin_7 // PC.07 -#define EXTMODULE_RX_GPIO_PIN_LL LL_GPIO_PIN_7 -#define EXTMODULE_RX_GPIO_PinSource GPIO_PinSource7 +#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 GPIO_AF_TIM8 +#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 DMA_Channel_5 +#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_TX_DMA_IRQn DMA2_Stream7_IRQn #define EXTMODULE_USART_TX_DMA_IRQHandler DMA2_Stream7_IRQHandler #define EXTMODULE_USART_TX_DMA_FLAG_TC DMA_IT_TCIF7 -#define EXTMODULE_USART_RX_DMA_CHANNEL DMA_Channel_5 +#define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMA_CHANNEL_5 #define EXTMODULE_USART_RX_DMA_STREAM DMA2_Stream2 #define EXTMODULE_USART_RX_DMA_IRQn DMA2_Stream2_IRQn #define EXTMODULE_USART_RX_DMA_IRQHandler DMA2_Stream2_IRQHandler @@ -508,7 +504,7 @@ #define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) //TIMER -#define EXTMODULE_TIMER_DMA_CHANNEL DMA_Channel_7 +#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 From 607da232df18d5eeb1a75ef92e3d50ee61cd02fe Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sun, 30 Apr 2023 08:32:20 +0800 Subject: [PATCH 89/99] Fixed MPM module. --- radio/src/targets/pl18/hal.h | 129 +++++++++++++++-------------------- 1 file changed, 56 insertions(+), 73 deletions(-) diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index ecc2938d097..23382af0b48 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -263,8 +263,12 @@ // 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 @@ -272,29 +276,23 @@ #define TELEMETRY_GPIO_PinSource_RX GPIO_PinSource6 #define TELEMETRY_GPIO_AF GPIO_AF_USART2 #define TELEMETRY_USART USART2 - -#define TELEMETRY_DMA_Stream_RX DMA1_Stream5 -#define TELEMETRY_DMA_Channel_RX DMA_Channel_4 -#define TELEMETRY_DMA_Stream_TX DMA1_Stream6 +#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() GPIO_ResetBits(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN); -#define TELEMETRY_DIR_INPUT() GPIO_SetBits(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN); - -// Telemetry TX/RX Inversion Control -#define TELEMETRY_INV_GPIO GPIOJ -#define TELEMETRY_RX_INV_GPIO_PIN GPIO_Pin_8 // PJ.08 -#define TELEMETRY_TX_INV_GPIO_PIN GPIO_Pin_7 // PJ.07 -#define TELEMETRY_TX_NORM() GPIO_ResetBits(TELEMETRY_INV_GPIO, TELEMETRY_TX_INV_GPIO_PIN); -#define TELEMETRY_TX_INV() GPIO_SetBits(TELEMETRY_INV_GPIO, TELEMETRY_TX_INV_GPIO_PIN); -#define TELEMETRY_RX_NORM() GPIO_ResetBits(TELEMETRY_INV_GPIO, TELEMETRY_RX_INV_GPIO_PIN); -#define TELEMETRY_RX_INV() GPIO_SetBits(TELEMETRY_INV_GPIO, TELEMETRY_RX_INV_GPIO_PIN); - +#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 @@ -361,19 +359,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_APB1Periph (RCC_APB1Periph_TIM6 | RCC_APB1Periph_DAC) @@ -436,26 +448,19 @@ // Flysky Hall Stick #define FLYSKY_HALL_SERIAL_USART UART4 #define FLYSKY_HALL_SERIAL_GPIO GPIOA -#define FLYSKY_HALL_DMA_Channel DMA_Channel_4 -#define FLYSKY_HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 -#define FLYSKY_HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 -#define FLYSKY_HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 -#define FLYSKY_HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 -#define FLYSKY_HALL_SERIAL_GPIO_AF GPIO_AF_UART4 +#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_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn -#define FLYSKY_HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn -#define FLYSKY_HALL_DMA_Stream_RX DMA1_Stream2 -#define FLYSKY_HALL_DMA_Stream_TX DMA1_Stream4 -#define FLYSKY_HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 - -#define FLYSKY_HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler -#define FLYSKY_HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler +#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 // External Module #define EXTMODULE @@ -465,8 +470,6 @@ #define EXTMODULE_RCC_AHB1Periph \ (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | \ RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) -#define EXTMODULE_RCC_APB1Periph 0 -#define EXTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART6) #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 @@ -489,20 +492,14 @@ #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_TX_DMA_IRQn DMA2_Stream7_IRQn -#define EXTMODULE_USART_TX_DMA_IRQHandler DMA2_Stream7_IRQHandler -#define EXTMODULE_USART_TX_DMA_FLAG_TC DMA_IT_TCIF7 #define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMA_CHANNEL_5 #define EXTMODULE_USART_RX_DMA_STREAM DMA2_Stream2 -#define EXTMODULE_USART_RX_DMA_IRQn DMA2_Stream2_IRQn -#define EXTMODULE_USART_RX_DMA_IRQHandler DMA2_Stream2_IRQHandler -#define EXTMODULE_USART_RX_DMA_FLAG_TC DMA_IT_TCIF2 +#define EXTMODULE_USART_RX_DMA_STREAM_LL LL_DMA_STREAM_2 #define EXTMODULE_USART_IRQHandler USART6_IRQHandler #define EXTMODULE_USART_IRQn USART6_IRQn -#define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) //TIMER #define EXTMODULE_TIMER_DMA_CHANNEL LL_DMA_CHANNEL_7 #define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 @@ -510,7 +507,6 @@ #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_TIMER_DMA_FLAG_TC DMA_IT_TCIF1 #define EXTMODULE_TX_INVERT_GPIO GPIOE #define EXTMODULE_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 @@ -523,34 +519,21 @@ #define EXTMODULE_RX_INVERTED() EXTMODULE_RX_INVERT_GPIO->BSRRL = EXTMODULE_RX_INVERT_GPIO_PIN // Trainer Port -#define TRAINERMODULE #define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD) -#define TRAINER_RCC_APB1Periph RCC_APB1Periph_TIM4 #define TRAINER_GPIO GPIOD -#define TRAINER_IN_GPIO_PIN GPIO_Pin_12 // PD.12 -#define TRAINER_IN_GPIO_PinSource GPIO_PinSource12 -#define TRAINER_OUT_GPIO_PIN GPIO_Pin_13 // PD.13 -#define TRAINER_OUT_GPIO_PinSource GPIO_PinSource13 + +#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_GPIO_AF GPIO_AF_TIM4 // TIM4_CH1 (in) + TIM4_CH2 (out) #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) -#define TRAINER_OUT_CCMR1 TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE; -#define TRAINER_IN_CCMR1 TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1 | TIM_CCMR1_CC1S_0; - -#define TRAINER_OUT_COUNTER_REGISTER TRAINER_TIMER->CCR2 -#define TRAINER_IN_COUNTER_REGISTER TRAINER_TIMER->CCR1 -#define TRAINER_SETUP_REGISTER TRAINER_TIMER->CCR3 -#define TRAINER_OUT_INTERRUPT_FLAG TIM_SR_CC1IF -#define TRAINER_OUT_INTERRUPT_ENABLE TIM_DIER_CC1IE -#define TRAINER_IN_INTERRUPT_ENABLE TIM_DIER_CC1IE -#define TRAINER_IN_INTERRUPT_FLAG TIM_SR_CC1IF -#define TRAINER_OUT_CCER TIM_CCER_CC2E -#define TRAINER_CCER_POLARYTY TIM_CCER_CC2P -#define TRAINER_IN_CCER TIM_CCER_CC1E //BLUETOOTH #define BLUETOOTH_ON_RCC_AHB1Periph RCC_AHB1Periph_GPIOI From 6859beb6a1a2588ad2f6201ecd7c9699188cda3f Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 18 May 2023 20:35:38 +0800 Subject: [PATCH 90/99] Fixed PL18 yaml definition. --- .../storage/yaml/yaml_datastructs_pl18.cpp | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 94242181a5d..07b9e4338fa 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -286,6 +286,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { 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 @@ -460,6 +473,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_SIGNED( "uartSampleMode", 2 ), YAML_UNSIGNED( "stickDeadZone", 3 ), YAML_PADDING( 1 ), + YAML_STRING("selectedTheme", 26), YAML_END }; static const struct YamlNode struct_unsigned_8[] = { @@ -485,7 +499,8 @@ static const struct YamlNode struct_TimerData[] = { YAML_UNSIGNED( "persistent", 2 ), YAML_SIGNED( "countdownStart", 2 ), YAML_UNSIGNED( "showElapsed", 1 ), - YAML_PADDING( 7 ), + YAML_UNSIGNED( "extraHaptic", 1 ), + YAML_PADDING( 6 ), YAML_STRING("name", 8), YAML_END }; @@ -847,7 +862,7 @@ static const struct YamlNode struct_LayoutPersistentData[] = { }; static const struct YamlNode struct_CustomScreenData[] = { YAML_IDX, - YAML_STRING("LayoutId", 10), + YAML_STRING("LayoutId", 12), YAML_STRUCT("layoutData", 6720, struct_LayoutPersistentData, NULL), YAML_END }; @@ -856,6 +871,15 @@ static const struct YamlNode struct_TopBarPersistentData[] = { 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), @@ -873,7 +897,8 @@ static const struct YamlNode struct_ModelData[] = { YAML_UNSIGNED( "throttleReversed", 1 ), YAML_UNSIGNED( "enableCustomThrottleWarning", 1 ), YAML_UNSIGNED( "disableTelemetryWarning", 1 ), - YAML_PADDING( 6 ), + YAML_UNSIGNED( "showInstanceIds", 1 ), + YAML_PADDING( 5 ), YAML_SIGNED( "customThrottleWarningPosition", 8 ), YAML_UNSIGNED( "beepANACenter", 16 ), YAML_ARRAY("mixData", 160, 64, struct_MixData, NULL), @@ -905,10 +930,14 @@ static const struct YamlNode struct_ModelData[] = { YAML_UNSIGNED( "potsWarnEnabled", 16 ), YAML_ARRAY("potsWarnPosition", 8, 5, struct_signed_8, NULL), YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), - YAML_ARRAY("screenData", 6800, 5, struct_CustomScreenData, 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_END }; static const struct YamlNode struct_PartialModel[] = { From 66236ae134e3b8ffb9a29d98eebea9c94eb96e53 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sat, 20 May 2023 16:57:00 +0800 Subject: [PATCH 91/99] Updated charging and power cycles. --- radio/src/targets/pl18/battery_driver.cpp | 28 ++++--- radio/src/targets/pl18/board.cpp | 99 ++++++++++++++++++++--- 2 files changed, 102 insertions(+), 25 deletions(-) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index e2d385ff605..612b81756a1 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -18,6 +18,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ + #include "opentx.h" #define __BATTERY_DRIVER_C__ @@ -57,7 +58,6 @@ STRUCT_BATTERY_CHARGER wCharger; // Wireless charger uint16_t wirelessLowCurrentDelay = 0; uint16_t wirelessHighCurrentDelay = 0; #endif -char buffer[1024]; void chargerDetection(STRUCT_BATTERY_CHARGER* charger, uint8_t chargerPinActive, uint8_t samplingCountThreshold) { @@ -238,22 +238,22 @@ uint16_t get_battery_charge_state() } bool isChargerActive() -{ +{ #if defined(WIRELESS_CHARGER) - if (uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady) + while (!(uCharger.isChargerDetectionReady && wCharger.isChargerDetectionReady)) { - return uCharger.hasCharger || wCharger.hasCharger; + get_battery_charge_state(); + delay_ms(10); } + return uCharger.hasCharger || wCharger.hasCharger; #else - if (uCharger.isChargerDetectionReady) + while (!uCharger.isChargerDetectionReady) { - return uCharger.hasCharger; + get_battery_charge_state(); + delay_ms(10); } + return uCharger.hasCharger; #endif - else - { - return true; - } } void battery_charge_init() @@ -410,8 +410,11 @@ void handle_battery_charge(uint32_t last_press_time) 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); + 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); @@ -424,7 +427,8 @@ void handle_battery_charge(uint32_t last_press_time) 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);*/ + lcd->drawSizedText(100, 130, buffer, strlen(buffer), CENTERED | COLOR_THEME_PRIMARY2); +#endif lcdRefresh(); } diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index f2448410c62..6ab4e066eff 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -26,7 +26,6 @@ #include "touch.h" #include "debug.h" -#include "hal/adc_driver.h" #include "stm32_hal_adc.h" #include "timers_driver.h" #include "../../debounce.h" @@ -147,12 +146,74 @@ void boardInit() uint32_t press_start = 0; uint32_t press_end = 0; - if (UNEXPECTED_SHUTDOWN()) { +/* 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()) { -// while(1) { uint32_t now = get_tmr10ms(); if (pwrPressed()) { press_end = now; @@ -173,8 +234,8 @@ void boardInit() delay_ms(10); press_end = 0; } - } - } + } + }*/ keysInit(); audioInit(); @@ -183,8 +244,6 @@ void boardInit() memset(&g_FATFS_Obj, 0, sizeof(g_FATFS_Obj)); monitorInit(); adcInit(&stm32_hal_adc_driver); - backlightInit(); - lcdInit(); hapticInit(); @@ -206,23 +265,37 @@ void boardInit() void boardOff() { - lcd->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_THEME_FOCUS); +// lcd->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_THEME_FOCUS); lcdOff(); - SysTick->CTRL = 0; // turn off systick - 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 - RTC->BKP0R = SHUTDOWN_REQUEST; - - pwrOff(); +#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 From 3ce7f51c09b88cb976aa38b7b732d7aee6f13c78 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 23 May 2023 20:37:31 +0800 Subject: [PATCH 92/99] Fixed errors in debug build. --- radio/src/targets/pl18/CMakeLists.txt | 1 + radio/src/targets/pl18/board.cpp | 5 ----- radio/src/targets/pl18/tp_cst340.cpp | 2 ++ 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 432d21b5741..9452897bfcb 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -152,6 +152,7 @@ set(FIRMWARE_SRC ${FIRMWARE_SRC} hal/adc_driver.cpp targets/common/arm/stm32/audio_dac_driver.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 diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index 6ab4e066eff..a837ebc3ab6 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -318,11 +318,6 @@ void boardOff() } } -const etx_serial_port_t* auxSerialGetPort(int port_nr) -{ - return nullptr; -} - int usbPlugged() { static PinDebounce debounce; diff --git a/radio/src/targets/pl18/tp_cst340.cpp b/radio/src/targets/pl18/tp_cst340.cpp index 0b65d8fb267..39dcaf97c0e 100644 --- a/radio/src/targets/pl18/tp_cst340.cpp +++ b/radio/src/targets/pl18/tp_cst340.cpp @@ -384,6 +384,7 @@ static void ft6x06_TS_GetXY(uint16_t * X, uint16_t * Y, uint32_t * event) 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)) @@ -403,6 +404,7 @@ static void touch_cst340_debug_info(void) TRACE("cst340: fw ver 0x%08X", fwVersion); TRACE("cst836u: chip id 0x%04X", chipId); #endif +#endif } /** From 9bcdf154994e4dbcd75c50e310e53c2fad519d95 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 25 May 2023 13:51:02 +0800 Subject: [PATCH 93/99] PL18 do not have specialized telemetry. --- radio/src/pulses/flysky.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/radio/src/pulses/flysky.cpp b/radio/src/pulses/flysky.cpp index e13f2ab8319..964b17c38a9 100644 --- a/radio/src/pulses/flysky.cpp +++ b/radio/src/pulses/flysky.cpp @@ -18,18 +18,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * For FlySky NV14 & PL18 boards. + * Dedicate for FlySky NV14 board. */ #include "opentx.h" #include "flysky.h" -#if defined(PCBNV14) #include "telemetry/flysky_nv14.h" -#else -#include "telemetry/flysky_pl18.h" -#endif #define IS_VALID_COMMAND_ID(id) ((id) < CMD_LAST) @@ -57,11 +53,7 @@ enum DEBUG_RF_FRAME_PRINT_E { //#define DEBUG_RF_FRAME_PRINT BOTH_FRAME_PRINT #define FLYSKY_MODULE_TIMEOUT 155 /* ms */ #define FLYSKY_PERIOD 4 /*ms*/ -#if defined(PCBNV14) #define NUM_OF_NV14_CHANNELS (14) -#else -#define NUM_OF_PL18_CHANNELS (14) -#endif #define VALID_CH_DATA(v) ((v) > 900 && (v) < 2100) #define FAILSAVE_SEND_COUNTER_MAX (400) @@ -250,7 +242,6 @@ inline void putFlySkyFrameHeader(uint8_t*& p_buf) initFlySkyCRC(); *p_buf++ = END; putFlySkyFrameByte(p_buf, _flysky_frame_index); -#endif } inline void putFlySkyFrameFooter(uint8_t*& p_buf) @@ -260,7 +251,6 @@ inline void putFlySkyFrameFooter(uint8_t*& p_buf) } putFlySkyByte(p_buf, _flysky_crc ^ 0xff); *p_buf++ = END; -#endif } void afhds2Command(uint8_t*& p_buf, uint8_t type, uint8_t cmd) @@ -423,11 +413,7 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) _flysky_timeout = FLYSKY_MODULE_TIMEOUT; break; case CMD_RX_SENSOR_DATA: -#if defined(PCBNV14) flySkyNv14ProcessTelemetryPacket(&resp->value, dataLen - 3); -#else - flySkyPl18ProcessTelemetryPacket(&resp->value, dataLen - 3); -#endif if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_NORMAL && _flysky_state >= STATE_IDLE) { setFlyskyState(STATE_SEND_CHANNELS); @@ -468,10 +454,8 @@ inline void parseResponse(uint8_t* buffer, uint8_t dataLen) } case CMD_GET_VERSION_INFO: { if (_flysky_state == STATE_GET_FW_VERSION_INIT) { -#if defined(PCBNV14) memcpy(&NV14internalModuleFwVersion, &resp->value + 1, sizeof(NV14internalModuleFwVersion)); -#endif setFlyskyState(STATE_SET_RECEIVER_ID); break; } @@ -522,7 +506,6 @@ void resetPulsesAFHDS2() { #if defined(HARDWARE_INTERNAL_MODULE) NV14internalModuleFwVersion = 0; - intmodulePulsesData.flysky.frame_index = 1; _flysky_frame_index = 1; setFlyskyState(STATE_SET_TX_POWER); _flysky_timeout = 0; @@ -537,6 +520,7 @@ void resetPulsesAFHDS2() void setupPulsesAFHDS2(uint8_t*& p_buf) { +#if defined(HARDWARE_INTERNAL_MODULE) auto buffer_start = p_buf; putFlySkyFrameHeader(p_buf); From f47e0daf50e157f57e2e97854f42dd65859448b3 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 29 May 2023 13:46:33 +0800 Subject: [PATCH 94/99] Fixed key bindings with new virtual keys construct. --- .../common/arm/stm32/bootloader/boot.cpp | 10 ---- radio/src/targets/pl18/board.h | 14 +++--- radio/src/targets/pl18/keys_driver.cpp | 49 +++++++++++-------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index a9d605d73b3..8aae189c626 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -421,16 +421,11 @@ int bootloaderMain() if (state == ST_START) { bootloaderDrawScreen(state, vpos); -#if defined(PCBPL18) - if (event == EVT_KEY_FIRST(KEY_PGDN)) { -#else if (IS_NEXT_EVENT(event)) { -#endif if (vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN) - 1) { vpos++; } continue; } else if (IS_PREVIOUS_EVENT(event)) { -#endif if (vpos > 0) { vpos--; } continue; } @@ -538,11 +533,7 @@ int bootloaderMain() if (nameCount < limit) { limit = nameCount; } -#if defined(PCBPL18) - if (event == EVT_KEY_REPT(KEY_PGDN) || event == EVT_KEY_FIRST(KEY_PGUP)) { -#else if (IS_NEXT_EVENT(event)) { -#endif if (vpos < limit - 1) { vpos += 1; } @@ -554,7 +545,6 @@ int bootloaderMain() } } else if (IS_PREVIOUS_EVENT(event)) { -#endif if (vpos > 0) { vpos -= 1; } diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 1e254e25d90..a05f1fd2a96 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -154,17 +154,17 @@ void stop_trainer_capture(); // Keys driver enum EnumKeys { + KEY_ENTER, + KEY_EXIT, KEY_PGUP, KEY_PGDN, - KEY_ENTER, + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, KEY_MODEL, - KEY_UP = KEY_MODEL, - KEY_EXIT, - KEY_DOWN = KEY_EXIT, - KEY_TELEM, - KEY_RIGHT = KEY_TELEM, KEY_RADIO, - KEY_LEFT = KEY_RADIO, + KEY_TELEM, TRM_BASE, TRM_LH_DWN = TRM_BASE, TRM_LH_UP, diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index 759c027a7f5..a6e5a91f3c1 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -45,6 +45,19 @@ enum PhysicalTrims 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 @@ -125,18 +138,12 @@ uint32_t readKeyMatrix() uint32_t readKeys() { uint32_t result = 0; - bool getKeys = true; + uint32_t mkeys = readKeyMatrix(); -/* -#if defined(LUA) - if (!isLuaStandaloneRunning()) { - getKeys = false; - } +#ifndef BOOT + if (getTrimsAsButtons()) { #endif -*/ - uint32_t mkeys = readKeyMatrix(); - if (getKeys) { if (~TRIMS_GPIO_REG_TR1U & TRIMS_GPIO_PIN_TR1U) result |= 1 << KEY_RADIO; if (~TRIMS_GPIO_REG_TR1D & TRIMS_GPIO_PIN_TR1D) @@ -144,9 +151,15 @@ uint32_t readKeys() if (~TRIMS_GPIO_REG_TR2U & TRIMS_GPIO_PIN_TR2U) result |= 1 << KEY_TELEM; - if (mkeys & (1 << TR3U)) result |= 1 << KEY_PGUP; - if (mkeys & (1 << TR3D)) result |= 1 << KEY_PGDN; + 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; @@ -158,14 +171,8 @@ uint32_t readKeys() uint32_t readTrims() { uint32_t result = 0; + if(getTrimsAsButtons()) return result; - bool getTrim = true; -#if defined(LUA) - if (isLuaStandaloneRunning()) { - getTrim = false; - } -#endif - if(!getTrim) return result; /* The output bit-order has to be: 0 LHL TR7L (Left equals down) 1 LHR TR7R @@ -222,7 +229,7 @@ bool keyDown() } /* TODO common to ARM */ -void readKeysAndTrims() +/*void readKeysAndTrims() { int i; @@ -236,14 +243,14 @@ void readKeysAndTrims() 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) From 2dc71a9d219aa7a4b0735a78f5d77e1fe6ee1544 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sat, 10 Jun 2023 15:00:44 +0800 Subject: [PATCH 95/99] Added internal MPM support via UART7. --- radio/src/targets/pl18/CMakeLists.txt | 3 +++ radio/src/targets/pl18/hal.h | 29 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 9452897bfcb..9ca1f18c4e3 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -6,6 +6,9 @@ option(GHOST "Ghost TX Module" ON) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) +set(INTERNAL_MODULES MULTI CACHE STRING "Internal modules") +set(DEFAULT_INTERNAL_MODULE MULTIMODULE CACHE STRING "Default internal module") +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) diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 23382af0b48..49b75d2d262 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -462,6 +462,35 @@ #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 GPIOH +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#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 From a5ae397afc515e18d02732403c13aeff83f14859 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sat, 17 Jun 2023 11:18:31 +0800 Subject: [PATCH 96/99] Fixed INT module power control and keys init pins. --- radio/src/targets/pl18/hal.h | 9 ++++----- radio/src/targets/pl18/keys_driver.cpp | 3 --- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 49b75d2d262..a08aa266ff0 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -112,10 +112,9 @@ // 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_0 | GPIO_Pin_1 | GPIO_Pin_14) -#define KEYS_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_13 ) /* PC8 allocated to SDIO D0, is not required to sample SWA ! */ +#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_GPIOF_PINS (GPIO_Pin_10) #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) @@ -464,8 +463,8 @@ // Internal Module #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) -#define INTMODULE_PWR_GPIO GPIOH -#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#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 diff --git a/radio/src/targets/pl18/keys_driver.cpp b/radio/src/targets/pl18/keys_driver.cpp index a6e5a91f3c1..ead1b7c39a9 100644 --- a/radio/src/targets/pl18/keys_driver.cpp +++ b/radio/src/targets/pl18/keys_driver.cpp @@ -317,9 +317,6 @@ void keysInit() GPIO_InitStructure.GPIO_Pin = KEYS_GPIOD_PINS; GPIO_Init(GPIOD, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = KEYS_GPIOF_PINS; - GPIO_Init(GPIOF, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = KEYS_GPIOH_PINS; GPIO_Init(GPIOH, &GPIO_InitStructure); From 8fda011d0103c279b780d2b324bb9eb4701b2262 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 21 Jun 2023 17:03:13 +0800 Subject: [PATCH 97/99] Added internal module on/off definitions. --- radio/src/targets/pl18/board.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index a05f1fd2a96..3d96a31a3e6 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -113,8 +113,8 @@ void SDRAM_Init(); // Pulses driver #if !defined(SIMU) -#define INTERNAL_MODULE_OFF() -#define INTERNAL_MODULE_ON() +#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) void EXTERNAL_MODULE_ON(); void EXTERNAL_MODULE_OFF(); #define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF From 18e3615a5a7e5c1f31b47f1582bb8e60f5bddd26 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 12 Jul 2023 17:17:35 +0800 Subject: [PATCH 98/99] Updated YAML definition. --- .../storage/yaml/yaml_datastructs_pl18.cpp | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 07b9e4338fa..dd7e6fda55b 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -474,6 +474,17 @@ static const struct YamlNode struct_RadioData[] = { 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[] = { @@ -702,6 +713,7 @@ static const struct YamlNode struct_anonymous_10[] = { 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[] = { @@ -726,7 +738,7 @@ static const struct YamlNode union_anonymous_4_elmts[] = { 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", 8, struct_anonymous_10, 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), @@ -938,6 +950,17 @@ static const struct YamlNode struct_ModelData[] = { 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[] = { From cafeb87a925d59ef4f448273d86cd9071e5d34ba Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 23 Aug 2023 09:55:10 +0800 Subject: [PATCH 99/99] Fixed some PL18 problems. --- radio/src/cli.cpp | 4 +- radio/src/gui/colorlcd/radio_diaganas.cpp | 2 +- .../colorlcd/radio_ghost_module_config.cpp | 2 + radio/src/lua/api_general.cpp | 8 +- .../common/arm/stm32/f4/system_stm32f4xx.c | 2 +- .../common/arm/stm32/usbd_storage_msd.cpp | 5 +- radio/src/targets/pl18/CMakeLists.txt | 114 +++++++++--------- radio/src/targets/pl18/board.cpp | 44 ++++--- radio/src/targets/pl18/board.h | 85 ++----------- .../src/targets/pl18/bootloader/boot_menu.cpp | 85 ++++++++++++- radio/src/targets/pl18/hal.h | 6 + 11 files changed, 196 insertions(+), 161 deletions(-) diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index 67cfd2c9299..da2616f6008 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1586,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; @@ -1658,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/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_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index e0e8c95ee65..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), diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 9c12fa72e69..566190fe70a 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1426,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) @@ -1675,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 @@ -2151,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 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/usbd_storage_msd.cpp b/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp index 16272325993..b05554c63ed 100644 --- a/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp +++ b/radio/src/targets/common/arm/stm32/usbd_storage_msd.cpp @@ -202,10 +202,11 @@ int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_si *block_num = RESERVED_SECTORS + EEPROM_SIZE/BLOCK_SIZE + FLASHSIZE/BLOCK_SIZE; #else *block_num = RESERVED_SECTORS + FLASHSIZE/BLOCK_SIZE; -#endif +#endif // EEPROM return 0; } -#endif +#endif // FWDRIVE + if (lun == STORAGE_SPI_FLASH_LUN) { #if !defined(SPI_FLASH) return -1; diff --git a/radio/src/targets/pl18/CMakeLists.txt b/radio/src/targets/pl18/CMakeLists.txt index 9ca1f18c4e3..0b202965a87 100644 --- a/radio/src/targets/pl18/CMakeLists.txt +++ b/radio/src/targets/pl18/CMakeLists.txt @@ -1,21 +1,22 @@ 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(INTERNAL_MODULES MULTI CACHE STRING "Internal modules") -set(DEFAULT_INTERNAL_MODULE MULTIMODULE CACHE STRING "Default internal module") 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 YES) -set(STORAGE SDCARD) -set(STORAGE_FORMAT RAW) +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) @@ -26,8 +27,48 @@ set(WIRELESS_CHARGER YES) 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() @@ -54,7 +95,8 @@ add_definitions(-DBATTERY_CHARGE) add_definitions(-DSOFTWARE_VOLUME) # defines existing internal modules -set(DEFAULT_INTERNAL_MODULE NONE CACHE STRING "No internal module") +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) @@ -67,6 +109,7 @@ set(FIRMWARE_DEPENDENCIES datacopy) set(HARDWARE_TOUCH ON) set(SOFTWARE_KEYBOARD ON) +set(FLYSKY_GIMBAL ON) add_definitions( -DSTM32F429_439xx -DSTM32F429xx @@ -91,6 +134,9 @@ 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 @@ -140,29 +186,27 @@ endif() set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} board.cpp - startup_stm32f42_43xxx.s ${LCD_DRIVER} ${TOUCH_DRIVER} sdram_driver.c - trainer_driver.cpp battery_driver.cpp backlight_driver.cpp - extmodule_helper.cpp - ../horus/flyskyHallStick_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/extmodule_serial_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 ) @@ -180,51 +224,3 @@ set(STM32LIB_SRC STM32F4xx_StdPeriph_Driver/src/stm32f4xx_syscfg.c ) -MACRO(GET_GCC_INCLUDE_PATH gcc_include_path) - if (WIN32) - execute_process(COMMAND arm-none-eabi-gcc -v -x c -E NUL ERROR_VARIABLE _gcc_output OUTPUT_QUIET) - else() - execute_process(COMMAND arm-none-eabi-gcc -v -x c -E - INPUT_FILE /dev/null ERROR_VARIABLE _gcc_output OUTPUT_QUIET) - endif() - - # Build an array of string from the GCC output - string(REPLACE "\n" ";" _gcc_output "${_gcc_output}") - - set(_capture_include FALSE) - set(_include_path "") - - # Go through the lines and capture between '"#include <...> search starts here:"' and 'End of search list.' - foreach(_line ${_gcc_output}) - if(${_line} STREQUAL "End of search list.") - set(_capture_include FALSE) - endif() - - if(_capture_include) - # Remove the leading and trailing empty characters - string(REPLACE "\r" "" _line ${_line}) - string(SUBSTRING "${_line}" 1 -1 _line) - set(_include_path "${_include_path}" "-I\"${_line}\"") - endif() - - if(${_line} STREQUAL "#include <...> search starts here:") - set(_capture_include TRUE) - endif() - endforeach() - set(${gcc_include_path} ${_include_path}) -ENDMACRO() - -if(PYTHONINTERP_FOUND) - GET_GCC_INCLUDE_PATH(gcc_include_path) - - add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp - WORKING_DIRECTORY ${RADIO_DIRECTORY}/src - COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs_private.h -DPCBFLYSKY -DPCBPL18 -DCPUARM -DSTM32F4 -DSTM32F429_439xx -DCOLORLCD -DBACKUP -DSIMU -I. -Igui/colorlcd -Itargets/pl18 -Itargets/common/arm/stm32 -I${THIRDPARTY_DIR} -I${THIRDPARTY_DIR}/libopenui/src -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/STM32F4xx_StdPeriph_Driver/inc -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Include ${gcc_include_path} -Wno-asm-operand-widths -Wno-pragma-once-outside-header ${SYSROOT_ARG} > ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp - DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py - ) - - add_custom_target(datacopy - DEPENDS ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp - ) -endif() - diff --git a/radio/src/targets/pl18/board.cpp b/radio/src/targets/pl18/board.cpp index a837ebc3ab6..795a20dccbc 100644 --- a/radio/src/targets/pl18/board.cpp +++ b/radio/src/targets/pl18/board.cpp @@ -20,13 +20,21 @@ */ #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" @@ -44,8 +52,6 @@ extern "C" { } #endif -extern void flysky_hall_stick_init( void ); - HardwareOptions hardwareOptions; void watchdogInit(unsigned int duration) @@ -62,6 +68,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--) @@ -72,7 +82,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 |\ @@ -95,18 +106,14 @@ void delay_self(int count) #define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ AUDIO_RCC_APB1Periph |\ - TRAINER_RCC_APB1Periph |\ FLYSKY_HALL_RCC_APB1Periph |\ - EXTMODULE_RCC_APB1Periph |\ - AUX_SERIAL_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 (ADC_RCC_APB2Periph |\ - HAPTIC_RCC_APB2Periph |\ - AUX_SERIAL_RCC_APB2Periph |\ - EXTMODULE_RCC_APB2Periph \ + HAPTIC_RCC_APB2Periph \ ) void boardInit() @@ -134,14 +141,16 @@ void boardInit() TRACE("RCC->CSR = %08x", RCC->CSR); pwrInit(); - extModuleInit(); + boardInitModulePorts(); + + init_trainer(); battery_charge_init(); - globalData.flyskygimbals = true; - flysky_hall_stick_init(); + globalData.flyskygimbals = flysky_gimbal_init(); init2MhzTimer(); init1msTimer(); TouchInit(); usbInit(); + flashInit(); uint32_t press_start = 0; uint32_t press_end = 0; @@ -239,9 +248,6 @@ void boardInit() keysInit(); audioInit(); - // we need to initialize g_FATFS_Obj here, because it is in .ram section (because of DMA access) - // and this section is un-initialized - memset(&g_FATFS_Obj, 0, sizeof(g_FATFS_Obj)); monitorInit(); adcInit(&stm32_hal_adc_driver); hapticInit(); @@ -266,6 +272,10 @@ void boardInit() void boardOff() { // lcd->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_THEME_FOCUS); +#if defined(SPI_FLASH) + flushFTL(); +#endif + lcdOff(); while (pwrPressed()) { diff --git a/radio/src/targets/pl18/board.h b/radio/src/targets/pl18/board.h index 3d96a31a3e6..acac5d0d872 100644 --- a/radio/src/targets/pl18/board.h +++ b/radio/src/targets/pl18/board.h @@ -26,6 +26,7 @@ #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" @@ -35,9 +36,9 @@ #endif #include "tp_cst340.h" -//#include "hallStick_driver.h" #include "lcd_driver.h" #include "battery_driver.h" +#include "watchdog_driver.h" #define FLASHSIZE 0x200000 #define BOOTLOADER_SIZE 0x20000 @@ -47,12 +48,6 @@ #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 -// 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 - extern uint16_t sessionTimer; #define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) @@ -84,7 +79,11 @@ 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(); @@ -115,8 +114,8 @@ void SDRAM_Init(); #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) -void EXTERNAL_MODULE_ON(); -void EXTERNAL_MODULE_OFF(); +#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) @@ -273,27 +272,6 @@ uint32_t readTrims(); #define DBLKEYS_PRESSED_RGT_UP(in) (false) #define DBLKEYS_PRESSED_LFT_DWN(in) (false) -#define WDG_DURATION 500 /*ms*/ -void watchdogInit(unsigned int duration); -#if defined(SIMU) - #define WAS_RESET_BY_WATCHDOG() (false) - #define WAS_RESET_BY_SOFTWARE() (false) - #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (false) - #define WDG_ENABLE(x) - #define WDG_RESET() -#else - #if defined(WATCHDOG) - #define WDG_ENABLE(x) watchdogInit(x) - #define WDG_RESET() IWDG->KR = 0xAAAA - #else - #define WDG_ENABLE(x) - #define WDG_RESET() - #endif - #define WAS_RESET_BY_WATCHDOG() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF)) - #define WAS_RESET_BY_SOFTWARE() (RCC->CSR & RCC_CSR_SFTRSTF) - #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_SFTRSTF)) -#endif - // ADC driver #define NUM_POTS 3 #define NUM_XPOTS 0 // NUM_POTS @@ -337,6 +315,8 @@ enum Analogs { #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) @@ -487,6 +467,9 @@ void usbJoystickUpdate(); } #endif +// SPI Flash driver +void flashInit(); + // Audio driver void audioInit(); void audioConsumeCurrentBuffer(); @@ -525,23 +508,6 @@ int32_t getVolume(); // Telemetry driver #define INTMODULE_FIFO_SIZE 512 #define TELEMETRY_FIFO_SIZE 512 -void telemetryPortInit(uint32_t baudrate, uint8_t mode); -void telemetryPortSetDirectionOutput(); -void telemetryPortSetDirectionInput(); -void sportSendBuffer(const uint8_t * buffer, uint32_t count); -bool sportGetByte(uint8_t * byte); -void telemetryClearFifo(); -void sportSendByte(uint8_t byte); -extern uint32_t telemetryErrors; - -// soft-serial -void telemetryPortInvertedInit(uint32_t baudrate); - -// Sport update driver -#define SPORT_UPDATE_POWER_ON() -#define SPORT_UPDATE_POWER_OFF() -#define SPORT_UPDATE_POWER_INIT() -#define IS_SPORT_UPDATE_POWER_ON() (false) // Haptic driver void hapticInit(); @@ -553,35 +519,10 @@ void hapticOn(uint32_t pwmPercent); //#define AUX_SERIAL #define DEBUG_BAUDRATE 115200 #define LUA_DEFAULT_BAUDRATE 115200 -extern uint8_t auxSerialMode; -#if defined __cplusplus -void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); -#endif -void auxSerialInit(unsigned int mode, unsigned int protocol); -void auxSerialPutc(char c); -#define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) -void auxSerialSbusInit(); -void auxSerialStop(); -#if defined(AUX_SERIAL_PWR_GPIO) -#define AUX_SERIAL_POWER_ON() auxSerialPowerOn() -#define AUX_SERIAL__POWER_OFF() auxSerialPowerOff() -#else -#define AUX_SERIAL_POWER_ON() -#define AUX_SERIAL__POWER_OFF() -#endif -#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) extern uint8_t currentTrainerMode; void checkTrainerSettings(); -#if defined(__cplusplus) -#include "fifo.h" -#include "dmafifo.h" -extern DMAFifo<512> telemetryFifo; -typedef Fifo AuxSerialRxFifo; -extern AuxSerialRxFifo auxSerialRxFifo; -#endif - // Touch panel driver bool touchPanelEventOccured(); struct TouchState touchPanelRead(); diff --git a/radio/src/targets/pl18/bootloader/boot_menu.cpp b/radio/src/targets/pl18/bootloader/boot_menu.cpp index b80261d24a9..002145deab6 100644 --- a/radio/src/targets/pl18/bootloader/boot_menu.cpp +++ b/radio/src/targets/pl18/bootloader/boot_menu.cpp @@ -23,6 +23,8 @@ #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" @@ -113,11 +115,20 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) 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, (opt == 0) ? 72 : 107, 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, "Or plug in a USB cable", CENTERED | BL_FOREGROUND); @@ -128,7 +139,59 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) "Current Firmware:", 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, "USB Connected", CENTERED | BL_FOREGROUND); } else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || @@ -163,7 +226,7 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) if (opt == FC_ERROR) { lcd->drawText(20, MESSAGE_TOP, - LV_SYMBOL_CLOSE " " STR_INVALID_FIRMWARE, + LV_SYMBOL_CLOSE " " TR_BL_INVALID_FIRMWARE, BL_FOREGROUND); } else if (opt == FC_OK) { VersionTag tag; @@ -232,3 +295,19 @@ void bootloaderDrawFilename(const char* str, uint8_t line, bool 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/hal.h b/radio/src/targets/pl18/hal.h index a08aa266ff0..6aed6cd1b0d 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -24,6 +24,12 @@ #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