diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 28110226c..ddffbad35 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -43,6 +43,7 @@ jobs: matrix: target: - x9dp2019 + - tx15 - tx16s - nv14 - el18 @@ -99,6 +100,7 @@ jobs: - tprov2;tpros;bumblebee - t20;t20v2;t14 - tx12mk2;boxer;gx12 + - tx15 - tx16s - f16 - v12;v14;v16 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a6d6beda8..2c838b27f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -36,6 +36,7 @@ jobs: - mt12 - nb4p - st16 + - tx15 container: image: ghcr.io/edgetx/edgetx-dev:latest volumes: diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 7c9632238..7c63ddeff 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -135,6 +135,8 @@ uint32_t Boards::getFourCC(Type board) return 0x4078746F; case BOARD_RADIOMASTER_TX16S: return 0x3878746F; + case BOARD_RADIOMASTER_TX15: + return 0x4978746F; case BOARD_RADIOMASTER_TX12: return 0x4178746F; case BOARD_RADIOMASTER_TX12_MK2: @@ -210,6 +212,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX15: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: case BOARD_FLYSKY_PA01: @@ -269,6 +272,7 @@ int Boards::getFlashSize(Type board) case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX15: case BOARD_FLYSKY_NV14: case BOARD_FLYSKY_EL18: case BOARD_FLYSKY_PA01: // 8MB SDRAM @@ -380,7 +384,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case LcdHeight: if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board)) return 480; - else if (IS_FAMILY_PL18(board) || IS_JUMPER_T15(board) || IS_FLYSKY_ST16(board)) + else if (IS_FAMILY_PL18(board) || IS_JUMPER_T15(board) || IS_FLYSKY_ST16(board) || IS_RADIOMASTER_TX15(board)) return 320; else if (IS_FLYSKY_PA01(board)) return 240; @@ -390,11 +394,11 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 64; case LcdWidth: - if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PA01(board)) + if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PA01(board)) return 320; else if (IS_FAMILY_PL18(board) || IS_FLYSKY_ST16(board)) return 480; - else if (IS_FAMILY_HORUS_OR_T16(board)) + else if (IS_FAMILY_HORUS_OR_T16(board) || IS_RADIOMASTER_TX15(board)) return 480; else if (IS_TARANIS_SMALL(board)) return 128; @@ -418,7 +422,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return IS_RADIOMASTER_MT12(board); case FunctionSwitchColors: - return IS_RADIOMASTER_GX12(board) || IS_FLYSKY_ST16(board) || IS_FLYSKY_PA01(board); + return IS_RADIOMASTER_GX12(board) || IS_FLYSKY_ST16(board) || IS_FLYSKY_PA01(board) || IS_RADIOMASTER_TX15(board); default: return getBoardJson(board)->getCapability(capability); @@ -648,6 +652,8 @@ QString Boards::getBoardName(Board::Type board) return "Radiomaster TX12 Mark II"; case BOARD_RADIOMASTER_TX16S: return "Radiomaster TX16S"; + case BOARD_RADIOMASTER_TX15: + return "Radiomaster TX15"; case BOARD_RADIOMASTER_ZORRO: return "Radiomaster Zorro"; case BOARD_RADIOMASTER_GX12: @@ -782,6 +788,7 @@ int Boards::getDefaultInternalModules(Board::Type board) case BOARD_FATFISH_F16: case BOARD_HELLORADIOSKY_V14: case BOARD_HELLORADIOSKY_V16: + case BOARD_RADIOMASTER_TX15: case BOARD_IFLIGHT_COMMANDO8: case BOARD_JUMPER_BUMBLEBEE: case BOARD_JUMPER_T12MAX: @@ -848,6 +855,7 @@ void Boards::getBattRange(Board::Type board, int& vmin, int& vmax, unsigned int& case BOARD_X10: case BOARD_X10_EXPRESS: case BOARD_RADIOMASTER_TX16S: + case BOARD_RADIOMASTER_TX15: case BOARD_JUMPER_T16: case BOARD_JUMPER_T18: case BOARD_JUMPER_T20: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 63bc75a50..27a645d5a 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -64,6 +64,7 @@ namespace Board { BOARD_JUMPER_T15, BOARD_JUMPER_T16, BOARD_RADIOMASTER_TX16S, + BOARD_RADIOMASTER_TX15, BOARD_JUMPER_T18, BOARD_JUMPER_T20, BOARD_RADIOMASTER_TX12, @@ -561,6 +562,11 @@ inline bool IS_RADIOMASTER_TX16S(Board::Type board) return board == Board::BOARD_RADIOMASTER_TX16S; } +inline bool IS_RADIOMASTER_TX15(Board::Type board) +{ + return board == Board::BOARD_RADIOMASTER_TX15; +} + inline bool IS_RADIOMASTER_TX12(Board::Type board) { return board == Board::BOARD_RADIOMASTER_TX12; @@ -618,12 +624,13 @@ inline bool IS_HELLORADIOSKY_V16(Board::Type board) inline bool IS_FAMILY_T16(Board::Type board) { - return board == Board::BOARD_JUMPER_T15 || + return board == Board::BOARD_JUMPER_T15 || board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_JUMPER_T18 || board == Board::BOARD_RADIOMASTER_TX16S || board == Board::BOARD_FATFISH_F16 || - board == Board::BOARD_HELLORADIOSKY_V16; + board == Board::BOARD_HELLORADIOSKY_V16 || + board == Board::BOARD_RADIOMASTER_TX15; } inline bool IS_FAMILY_T12(Board::Type board) diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index d5ab99688..a7e58fd41 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -233,6 +233,8 @@ void GeneralSettings::init() strcpy(bluetoothName, "pl18u"); else if (IS_FLYSKY_ST16(board)) strcpy(bluetoothName, "st16"); + else if (IS_RADIOMASTER_TX15(board)) + strcpy(bluetoothName, "tx15"); else if (IS_FAMILY_HORUS_OR_T16(board)) strcpy(bluetoothName, "horus"); else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board)) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index dc4f32119..47a7c62e7 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -326,7 +326,7 @@ int OpenTxFirmware::getCapability(::Capability capability) IS_JUMPER_T15(board) || IS_JUMPER_T18(board) || IS_JUMPER_T20(board) || IS_JUMPER_TPRO(board) || IS_RADIOMASTER_BOXER(board) || IS_RADIOMASTER_GX12(board) || IS_RADIOMASTER_MT12(board) || IS_RADIOMASTER_POCKET(board) || IS_RADIOMASTER_TX12(board) || IS_RADIOMASTER_TX12_MK2(board) || - IS_RADIOMASTER_TX16S(board) || IS_RADIOMASTER_ZORRO(board)); + IS_RADIOMASTER_TX16S(board) || IS_RADIOMASTER_ZORRO(board) || IS_RADIOMASTER_TX15(board)); case HasSoftwareSerialPower: return IS_RADIOMASTER_TX16S(board); case HasIntModuleMulti: @@ -818,6 +818,13 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); addOpenTxRfOptions(firmware, FLEX); + /* Radiomaster TX15 board */ + firmware = new OpenTxFirmware(FIRMWAREID("tx15"), Firmware::tr("Radiomaster TX15"), BOARD_RADIOMASTER_TX15); + addOpenTxFrskyOptions(firmware); + addOpenTxRfOptions(firmware, FLEX); + firmware->addOptionsGroup({opt_bt, opt_internal_gps}); + registerOpenTxFirmware(firmware); + /* Radiomaster TX16S board */ firmware = new OpenTxFirmware(FIRMWAREID("tx16s"), Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); diff --git a/fw.json b/fw.json index 5fb7dcaeb..afa1e42b1 100644 --- a/fw.json +++ b/fw.json @@ -31,12 +31,13 @@ ["Jumper T-Pro V2", "tprov2-"], ["Jumper T-Pro S", "tpros-"], ["RadioMaster Boxer", "boxer-"], + ["RadioMaster GX12", "gx12-"] ["RadioMaster MT12","mt12-"], ["RadioMaster Pocket", "pocket-"], ["RadioMaster TX12MK2", "tx12mk2-"], + ["RadioMaster TX15", "tx15s-"], ["RadioMaster TX16S", "tx16s-"], ["RadioMaster Zorro","zorro-"], - ["RadioMaster GX12", "gx12-"] ], "changelog": "- Major initial bug fixes" } diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index dcb9355c0..e3988c7ba 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 PL18 ST16 PA01) +set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S NV14 PL18 ST16 PA01 TX15) set(RADIO_LANGUAGES CN CZ DA DE EN ES FI FR HE HU IT JP KO NL PL PT RU SE SK TW UA) set(TTS_LANGUAGES CN CZ DA DE EN ES FR HE HU IT JP KO NL PL PT RU SE SK UA) @@ -90,6 +90,8 @@ elseif(PCB STREQUAL ST16) include(targets/st16/CMakeLists.txt) elseif(PCB STREQUAL PA01) include(targets/pa01/CMakeLists.txt) +elseif(PCB STREQUAL TX15) + include(targets/tx15/CMakeLists.txt) elseif(PCB STREQUAL STM32H7S78_DK) include(targets/stm32h7s78-dk/CMakeLists.txt) elseif(PCB STREQUAL X9E OR PCB STREQUAL X9D+ OR PCB STREQUAL X9D OR PCB STREQUAL X7 OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES OR PCB STREQUAL XLITE OR PCB STREQUAL XLITES) @@ -626,6 +628,12 @@ if (DEFINED TARGET_EXTRAM_SIZE) ) endif() +if (DEFINED TARGET_PSRAM_START) + target_link_options(firmware PRIVATE + -Wl,--defsym=__PSRAM_START__=${TARGET_PSRAM_START} + ) +endif() + if (LUA) # Lua needs %g and %f target_link_options(firmware PRIVATE diff --git a/radio/src/boards/generic_stm32/rgb_leds.cpp b/radio/src/boards/generic_stm32/rgb_leds.cpp index e4b568821..22365cc7f 100644 --- a/radio/src/boards/generic_stm32/rgb_leds.cpp +++ b/radio/src/boards/generic_stm32/rgb_leds.cpp @@ -79,7 +79,9 @@ static void rgbLedStart() LED_STRIP_REFRESH_PERIOD, true); } - timer_start(&_refresh_timer); + if (timer_start(&_refresh_timer) != 0) { + TRACE("Failed to start RGB LED refresh timer"); + }; } void rgbLedStop() diff --git a/radio/src/bootloader/CMakeLists.txt b/radio/src/bootloader/CMakeLists.txt index f83a98689..905d967d3 100644 --- a/radio/src/bootloader/CMakeLists.txt +++ b/radio/src/bootloader/CMakeLists.txt @@ -83,6 +83,12 @@ if (DEFINED TARGET_EXTRAM_SIZE) ) endif() +if (DEFINED TARGET_PSRAM_START) + target_link_options(bootloader PRIVATE + -Wl,--defsym=__PSRAM_START__=${TARGET_PSRAM_START} + ) +endif() + if (ENABLE_BOOTLOADER_DEBUG) target_compile_definitions(board_bl PRIVATE -DDEBUG) target_compile_definitions(bootloader PRIVATE -DDEBUG) diff --git a/radio/src/bootloader/boot.cpp b/radio/src/bootloader/boot.cpp index 0e0ff8e00..805977d64 100644 --- a/radio/src/bootloader/boot.cpp +++ b/radio/src/bootloader/boot.cpp @@ -114,10 +114,6 @@ void bootloaderInitApp() rotaryEncoderInit(); #endif -#if defined(DEBUG) - initSerialPorts(); -#endif - __enable_irq(); TRACE("\nBootloader started :)"); diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index b1a7b8ac2..f85c8ee12 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -88,7 +88,7 @@ static inline void check_struct() #if defined(PCBXLITES) CHKSIZE(RadioData, 945); -#elif defined(RADIO_ST16) || defined(PCBPA01) +#elif defined(RADIO_ST16) || defined(PCBPA01) || defined(RADIO_TX15) CHKSIZE(RadioData, 1159); #elif defined(COLORLCD) CHKSIZE(RadioData, 1039); @@ -126,6 +126,8 @@ static inline void check_struct() CHKSIZE(ModelData, 27522); #elif defined(RADIO_T15) CHKSIZE(ModelData, 27683); +#elif defined(RADIO_TX15) + CHKSIZE(ModelData, 27761); #elif defined(RADIO_H7RS) // CHKSIZE() #elif defined(PCBHORUS) diff --git a/radio/src/drivers/icm42607C.cpp b/radio/src/drivers/icm42607C.cpp new file mode 100644 index 000000000..269cd8fe7 --- /dev/null +++ b/radio/src/drivers/icm42607C.cpp @@ -0,0 +1,182 @@ +/* +* 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. + */ + +/* + WARNING: while this ICM42607C belong to the ICM426xx familly, it is NOT compatible + with code for generic ICM4267XX. All register have different addresses +*/ + +#include "edgetx.h" +#include "hal/i2c_driver.h" +#include "stm32_i2c_driver.h" +#include "icm42607C.h" +#include "imu_filter.h" +#include "stm32_gpio.h" +#include "inactivity_timer.h" +#include "hal.h" + +int16_t getGyroTemperature() +{ + uint8_t reg = TEMP_DATA_X0_REG; + uint8_t buf[2] = {0}; + + if (stm32_i2c_read(IMU_I2C_BUS, ICM426xx_I2C_ADDR, reg, 1, buf, 2, 1000) < 0) { + TRACE("ICM426xx ERROR: TEMP_DATA_X0_REG i2c read error"); + return -1; + } + + return (((buf[0] << 8) | buf[1]) / 128) + 25; +} + +int write_cmd(uint8_t reg, uint8_t val) +{ + return i2c_write(IMU_I2C_BUS, ICM426xx_I2C_ADDR, reg, 1, &val, 1); +} + +#if defined(IMU_INT_GPIO) +static void imu_exti_isr(void) +{ + inactivityTimerReset(ActivitySource::MainControls); +} +#endif + +static int write_mreg1(uint8_t reg, uint8_t val) { + if (write_cmd(BLK_SEL_W_REG, 0x00) < 0) return -1; + if (write_cmd(MADDR_W_REG, reg) < 0) return -1; + if (write_cmd(M_W_REG, val) < 0) return -1; + delay_us(10); //42627-pdf p12“Accessing MREG1, MREG2 And MREG3 Registers + return 0; +} + +int gyroInit(void) +{ + TRACE("ICM426xx I2C Init"); + + if (i2c_init(IMU_I2C_BUS) < 0) { + TRACE("ICM426xx ERROR: i2c_init bus error"); + return -1; + } + + if (i2c_dev_ready(IMU_I2C_BUS, ICM426xx_I2C_ADDR) < 0) { + TRACE("ICM426xx device init error"); + return -1; + } + + uint8_t data = 0; + if (stm32_i2c_read(IMU_I2C_BUS, ICM426xx_I2C_ADDR, 0x75, 1, &data, 1, 1000) < 0) { + TRACE("ICM426xx ERROR: i2c read error"); + return -1; + } + + if (data != 0x61) { + TRACE("ICM426xx ERROR: this code only works on ICM42607C"); + return -1; + } + + if(write_cmd(SIGNAL_PATH_RESET, SOFT_RESET_CMD) < 0) { + TRACE("ICM426xx ERROR: SOFT_RESET_CMD error"); + return -1; + } + delay_ms(100); + + if(write_cmd(PWR_MGMT0_REG, PWR_MGMT0_ENABLE) < 0) { + TRACE("ICM426xx ERROR: PWR_MGMT0_REG error"); + return -1; + } + delay_ms(10); + + // Set gyro to 2000 dps, 1kHz ODR + if(write_cmd(GYRO_CONFIG0_REG, GYRO_ODR_1KHZ) < 0) { + TRACE("ICM426xx ERROR: GYRO_CONFIG0_REG error"); + return -1; + } + delay_ms(1); + + // Set accel to ±4g, 1kHz ODR + if (write_cmd(ACCEL_CONFIG0_REG, ACCEL_ODR_1KHZ) < 0) { + TRACE("ICM426xx ERROR: ACCEL_CONFIG0_REG error"); + return -1; + } + delay_ms(1); + + + if (write_cmd(INT_CONFIG_REG, 0x00) < 0) { + TRACE("ICM426xx ERROR: INT_CONFIG_REG error"); + return -1; + } + delay_ms(1); + +#if defined(IMU_INT_GPIO) + // CCEL_WOM_X_THR:0-255(0-1g),0x10--16mg + if (write_mreg1(ACCEL_WOM_X_THR_REG, 0xFE) < 0) return -1; + if (write_mreg1(ACCEL_WOM_Y_THR_REG, 0xFE) < 0) return -1; + if (write_mreg1(ACCEL_WOM_Z_THR_REG, 0xFE) < 0) return -1; + + delay_ms(1); + // config WoM + if (write_cmd(WOM_CONFIG_REG, 0x03) < 0) return -1; // OR mode + delay_ms(1); + // enable INT1 + if (write_cmd(INT_SOURCE1_REG, 0x07) < 0) return -1; // XYZ interrupt + delay_ms(1); + // config INT1 pin + if (write_cmd(INT_CONFIG_REG, 0x00) < 0) return -1; + + gpio_init_int(IMU_INT_GPIO, GPIO_IN_PU, GPIO_FALLING, imu_exti_isr); +#endif + + return 0; +} + +int gyroRead(uint8_t buffer[IMU_BUFFER_LENGTH]) +{ + IMU_RawData_t imu_raw; + IMU_FilteredData_t *filtered; + + uint8_t buf[6]; + uint8_t reg = GYRO_DATA_X0_REG; + if (stm32_i2c_read(IMU_I2C_BUS, ICM426xx_I2C_ADDR, reg, 1, buf, 6, 1000) < 0) { + TRACE("ICM426xx ERROR: i2c read error"); + return -1; + } + imu_raw.gyro_x = -((int16_t)(buf[0] << 8) | buf[1]); // x + imu_raw.gyro_y = -((int16_t)(buf[2] << 8) | buf[3]); // y + imu_raw.gyro_z = ((int16_t)(buf[4] << 8) | buf[5]); // z + + reg = ACCEL_DATA_X0_REG; + if (stm32_i2c_read(IMU_I2C_BUS, ICM426xx_I2C_ADDR, reg, 1, buf, 6, 1000) < 0) { + TRACE("ICM426xx ERROR: i2c read error"); + return -1; + } + imu_raw.accel_x = ((int16_t)(buf[0] << 8) | buf[1]); // x + imu_raw.accel_y = ((int16_t)(buf[2] << 8) | buf[3]); // y + imu_raw.accel_z = ((int16_t)(buf[4] << 8) | buf[5]); // z + + // Process through filters + process_imu_data(&imu_raw); + filtered = get_filtered_imu_data(); + + *(int16_t*)&buffer[6] = -(int16_t)filtered->accel_x; // x + *(int16_t*)&buffer[8] = -(int16_t)filtered->accel_y; // y + *(int16_t*)&buffer[10] = (int16_t)filtered->accel_z; // z + + return 0; +} diff --git a/radio/src/drivers/icm42607C.h b/radio/src/drivers/icm42607C.h new file mode 100644 index 000000000..9813fb121 --- /dev/null +++ b/radio/src/drivers/icm42607C.h @@ -0,0 +1,70 @@ +/* +* 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. + */ + +/* + WARNING: while this ICM42607C belong to the ICM426xx familly, it is NOT compatible + with code for generic ICM4267XX. All register have different addresses +*/ + +#pragma once + +#define ICM426xx_I2C_ADDR 0x68 + +// Register addresses +#define WHO_AM_I_REG 0x75 +#define PWR_MGMT0_REG 0x1F +#define SIGNAL_PATH_RESET 0x02 +#define ACCEL_CONFIG0_REG 0x21 +#define GYRO_CONFIG0_REG 0x20 +#define INT_CONFIG_REG 0x06 +#define TEMP_DATA_X0_REG 0x09 + +// Config values +#define SOFT_RESET_CMD 0x10 + +#define PWR_MGMT0_ENABLE 0x0F // ACCEL_LNM + GYRO_LNM +#define ACCEL_ODR_1KHZ 0x45 // Accel 1 kHz ±4g +#define GYRO_ODR_1KHZ 0x05 // Gyro 1 kHz ±2000 dps + +#define GYRO_DATA_X0_REG 0x11 +#define ACCEL_DATA_X0_REG 0x0B + +#define BLK_SEL_W_REG 0x79 +#define MADDR_W_REG 0x7A +#define M_W_REG 0x7B +#define ACCEL_WOM_X_THR_REG 0x4B +#define ACCEL_WOM_Y_THR_REG 0x4C +#define ACCEL_WOM_Z_THR_REG 0x4D +#define WOM_CONFIG_REG 0x27 +#define INT_SOURCE1_REG 0x2C +#define INT_CONFIG_REG 0x06 +#define INT_STATUS2_REG 0x3B + +typedef struct { + float fTemperatureDegC; //°C + float fAccX, fAccY, fAccZ; // m/s^2 + float fGyroXradps, fGyroYradps, fGyroZradps; // rad/s +} sIMUoutput; + +extern sIMUoutput IMUoutput; + +int gyroInit(void); +int gyroRead(unsigned char*); diff --git a/radio/src/drivers/imu_filter.cpp b/radio/src/drivers/imu_filter.cpp new file mode 100644 index 000000000..a0694b1dd --- /dev/null +++ b/radio/src/drivers/imu_filter.cpp @@ -0,0 +1,180 @@ +/* +* Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "edgetx.h" +#include "imu_filter.h" +#include +#include + +// Filter parameters +#define COMPLEMENTARY_ALPHA 0.92f // Complementary filter coefficient +#define LP_FILTER_ALPHA 0.9f // Low-pass filter coefficient +#define SAMPLE_TIME_S 0.005f // Gyro is read every 5ms as part of execMixerFrequentActions() +#define RAD_TO_DEG 57.2958f +#define DEG_TO_RAD 0.0174533f + +// Filter state variables +static IMU_FilteredData_t filtered_data = {0}; +static float prev_accel[3] = {0}; +static float prev_gyro[3] = {0}; +static uint8_t filter_initialized = 0; + +// Low-pass filter implementation +float low_pass_filter(float new_value, float prev_value, float alpha) { + return alpha * prev_value + (1.0f - alpha) * new_value; +} + +// Calculate roll and pitch from accelerometer data +void calculate_accel_angles(float ax, float ay, float az, float *roll, float *pitch) { + // Normalize accelerometer data + float norm = sqrtf(ax*ax + ay*ay + az*az); + if (norm == 0.0f) return; + + ax /= norm; + ay /= norm; + az /= norm; + + // Calculate angles + *roll = atan2f(ay, sqrtf(ax*ax + az*az)); + *pitch = atan2f(-ax, sqrtf(ay*ay + az*az)); +} + +// Complementary filter for attitude estimation +void complementary_filter(IMU_RawData_t *raw_data, float dt) { + float accel_roll, accel_pitch; + + // Calculate angles from accelerometer + calculate_accel_angles(raw_data->accel_x, raw_data->accel_y, raw_data->accel_z, + &accel_roll, &accel_pitch); + + if (!filter_initialized) { + // Initialize filter with accelerometer angles + filtered_data.roll = accel_roll; + filtered_data.pitch = accel_pitch; + filtered_data.yaw = 0.0f; + filter_initialized = 1; + } else { + // Integrate gyroscope data + float gyro_roll = filtered_data.roll + raw_data->gyro_x * dt; + float gyro_pitch = filtered_data.pitch + raw_data->gyro_y * dt; + float gyro_yaw = filtered_data.yaw + raw_data->gyro_z * dt; + + // Apply complementary filter + filtered_data.roll = COMPLEMENTARY_ALPHA * gyro_roll + + (1.0f - COMPLEMENTARY_ALPHA) * accel_roll; + filtered_data.pitch = COMPLEMENTARY_ALPHA * gyro_pitch + + (1.0f - COMPLEMENTARY_ALPHA) * accel_pitch; + filtered_data.yaw = gyro_yaw; // No magnetometer correction in this example + + // Keep yaw within -π to π range + while (filtered_data.yaw > M_PI) filtered_data.yaw -= 2.0f * M_PI; + while (filtered_data.yaw < -M_PI) filtered_data.yaw += 2.0f * M_PI; + } +} + +// Apply low-pass filter to raw sensor data +void apply_lowpass_filter(IMU_RawData_t *raw_data) { + // Filter accelerometer data + filtered_data.accel_x = low_pass_filter(raw_data->accel_x, prev_accel[0], LP_FILTER_ALPHA); + filtered_data.accel_y = low_pass_filter(raw_data->accel_y, prev_accel[1], LP_FILTER_ALPHA); + filtered_data.accel_z = low_pass_filter(raw_data->accel_z, prev_accel[2], LP_FILTER_ALPHA); + + // Filter gyroscope data + filtered_data.gyro_x = low_pass_filter(raw_data->gyro_x, prev_gyro[0], LP_FILTER_ALPHA); + filtered_data.gyro_y = low_pass_filter(raw_data->gyro_y, prev_gyro[1], LP_FILTER_ALPHA); + filtered_data.gyro_z = low_pass_filter(raw_data->gyro_z, prev_gyro[2], LP_FILTER_ALPHA); + + // Update previous values + prev_accel[0] = filtered_data.accel_x; + prev_accel[1] = filtered_data.accel_y; + prev_accel[2] = filtered_data.accel_z; + + prev_gyro[0] = filtered_data.gyro_x; + prev_gyro[1] = filtered_data.gyro_y; + prev_gyro[2] = filtered_data.gyro_z; +} + +// Main IMU filter processing function +void process_imu_data(IMU_RawData_t *raw_data) { + // Apply low-pass filtering to raw data + apply_lowpass_filter(raw_data); + + // Apply complementary filter for attitude estimation + complementary_filter(raw_data, SAMPLE_TIME_S); +} + +// Get filtered IMU data +IMU_FilteredData_t* get_filtered_imu_data(void) { + return &filtered_data; +} + +// Reset filter state +void reset_imu_filter(void) { + memset(&filtered_data, 0, sizeof(filtered_data)); + memset(prev_accel, 0, sizeof(prev_accel)); + memset(prev_gyro, 0, sizeof(prev_gyro)); + filter_initialized = 0; +} + +// Advanced Kalman filter structure (optional implementation) +typedef struct { + float Q_angle; // Process noise variance for accelerometer + float Q_bias; // Process noise variance for gyro bias + float R_measure; // Measurement noise variance + float angle; // Estimated angle + float bias; // Estimated bias + float rate; // Unbiased rate + float P[2][2]; // Error covariance matrix +} kalman_state_t; + +// Kalman filter implementation (more advanced option) +float kalman_filter(kalman_state_t *state, float new_angle, float new_rate, float dt) { + // Prediction step + state->rate = new_rate - state->bias; + state->angle += dt * state->rate; + + // Update error covariance + state->P[0][0] += dt * (dt * state->P[1][1] - state->P[0][1] - state->P[1][0] + state->Q_angle); + state->P[0][1] -= dt * state->P[1][1]; + state->P[1][0] -= dt * state->P[1][1]; + state->P[1][1] += state->Q_bias * dt; + + // Innovation + float S = state->P[0][0] + state->R_measure; + float K[2] = {state->P[0][0] / S, state->P[1][0] / S}; + + float y = new_angle - state->angle; + + // Update step + state->angle += K[0] * y; + state->bias += K[1] * y; + + // Update error covariance + float P00_temp = state->P[0][0]; + float P01_temp = state->P[0][1]; + + state->P[0][0] -= K[0] * P00_temp; + state->P[0][1] -= K[0] * P01_temp; + state->P[1][0] -= K[1] * P00_temp; + state->P[1][1] -= K[1] * P01_temp; + + return state->angle; +} \ No newline at end of file diff --git a/radio/src/drivers/imu_filter.h b/radio/src/drivers/imu_filter.h new file mode 100644 index 000000000..2899ae656 --- /dev/null +++ b/radio/src/drivers/imu_filter.h @@ -0,0 +1,38 @@ +/* +* 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 + +// IMU data structure +typedef struct { + float accel_x, accel_y, accel_z; // Accelerometer data (m/s²) + float gyro_x, gyro_y, gyro_z; // Gyroscope data (rad/s) + float mag_x, mag_y, mag_z; // Magnetometer data (optional) +} IMU_RawData_t; + +typedef struct { + float roll, pitch, yaw; // Filtered attitude (radians) + float accel_x, accel_y, accel_z; // Filtered acceleration + float gyro_x, gyro_y, gyro_z; // Filtered gyroscope +} IMU_FilteredData_t; + +void process_imu_data(IMU_RawData_t *raw_data); +IMU_FilteredData_t* get_filtered_imu_data(void); \ No newline at end of file diff --git a/radio/src/drivers/pca95xx.cpp b/radio/src/drivers/pca95xx.cpp index 71ecaa11c..7de76114c 100644 --- a/radio/src/drivers/pca95xx.cpp +++ b/radio/src/drivers/pca95xx.cpp @@ -20,6 +20,7 @@ */ #include "pca95xx.h" +#include "debug.h" #define REG_INPUT 0x00 #define REG_OUTPUT 0x02 @@ -46,10 +47,12 @@ int pca95xx_init(pca95xx_t* dev, etx_i2c_bus_t bus, uint16_t addr) dev->output = 0; if (i2c_init(bus) < 0) { + TRACE("PCA95 bus init error"); return -1; } if (i2c_dev_ready(bus, addr) < 0) { + TRACE("PCA95 device init error"); return -1; } diff --git a/radio/src/drivers/pca95xx.h b/radio/src/drivers/pca95xx.h index c4f598127..9c6df92ea 100644 --- a/radio/src/drivers/pca95xx.h +++ b/radio/src/drivers/pca95xx.h @@ -23,6 +23,11 @@ #include "hal/i2c_driver.h" +/* WARNING + Ti PCA95 chip do NOT number pin sequentially, it goes PIN0 to PIN7 then PIN10 to PIN17. + So when schematic indicate P10, it is PIN_8 in the following table + */ + #define PCA95XX_PIN_0 (1 << 0) #define PCA95XX_PIN_1 (1 << 1) #define PCA95XX_PIN_2 (1 << 2) @@ -40,6 +45,23 @@ #define PCA95XX_PIN_14 (1 << 14) #define PCA95XX_PIN_15 (1 << 15) +#define PCA95XX_P0 PCA95XX_PIN_0 +#define PCA95XX_P1 PCA95XX_PIN_1 +#define PCA95XX_P2 PCA95XX_PIN_2 +#define PCA95XX_P3 PCA95XX_PIN_3 +#define PCA95XX_P4 PCA95XX_PIN_4 +#define PCA95XX_P5 PCA95XX_PIN_5 +#define PCA95XX_P6 PCA95XX_PIN_6 +#define PCA95XX_P7 PCA95XX_PIN_7 +#define PCA95XX_P10 PCA95XX_PIN_8 +#define PCA95XX_P11 PCA95XX_PIN_9 +#define PCA95XX_P12 PCA95XX_PIN_10 +#define PCA95XX_P13 PCA95XX_PIN_11 +#define PCA95XX_P14 PCA95XX_PIN_12 +#define PCA95XX_P15 PCA95XX_PIN_13 +#define PCA95XX_P16 PCA95XX_PIN_14 +#define PCA95XX_P17 PCA95XX_PIN_15 + typedef struct { etx_i2c_bus_t bus; uint16_t addr; diff --git a/radio/src/drivers/tas2505.cpp b/radio/src/drivers/tas2505.cpp index 50a002d75..a9187ccc5 100644 --- a/radio/src/drivers/tas2505.cpp +++ b/radio/src/drivers/tas2505.cpp @@ -22,6 +22,7 @@ #include "tas2505.h" #include "stm32_i2s.h" #include "timers_driver.h" +#include "hal/audio_driver.h" #include "debug.h" @@ -126,3 +127,25 @@ int tas2505_init(tas2505_t* dev) return 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 60 + +void tas2505_set_volume(tas2505_t* dev, uint8_t volume) +{ + if (volume > VOLUME_LEVEL_MAX) { + volume = VOLUME_LEVEL_MAX; + } + // maximum volume is 0x00 and total silence is 0xFE + if (volume == 0) { + tas2505_write_reg(dev, TAS2505_SPKVOL1, 0xFE); + tas2505_write_reg(dev, TAS2505_HP_VOL, 0xFE); + } else { + uint32_t vol = (VOLUME_MIN_DB * 2) - + ((uint32_t)volume * (VOLUME_MIN_DB * 2)) / VOLUME_LEVEL_MAX; + tas2505_write_reg(dev, TAS2505_SPKVOL1, vol); + tas2505_write_reg(dev, TAS2505_HP_VOL, vol); + } +} diff --git a/radio/src/drivers/tas2505.h b/radio/src/drivers/tas2505.h index cc119d362..85b3e4712 100644 --- a/radio/src/drivers/tas2505.h +++ b/radio/src/drivers/tas2505.h @@ -94,3 +94,4 @@ typedef struct { int tas2505_probe(tas2505_t* dev, etx_i2c_bus_t bus, uint16_t addr); int tas2505_init(tas2505_t* dev); +void tas2505_set_volume(tas2505_t* dev, uint8_t volume); diff --git a/radio/src/drivers/uf2_ghostfat.cpp b/radio/src/drivers/uf2_ghostfat.cpp index b8eda39c1..d1092dd81 100644 --- a/radio/src/drivers/uf2_ghostfat.cpp +++ b/radio/src/drivers/uf2_ghostfat.cpp @@ -477,7 +477,7 @@ int uf2_fat_write_block(uint32_t block_no, uint8_t *data) wr_st->written_mask[pos] |= mask; wr_st->num_written++; TRACE_DEBUG("[UF2] wr #%d (%d / %d)\n", bl->blockNo, - state->num_written, bl->numBlocks); + wr_st->num_written, bl->numBlocks); } if (wr_st->num_written >= wr_st->num_blocks) { TRACE_DEBUG("[UF2] done: reboot\n"); diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index f4a6efe2b..a89a2ab09 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -36,6 +36,7 @@ #include "hal/abnormal_reboot.h" #include "hal/usb_driver.h" #include "hal/audio_driver.h" +#include "hal/rgbleds.h" #include "timers_driver.h" @@ -401,6 +402,9 @@ void generalDefault() #if defined(MANUFACTURER_RADIOMASTER) g_eeGeneral.audioMuteEnable = 1; +#if defined(RADIO_TX15) + g_eeGeneral.backlightBright = 50; // Screen looks off if not set high enough +#endif #endif // disable Custom Script @@ -1123,6 +1127,10 @@ void edgeTxClose(uint8_t shutdown) #endif sdDone(); + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) + turnOffRGBLeds(); +#endif } void edgeTxResume() diff --git a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp index b5c416a8c..6694ecee6 100644 --- a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp @@ -34,8 +34,8 @@ #if LANDSCAPE static const lv_coord_t col_dsc[] = { - LV_GRID_FR(32), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), - LV_GRID_FR(32), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), + LV_GRID_FR(34), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), + LV_GRID_FR(34), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_FR(40), LV_GRID_TEMPLATE_LAST }; @@ -91,16 +91,15 @@ class AnaViewWindow : public Window new StaticText(line, rect_t{}, s); - auto lbl = new DynamicText( - line, rect_t{}, + auto lbl = new DynamicText(line, rect_t{}, [=]() { return std::to_string((int16_t)calibratedAnalogs[i] * 25 / 256); - }); - etx_obj_add_style(lbl->getLvObj(), styles->text_align_right, LV_PART_MAIN); + }, COLOR_THEME_PRIMARY1_INDEX, RIGHT); - lbl = new DynamicText( - line, rect_t{}, [=]() { return std::to_string((int16_t)column3(i)); }); - etx_obj_add_style(lbl->getLvObj(), styles->text_align_right, LV_PART_MAIN); + lbl = new DynamicText(line, rect_t{}, + [=]() { + return std::to_string((int16_t)column3(i)); + }, COLOR_THEME_PRIMARY1_INDEX, RIGHT); if (column4size() > 0) { lbl = new DynamicText( @@ -108,8 +107,7 @@ class AnaViewWindow : public Window [=]() { return std::string(column4prefix()) + std::to_string((int16_t)column4(i)); - }); - etx_obj_add_style(lbl->getLvObj(), (column4size() == 2) ? styles->text_align_left : styles->text_align_right, LV_PART_MAIN); + }, COLOR_THEME_PRIMARY1_INDEX, (column4size() == 2) ? 0 : RIGHT); #if LANDSCAPE lv_obj_set_grid_cell(lbl->getLvObj(), LV_GRID_ALIGN_STRETCH, 3 + (i & 1) * 5, column4size(), @@ -126,11 +124,31 @@ class AnaViewWindow : public Window lbl = new DynamicText( line, rect_t{}, [=]() { return std::to_string((int16_t)column5(i)); }); - etx_obj_add_style(lbl->getLvObj(), styles->text_align_left, LV_PART_MAIN); } else { grid.nextCell(); } } + +#if defined(IMU_ICM4207C) && LANDSCAPE + line = newLine(grid); + lv_obj_set_style_pad_column(line->getLvObj(), PAD_SMALL, LV_PART_MAIN); + + new StaticText(line, rect_t{}, "Tilt X"); + new DynamicText( + line, rect_t{}, + [=]() { + return std::to_string((int16_t) gyro.scaledX()); + }, COLOR_THEME_PRIMARY1_INDEX, RIGHT); + + for (int i = 0; i < 3; i++) {grid.nextCell();} + + new StaticText(line, rect_t{}, "Tilt Y"); + new DynamicText( + line, rect_t{}, + [=]() { + return std::to_string((int16_t) gyro.scaledY()); + }, COLOR_THEME_PRIMARY1_INDEX, RIGHT); +#endif } void checkEvents() override { Window::checkEvents(); } diff --git a/radio/src/gyro.cpp b/radio/src/gyro.cpp index e74f12e69..4c7177dc5 100644 --- a/radio/src/gyro.cpp +++ b/radio/src/gyro.cpp @@ -20,6 +20,8 @@ */ #include "edgetx.h" +#include "hal.h" +#include "debug.h" #define _USE_MATH_DEFINES #include @@ -59,14 +61,24 @@ void Gyro::wakeup() // stopping the sensor forever errors = 0; - int16_t gx = values[0]; - int16_t gy = values[1]; - // int16_t gz = values[2]; +#if defined(IMU_ICM4207C) + raw_ax = values[3]; + raw_ay = values[4]; + int16_t ax = raw_ax - offset_x; + int16_t ay = raw_ay - offset_y; + // Use only ACC value, they are really reliable + outputs[0] = (ax * float(RESX)) / range_x; + outputs[1] = (ay * float(RESX)) / range_y; +#else int16_t ax = values[3]; int16_t ay = values[4]; int16_t az = values[5]; + int16_t gx = values[0]; + int16_t gy = values[1]; + int16_t gz = values[2]; + // integrate gyro roll -= gx * SCALE_FACT_GYRO * DT; pitch += gy * SCALE_FACT_GYRO * DT; @@ -85,6 +97,7 @@ void Gyro::wakeup() outputs[0] = deg2RESX(roll); outputs[1] = deg2RESX(pitch); +#endif } int16_t Gyro::scaledX() @@ -103,3 +116,17 @@ int16_t Gyro::scaledY() // RESX); return limit(-RESX, outputs[1], RESX); } + +void Gyro::setIMU_X(int16_t offset, int16_t range) +{ + if (offset == -1) offset_x = raw_ax; + else offset_x = offset; + range_x = range * 8192 / 180; +} + +void Gyro::setIMU_Y(int16_t offset, int16_t range) +{ + if (offset == -1) offset_y = raw_ay; + else offset_y = offset; + range_y = range * 8192 / 180; +} \ No newline at end of file diff --git a/radio/src/gyro.h b/radio/src/gyro.h index 16bc37f89..5336b61d0 100644 --- a/radio/src/gyro.h +++ b/radio/src/gyro.h @@ -39,11 +39,19 @@ class Gyro uint8_t errors = 0; float roll = 0.0; float pitch = 0.0; + int16_t offset_x = 0; + int16_t offset_y = 0; + int16_t range_x = 8192; + int16_t range_y = 8192; + int16_t raw_ax = 0; + int16_t raw_ay = 0; public: int16_t outputs[2]; void wakeup(); + void setIMU_X(int16_t, int16_t); + void setIMU_Y(int16_t, int16_t); int16_t scaledX(); int16_t scaledY(); diff --git a/radio/src/hal/rgbleds.cpp b/radio/src/hal/rgbleds.cpp index bfc095ecb..d9ecbe66f 100644 --- a/radio/src/hal/rgbleds.cpp +++ b/radio/src/hal/rgbleds.cpp @@ -63,7 +63,7 @@ bool getFSLedState(uint8_t index) { uint32_t getFSLedRGBColor(uint8_t index) { - return fsGetLedRGB(index); + return fsGetLedRGB(index); } #else void setFSLedOFF(uint8_t index) { @@ -81,3 +81,8 @@ bool getFSLedState(uint8_t index) { return fsLedState(index); } #endif + +void turnOffRGBLeds() +{ + rgbLedClearAll(); +} diff --git a/radio/src/hal/rgbleds.h b/radio/src/hal/rgbleds.h index 575230c2a..287c5140d 100644 --- a/radio/src/hal/rgbleds.h +++ b/radio/src/hal/rgbleds.h @@ -30,3 +30,4 @@ void setFSLedOFF(uint8_t index); void setFSLedON(uint8_t index); bool getFSLedState(uint8_t index); uint32_t getFSLedRGBColor(uint8_t index); +void turnOffRGBLeds(); diff --git a/radio/src/io/uf2.cpp b/radio/src/io/uf2.cpp index cb8654188..df2f1753f 100644 --- a/radio/src/io/uf2.cpp +++ b/radio/src/io/uf2.cpp @@ -150,6 +150,7 @@ static uint8_t* skipUF2Extensions(uint8_t* data) { #if defined(STM32) && !defined(SIMU) void writeUF2FirmwareVersion(void* block) { +#if !defined(SIMU) UF2_Block* uf2 = (UF2_Block*)block; uint8_t* end_data = skipUF2Extensions(uf2->data + uf2->payloadSize); @@ -189,10 +190,12 @@ void writeUF2FirmwareVersion(void* block) memcpy(end_data, version, version_len); } } +#endif } void writeUF2RebootBlock(void* block) { +#if !defined(SIMU) extern uint32_t _reboot_cmd; UF2_Block* uf2 = (UF2_Block*)block; @@ -209,5 +212,6 @@ void writeUF2RebootBlock(void* block) uint32_t reboot_addr = BOOTLOADER_ADDRESS; memcpy(end_data, &reboot_addr, sizeof(reboot_addr)); +#endif } #endif diff --git a/radio/src/io/uf2_flash.cpp b/radio/src/io/uf2_flash.cpp index f73f72c20..46539b640 100644 --- a/radio/src/io/uf2_flash.cpp +++ b/radio/src/io/uf2_flash.cpp @@ -66,6 +66,7 @@ void UF2FirmwareUpdate::flashFirmware(const char* filename, uint32_t sect_len = drv->get_sector_size(sector); if (drv->erase_sector(addr) < 0) break; last_erased = block.targetAddr + sect_len - 1; + TRACE("[UF2] erased %dKB @ 0x%08X", sect_len / 1024, addr); } } @@ -92,7 +93,9 @@ void UF2FirmwareUpdate::flashFirmware(const char* filename, if (drv) { uint32_t len = block.payloadSize; uint8_t* data = (uint8_t*)block.data; - drv->program(addr, data, len); + if (drv->program(addr, data, len) < 0) break; + TRACE("[UF2] written %d bytes @ 0x%08X (%d of %d)", len, addr, + block.blockNo + 1, block.numBlocks); } } diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index af40aea4e..ee23bb7d1 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -2989,6 +2989,69 @@ static int luaGetStickMode(lua_State* const L) return 1; } +/*luadoc +@function setIMU_X(offset, range) + +@param offset: integer, offset in angular degree. -1 to offset to current X position + +@param range: integer, range in angular degree. 180° max.90 means min/max value will be reached at 45° from offset position + +@status current Introduced in 3.0 +*/ + +static int luaSetIMU_X(lua_State* const L) +{ +#if defined(IMU) + int16_t offset = luaL_checkinteger(L, 1); + int16_t range = luaL_checkinteger(L, 2); + + if (offset < -180 || offset > 180) { + lua_pushboolean(L, false); + return 1; + } + if (range < 0 || range > 180) { + lua_pushboolean(L, false); + return 1; + } + + gyro.setIMU_X(offset, range); +#endif + lua_pushboolean(L, true); + return 1; +} + +/*luadoc +@function setIMU_Y(offset, range) + +@param offset: integer, offset in angular degree. -1 to offset to current Y position + +@param range: integer, range in angular degree, 180° max. 90 means min/max value will be reached at 45° from offset position + +@status current Introduced in 3.0 +*/ + +static int luaSetIMU_Y(lua_State* const L) +{ +#if defined(IMU) + int16_t offset = luaL_checkinteger(L, 1); + int16_t range = luaL_checkinteger(L, 2); + + if (offset < -180 || offset > 180) { + lua_pushboolean(L, false); + return 1; + } + if (range < 0 || range > 180) { + lua_pushboolean(L, false); + return 1; + } + + gyro.setIMU_Y(offset, range); +#endif + lua_pushboolean(L, true); + return 1; +} + + #define KEY_EVENTS(xxx, yyy) \ { "EVT_"#xxx"_FIRST", LRO_NUMVAL(EVT_KEY_FIRST(yyy)) }, \ { "EVT_"#xxx"_BREAK", LRO_NUMVAL(EVT_KEY_BREAK(yyy)) }, \ @@ -3084,6 +3147,8 @@ LROT_BEGIN(etxlib, NULL, 0) LROT_FUNCENTRY( setCFSLedColor, luaSetCFSLedColor ) #endif LROT_FUNCENTRY( getStickMode, luaGetStickMode ) + LROT_FUNCENTRY( setIMU_X, luaSetIMU_X ) + LROT_FUNCENTRY( setIMU_Y, luaSetIMU_Y ) LROT_END(etxlib, NULL, 0) LROT_BEGIN(etxcst, NULL, 0) diff --git a/radio/src/storage/yaml/CMakeLists.txt b/radio/src/storage/yaml/CMakeLists.txt index afab89eb1..69c75cabb 100644 --- a/radio/src/storage/yaml/CMakeLists.txt +++ b/radio/src/storage/yaml/CMakeLists.txt @@ -38,6 +38,8 @@ elseif(PCB STREQUAL ST16) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_st16.cpp) elseif(PCB STREQUAL PA01) set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_pa01.cpp) +elseif(PCB STREQUAL TX15) + set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tx15.cpp) elseif(PCB STREQUAL 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 f0129cfcb..3bf47a0ee 100644 --- a/radio/src/storage/yaml/yaml_datastructs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs.cpp @@ -36,10 +36,12 @@ #else #include "yaml_datastructs_x10.cpp" #endif +#elif defined(PCBTX15) + #include "yaml_datastructs_tx15.cpp" #elif defined(PCBPL18) #if defined(RADIO_NB4P) #include "yaml_datastructs_nb4p.cpp" - #elif defined(RADIO_NV14_FAMILY) + #elif defined(RADIO_NV14_FAMILY) #include "yaml_datastructs_nv14_family.cpp" #elif defined(RADIO_PL18U) #include "yaml_datastructs_pl18u.cpp" diff --git a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp new file mode 100644 index 000000000..01d8a3f61 --- /dev/null +++ b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp @@ -0,0 +1,985 @@ +// generated by generate_yaml.py + +// +// Enums first +// + +const struct YamlIdStr enum_BacklightMode[] = { + { e_backlight_mode_off, "backlight_mode_off" }, + { e_backlight_mode_keys, "backlight_mode_keys" }, + { e_backlight_mode_sticks, "backlight_mode_sticks" }, + { e_backlight_mode_all, "backlight_mode_all" }, + { e_backlight_mode_on, "backlight_mode_on" }, + { 0, NULL } +}; +const struct YamlIdStr enum_AntennaModes[] = { + { ANTENNA_MODE_INTERNAL, "MODE_INTERNAL" }, + { ANTENNA_MODE_ASK, "MODE_ASK" }, + { ANTENNA_MODE_PER_MODEL, "MODE_PER_MODEL" }, + { ANTENNA_MODE_EXTERNAL, "MODE_EXTERNAL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModuleType[] = { + { MODULE_TYPE_NONE, "TYPE_NONE" }, + { MODULE_TYPE_PPM, "TYPE_PPM" }, + { MODULE_TYPE_XJT_PXX1, "TYPE_XJT_PXX1" }, + { MODULE_TYPE_ISRM_PXX2, "TYPE_ISRM_PXX2" }, + { MODULE_TYPE_DSM2, "TYPE_DSM2" }, + { MODULE_TYPE_CROSSFIRE, "TYPE_CROSSFIRE" }, + { MODULE_TYPE_MULTIMODULE, "TYPE_MULTIMODULE" }, + { MODULE_TYPE_R9M_PXX1, "TYPE_R9M_PXX1" }, + { MODULE_TYPE_R9M_PXX2, "TYPE_R9M_PXX2" }, + { MODULE_TYPE_R9M_LITE_PXX1, "TYPE_R9M_LITE_PXX1" }, + { MODULE_TYPE_R9M_LITE_PXX2, "TYPE_R9M_LITE_PXX2" }, + { MODULE_TYPE_GHOST, "TYPE_GHOST" }, + { MODULE_TYPE_R9M_LITE_PRO_PXX2, "TYPE_R9M_LITE_PRO_PXX2" }, + { MODULE_TYPE_SBUS, "TYPE_SBUS" }, + { MODULE_TYPE_XJT_LITE_PXX2, "TYPE_XJT_LITE_PXX2" }, + { MODULE_TYPE_FLYSKY_AFHDS2A, "TYPE_FLYSKY_AFHDS2A" }, + { MODULE_TYPE_FLYSKY_AFHDS3, "TYPE_FLYSKY_AFHDS3" }, + { MODULE_TYPE_LEMON_DSMP, "TYPE_LEMON_DSMP" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TrainerMultiplex[] = { + { TRAINER_OFF, "OFF" }, + { TRAINER_ADD, "ADD" }, + { TRAINER_REPL, "REPL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_BeeperMode[] = { + { e_mode_quiet, "mode_quiet" }, + { e_mode_alarms, "mode_alarms" }, + { e_mode_nokeys, "mode_nokeys" }, + { e_mode_all, "mode_all" }, + { 0, NULL } +}; +const struct YamlIdStr enum_BluetoothModes[] = { + { BLUETOOTH_OFF, "OFF" }, + { BLUETOOTH_TELEMETRY, "TELEMETRY" }, + { BLUETOOTH_TRAINER, "TRAINER" }, + { 0, NULL } +}; +const struct YamlIdStr enum_Functions[] = { + { FUNC_OVERRIDE_CHANNEL, "OVERRIDE_CHANNEL" }, + { FUNC_TRAINER, "TRAINER" }, + { FUNC_INSTANT_TRIM, "INSTANT_TRIM" }, + { FUNC_RESET, "RESET" }, + { FUNC_SET_TIMER, "SET_TIMER" }, + { FUNC_ADJUST_GVAR, "ADJUST_GVAR" }, + { FUNC_VOLUME, "VOLUME" }, + { FUNC_SET_FAILSAFE, "SET_FAILSAFE" }, + { FUNC_RANGECHECK, "RANGECHECK" }, + { FUNC_BIND, "BIND" }, + { FUNC_PLAY_SOUND, "PLAY_SOUND" }, + { FUNC_PLAY_TRACK, "PLAY_TRACK" }, + { FUNC_PLAY_VALUE, "PLAY_VALUE" }, + { FUNC_PLAY_SCRIPT, "PLAY_SCRIPT" }, + { FUNC_BACKGND_MUSIC, "BACKGND_MUSIC" }, + { FUNC_BACKGND_MUSIC_PAUSE, "BACKGND_MUSIC_PAUSE" }, + { FUNC_VARIO, "VARIO" }, + { FUNC_HAPTIC, "HAPTIC" }, + { FUNC_LOGS, "LOGS" }, + { FUNC_BACKLIGHT, "BACKLIGHT" }, + { FUNC_SCREENSHOT, "SCREENSHOT" }, + { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, + { FUNC_SET_SCREEN, "SET_SCREEN" }, + { FUNC_DISABLE_AUDIO_AMP, "DISABLE_AUDIO_AMP" }, + { FUNC_RGB_LED, "RGB_LED" }, + { FUNC_PUSH_CUST_SWITCH, "PUSH_CUST_SWITCH" }, + { FUNC_TEST, "TEST" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwitchConfig[] = { + { SWITCH_NONE, "NONE" }, + { SWITCH_TOGGLE, "TOGGLE" }, + { SWITCH_2POS, "2POS" }, + { SWITCH_3POS, "3POS" }, + { SWITCH_GLOBAL, "GLOBAL" }, + { SWITCH_none, "none" }, + { SWITCH_toggle, "toggle" }, + { SWITCH_2pos, "2pos" }, + { SWITCH_3pos, "3pos" }, + { 0, NULL } +}; +const struct YamlIdStr enum_fsStartPositionType[] = { + { FS_START_OFF, "START_OFF" }, + { FS_START_ON, "START_ON" }, + { FS_START_PREVIOUS, "START_PREVIOUS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_booleanEnum[] = { + { BOOL_OFF, "OFF" }, + { BOOL_ON, "ON" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TimerModes[] = { + { TMRMODE_OFF, "OFF" }, + { TMRMODE_ON, "ON" }, + { TMRMODE_START, "START" }, + { TMRMODE_THR, "THR" }, + { TMRMODE_THR_REL, "THR_REL" }, + { TMRMODE_THR_START, "THR_START" }, + { 0, NULL } +}; +const struct YamlIdStr enum_MixerMultiplex[] = { + { MLTPX_ADD, "ADD" }, + { MLTPX_MUL, "MUL" }, + { MLTPX_REPL, "REPL" }, + { 0, NULL } +}; +const struct YamlIdStr enum_MixSources[] = { + { MIXSRC_NONE, "NONE" }, + { MIXSRC_TILT_X, "TILT_X" }, + { MIXSRC_TILT_Y, "TILT_Y" }, + { MIXSRC_SPACEMOUSE_A, "SPACEMOUSE_A" }, + { MIXSRC_SPACEMOUSE_B, "SPACEMOUSE_B" }, + { MIXSRC_SPACEMOUSE_C, "SPACEMOUSE_C" }, + { MIXSRC_SPACEMOUSE_D, "SPACEMOUSE_D" }, + { MIXSRC_SPACEMOUSE_E, "SPACEMOUSE_E" }, + { MIXSRC_SPACEMOUSE_F, "SPACEMOUSE_F" }, + { MIXSRC_MIN, "MIN" }, + { MIXSRC_MAX, "MAX" }, + { MIXSRC_TX_VOLTAGE, "TX_VOLTAGE" }, + { MIXSRC_TX_TIME, "TX_TIME" }, + { MIXSRC_TX_GPS, "TX_GPS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_LogicalSwitchesFunctions[] = { + { LS_FUNC_NONE, "FUNC_NONE" }, + { LS_FUNC_VEQUAL, "FUNC_VEQUAL" }, + { LS_FUNC_VALMOSTEQUAL, "FUNC_VALMOSTEQUAL" }, + { LS_FUNC_VPOS, "FUNC_VPOS" }, + { LS_FUNC_VNEG, "FUNC_VNEG" }, + { LS_FUNC_APOS, "FUNC_APOS" }, + { LS_FUNC_ANEG, "FUNC_ANEG" }, + { LS_FUNC_AND, "FUNC_AND" }, + { LS_FUNC_OR, "FUNC_OR" }, + { LS_FUNC_XOR, "FUNC_XOR" }, + { LS_FUNC_EDGE, "FUNC_EDGE" }, + { LS_FUNC_EQUAL, "FUNC_EQUAL" }, + { LS_FUNC_GREATER, "FUNC_GREATER" }, + { LS_FUNC_LESS, "FUNC_LESS" }, + { LS_FUNC_DIFFEGREATER, "FUNC_DIFFEGREATER" }, + { LS_FUNC_ADIFFEGREATER, "FUNC_ADIFFEGREATER" }, + { LS_FUNC_TIMER, "FUNC_TIMER" }, + { LS_FUNC_STICKY, "FUNC_STICKY" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwashType[] = { + { SWASH_TYPE_NONE, "TYPE_NONE" }, + { SWASH_TYPE_120, "TYPE_120" }, + { SWASH_TYPE_120X, "TYPE_120X" }, + { SWASH_TYPE_140, "TYPE_140" }, + { SWASH_TYPE_90, "TYPE_90" }, + { 0, NULL } +}; +const struct YamlIdStr enum_SwitchSources[] = { + { SWSRC_NONE, "NONE" }, + { SWSRC_ON, "ON" }, + { SWSRC_ONE, "ONE" }, + { SWSRC_TELEMETRY_STREAMING, "TELEMETRY_STREAMING" }, + { SWSRC_RADIO_ACTIVITY, "RADIO_ACTIVITY" }, + { SWSRC_TRAINER_CONNECTED, "TRAINER_CONNECTED" }, + { SWSRC_OFF, "OFF" }, + { 0, NULL } +}; +const struct YamlIdStr enum_PotsWarnMode[] = { + { POTS_WARN_OFF, "WARN_OFF" }, + { POTS_WARN_MANUAL, "WARN_MANUAL" }, + { POTS_WARN_AUTO, "WARN_AUTO" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ModelOverridableEnable[] = { + { OVERRIDE_GLOBAL, "GLOBAL" }, + { OVERRIDE_OFF, "OFF" }, + { OVERRIDE_ON, "ON" }, + { 0, NULL } +}; +const struct YamlIdStr enum_FailsafeModes[] = { + { FAILSAFE_NOT_SET, "NOT_SET" }, + { FAILSAFE_HOLD, "HOLD" }, + { FAILSAFE_CUSTOM, "CUSTOM" }, + { FAILSAFE_NOPULSES, "NOPULSES" }, + { FAILSAFE_RECEIVER, "RECEIVER" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorFormula[] = { + { TELEM_FORMULA_ADD, "FORMULA_ADD" }, + { TELEM_FORMULA_AVERAGE, "FORMULA_AVERAGE" }, + { TELEM_FORMULA_MIN, "FORMULA_MIN" }, + { TELEM_FORMULA_MAX, "FORMULA_MAX" }, + { TELEM_FORMULA_MULTIPLY, "FORMULA_MULTIPLY" }, + { TELEM_FORMULA_TOTALIZE, "FORMULA_TOTALIZE" }, + { TELEM_FORMULA_CELL, "FORMULA_CELL" }, + { TELEM_FORMULA_CONSUMPTION, "FORMULA_CONSUMPTION" }, + { TELEM_FORMULA_DIST, "FORMULA_DIST" }, + { 0, NULL } +}; +const struct YamlIdStr enum_TelemetrySensorType[] = { + { TELEM_TYPE_CUSTOM, "TYPE_CUSTOM" }, + { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, + { 0, NULL } +}; +const struct YamlIdStr enum_ZoneOptionValueEnum[] = { + { ZOV_Unsigned, "Unsigned" }, + { ZOV_Signed, "Signed" }, + { ZOV_Bool, "Bool" }, + { ZOV_String, "String" }, + { ZOV_Source, "Source" }, + { ZOV_Color, "Color" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; + +// +// Structs last +// + +static const struct YamlNode struct_CalibData[] = { + YAML_IDX_CUST("calib",r_calib,w_calib), + YAML_SIGNED( "mid", 16 ), + YAML_SIGNED( "spanNeg", 16 ), + YAML_SIGNED( "spanPos", 16 ), + YAML_END +}; +static const struct YamlNode struct_signed_16[] = { + YAML_IDX, + YAML_SIGNED( "val", 16 ), + YAML_END +}; +static const struct YamlNode struct_TrainerMix[] = { + YAML_IDX, + YAML_UNSIGNED( "srcChn", 6 ), + YAML_ENUM("mode", 2, enum_TrainerMultiplex, NULL), + YAML_SIGNED( "studWeight", 8 ), + YAML_END +}; +static const struct YamlNode struct_TrainerData[] = { + YAML_ARRAY("calib", 16, 4, struct_signed_16, NULL), + YAML_ARRAY("mix", 16, 4, struct_TrainerMix, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_1[] = { + YAML_STRING("name", 8), + YAML_END +}; +static const struct YamlNode struct_anonymous_2[] = { + YAML_SIGNED( "val", 16 ), + YAML_UNSIGNED( "mode", 8 ), + YAML_UNSIGNED( "param", 8 ), + YAML_SIGNED( "val2", 32 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_3[] = { + YAML_SIGNED( "val1", 32 ), + YAML_SIGNED( "val2", 32 ), + YAML_END +}; +static const struct YamlNode union_anonymous_0_elmts[] = { + YAML_STRUCT("play", 64, struct_anonymous_1, NULL), + YAML_STRUCT("all", 64, struct_anonymous_2, NULL), + YAML_STRUCT("clear", 64, struct_anonymous_3, NULL), + YAML_END +}; +static const struct YamlNode struct_CustomFunctionData[] = { + YAML_IDX, + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_ENUM("func", 6, enum_Functions, NULL), + YAML_CUSTOM("def",r_customFn,w_customFn), + YAML_PADDING( 64 ), + YAML_PADDING( 1 ), + YAML_PADDING( 7 ), + YAML_END +}; +static const struct YamlNode struct_RGBLedColor[] = { + YAML_UNSIGNED( "r", 8 ), + YAML_UNSIGNED( "g", 8 ), + YAML_UNSIGNED( "b", 8 ), + YAML_END +}; +static const struct YamlNode struct_switchDef[] = { + YAML_IDX_CUST("sw",sw_idx_read,sw_idx_write), + YAML_STRING("name", 3), + YAML_ENUM("type", 3, enum_SwitchConfig, NULL), + YAML_ENUM("start", 2, enum_fsStartPositionType, switch_is_cfs), + YAML_ENUM("onColorLuaOverride", 1, enum_booleanEnum, switch_is_cfs), + YAML_ENUM("offColorLuaOverride", 1, enum_booleanEnum, switch_is_cfs), + YAML_PADDING( 1 ), + YAML_STRUCT("onColor", 24, struct_RGBLedColor, switch_is_cfs), + YAML_STRUCT("offColor", 24, struct_RGBLedColor, switch_is_cfs), + YAML_END +}; +static const struct YamlNode struct_RadioData[] = { + YAML_UNSIGNED( "manuallyEdited", 1 ), + YAML_SIGNED( "timezoneMinutes", 3 ), + YAML_UNSIGNED( "ppmunit", 2 ), + YAML_PADDING( 2 ), + YAML_CUSTOM("semver",nullptr,w_semver), + YAML_CUSTOM("board",nullptr,w_board), + YAML_ARRAY("calib", 48, 20, struct_CalibData, NULL), + YAML_PADDING( 16 ), + YAML_UNSIGNED( "vBatWarn", 8 ), + YAML_SIGNED( "txVoltageCalibration", 8 ), + YAML_ENUM("backlightMode", 3, enum_BacklightMode, NULL), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_UNSIGNED( "disableRtcWarning", 1 ), + YAML_UNSIGNED( "keysBacklight", 1 ), + YAML_UNSIGNED( "dontPlayHello", 1 ), + YAML_ENUM("internalModule", 8, enum_ModuleType, NULL), + YAML_STRUCT("trainer", 128, struct_TrainerData, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_PADDING( 2 ), + YAML_UNSIGNED( "fai", 1 ), + YAML_SIGNED_CUST( "beepMode", 2, r_beeperMode, w_beeperMode ), + YAML_UNSIGNED( "alarmsFlash", 1 ), + YAML_UNSIGNED( "disableMemoryWarning", 1 ), + YAML_UNSIGNED( "disableAlarmWarning", 1 ), + YAML_UNSIGNED( "stickMode", 2 ), + YAML_SIGNED( "timezone", 5 ), + YAML_UNSIGNED( "adjustRTC", 1 ), + YAML_UNSIGNED( "inactivityTimer", 8 ), + YAML_CUSTOM("telemetryBaudrate",r_telemetryBaudrate,nullptr), + YAML_UNSIGNED( "internalModuleBaudrate", 3 ), + YAML_SIGNED( "splashMode", 3 ), + YAML_SIGNED_CUST( "hapticMode", 2, r_beeperMode, w_beeperMode ), + YAML_SIGNED( "switchesDelay", 8 ), + YAML_UNSIGNED( "lightAutoOff", 8 ), + YAML_UNSIGNED( "templateSetup", 8 ), + YAML_SIGNED( "PPM_Multiplier", 8 ), + YAML_SIGNED_CUST( "hapticLength", 8, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "beepLength", 3, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "hapticStrength", 3, r_5pos, w_5pos ), + YAML_UNSIGNED( "gpsFormat", 1 ), + YAML_UNSIGNED( "audioMuteEnable", 1 ), + YAML_UNSIGNED_CUST( "speakerPitch", 8, r_spPitch, w_spPitch ), + YAML_SIGNED_CUST( "speakerVolume", 8, r_vol, w_vol ), + YAML_SIGNED_CUST( "vBatMin", 8, r_vbat_min, w_vbat_min ), + YAML_SIGNED_CUST( "vBatMax", 8, r_vbat_max, w_vbat_max ), + YAML_UNSIGNED( "backlightBright", 8 ), + YAML_UNSIGNED( "globalTimer", 32 ), + YAML_UNSIGNED( "bluetoothBaudrate", 4 ), + YAML_ENUM("bluetoothMode", 4, enum_BluetoothModes, NULL), + YAML_UNSIGNED( "countryCode", 2 ), + YAML_SIGNED( "pwrOnSpeed", 3 ), + YAML_SIGNED( "pwrOffSpeed", 3 ), + YAML_CUSTOM("jitterFilter",r_jitterFilter,nullptr), + YAML_UNSIGNED( "noJitterFilter", 1 ), + YAML_UNSIGNED( "imperial", 1 ), + YAML_UNSIGNED( "disableRssiPoweroffAlarm", 1 ), + YAML_UNSIGNED( "USBMode", 2 ), + YAML_UNSIGNED( "jackMode", 2 ), + YAML_PADDING( 1 ), + YAML_STRING("ttsLanguage", 2), + YAML_SIGNED_CUST( "beepVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "wavVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "varioVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "backgroundVolume", 4, r_5pos, w_5pos ), + YAML_SIGNED_CUST( "varioPitch", 8, r_vPitch, w_vPitch ), + YAML_SIGNED_CUST( "varioRange", 8, r_vPitch, w_vPitch ), + YAML_SIGNED( "varioRepeat", 8 ), + YAML_ARRAY("customFn", 88, 64, struct_CustomFunctionData, cfn_is_active), + YAML_CUSTOM("auxSerialMode",r_serialMode,nullptr), + YAML_CUSTOM("aux2SerialMode",r_serialMode,nullptr), + YAML_ARRAY("serialPort", 8, 4, struct_serialConfig, nullptr), + YAML_ARRAY("sticksConfig", 0, MAX_STICKS, struct_stickConfig, stick_name_valid), + YAML_ARRAY("slidersConfig", 0, MAX_POTS, struct_sliderConfig, nullptr), + YAML_PADDING( 8 ), + YAML_ARRAY("potsConfig", 4, 16, struct_potConfig, nullptr), + YAML_ARRAY("switchConfig", 80, 20, struct_switchDef, switchIsActive), + YAML_ARRAY("flexSwitches", 0, MAX_FLEX_SWITCHES, struct_flexSwitch, flex_sw_valid), + YAML_STRING("currModelFilename", 17), + YAML_UNSIGNED( "modelQuickSelect", 1 ), + YAML_UNSIGNED( "blOffBright", 7 ), + YAML_STRING("bluetoothName", 10), + YAML_STRING("ownerRegistrationID", 8), + YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), + YAML_UNSIGNED( "rotEncMode", 3 ), + YAML_SIGNED( "uartSampleMode", 2 ), + YAML_UNSIGNED( "stickDeadZone", 3 ), + YAML_SIGNED( "imuMax", 8 ), + YAML_SIGNED( "imuOffset", 8 ), + YAML_STRING("selectedTheme", 26), + YAML_UNSIGNED( "labelSingleSelect", 1 ), + YAML_UNSIGNED( "labelMultiMode", 1 ), + YAML_UNSIGNED( "favMultiMode", 1 ), + YAML_UNSIGNED( "modelSelectLayout", 2 ), + 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_UNSIGNED( "disableTrainerPoweroffAlarm", 1 ), + YAML_UNSIGNED( "disablePwrOnOffHaptic", 1 ), + YAML_PADDING( 6 ), + YAML_UNSIGNED( "pwrOffIfInactive", 8 ), + YAML_END +}; +static const struct YamlNode struct_unsigned_8[] = { + YAML_IDX, + YAML_UNSIGNED( "val", 8 ), + YAML_END +}; +static const struct YamlNode struct_ModelHeader[] = { + YAML_STRING("name", 15), + YAML_ARRAY("modelId", 8, 2, struct_unsigned_8, NULL), + YAML_STRING("bitmap", 14), + YAML_STRING("labels", 100), + YAML_END +}; +static const struct YamlNode struct_TimerData[] = { + YAML_IDX, + YAML_UNSIGNED( "start", 22 ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_SIGNED( "value", 22 ), + YAML_ENUM("mode", 3, enum_TimerModes, NULL), + YAML_UNSIGNED( "countdownBeep", 2 ), + YAML_UNSIGNED( "minuteBeep", 1 ), + YAML_UNSIGNED( "persistent", 2 ), + YAML_SIGNED( "countdownStart", 2 ), + YAML_UNSIGNED( "showElapsed", 1 ), + YAML_UNSIGNED( "extraHaptic", 1 ), + YAML_PADDING( 6 ), + YAML_STRING("name", 8), + YAML_END +}; +static const struct YamlNode struct_CurveRef[] = { + YAML_UNSIGNED( "type", 5 ), + YAML_UNSIGNED_CUST( "value", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_END +}; +static const struct YamlNode struct_MixData[] = { + YAML_UNSIGNED( "destCh", 5 ), + YAML_SIGNED_CUST( "srcRaw", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_UNSIGNED( "carryTrim", 1 ), + YAML_UNSIGNED( "mixWarn", 2 ), + YAML_ENUM("mltpx", 2, enum_MixerMultiplex, NULL), + YAML_UNSIGNED( "delayPrec", 1 ), + YAML_UNSIGNED( "speedPrec", 1 ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + YAML_PADDING( 1 ), + YAML_UNSIGNED_CUST( "weight", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_UNSIGNED_CUST( "offset", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_STRUCT("curve", 16, struct_CurveRef, NULL), + YAML_UNSIGNED( "delayUp", 8 ), + YAML_UNSIGNED( "delayDown", 8 ), + YAML_UNSIGNED( "speedUp", 8 ), + YAML_UNSIGNED( "speedDown", 8 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_LimitData[] = { + YAML_IDX, + YAML_SIGNED_CUST( "min", 11, in_read_weight, in_write_weight ), + YAML_SIGNED_CUST( "max", 11, in_read_weight, in_write_weight ), + YAML_SIGNED( "ppmCenter", 10 ), + YAML_SIGNED_CUST( "offset", 11, in_read_weight, in_write_weight ), + YAML_UNSIGNED( "symetrical", 1 ), + YAML_UNSIGNED( "revert", 1 ), + YAML_PADDING( 3 ), + YAML_SIGNED( "curve", 8 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_ExpoData[] = { + YAML_UNSIGNED( "mode", 2 ), + YAML_UNSIGNED( "scale", 14 ), + YAML_CUSTOM("carryTrim",r_carryTrim,nullptr), + YAML_SIGNED( "trimSource", 6 ), + YAML_SIGNED_CUST( "srcRaw", 10, r_mixSrcRawEx, w_mixSrcRawEx ), + YAML_UNSIGNED_CUST( "weight", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_UNSIGNED_CUST( "offset", 11, r_sourceNumVal, w_sourceNumVal ), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_STRUCT("curve", 16, struct_CurveRef, NULL), + YAML_UNSIGNED( "chn", 5 ), + YAML_UNSIGNED_CUST( "flightModes", 9, r_flightModes, w_flightModes ), + YAML_PADDING( 2 ), + YAML_STRING("name", 6), + YAML_END +}; +static const struct YamlNode struct_CurveHeader[] = { + YAML_IDX, + YAML_UNSIGNED( "type", 1 ), + YAML_UNSIGNED( "smooth", 1 ), + YAML_SIGNED( "points", 6 ), + YAML_STRING("name", 3), + YAML_END +}; +static const struct YamlNode struct_signed_8[] = { + YAML_IDX, + YAML_SIGNED( "val", 8 ), + YAML_END +}; +static const struct YamlNode struct_LogicalSwitchData[] = { + YAML_IDX, + YAML_ENUM("func", 8, enum_LogicalSwitchesFunctions, NULL), + YAML_CUSTOM("def",r_logicSw,w_logicSw), + YAML_PADDING( 10 ), + YAML_PADDING( 10 ), + YAML_SIGNED_CUST( "andsw", 10, r_swtchSrc, w_swtchSrc ), + YAML_UNSIGNED( "lsPersist", 1 ), + YAML_UNSIGNED( "lsState", 1 ), + YAML_PADDING( 16 ), + YAML_UNSIGNED( "delay", 8 ), + YAML_UNSIGNED( "duration", 8 ), + YAML_END +}; +static const struct YamlNode struct_SwashRingData[] = { + YAML_ENUM("type", 8, enum_SwashType, NULL), + YAML_UNSIGNED( "value", 8 ), + YAML_UNSIGNED_CUST( "collectiveSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED_CUST( "aileronSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_UNSIGNED_CUST( "elevatorSource", 8, r_mixSrcRaw, w_mixSrcRaw ), + YAML_SIGNED( "collectiveWeight", 8 ), + YAML_SIGNED( "aileronWeight", 8 ), + YAML_SIGNED( "elevatorWeight", 8 ), + YAML_END +}; +static const struct YamlNode struct_trim_t[] = { + YAML_IDX, + YAML_SIGNED( "value", 11 ), + YAML_UNSIGNED( "mode", 5 ), + YAML_END +}; +static const struct YamlNode struct_FlightModeData[] = { + YAML_IDX, + YAML_ARRAY("trim", 16, 8, struct_trim_t, NULL), + YAML_STRING("name", 10), + YAML_SIGNED_CUST( "swtch", 10, r_swtchSrc, w_swtchSrc ), + YAML_PADDING( 6 ), + YAML_UNSIGNED( "fadeIn", 8 ), + YAML_UNSIGNED( "fadeOut", 8 ), + YAML_ARRAY("gvars", 16, 9, struct_signed_16, gvar_is_active), + YAML_END +}; +static const struct YamlNode struct_GVarData[] = { + YAML_IDX, + YAML_STRING("name", 3), + YAML_UNSIGNED( "min", 12 ), + YAML_UNSIGNED( "max", 12 ), + YAML_UNSIGNED( "popup", 1 ), + YAML_UNSIGNED( "prec", 1 ), + YAML_UNSIGNED( "unit", 2 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_VarioData[] = { + YAML_UNSIGNED_CUST( "source", 7, r_tele_sensor, w_tele_sensor ), + YAML_UNSIGNED( "centerSilent", 1 ), + YAML_SIGNED( "centerMax", 8 ), + YAML_SIGNED( "centerMin", 8 ), + YAML_SIGNED( "min", 8 ), + YAML_SIGNED( "max", 8 ), + YAML_END +}; +static const struct YamlNode struct_RssiAlarmData[] = { + YAML_CUSTOM("disabled",r_rssiDisabled,nullptr), + YAML_CUSTOM("warning",r_rssiWarning,nullptr), + YAML_CUSTOM("critical",r_rssiCritical,nullptr), + YAML_END +}; +static const struct YamlNode struct_RFAlarmData[] = { + YAML_SIGNED( "warning", 8 ), + YAML_SIGNED( "critical", 8 ), + YAML_END +}; +static const struct YamlNode struct_PpmModule[] = { + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_UNSIGNED( "outputType", 1 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_5[] = { + YAML_PADDING( 8 ), + YAML_UNSIGNED( "disableTelemetry", 1 ), + YAML_UNSIGNED( "disableMapping", 1 ), + YAML_UNSIGNED( "autoBindMode", 1 ), + YAML_UNSIGNED( "lowPowerMode", 1 ), + YAML_UNSIGNED( "receiverTelemetryOff", 1 ), + YAML_UNSIGNED( "receiverHigherChannels", 1 ), + YAML_PADDING( 2 ), + YAML_SIGNED( "optionValue", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_6[] = { + YAML_UNSIGNED( "power", 2 ), + YAML_PADDING( 2 ), + YAML_UNSIGNED( "receiverTelemetryOff", 1 ), + YAML_UNSIGNED( "receiverHigherChannels", 1 ), + YAML_SIGNED( "antennaMode", 2 ), + YAML_PADDING( 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_7[] = { + YAML_PADDING( 6 ), + YAML_UNSIGNED( "noninverted", 1 ), + YAML_PADDING( 1 ), + YAML_SIGNED( "refreshRate", 8 ), + YAML_END +}; +static const struct YamlNode struct_string_64[] = { + YAML_IDX, + YAML_STRING("val", 8), + YAML_END +}; +static const struct YamlNode struct_anonymous_8[] = { + YAML_UNSIGNED( "receivers", 7 ), + YAML_UNSIGNED( "racingMode", 1 ), + YAML_ARRAY("receiverName", 64, 3, struct_string_64, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_9[] = { + YAML_ARRAY("rx_id", 8, 4, struct_unsigned_8, NULL), + YAML_UNSIGNED( "mode", 3 ), + YAML_UNSIGNED( "rfPower", 1 ), + YAML_UNSIGNED( "reserved", 4 ), + YAML_ARRAY("rx_freq", 8, 2, struct_unsigned_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_10[] = { + YAML_UNSIGNED( "emi", 2 ), + YAML_UNSIGNED( "telemetry", 1 ), + YAML_UNSIGNED( "phyMode", 3 ), + YAML_UNSIGNED( "reserved", 2 ), + YAML_UNSIGNED( "rfPower", 8 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_11[] = { + YAML_UNSIGNED( "raw12bits", 1 ), + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_PADDING( 4 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_12[] = { + YAML_UNSIGNED( "telemetryBaudrate", 3 ), + YAML_UNSIGNED( "crsfArmingMode", 1 ), + YAML_PADDING( 4 ), + YAML_SIGNED_CUST( "crsfArmingTrigger", 10, r_swtchSrc, w_swtchSrc ), + YAML_SIGNED( "spare3", 6 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_13[] = { + YAML_UNSIGNED( "flags", 8 ), + YAML_END +}; +static const struct YamlNode union_anonymous_4_elmts[] = { + YAML_ARRAY("raw", 8, 25, struct_unsigned_8, NULL), + YAML_STRUCT("ppm", 16, struct_PpmModule, NULL), + YAML_STRUCT("multi", 24, struct_anonymous_5, NULL), + YAML_STRUCT("pxx", 16, struct_anonymous_6, NULL), + YAML_STRUCT("sbus", 16, struct_anonymous_7, NULL), + YAML_STRUCT("pxx2", 200, struct_anonymous_8, NULL), + YAML_STRUCT("flysky", 56, struct_anonymous_9, NULL), + YAML_STRUCT("afhds3", 16, struct_anonymous_10, NULL), + YAML_STRUCT("ghost", 8, struct_anonymous_11, NULL), + YAML_STRUCT("crsf", 24, struct_anonymous_12, NULL), + YAML_STRUCT("dsmp", 8, struct_anonymous_13, NULL), + YAML_END +}; +static const struct YamlNode struct_ModuleData[] = { + YAML_IDX, + YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), + YAML_UNSIGNED( "channelsStart", 8 ), + YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), + YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), + YAML_PADDING( 4 ), + YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), + YAML_END +}; +static const struct YamlNode struct_TrainerModuleData[] = { + YAML_UNSIGNED_CUST( "mode", 8, r_trainerMode, w_trainerMode ), + YAML_UNSIGNED( "channelsStart", 8 ), + YAML_SIGNED( "channelsCount", 8 ), + YAML_SIGNED( "frameLength", 8 ), + YAML_SIGNED( "delay", 6 ), + YAML_UNSIGNED( "pulsePol", 1 ), + YAML_PADDING( 1 ), + YAML_END +}; +static const struct YamlNode union_ScriptDataInput_elmts[] = { + YAML_SIGNED( "value", 16 ), + YAML_UNSIGNED_CUST( "source", 16, r_mixSrcRaw, w_mixSrcRaw ), + YAML_END +}; +static const struct YamlNode union_ScriptDataInput[] = { + YAML_IDX, + YAML_UNION("u", 16, union_ScriptDataInput_elmts, select_script_input), + YAML_END +}; +static const struct YamlNode struct_ScriptData[] = { + YAML_IDX, + YAML_STRING("file", 6), + YAML_STRING("name", 6), + YAML_ARRAY("inputs", 16, 6, union_ScriptDataInput, NULL), + YAML_END +}; +static const struct YamlNode struct_string_32[] = { + YAML_IDX, + YAML_STRING("val", 4), + YAML_END +}; +static const struct YamlNode union_anonymous_14_elmts[] = { + YAML_UNSIGNED( "id", 16 ), + YAML_UNSIGNED( "persistentValue", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_16[] = { + YAML_UNSIGNED( "physID", 5 ), + YAML_UNSIGNED( "rxIndex", 3 ), + YAML_END +}; +static const struct YamlNode union_anonymous_15_elmts[] = { + YAML_STRUCT("frskyInstance", 8, struct_anonymous_16, NULL), + YAML_UNSIGNED( "instance", 8 ), + YAML_ENUM("formula", 8, enum_TelemetrySensorFormula, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_18[] = { + YAML_UNSIGNED( "ratio", 16 ), + YAML_SIGNED( "offset", 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_19[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_UNSIGNED( "index", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_20[] = { + YAML_ARRAY("sources", 8, 4, struct_signed_8, NULL), + YAML_END +}; +static const struct YamlNode struct_anonymous_21[] = { + YAML_UNSIGNED( "source", 8 ), + YAML_PADDING( 24 ), + YAML_END +}; +static const struct YamlNode struct_anonymous_22[] = { + YAML_UNSIGNED( "gps", 8 ), + YAML_UNSIGNED( "alt", 8 ), + YAML_PADDING( 16 ), + YAML_END +}; +static const struct YamlNode union_anonymous_17_elmts[] = { + YAML_STRUCT("custom", 32, struct_anonymous_18, NULL), + YAML_STRUCT("cell", 32, struct_anonymous_19, NULL), + YAML_STRUCT("calc", 32, struct_anonymous_20, NULL), + YAML_STRUCT("consumption", 32, struct_anonymous_21, NULL), + YAML_STRUCT("dist", 32, struct_anonymous_22, NULL), + YAML_UNSIGNED( "param", 32 ), + YAML_END +}; +static const struct YamlNode struct_TelemetrySensor[] = { + YAML_IDX, + YAML_UNION("id1", 16, union_anonymous_14_elmts, select_id1), + YAML_UNION("id2", 8, union_anonymous_15_elmts, select_id2), + YAML_STRING("label", 4), + YAML_UNSIGNED( "subId", 8 ), + YAML_ENUM("type", 1, enum_TelemetrySensorType, NULL), + YAML_PADDING( 1 ), + YAML_UNSIGNED( "unit", 6 ), + YAML_UNSIGNED( "prec", 2 ), + YAML_UNSIGNED( "autoOffset", 1 ), + YAML_UNSIGNED( "filter", 1 ), + YAML_UNSIGNED( "logs", 1 ), + YAML_UNSIGNED( "persistent", 1 ), + YAML_UNSIGNED( "onlyPositive", 1 ), + YAML_PADDING( 1 ), + YAML_UNION("cfg", 32, union_anonymous_17_elmts, select_sensor_cfg), + YAML_END +}; +static const struct YamlNode union_ZoneOptionValue_elmts[] = { + YAML_UNSIGNED( "unsignedValue", 32 ), + YAML_SIGNED( "signedValue", 32 ), + YAML_UNSIGNED( "boolValue", 32 ), + YAML_STRING("stringValue", 12), + 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, NULL), + YAML_UNION("value", 96, union_ZoneOptionValue_elmts, select_zov), + YAML_END +}; +static const struct YamlNode struct_WidgetPersistentData[] = { + YAML_ARRAY("options", 128, 10, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_ZonePersistentData[] = { + YAML_IDX, + YAML_STRING("widgetName", 20), + YAML_STRUCT("widgetData", 1280, struct_WidgetPersistentData, NULL), + YAML_END +}; +static const struct YamlNode struct_LayoutPersistentData[] = { + YAML_ARRAY("zones", 1440, 10, struct_ZonePersistentData, NULL), + YAML_ARRAY("options", 128, 10, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_CustomScreenData[] = { + YAML_IDX, + YAML_STRING("LayoutId", 12), + YAML_STRUCT("layoutData", 15680, struct_LayoutPersistentData, NULL), + YAML_END +}; +static const struct YamlNode struct_TopBarPersistentData[] = { + YAML_ARRAY("zones", 1440, 6, struct_ZonePersistentData, NULL), + YAML_ARRAY("options", 128, 1, struct_ZoneOptionValueTyped, NULL), + YAML_END +}; +static const struct YamlNode struct_customSwitch[] = { + YAML_IDX_CUST("sw",cfs_idx_read,cfs_idx_write), + YAML_STRING("name", 3), + YAML_ENUM("type", 3, enum_SwitchConfig, NULL), + YAML_UNSIGNED( "group", 2 ), + YAML_ENUM("start", 2, enum_fsStartPositionType, NULL), + YAML_UNSIGNED( "state", 1 ), + YAML_ENUM("onColorLuaOverride", 1, enum_booleanEnum, NULL), + YAML_ENUM("offColorLuaOverride", 1, enum_booleanEnum, NULL), + YAML_PADDING( 6 ), + YAML_STRUCT("onColor", 24, struct_RGBLedColor, isAlwaysActive), + YAML_STRUCT("offColor", 24, struct_RGBLedColor, isAlwaysActive), + YAML_END +}; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh, NULL), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; +static const struct YamlNode struct_ModelData[] = { + YAML_CUSTOM("semver",nullptr,w_semver), + YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL), + YAML_UNSIGNED( "telemetryProtocol", 3 ), + YAML_UNSIGNED( "thrTrim", 1 ), + YAML_UNSIGNED( "noGlobalFunctions", 1 ), + YAML_UNSIGNED( "displayTrims", 2 ), + YAML_UNSIGNED( "ignoreSensorIds", 1 ), + YAML_SIGNED( "trimInc", 3 ), + YAML_UNSIGNED( "disableThrottleWarning", 1 ), + YAML_UNSIGNED( "displayChecklist", 1 ), + YAML_UNSIGNED( "extendedLimits", 1 ), + YAML_UNSIGNED( "extendedTrims", 1 ), + YAML_UNSIGNED( "throttleReversed", 1 ), + YAML_UNSIGNED( "enableCustomThrottleWarning", 1 ), + YAML_UNSIGNED( "disableTelemetryWarning", 1 ), + YAML_UNSIGNED( "showInstanceIds", 1 ), + YAML_UNSIGNED( "checklistInteractive", 1 ), + YAML_PADDING( 4 ), + YAML_SIGNED( "customThrottleWarningPosition", 8 ), + YAML_UNSIGNED( "beepANACenter", 16 ), + YAML_ARRAY("mixData", 160, 64, struct_MixData, NULL), + YAML_ARRAY("limitData", 104, 32, struct_LimitData, NULL), + YAML_ARRAY("expoData", 144, 64, struct_ExpoData, NULL), + YAML_ARRAY("curves", 32, 32, struct_CurveHeader, NULL), + YAML_ARRAY("points", 8, 512, struct_signed_8, NULL), + YAML_ARRAY("logicalSw", 72, 64, struct_LogicalSwitchData, NULL), + YAML_ARRAY("customFn", 88, 64, struct_CustomFunctionData, cfn_is_active), + YAML_STRUCT("swashR", 64, struct_SwashRingData, swash_is_active), + YAML_ARRAY("flightModeData", 384, 9, struct_FlightModeData, fmd_is_active), + YAML_UNSIGNED_CUST( "thrTraceSrc", 8, r_thrSrc, w_thrSrc ), + YAML_CUSTOM("switchWarningState",r_swtchWarn,nullptr), + YAML_ARRAY("switchWarning", 2, 32, struct_swtchWarn, nullptr), + YAML_ARRAY("gvars", 56, 9, struct_GVarData, NULL), + YAML_STRUCT("varioData", 40, struct_VarioData, NULL), + YAML_UNSIGNED_CUST( "rssiSource", 8, r_tele_sensor, w_tele_sensor ), + YAML_STRUCT("rssiAlarms", 0, struct_RssiAlarmData, NULL), + YAML_STRUCT("rfAlarms", 16, struct_RFAlarmData, NULL), + YAML_UNSIGNED( "thrTrimSw", 3 ), + YAML_ENUM("potsWarnMode", 2, enum_PotsWarnMode, NULL), + YAML_ENUM("jitterFilter", 2, enum_ModelOverridableEnable, NULL), + YAML_PADDING( 1 ), + YAML_ARRAY("moduleData", 232, 2, struct_ModuleData, NULL), + YAML_ARRAY("failsafeChannels", 16, 32, struct_signed_16, NULL), + YAML_STRUCT("trainerData", 40, struct_TrainerModuleData, NULL), + YAML_ARRAY("scriptsData", 192, 9, struct_ScriptData, NULL), + YAML_ARRAY("inputNames", 32, 32, struct_string_32, NULL), + YAML_UNSIGNED( "potsWarnEnabled", 16 ), + YAML_ARRAY("potsWarnPosition", 8, 16, struct_signed_8, NULL), + YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), + YAML_ARRAY("screenData", 15776, 10, struct_CustomScreenData, NULL), + YAML_STRUCT("topbarData", 8768, struct_TopBarPersistentData, NULL), + YAML_ARRAY("topbarWidgetWidth", 8, 6, struct_unsigned_8, NULL), + YAML_UNSIGNED( "view", 8 ), + YAML_STRING("modelRegistrationID", 8), + YAML_CUSTOM("functionSwitchConfig",r_functionSwitchConfig,nullptr), + YAML_CUSTOM("functionSwitchGroup",r_functionSwitchGroup,nullptr), + YAML_CUSTOM("functionSwitchStartConfig",r_functionSwitchStartConfig,nullptr), + YAML_CUSTOM("functionSwitchLogicalState",r_functionSwitchLogicalState,nullptr), + YAML_ARRAY("switchNames", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsNameConfig, nullptr), + YAML_ARRAY("functionSwitchLedONColor", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsOnColorConfig, nullptr), + YAML_ARRAY("functionSwitchLedOFFColor", 0, NUM_FUNCTIONS_SWITCHES, struct_cfsOffColorConfig, nullptr), + YAML_ARRAY("customSwitches", 88, 6, struct_customSwitch, isAlwaysActive), + YAML_ARRAY("cfsGroupOn", 1, 8, struct_cfsGroupOn, cfsGroupIsActive), + YAML_PADDING( 8 ), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode, NULL), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), + YAML_ENUM("radioThemesDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("radioGFDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("radioTrainerDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelHeliDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelFMDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelCurvesDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelGVDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelLSDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelSFDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelCustomScriptsDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_ENUM("modelTelemetryDisabled", 2, enum_ModelOverridableEnable, NULL), + YAML_END +}; +static const struct YamlNode struct_PartialModel[] = { + YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), + YAML_ARRAY("timers", 136, 3, struct_TimerData, NULL), + YAML_END +}; + +#define MAX_RADIODATA_MODELDATA_PARTIALMODEL_STR_LEN 29 + +static const struct YamlNode __RadioData_root_node = YAML_ROOT( struct_RadioData ); + +const YamlNode* get_radiodata_nodes() +{ + return &__RadioData_root_node; +} +static const struct YamlNode __ModelData_root_node = YAML_ROOT( struct_ModelData ); + +const YamlNode* get_modeldata_nodes() +{ + return &__ModelData_root_node; +} +static const struct YamlNode __PartialModel_root_node = YAML_ROOT( struct_PartialModel ); + +const YamlNode* get_partialmodel_nodes() +{ + return &__PartialModel_root_node; +} + diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index 47437b215..64d7f9119 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -90,6 +90,11 @@ uint8_t potsPos[MAX_POTS]; uint16_t fsPreviousState = 0; +uint8_t isSwitch3Pos(uint8_t idx) +{ + return IS_CONFIG_3POS(idx); +} + void setFSStartupPosition() { for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { diff --git a/radio/src/targets/common/arm/stm32/diskio_sdio.cpp b/radio/src/targets/common/arm/stm32/diskio_sdio.cpp index 229e9f879..d4c48d97b 100644 --- a/radio/src/targets/common/arm/stm32/diskio_sdio.cpp +++ b/radio/src/targets/common/arm/stm32/diskio_sdio.cpp @@ -90,18 +90,15 @@ #endif -#if defined(SDMMC1) && defined(SD_SDIO) -# if !defined(SD_SDIO_IRQHandler) -# define SD_SDIO_IRQHandler SDMMC1_IRQHandler -# define SD_SDIO_IRQn SDMMC1_IRQn -# endif +#if defined(SDMMC1) # define SD_SDIO_TypeDef SDMMC_TypeDef # define SD_SDIO_CLOCK_EDGE_RISING SDMMC_CLOCK_EDGE_RISING # define SD_SDIO_CLOCK_POWER_SAVE_DISABLE SDMMC_CLOCK_POWER_SAVE_DISABLE # define SD_SDIO_BUS_WIDE_1B SDMMC_BUS_WIDE_1B # define SD_SDIO_BUS_WIDE_4B SDMMC_BUS_WIDE_4B # define SD_SDIO_HARDWARE_FLOW_CONTROL_DISABLE SDMMC_HARDWARE_FLOW_CONTROL_DISABLE -#elif !defined(SDMMC1) +# define SD_SDIO_IRQn SDMMC1_IRQn +#else # if !defined(SD_SDIO) # define SD_SDIO SDIO # endif @@ -118,6 +115,24 @@ # define SD_SDIO_HARDWARE_FLOW_CONTROL_DISABLE SDIO_HARDWARE_FLOW_CONTROL_DISABLE #endif +struct { + SD_SDIO_TypeDef* periph = SD_SDIO; + gpio_t D0 = SD_SDIO_PIN_D0; + gpio_t D1 = SD_SDIO_PIN_D1; + gpio_t D2 = SD_SDIO_PIN_D2; + gpio_t D3 = SD_SDIO_PIN_D3; + gpio_t CMD = SD_SDIO_PIN_CMD; + gpio_t CLK = SD_SDIO_PIN_CLK; + gpio_af_t AF_D0 = SD_SDIO_AF_D0; + gpio_af_t AF_D1 = SD_SDIO_AF_D1; + gpio_af_t AF_D2 = SD_SDIO_AF_D2; + gpio_af_t AF_D3 = SD_SDIO_AF_D3; + gpio_af_t AF_CMD = SD_SDIO_AF_CMD; + gpio_af_t AF_CLK = SD_SDIO_AF_CLK; + IRQn_Type IRQn = SD_SDIO_IRQn; + uint32_t SD_CLK_DIV = SD_SDIO_TRANSFER_CLK_DIV; +} currentSD; + #if FF_MAX_SS != FF_MIN_SS #error "Variable sector size is not supported" #endif @@ -156,8 +171,12 @@ extern uint32_t _heap_start; // HAL state static SD_HandleTypeDef sdio; #if defined(SD_SDIO_DMA) +#if defined(STM32H7) || defined(STM32H7RS) || defined(STM32H5) +static_assert(false,"H7/H5 use dedicated DMA controler"); +#else static DMA_HandleTypeDef sdioTxDma; #endif +#endif // Disk status static volatile uint32_t WriteStatus; @@ -179,26 +198,26 @@ static void _sd_sdio_clk_enable(SD_SDIO_TypeDef* periph) static void sdio_low_level_init(void) { - _sd_sdio_clk_enable(SD_SDIO); + _sd_sdio_clk_enable(currentSD.periph); // data pins - gpio_init(SD_SDIO_PIN_D0, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_D0, SD_SDIO_AF_D0); + gpio_init(currentSD.D0, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.D0, currentSD.AF_D0); - gpio_init(SD_SDIO_PIN_D1, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_D1, SD_SDIO_AF_D1); - - gpio_init(SD_SDIO_PIN_D2, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_D2, SD_SDIO_AF_D2); + gpio_init(currentSD.D1, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.D1, currentSD.AF_D1); - gpio_init(SD_SDIO_PIN_D3, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_D3, SD_SDIO_AF_D3); + gpio_init(currentSD.D2, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.D2, currentSD.AF_D2); - gpio_init(SD_SDIO_PIN_CMD, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_CMD, SD_SDIO_AF_CMD); - - gpio_init(SD_SDIO_PIN_CLK, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(SD_SDIO_PIN_CLK, SD_SDIO_AF_CLK); + gpio_init(currentSD.D3, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.D3, currentSD.AF_D3); + + gpio_init(currentSD.CMD, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.CMD, currentSD.AF_CMD); + + gpio_init(currentSD.CLK, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); + gpio_set_af(currentSD.CLK, currentSD.AF_CLK); #if defined(SD_SDIO_HAS_TRANSCEIVER) gpio_init_af(SD_SDIO_PIN_CKIN, SD_SDIO_AF_CKIN, GPIO_PIN_SPEED_HIGH); @@ -208,8 +227,8 @@ static void sdio_low_level_init(void) #endif // SDIO Interrupt ENABLE - NVIC_SetPriority(SD_SDIO_IRQn, 0); - NVIC_EnableIRQ(SD_SDIO_IRQn); + NVIC_SetPriority(currentSD.IRQn, 0); + NVIC_EnableIRQ(currentSD.IRQn); #if defined(SD_SDIO_DMA) // Init SDIO DMA instance @@ -231,7 +250,7 @@ static void sdio_low_level_init(void) __HAL_LINKDMA(&sdio, hdmatx, sdioTxDma); __HAL_LINKDMA(&sdio, hdmarx, sdioTxDma); - + // DMA2 STREAMx Interrupt ENABLE NVIC_SetPriority(SD_SDIO_DMA_IRQn, 0); NVIC_EnableIRQ(SD_SDIO_DMA_IRQn); @@ -241,6 +260,27 @@ static void sdio_low_level_init(void) HAL_SD_CardInfoTypeDef cardInfo; static DSTATUS sdio_initialize(BYTE lun) { +#if defined(SD2_PRESENT_GPIO) + /* Define active SD */ + if (gpio_read(SD2_PRESENT_GPIO) == 0) { + currentSD.periph = SDMMC2; + currentSD.D0 = SD2_SDIO_PIN_D0; + currentSD.AF_D0 = SD2_SDIO_AF_D0; + currentSD.D1 = SD2_SDIO_PIN_D1; + currentSD.AF_D1 = SD2_SDIO_AF_D1; + currentSD.D2 = SD2_SDIO_PIN_D2; + currentSD.AF_D2 = SD2_SDIO_AF_D2; + currentSD.D3 = SD2_SDIO_PIN_D3; + currentSD.AF_D3 = SD2_SDIO_AF_D3; + currentSD.CMD = SD2_SDIO_PIN_CMD; + currentSD.AF_CMD = SD2_SDIO_AF_CMD; + currentSD.CLK = SD2_SDIO_PIN_CLK; + currentSD.AF_CLK = SD2_SDIO_AF_CLK; + currentSD.IRQn = SDMMC2_IRQn; + currentSD.SD_CLK_DIV = SD2_SDIO_TRANSFER_CLK_DIV; + } +#endif + /* SDIO Peripheral Low Level Init */ sdio_low_level_init(); @@ -248,7 +288,7 @@ static DSTATUS sdio_initialize(BYTE lun) /*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */ /*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */ - sdio.Instance = SD_SDIO; + sdio.Instance = currentSD.periph; sdio.Init.ClockEdge = SD_SDIO_CLOCK_EDGE_RISING; sdio.Init.ClockPowerSave = SD_SDIO_CLOCK_POWER_SAVE_DISABLE; sdio.Init.BusWide = SD_SDIO_BUS_WIDE_1B; @@ -259,7 +299,7 @@ static DSTATUS sdio_initialize(BYTE lun) #if defined(SD_SDIO_CLOCK_BYPASS_DISABLE) sdio.Init.ClockBypass = SD_SDIO_CLOCK_BYPASS_DISABLE; #endif - sdio.Init.ClockDiv = SD_SDIO_TRANSFER_CLK_DIV; + sdio.Init.ClockDiv = currentSD.SD_CLK_DIV; HAL_StatusTypeDef halStatus = HAL_SD_Init(&sdio); if (halStatus != HAL_OK) { @@ -311,7 +351,7 @@ static SDTransferState sdio_check_card_state() else if (cardstate == HAL_SD_CARD_ERROR) { return SD_TRANSFER_ERROR; } - + return SD_TRANSFER_BUSY; } @@ -333,7 +373,11 @@ static DSTATUS sdio_status(BYTE lun) { DSTATUS stat = RES_OK; -#if defined(SD_PRESENT_GPIO) +#if defined(SD2_PRESENT_GPIO) + if (currentSD.periph == SDMMC2 && gpio_read(SD2_PRESENT_GPIO)) { + stat |= STA_NODISK; + } +#elif defined(SD_PRESENT_GPIO) if (gpio_read(SD_PRESENT_GPIO)) { stat |= STA_NODISK; } @@ -551,12 +595,28 @@ extern "C" void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) ReadStatus = 1; } +#if defined(SDMMC1) +#if defined(SDMMC1) +extern "C" void SDMMC1_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_SDIO); + HAL_SD_IRQHandler(&sdio); +} +#endif +#if defined(SDMMC2) && defined(SD2_PRESENT_GPIO) +extern "C" void SDMMC2_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_SDIO); + HAL_SD_IRQHandler(&sdio); +} +#endif +#else extern "C" void SD_SDIO_IRQHandler(void) { DEBUG_INTERRUPT(INT_SDIO); HAL_SD_IRQHandler(&sdio); } - +#endif #if defined(SD_SDIO_DMA) extern "C" void SD_SDIO_DMA_IRQHANDLER(void) { diff --git a/radio/src/targets/common/arm/stm32/flysky_gimbal_driver.cpp b/radio/src/targets/common/arm/stm32/flysky_gimbal_driver.cpp index 77b89effd..600b11fad 100644 --- a/radio/src/targets/common/arm/stm32/flysky_gimbal_driver.cpp +++ b/radio/src/targets/common/arm/stm32/flysky_gimbal_driver.cpp @@ -48,8 +48,10 @@ static const stm32_usart_t fsUSART = { .IRQn = FLYSKY_HALL_SERIAL_USART_IRQn, .IRQ_Prio = 4, .txDMA = FLYSKY_HALL_SERIAL_DMA, +#if defined(FLYSKY_HALL_DMA_Stream_TX) .txDMA_Stream = FLYSKY_HALL_DMA_Stream_TX, .txDMA_Channel = FLYSKY_HALL_DMA_Channel, +#endif .rxDMA = FLYSKY_HALL_SERIAL_DMA, .rxDMA_Stream = FLYSKY_HALL_DMA_Stream_RX, .rxDMA_Channel = FLYSKY_HALL_DMA_Channel, @@ -263,12 +265,17 @@ bool flysky_gimbal_init() { if (flysky_gimbal_init_uart() != 0) return false; - // Wait 70ms for FlySky gimbals to respond. According to LA trace, minimally - // 23ms is required _fs_gimbal_detected = false; - for (uint8_t i = 0; i < 70; i++) { + // Wait 70ms for serial gimbals to respond + for (uint16_t i = 0; i < 70; i++) { +#if defined(HALL_SYNC) && !defined(SIMU) + gpio_set(HALL_SYNC); +#endif delay_ms(1); +#if defined(HALL_SYNC) && !defined(SIMU) + gpio_clear(HALL_SYNC); +#endif if (_fs_gimbal_detected) { // Try to obtain the version of gimbal for operation mode selection _fs_cmd_get_version(); diff --git a/radio/src/targets/common/arm/stm32/h7/memory_sections.h b/radio/src/targets/common/arm/stm32/h7/memory_sections.h index 76c7dcfaa..62b77fa18 100644 --- a/radio/src/targets/common/arm/stm32/h7/memory_sections.h +++ b/radio/src/targets/common/arm/stm32/h7/memory_sections.h @@ -24,7 +24,7 @@ #define __INIT_HOOK __attribute__((section(".init_hook"))) #define __CCMRAM __attribute__((section(".ccm"), aligned(4))) -#define __DMA __attribute__((section(".ram"), aligned(4))) +#define __DMA __attribute__((section(".ram"), aligned(32))) #define __DMA_NO_CACHE __attribute__((section(".dram"), aligned(4))) #define __IRAM __attribute__((section(".iram"))) #define __SDRAM __attribute__((section(".sdram"), aligned(4))) diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index b1c6f025c..01913c95f 100644 --- a/radio/src/targets/common/arm/stm32/pwr_driver.cpp +++ b/radio/src/targets/common/arm/stm32/pwr_driver.cpp @@ -32,6 +32,10 @@ void pwrInit() gpio_init(SD_PRESENT_GPIO, GPIO_IN_PU, GPIO_PIN_SPEED_LOW); #endif +#if defined(SD2_PRESENT_GPIO) + gpio_init(SD2_PRESENT_GPIO, GPIO_IN_PU, GPIO_PIN_SPEED_LOW); +#endif + #if defined(INTMODULE_BOOTCMD_GPIO) gpio_init(INTMODULE_BOOTCMD_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); gpio_write(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_DEFAULT); diff --git a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp index 634d2303d..a8658dec3 100644 --- a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp @@ -154,6 +154,7 @@ void rotaryEncoderStartDelay() void rotaryEncoderInit() { +#if defined(ROTARY_ENCODER_GPIO) LL_GPIO_InitTypeDef pinInit; LL_GPIO_StructInit(&pinInit); pinInit.Mode = LL_GPIO_MODE_INPUT; @@ -186,6 +187,44 @@ void rotaryEncoderInit() uint32_t trigger = LL_EXTI_TRIGGER_RISING_FALLING; stm32_exti_enable(ROTARY_ENCODER_EXTI_LINE1, trigger, rotaryEncoderStartDelay); stm32_exti_enable(ROTARY_ENCODER_EXTI_LINE2, trigger, rotaryEncoderStartDelay); +#else + LL_GPIO_InitTypeDef pinInit; + LL_GPIO_StructInit(&pinInit); + pinInit.Mode = LL_GPIO_MODE_INPUT; + pinInit.Pull = LL_GPIO_PULL_UP; + pinInit.Pin = ROTARY_ENCODER_GPIO_PIN_A; + + stm32_gpio_enable_clock(ROTARY_ENCODER_GPIO_A); + stm32_gpio_enable_clock(ROTARY_ENCODER_GPIO_B); + LL_GPIO_Init(ROTARY_ENCODER_GPIO_A, &pinInit); + pinInit.Pin = ROTARY_ENCODER_GPIO_PIN_B; + LL_GPIO_Init(ROTARY_ENCODER_GPIO_B, &pinInit); + + stm32_timer_enable_clock(ROTARY_ENCODER_TIMER); + ROTARY_ENCODER_TIMER->ARR = 99; // 100uS + ROTARY_ENCODER_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS + ROTARY_ENCODER_TIMER->CCER = 0; + ROTARY_ENCODER_TIMER->CCMR1 = 0; + ROTARY_ENCODER_TIMER->EGR = 0; + ROTARY_ENCODER_TIMER->CR1 = 0; + ROTARY_ENCODER_TIMER->DIER |= TIM_DIER_UIE; + +#if defined(LL_APB4_GRP1_PERIPH_SYSCFG) + LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_SYSCFG); +#elif defined(LL_APB2_GRP1_PERIPH_SYSCFG) + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); +#else + #error "Unsupported SYSCFG clock" +#endif + + LL_SYSCFG_SetEXTISource(ROTARY_ENCODER_EXTI_PORT_A, ROTARY_ENCODER_EXTI_SYS_LINE1); + LL_SYSCFG_SetEXTISource(ROTARY_ENCODER_EXTI_PORT_B, ROTARY_ENCODER_EXTI_SYS_LINE2); + + uint32_t trigger = LL_EXTI_TRIGGER_RISING_FALLING; + stm32_exti_enable(ROTARY_ENCODER_EXTI_LINE1, trigger, rotaryEncoderStartDelay); + stm32_exti_enable(ROTARY_ENCODER_EXTI_LINE2, trigger, rotaryEncoderStartDelay); +#endif + NVIC_EnableIRQ(ROTARY_ENCODER_TIMER_IRQn); NVIC_SetPriority(ROTARY_ENCODER_TIMER_IRQn, 7); diff --git a/radio/src/targets/common/arm/stm32/rtc_driver.cpp b/radio/src/targets/common/arm/stm32/rtc_driver.cpp index cd07d6a88..8da3296f8 100644 --- a/radio/src/targets/common/arm/stm32/rtc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rtc_driver.cpp @@ -67,8 +67,14 @@ void rtcInit() __HAL_RCC_PWR_CLK_ENABLE(); #endif + HAL_PWR_EnableBkUpAccess(); +#if defined(LSE_DRIVE_STRENGTH) + __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF); + __HAL_RCC_LSEDRIVE_CONFIG(LSE_DRIVE_STRENGTH); +#endif + // Enable LSE Oscillator - RCC_OscInitTypeDef RCC_OscInitStruct; + RCC_OscInitTypeDef RCC_OscInitStruct = {}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; #if !defined(STM32H7RS) && !defined(STM32H5) RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; diff --git a/radio/src/targets/common/arm/stm32/stm32_adc.cpp b/radio/src/targets/common/arm/stm32/stm32_adc.cpp index 6ab2966ea..75d8c860b 100644 --- a/radio/src/targets/common/arm/stm32/stm32_adc.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_adc.cpp @@ -33,8 +33,12 @@ #include #include "FreeRTOSConfig.h" - -#define OVERSAMPLING 4 +#if defined(STM32H7) || defined(STM32H7RS) || defined(STM32H5) + // Disable software oversampling as HW one is used + #define OVERSAMPLING 1 +#else + #define OVERSAMPLING 4 +#endif #define SAMPLING_TIMEOUT_US 500 @@ -78,6 +82,9 @@ static uint8_t _adc_run; static uint8_t _adc_oversampling_disabled; static uint16_t _adc_oversampling[MAX_ADC_INPUTS]; +// Indicates ADC timeout has occured +static bool _adc_timeout_error; + void stm32_hal_set_inputs_mask(uint32_t inputs) { _adc_input_inhibt_mask |= inputs; @@ -232,6 +239,12 @@ static void adc_setup_scan_mode(ADC_TypeDef* ADCx, uint8_t nconv) wait_loop_index--; } } + + /* Start ADC calibration in mode single-ended or differential */ + LL_ADC_StartCalibration(ADCx, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED); + + /* Wait for calibration completion */ + while (LL_ADC_IsCalibrationOnGoing(ADCx) != 0UL); #endif #if defined(LL_ADC_RESOLUTION_12B_OPT) @@ -253,6 +266,17 @@ static void adc_setup_scan_mode(ADC_TypeDef* ADCx, uint8_t nconv) #endif } +#if defined(STM32H7RS) || defined(STM32H7) || defined(STM32H5) + // set hardware oversampling + if (!_adc_oversampling_disabled && ADCx != ADC3) { + // LL_ADC_OVS_RATIO_x in stm32h7xx_ll_adc.h is broken + // so actual oversampling count need to be used + // ADC3 oversampling is disabled because of issues in LL libs + LL_ADC_ConfigOverSamplingRatioShift(ADCx, 16, LL_ADC_OVS_SHIFT_RIGHT_4); + LL_ADC_SetOverSamplingScope(ADCx, LL_ADC_OVS_GRP_REGULAR_CONTINUED); + } +#endif + LL_ADC_REG_Init(ADCx, &adcRegInit); // Enable ADC @@ -756,10 +780,12 @@ void stm32_hal_adc_wait_completion(const stm32_adc_t* ADCs, uint8_t n_ADC, // busy wait if ((uint32_t)(timersGetUsTick() - timeout) >= SAMPLING_TIMEOUT_US) { TRACE("ADC timeout"); + _adc_timeout_error = true; _adc_started_mask = 0; return; } } + _adc_timeout_error = false; } void stm32_hal_adc_disable_oversampling() @@ -787,7 +813,7 @@ static void _adc_chain_conversions(const stm32_adc_t* adc) for (uint8_t i = 0; i < _adc_n_inputs; i++) { if (~_adc_input_mask & (1 << i)) continue; if (_adc_inhibit_mask & (1 << i)) continue; - adcValues[i] = _adc_oversampling[i] / OVERSAMPLING; + if (!_adc_timeout_error) adcValues[i] = _adc_oversampling[i] / OVERSAMPLING; } // we're done! diff --git a/radio/src/targets/common/arm/stm32/stm32_i2c_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_i2c_driver.cpp index d3f4600da..e74ce57fb 100644 --- a/radio/src/targets/common/arm/stm32/stm32_i2c_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_i2c_driver.cpp @@ -360,7 +360,14 @@ static int i2c_init_clock_source(I2C_TypeDef* instance) #endif #if defined(LL_RCC_I2C123_CLKSOURCE) - LL_RCC_SetClockSource(LL_RCC_I2C123_CLKSOURCE_PCLK1); +# if defined(LL_RCC_I2C4_CLKSOURCE) + if (instance == I2C4) { + LL_RCC_SetClockSource(LL_RCC_I2C4_CLKSOURCE_PCLK4); + } else +# endif + { + LL_RCC_SetClockSource(LL_RCC_I2C123_CLKSOURCE_PCLK1); + } #endif return 0; @@ -371,15 +378,17 @@ static int i2c_init_clock_source(I2C_TypeDef* instance) static int i2c_enable_clock(I2C_TypeDef* instance) { /* Peripheral clock enable */ - if (instance == I2C1) { + if (instance == I2C1) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); - } else if (instance == I2C2) { + else if (instance == I2C2) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C2); - } else if (instance == I2C3) { + else if (instance == I2C3) LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); - } else { - return -1; - } +#if defined(I2C4) + else if (instance == I2C4) + LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_I2C4); +#endif + else return -1; return 0; } @@ -393,6 +402,10 @@ static int i2c_disable_clock(I2C_TypeDef* instance) LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C2); else if (instance == I2C3) LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C3); +#if defined(I2C4) + else if (instance == I2C4) + LL_APB4_GRP1_DisableClock(LL_APB4_GRP1_PERIPH_I2C4); +#endif else return -1; @@ -472,17 +485,17 @@ int stm32_i2c_init(uint8_t bus, uint32_t clock_rate, const stm32_i2c_hw_def_t* h if (i2c_gpio_init(hw_def) < 0) { TRACE("I2C ERROR: HAL_I2C_MspInit() I2C_GPIO misconfiguration"); - return -1; + return -2; } if (i2c_enable_clock(hw_def->I2Cx) < 0) { TRACE("I2C ERROR: HAL_I2C_MspInit() I2C misconfiguration"); - return -1; + return -3; } if (HAL_I2C_Init(h) != HAL_OK) { TRACE("I2C ERROR: HAL_I2C_Init() failed"); - return -1; + return -4; } #if defined(I2C_FLTR_ANOFF) && defined(I2C_FLTR_DNF) || defined(STM32H7) || \ diff --git a/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp index cae16721f..3f2136c43 100644 --- a/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_usart_driver.cpp @@ -480,6 +480,7 @@ bool stm32_usart_init(const stm32_usart_t* usart, const etx_serial_init* params) if (params->direction & ETX_Dir_TX) usartInit.TransferDirection |= LL_USART_DIRECTION_TX; + LL_USART_Init(usart->USARTx, &usartInit); LL_USART_Enable(usart->USARTx); diff --git a/radio/src/targets/common/arm/stm32/usbd_conf.c b/radio/src/targets/common/arm/stm32/usbd_conf.c index f686cbc88..d08055529 100644 --- a/radio/src/targets/common/arm/stm32/usbd_conf.c +++ b/radio/src/targets/common/arm/stm32/usbd_conf.c @@ -380,6 +380,11 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) hpcd_USB_OTG.Init.speed = PCD_SPEED_FULL; hpcd_USB_OTG.Init.dma_enable = DISABLE; hpcd_USB_OTG.Init.phy_itface = PCD_PHY_EMBEDDED; +#if defined(STM32H7) + hpcd_USB_OTG.Init.battery_charging_enable = DISABLE; + hpcd_USB_OTG.Init.use_external_vbus = ENABLE; +#endif + hpcd_USB_OTG.Init.Sof_enable = ENABLE; hpcd_USB_OTG.Init.low_power_enable = DISABLE; hpcd_USB_OTG.Init.lpm_enable = DISABLE; diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 4ba5a3baf..0679f50bb 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -864,7 +864,6 @@ #define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn #define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler #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) // Uncomment only one line below to select which storage to use diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index 1cb30666a..6b1e34435 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -269,7 +269,6 @@ #define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn #define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler #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) #define STORAGE_USE_SDIO diff --git a/radio/src/targets/simu/led_driver.cpp b/radio/src/targets/simu/led_driver.cpp index ccf6f3b3a..4201222da 100644 --- a/radio/src/targets/simu/led_driver.cpp +++ b/radio/src/targets/simu/led_driver.cpp @@ -29,6 +29,7 @@ void ledGreen() {} void ledBlue() {} void ledOff() {} void rgbLedColorApply() {} +void rgbLedClearAll() {} bool fsLedState(uint8_t i) { diff --git a/radio/src/targets/st16/hal.h b/radio/src/targets/st16/hal.h index f6abdbbd9..c32be675d 100644 --- a/radio/src/targets/st16/hal.h +++ b/radio/src/targets/st16/hal.h @@ -205,7 +205,7 @@ #define ADC_DMA_STREAM LL_DMA_STREAM_4 #define ADC_DMA_STREAM_IRQ DMA2_Stream4_IRQn #define ADC_DMA_STREAM_IRQHandler DMA2_Stream4_IRQHandler -#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_64CYCLES_5 +#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_8CYCLES_5 #define ADC_EXT ADC3 #define ADC_EXT_CHANNELS \ diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index d5194c08e..3c5503965 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -366,7 +366,7 @@ void setTopBatteryValue(uint32_t volts); #define BATTERY_DIVIDER 39500 #else #define BATTERY_DIVIDER 26214 - #endif + #endif #define VOLTAGE_DROP 20 #endif diff --git a/radio/src/targets/tx15/CMakeLists.txt b/radio/src/targets/tx15/CMakeLists.txt new file mode 100644 index 000000000..8a31b9d85 --- /dev/null +++ b/radio/src/targets/tx15/CMakeLists.txt @@ -0,0 +1,196 @@ +option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) +option(PXX1 "PXX1 protocol support" ON) +option(PXX2 "PXX2 protocol support" OFF) +option(AFHDS3 "AFHDS3 TX Module" ON) +option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) +option(GHOST "Ghost TX Module" ON) +option(MODULE_SIZE_STD "Standard size TX Module" ON) +option(LUA_MIXER "Enable LUA mixer/model scripts support" ON) +option(DISK_CACHE "Enable SD card disk cache" ON) + +set(FIRMWARE_QSPI YES) +set(FIRMWARE_FORMAT_UF2 YES) +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") +set(CPU_TYPE STM32H7) +set(HSE_VALUE 48000000) +set(SDCARD YES) +set(STORAGE_MODELSLIST YES) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +set(BITMAPS_DIR 480x272) +set(TARGET_DIR tx15) +set(RTC_BACKUP_RAM YES) +set(PPM_LIMITS_SYMETRICAL YES) +set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)") +set(HARDWARE_EXTERNAL_MODULE YES) +set(INTERNAL_MODULE_AFHDS3 NO) +set(ROTARY_ENCODER YES) +set(FUNCTION_SWITCHES_WITH_RGB YES) + +#option(STICKS_DEAD_ZONE "Enable sticks dead zone" YES) + +# IMU support +set(IMU ON) +add_definitions(-DIMU_ICM4207C) + +# for size report script +set(CPU_TYPE_FULL STM32H750xB) +set(TARGET_LINKER_DIR stm32h750_sdram) +set(TARGET_EXTRAM_START 0xD0000000) +set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=8192") +set(FIRMWARE_STRIP_BOOTLOADER YES) + +add_definitions(-DPCBTX15 -DPCBHORUS) +add_definitions(-DBATTERY_CHARGE) +add_definitions(-DSTM32_SUPPORT_32BIT_TIMERS) +add_definitions(-DFIRMWARE_QSPI) +add_definitions(-DFIRMWARE_FORMAT_UF2) +add_definitions(-DUSE_CUSTOM_EXTI_IRQ) +add_definitions(-DUSB_CHARGER) + +set(FLAVOUR tx15) +add_definitions(-DRADIO_TX15) +add_definitions(-DMANUFACTURER_RADIOMASTER) + +# defines existing internal modules +set(INTERNAL_MODULES CRSF CACHE STRING "Internal modules") +set(DEFAULT_INTERNAL_MODULE CROSSFIRE CACHE STRING "Default internal module") + +set(BITMAPS_TARGET bm480_bitmaps) +set(FONTS_TARGET x12_fonts) +set(LCD_DRIVER lcd_driver.cpp) +set(HARDWARE_TOUCH YES) +set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) +set(FIRMWARE_DEPENDENCIES datacopy) +set(SOFTWARE_KEYBOARD ON) +set(FLYSKY_GIMBAL ON) +set(FUNCTION_SWITCHES ON) +set(USB_CHARGER YES) +set(AUX_SERIAL YES) +set(AUX2_SERIAL YES) + +add_definitions( + -DSTM32H750XBTx -DSTM32H750xx + -DSDRAM -DCCMRAM + -DCOLORLCD -DLIBOPENUI + -DHARDWARE_TOUCH -DHARDWARE_KEYS -DSOFTWARE_KEYBOARD + -DWS2812_MAX_LEDS=26 +) + +set(SDRAM ON) + +add_definitions(-DAUDIO -DVOICE -DRTCLOCK) +add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) +add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) + +if(STICKS_DEAD_ZONE) + add_definitions(-DSTICK_DEAD_ZONE) +endif() + +if(NOT UNEXPECTED_SHUTDOWN) + add_definitions(-DNO_UNEXPECTED_SHUTDOWN) +endif() + +if(DISK_CACHE) + set(SRC ${SRC} disk_cache.cpp) + add_definitions(-DDISK_CACHE) +endif() + +if(FUNCTION_SWITCHES_WITH_RGB) + set(FUNCTION_SWITCHES YES) + add_definitions(-DFUNCTION_SWITCHES) + add_definitions(-DFUNCTION_SWITCHES_RGB_LEDS) +endif() + +if(FUNCTION_SWITCHES) +add_definitions(-DFUNCTION_SWITCHES) +endif() + +set(AFHDS3 ON) + +# VCP CLI +set(ENABLE_SERIAL_PASSTHROUGH ON CACHE BOOL "Enable serial passthrough") +set(CLI ON CACHE BOOL "Enable CLI") + +set(TARGET_SRC_DIR targets/${TARGET_DIR}) + +set(BOARD_COMMON_SRC + ${TARGET_SRC_DIR}/board.cpp + ${TARGET_SRC_DIR}/bsp_io.cpp + ${TARGET_SRC_DIR}/key_driver.cpp + ${TARGET_SRC_DIR}/lcd_driver.cpp + ${TARGET_SRC_DIR}/backlight_driver.cpp + ${TARGET_SRC_DIR}/haptic_driver.cpp + ${TARGET_SRC_DIR}/extflash_driver.cpp + ${TARGET_SRC_DIR}/usb_charger_driver.cpp + targets/common/arm/stm32/abnormal_reboot.cpp + targets/common/arm/stm32/delays_driver.cpp + targets/common/arm/stm32/dma2d.cpp + targets/common/arm/stm32/flash_driver.cpp + targets/common/arm/stm32/pwr_driver.cpp + targets/common/arm/stm32/rotary_encoder_driver.cpp + targets/common/arm/stm32/rtc_driver.cpp + targets/common/arm/stm32/watchdog_driver.cpp + drivers/pca95xx.cpp +) + +# Bootloader board library +add_library(board_bl OBJECT EXCLUDE_FROM_ALL + ${BOARD_COMMON_SRC} + ${TARGET_SRC_DIR}/system_clock.c + ${TARGET_SRC_DIR}/sdram_driver.cpp + ${TARGET_SRC_DIR}/usb_charger_driver.cpp + ${RADIO_SRC_DIR}/gui/colorlcd/boot_menu.cpp + targets/common/arm/stm32/stm32_qspi.cpp + targets/common/arm/stm32/stm32_pulse_driver.cpp +) +add_dependencies(board_bl minimal_board_lib) +set(BOOTLOADER_SRC ${BOOTLOADER_SRC} $) + +# Firmware board library +add_library(board OBJECT EXCLUDE_FROM_ALL + ${BOARD_COMMON_SRC} + ${TARGET_SRC_DIR}/led_driver.cpp + ${TARGET_SRC_DIR}/tp_gt911.cpp + ${TARGET_SRC_DIR}/audio_driver.cpp + drivers/icm42607C.cpp + drivers/imu_filter.cpp + drivers/tas2505.cpp + targets/common/arm/stm32/heartbeat_driver.cpp + targets/common/arm/stm32/mixer_scheduler_driver.cpp + targets/common/arm/stm32/module_timer_driver.cpp + targets/common/arm/stm32/stm32_pulse_driver.cpp + targets/common/arm/stm32/stm32_softserial_driver.cpp + targets/common/arm/stm32/stm32_qspi.cpp + targets/common/arm/stm32/stm32_i2s.cpp + targets/common/arm/stm32/stm32_switch_driver.cpp + targets/common/arm/stm32/stm32_ws2812.cpp + targets/common/arm/stm32/trainer_driver.cpp +) +add_dependencies(board board_lib) +set(FIRMWARE_SRC ${FIRMWARE_SRC} $) + +include_directories(${RADIO_SRC_DIR}/fonts/colorlcd gui/${GUI_DIR} gui/${GUI_DIR}/layouts) + +if(BOOTLOADER) + set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ../../bootloader/loadboot.cpp + ) +endif() + +set(SRC + ${SRC} + io/frsky_firmware_update.cpp + io/multi_firmware_update.cpp + ) + +if (MULTIMODULE) + add_definitions(-DMULTI_PROTOLIST) + set(SRC ${SRC} + io/multi_protolist.cpp + ) +endif() + +# Make malloc() thread-safe +add_definitions(-DTHREADSAFE_MALLOC) diff --git a/radio/src/targets/tx15/audio_driver.cpp b/radio/src/targets/tx15/audio_driver.cpp new file mode 100644 index 000000000..9be1fa133 --- /dev/null +++ b/radio/src/targets/tx15/audio_driver.cpp @@ -0,0 +1,156 @@ +#include "drivers/tas2505.h" + +#include "stm32_dma.h" +#include "stm32_i2s.h" +#include "stm32_i2c_driver.h" + +#include "stm32_hal_ll.h" +#include "timers_driver.h" + +#include "audio.h" +#include "debug.h" + +#define DEFAULT_DAC_VOL -15 // -7dB +#define DEFAULT_HP_VOL 20 +#define DEFAULT_SPK_VOL 40 + +static tas2505_t _tas2505 = { + .ndac = 2, + .mdac = 1, + .dosr = 256, + .dac_vol = DEFAULT_DAC_VOL, + .hp_drv = 0, + .hp_vol = DEFAULT_HP_VOL, + .spk_drv = TAS2505_SPK_DRV_32_DB, + .spk_vol = DEFAULT_SPK_VOL, +}; + +// +// TX15 pinout +// +// Pin MCU TAS2505 +// --------------------------------- +// PC6 (I2S2_MCK): MCLK <-> MCLK +// PI0 (I2S2_WS): WCLK <-> WCLK +// PI1 (I2S2_CK): BCLK <-> BCLK +// PI3 (I2S2_SDO): DOUT <-> DIN +// PC2 (I2S2_SDI): DIN <-> DOUT + + +const stm32_i2s_t _i2s_dev = { + .SPIx = AUDIO_SPI, + .MCK = {GPIO_PIN(GPIOC, 6), GPIO_AF5}, + .WS = {GPIO_PIN(GPIOI, 0), GPIO_AF5}, + .CK = {GPIO_PIN(GPIOI, 1), GPIO_AF5}, + .SDO = {GPIO_PIN(GPIOI, 3), GPIO_AF5}, + .SDI = {GPIO_PIN(GPIOI, 2), GPIO_AF5}, + + .DMA = I2S_DMA, + .DMA_Stream = I2S_DMA_Stream, + .DMA_PeriphRequest = I2S_DMA_Stream_Request, +}; + +// 16 bit, 2 channels +#define DMA_BUFFER_HALF_LEN AUDIO_BUFFER_SIZE + +// 2 buffers +#define DMA_BUFFER_LEN (DMA_BUFFER_HALF_LEN * 2) + +static int32_t _dma_buffer[DMA_BUFFER_LEN] __DMA_NO_CACHE; + +static void enable_dma_irqs() +{ + LL_DMA_EnableIT_HT(I2S_DMA, I2S_DMA_Stream); + LL_DMA_EnableIT_TC(I2S_DMA, I2S_DMA_Stream); + + NVIC_SetPriority(I2S_DMA_Stream_IRQn, 7); + NVIC_EnableIRQ(I2S_DMA_Stream_IRQn); +} + +int audioInit() +{ + gpio_init(AUDIO_HP_DETECT_PIN, GPIO_IN_PU, GPIO_PIN_SPEED_LOW); + + memset(_dma_buffer, 0, sizeof(_dma_buffer)); + + if (tas2505_probe(&_tas2505, 0, 0x30 >> 1) < 0) { + return -1; + } + + if (stm32_i2s_init(&_i2s_dev)) { + return -1; + } + + enable_dma_irqs(); + return tas2505_init(&_tas2505); +} + +static volatile uint32_t _dma_buffer_offset = 0; + +void audioConsumeCurrentBuffer() +{ + if (!stm32_i2s_is_xfer_started(&_i2s_dev)) { + + auto buffer = audioQueue.buffersFifo.getNextFilledBuffer(); + if (!buffer) return; + + unsigned idx = 0; + for (; idx < buffer->size; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = (uint32_t)buffer->data[idx]; + } + for (; idx < DMA_BUFFER_HALF_LEN; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = 0; + } + audioQueue.buffersFifo.freeNextFilledBuffer(); + stm32_i2s_config_dma_stream(&_i2s_dev, _dma_buffer, DMA_BUFFER_LEN * 2); + stm32_i2s_start_dma_stream(&_i2s_dev); + } +} + +static inline uint32_t _calc_offset(uint8_t tc) +{ + return tc * DMA_BUFFER_HALF_LEN; +} + +static void audio_update_dma_buffer(uint8_t tc) +{ + _dma_buffer_offset = _calc_offset(tc); + + auto buffer = audioQueue.buffersFifo.getNextFilledBuffer(); + if (!buffer) { + unsigned idx = 0; + for (; idx < DMA_BUFFER_HALF_LEN; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = 0; + } + } else { + unsigned idx = 0; + for (; idx < buffer->size; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = (uint16_t)buffer->data[idx]; + } + for (; idx < DMA_BUFFER_HALF_LEN; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = 0; + } + audioQueue.buffersFifo.freeNextFilledBuffer(); + } +} + +void audioSetVolume(uint8_t volume) +{ + tas2505_set_volume(&_tas2505, volume * 9 / 10); // TX15 HP cannot handle the full power of TAS2505 +} + +extern "C" void DMA1_Stream4_IRQHandler(void) +{ + if (stm32_dma_check_ht_flag(I2S_DMA, I2S_DMA_Stream)) { + audio_update_dma_buffer(0); + } + + if (stm32_dma_check_tc_flag(I2S_DMA, I2S_DMA_Stream)) { + audio_update_dma_buffer(1); + } +} diff --git a/radio/src/targets/tx15/backlight_driver.cpp b/radio/src/targets/tx15/backlight_driver.cpp new file mode 100644 index 000000000..1d0c5a40d --- /dev/null +++ b/radio/src/targets/tx15/backlight_driver.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/gpio.h" +#include "stm32_gpio.h" +#include "stm32_timer.h" +#include "stm32_pulse_driver.h" + +#include "edgetx_types.h" +#include "board.h" + +#include "globals.h" +#include "lcd_driver.h" + +static const stm32_pulse_timer_t _bl_timer = { + .GPIO = (gpio_t)BACKLIGHT_GPIO, + .GPIO_Alternate = BACKLIGHT_GPIO_AF, + .TIMx = BACKLIGHT_TIMER, + .TIM_Freq = BACKLIGHT_TIMER_FREQ, + .TIM_Channel = BACKLIGHT_TIMER_CHANNEL, + .TIM_IRQn = (IRQn_Type)-1, + .DMAx = nullptr, + .DMA_Stream = 0, + .DMA_Channel = 0, + .DMA_IRQn = (IRQn_Type)-1, + .DMA_TC_CallbackPtr = nullptr, +}; + +void backlightLowInit() +{ + gpio_init(BACKLIGHT_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); + gpio_clear(BACKLIGHT_GPIO); +} + +void backlightInit() +{ + stm32_pulse_init(&_bl_timer, 1000000); + stm32_pulse_config_output(&_bl_timer, true, LL_TIM_OCMODE_PWM1, 100); + LL_TIM_SetAutoReload(_bl_timer.TIMx, 100); + LL_TIM_EnableCounter(_bl_timer.TIMx); +} + +uint8_t lastDutyCycle = 0; + +void backlightEnable(uint8_t dutyCycle) +{ + stm32_pulse_set_cmp_val(&_bl_timer, dutyCycle); + + if(!dutyCycle) { + //experimental to turn off LCD when no backlight + if(lcdOffFunction) lcdOffFunction(); + } + else if(!lastDutyCycle) { + if(lcdOnFunction) lcdOnFunction(); + else lcdInit(); + } + + lastDutyCycle = dutyCycle; +} + +void lcdOff() { + backlightEnable(0); +} + +void lcdOn(){ + // if(lcdOnFunction) lcdOnFunction(); + // else lcdInit(); + backlightEnable(BACKLIGHT_LEVEL_MAX); +} + +bool boardBacklightOn; + +bool isBacklightEnabled() +{ + return boardBacklightOn; +} diff --git a/radio/src/targets/tx15/board.cpp b/radio/src/targets/tx15/board.cpp new file mode 100644 index 000000000..93d1e57c4 --- /dev/null +++ b/radio/src/targets/tx15/board.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stm32_adc.h" +#include "stm32_gpio.h" +#include "stm32_i2c_driver.h" +#include "stm32_hal.h" +#include "stm32_ws2812.h" +#include "stm32_spi.h" + +#include "flash_driver.h" +#include "extflash_driver.h" + +#include "board.h" +#include "boards/generic_stm32/module_ports.h" +#include "boards/generic_stm32/rgb_leds.h" +#include "bsp_io.h" + +#include "hal/adc_driver.h" +#include "hal/flash_driver.h" +#include "hal/trainer_driver.h" +#include "hal/rotary_encoder.h" +#include "hal/switch_driver.h" +#include "hal/abnormal_reboot.h" +#include "hal/watchdog_driver.h" +#include "hal/usb_driver.h" +#include "hal/gpio.h" +#include "hal/rgbleds.h" + +#include "globals.h" +#include "sdcard.h" +#include "debug.h" + +#include "flysky_gimbal_driver.h" +#include "timers_driver.h" + +#include "bitmapbuffer.h" +#include "colors.h" + +#include "touch_driver.h" + +#include + +// common ADC driver +extern const etx_hal_adc_driver_t _adc_driver; + +// RGB LED timer +extern const stm32_pulse_timer_t _led_timer; + +static void led_strip_off() +{ + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + ws2812_set_color(i, 0, 0, 0); + } + ws2812_update(&_led_timer); +} + +void INTERNAL_MODULE_ON() +{ + gpio_set(INTMODULE_PWR_GPIO); +} + +void INTERNAL_MODULE_OFF() +{ + gpio_clear(INTMODULE_PWR_GPIO); +} + +void EXTERNAL_MODULE_ON() +{ + gpio_set(EXTMODULE_PWR_GPIO); +} + +void EXTERNAL_MODULE_OFF() +{ + gpio_clear(EXTMODULE_PWR_GPIO); +} + +void boardBLEarlyInit() +{ + // TAS2505 requires reset pin to be low on power on + gpio_init(AUDIO_RESET_PIN, GPIO_OUT, GPIO_PIN_SPEED_LOW); + gpio_clear(AUDIO_RESET_PIN); + + timersInit(); + bsp_io_init(); + usbChargerInit(); +} + +void boardBLPreJump() +{ + // TAS2505 requires reset pin to be high only after power on + // https://www.ti.com/lit/ug/slau472c/slau472c.pd fig 4.2 + gpio_set(AUDIO_RESET_PIN); + ExtFLASH_Init(); + SDRAM_Init(); + + // Stop 1ms timer + MS_TIMER->CR1 &= ~TIM_CR1_CEN; +} + +void boardBLInit() +{ + ExtFLASH_Init(); + SDRAM_Init(); + + // register external FLASH for DFU + usbRegisterDFUMedia((void*)extflash_dfu_media); + + // register internal & external FLASH for UF2 + flashRegisterDriver(FLASH_BANK1_BASE, BOOTLOADER_SIZE, &stm32_flash_driver); + flashRegisterDriver(QSPI_BASE, 8 * 1024 * 1024, &extflash_driver); +} + +void boardInit() +{ + // enable interrupts + __enable_irq(); + + ledInit(); + boardInitModulePorts(); + + pwrInit(); + delaysInit(); + timersInit(); + + bsp_io_init(); + usbChargerInit(); + gpio_set(LED_BLUE_GPIO); + gpio_init(HALL_SYNC, GPIO_OUT, GPIO_PIN_SPEED_LOW); + + ExtFLASH_InitRuntime(); + + // register internal & external FLASH for UF2 + flashRegisterDriver(FLASH_BANK1_BASE, BOOTLOADER_SIZE, &stm32_flash_driver); + flashRegisterDriver(QSPI_BASE, QSPI_FLASH_SIZE, &extflash_driver); + + // init_trainer(); + auto inittime = flysky_gimbal_init(); + if (inittime) + TRACE("Serial gimbal detected in %d ms", inittime); + else + TRACE("No serial gimbal detected"); + + usbInit(); + + rgbLedInit(); + led_strip_off(); + + keysInit(); + switchInit(); + rotaryEncoderInit(); + touchPanelInit(); + audioInit(); + adcInit(&_adc_driver); + hapticInit(); + + // RTC must be initialized before rambackupRestore() is called + rtcInit(); +} + +extern void rtcDisableBackupReg(); + +void boardOff() +{ + lcdOff(); + + while (pwrPressed()) { + WDG_RESET(); + } + + SysTick->CTRL = 0; // turn off systick + + // Shutdown the Haptic + hapticDone(); + + rtcDisableBackupReg(); + + 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_CR1_MRUDS) && defined(PWR_CR1_LPUDS) && defined(PWR_CR1_FPDS) + MODIFY_REG(PWR->CR1, (PWR_CR1_PDDS | PWR_CR1_LPDS | PWR_CR1_FPDS | PWR_CR1_LPUDS | PWR_CR1_MRUDS), PDMode); +#elif defined(PWR_CR_MRLVDS) && defined(PWR_CR_LPLVDS) && defined(PWR_CR_FPDS) + MODIFY_REG(PWR->CR1, (PWR_CR1_PDDS | PWR_CR1_LPDS | PWR_CR1_FPDS | PWR_CR1_LPLVDS | PWR_CR1_MRLVDS), PDMode); +#else +// MODIFY_REG(PWR->CR1, (PWR_CR1_P_PDDS| PWR_CR1_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/tx15/board.h b/radio/src/targets/tx15/board.h new file mode 100644 index 000000000..c11728f41 --- /dev/null +++ b/radio/src/targets/tx15/board.h @@ -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. + */ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include "definitions.h" +#include "edgetx_constants.h" + +#include "board_common.h" +#include "hal.h" +#include "hal/serial_port.h" +#include "hal/watchdog_driver.h" + +#define BOOTLOADER_SIZE 0x10000 +#define BOOTLOADER_ADDRESS 0x08000000 +#define FIRMWARE_ADDRESS 0x90000000 +#define FIRMWARE_LEN(fsize) (fsize) +#define FIRMWARE_MAX_LEN FLASHSIZE +#define APP_START_ADDRESS (uint32_t)(FIRMWARE_ADDRESS) + +#define BOOTLOADER_KEYS 0x42 + +#define MB *1024*1024 +#define LUA_MEM_EXTRA_MAX (2 MB) // max allowed memory usage for Lua bitmaps (in bytes) +#define LUA_MEM_MAX (6 MB) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited + +extern uint16_t sessionTimer; + +#define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) + +// Board driver +void boardInit(); +void boardOff(); + +// CPU Unique ID +#define LEN_CPU_UID (3*8+2) +void getCPUUniqueID(char * s); + +// Flash Write driver +#define FLASH_PAGESIZE 256 +void unlockFlash(); +void lockFlash(); +void flashWrite(uint32_t * address, const uint32_t * buffer); +uint32_t isFirmwareStart(const uint8_t * buffer); +uint32_t isBootloaderStart(const uint8_t * buffer); + +// SDRAM driver +extern "C" void SDRAM_Init(); + +// Pulses driver +#if !defined(SIMU) + +void INTERNAL_MODULE_ON(); +void INTERNAL_MODULE_OFF(); +void EXTERNAL_MODULE_ON(); +void EXTERNAL_MODULE_OFF(); +#define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF +#define BLUETOOTH_MODULE_ON() gpio_clear(BLUETOOTH_ON_GPIO) +#define BLUETOOTH_MODULE_OFF() gpio_set(BLUETOOTH_ON_GPIO) +#define IS_INTERNAL_MODULE_ON() (false) +#define IS_EXTERNAL_MODULE_ON() (gpio_read(EXTMODULE_PWR_GPIO) ? 1 : 0) + +#else + +#define INTERNAL_MODULE_OFF() +#define INTERNAL_MODULE_ON() +#define EXTERNAL_MODULE_ON() +#define EXTERNAL_MODULE_OFF() +#define BLUETOOTH_MODULE_ON() +#define BLUETOOTH_MODULE_OFF() +#define IS_INTERNAL_MODULE_ON() (false) +#define IS_EXTERNAL_MODULE_ON() (false) + +#endif // defined(SIMU) + +#if defined(FUNCTION_SWITCHES) +#define NUM_FUNCTIONS_SWITCHES 6 +#define NUM_FUNCTIONS_GROUPS 3 +#define DEFAULT_FS_CONFIG \ + (SWITCH_2POS << 10) + (SWITCH_2POS << 8) + (SWITCH_2POS << 6) + \ + (SWITCH_2POS << 4) + (SWITCH_2POS << 2) + (SWITCH_2POS << 0) + +#define DEFAULT_FS_GROUPS \ + (1 << 10) + (1 << 8) + (1 << 6) + (1 << 4) + (1 << 2) + \ + (1 << 0) // Set all FS to group 1 to act like a 6pos + +#define DEFAULT_FS_STARTUP_CONFIG \ + ((FS_START_PREVIOUS << 10) + (FS_START_PREVIOUS << 8) + \ + (FS_START_PREVIOUS << 6) + (FS_START_PREVIOUS << 4) + \ + (FS_START_PREVIOUS << 2) + \ + (FS_START_PREVIOUS << 0)) // keep last state by default + +#else +#define NUM_FUNCTIONS_SWITCHES 0 +#endif + +#define NUM_TRIMS 8 +#define DEFAULT_STICK_DEADZONE 2 + +#define BATTERY_WARN 74 // 7.4V +#define BATTERY_MIN 70 // 6.8V +#define BATTERY_MAX 86 // 8.6V + +// --- MOSFET ---- R1 --- MCU +// |__ R2 --- GND +// +#define VBAT_DIV_R1 100 // kOhms +#define VBAT_DIV_R2 32 // kOhms +#define VBAT_MOSFET_DROP 0 // * 10mV + +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif + +// Power driver +#define SOFT_PWR_CTRL +#define POWER_ON_DELAY 10 // 1s +void pwrInit(); +void extModuleInit(); +uint32_t pwrCheck(); +uint32_t lowPowerCheck(); + +void pwrOn(); +void pwrSoftReboot(); +void pwrOff(); +void pwrResetHandler(); +bool pwrPressed(); +bool pwrOffPressed(); +#if defined(PWR_EXTRA_SWITCH_GPIO) + bool pwrForcePressed(); +#else + #define pwrForcePressed() false +#endif +uint32_t pwrPressedDuration();; + +const etx_serial_port_t* auxSerialGetPort(int port_nr); +#define AUX_SERIAL_POWER_ON() +#define AUX_SERIAL_POWER_OFF() + +// LED driver +void ledInit(); +void ledOff(); +void ledRed(); +void ledBlue(); +void ledGreen(); + +// LCD driver +void lcdSetInitalFrameBuffer(void* fbAddress); +void lcdInit(); +void lcdCopy(void * dest, void * src); + +void lcdOff(); +void lcdOn(); + +#define lcdRefreshWait(...) + +// Backlight driver +#define BACKLIGHT_LEVEL_MAX 100 +#define BACKLIGHT_FORCED_ON BACKLIGHT_LEVEL_MAX + 1 +#define BACKLIGHT_LEVEL_MIN 1 + +extern bool boardBacklightOn; +void backlightLowInit( void ); +void backlightInit(); +void backlightEnable(uint8_t dutyCycle); +void backlightFullOn(); +bool isBacklightEnabled(); + +#define BACKLIGHT_ENABLE() \ + { \ + boardBacklightOn = true; \ + backlightEnable(BACKLIGHT_LEVEL_MAX - currentBacklightBright); \ + } + +#define BACKLIGHT_DISABLE() \ + { \ + boardBacklightOn = false; \ + backlightEnable(((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && \ + (g_eeGeneral.backlightMode != e_backlight_mode_off)) \ + ? 0 \ + : g_eeGeneral.blOffBright); \ + } + +#define USB_NAME "TX15" +#define USB_MANUFACTURER 'R', 'M', ' ', ' ', ' ', ' ', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'T', 'X', '1', '5', ' ', ' ', ' ', ' ' /* 8 Bytes */ + +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +int audioInit(); +void audioConsumeCurrentBuffer(); + +// Telemetry driver +#define INTMODULE_FIFO_SIZE 512 +#define TELEMETRY_FIFO_SIZE 512 + +// Haptic driver +void hapticInit(); +void hapticDone(); +void hapticOff(); +void hapticOn(uint32_t pwmPercent); + +// Second serial port driver +//#define AUX_SERIAL +#define DEBUG_BAUDRATE 115200 +#define LUA_DEFAULT_BAUDRATE 115200 + +extern uint8_t currentTrainerMode; +void checkTrainerSettings(); + +// Touch panel driver +bool touchPanelEventOccured(); +struct TouchState touchPanelRead(); +struct TouchState getInternalTouchState(); + +// USB Charger +#if defined(USB_CHARGER) +void usbChargerInit(); +bool usbChargerLed(); +#endif + +#endif // _BOARD_H_ diff --git a/radio/src/targets/tx15/bsp_io.cpp b/radio/src/targets/tx15/bsp_io.cpp new file mode 100644 index 000000000..94ca1be71 --- /dev/null +++ b/radio/src/targets/tx15/bsp_io.cpp @@ -0,0 +1,219 @@ +/* + * 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 "bsp_io.h" + +#include "stm32_ws2812.h" +#include "stm32_switch_driver.h" +#include "stm32_i2c_driver.h" +#include "hal/switch_driver.h" +#include "drivers/pca95xx.h" +#include "timers_driver.h" +#include "delays_driver.h" +#include "hal.h" + +#if !defined(BOOT) +#include "os/async.h" +#include "os/timer.h" +#endif + +#include "debug.h" + +extern const stm32_switch_t* boardGetSwitchDef(uint8_t idx); + +struct bsp_io_expander { + pca95xx_t exp; + uint32_t mask; + uint32_t state; +}; + +static volatile bool _poll_switches_in_queue = false; + +static bsp_io_expander _io_switches; +static bsp_io_expander _io_fs_switches; + +#if !defined(BOOT) +static timer_handle_t _poll_timer = TIMER_INITIALIZER; +#endif + +static void _init_io_expander(bsp_io_expander* io, uint32_t mask) +{ + io->mask = mask; + io->state = 0; +} + +static volatile uint8_t pca95xx_errors = 0; + +static uint32_t _read_io_expander(bsp_io_expander* io) +{ + uint16_t value = 0; + if (pca95xx_read(&io->exp, io->mask, &value) == 0) { + io->state = value; + } else { + if (pca95xx_errors++ > 5) { + gpio_clear(IO_RESET_GPIO); + pca95xx_errors = 0; + delay_us(1); // Only 6ns are needed according to PCA datasheet, but lets be safe + gpio_set(IO_RESET_GPIO); + } + } + + return io->state; +} + +#if !defined(BOOT) +typedef enum { + TRIGGERED_BY_TIMER = 0, + TRIGGERED_BY_IRQ, +} trigger_source_t; + +static void _poll_switches(void *param1, uint32_t trigger_source) +{ + if (trigger_source == TRIGGERED_BY_IRQ) { + _poll_switches_in_queue = false; + timer_reset(&_poll_timer); + } + + _read_io_expander(&_io_switches); + _read_io_expander(&_io_fs_switches); +} + +static void _io_int_handler() +{ + async_call_isr(_poll_switches, &_poll_switches_in_queue, nullptr, + TRIGGERED_BY_IRQ); +} + +static void _poll_cb(timer_handle_t* timer) +{ + (void)timer; + _poll_switches(nullptr, TRIGGERED_BY_TIMER); +} + +static void start_poll_timer() +{ + timer_create(&_poll_timer, _poll_cb, "portex", 100, true); + timer_start(&_poll_timer); +} +#endif + +int bsp_io_init() +{ + int i2cError = i2c_init(I2C_Bus_1); + if (i2cError < 0) { + TRACE("I2C INIT ERROR: %d", i2cError); + return -1; + } + + // setup expanders reset pin + gpio_init(IO_RESET_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); + gpio_clear(IO_RESET_GPIO); + gpio_set(IO_RESET_GPIO); + + // configure expander 1 + _init_io_expander(&_io_fs_switches, 0xFF3F); + if (pca95xx_init(&_io_fs_switches.exp, I2C_Bus_1, 0x74) < 0) { + TRACE("EXP1 INIT ERROR"); + return -1; + } + + // configure expander 2 + _init_io_expander(&_io_switches, 0xFFF8); + if (pca95xx_init(&_io_switches.exp, I2C_Bus_1, 0x75) < 0) { + TRACE("EXP2 INIT ERROR"); + return -1; + } + +#if !defined(BOOT) + // setup expanders pin change interrupt + gpio_init_int(IO_INT_GPIO, GPIO_IN, GPIO_FALLING, _io_int_handler); +#endif + + _read_io_expander(&_io_switches); + _read_io_expander(&_io_fs_switches); + +#if !defined(BOOT) + start_poll_timer(); +#endif + + return 0; +} + +uint32_t bsp_get_fs_switches() +{ + return _io_fs_switches.state; +} + +void boardInitSwitches() +{ + bsp_io_init(); +} + +static SwitchHwPos _get_switch_pos(uint8_t idx) +{ + SwitchHwPos pos = SWITCH_HW_UP; + + const stm32_switch_t* def = boardGetSwitchDef(idx); + uint32_t state = _io_switches.state; + + if (def->isCustomSwitch) { + if ((state & def->Pin_high) == 0) { + return SWITCH_HW_DOWN; + } else { + return SWITCH_HW_UP; + } + } + else if (!def->Pin_low) { + // 2POS switch + if ((state & def->Pin_high) == 0) { + pos = SWITCH_HW_DOWN; + } + } else { + bool hi = state & def->Pin_high; + bool lo = state & def->Pin_low; + + if (hi && lo) { + pos = SWITCH_HW_MID; + } else if (!hi && lo) { + pos = SWITCH_HW_DOWN; + } + } + + return pos; +} + +static SwitchHwPos _get_fs_switch_pos(uint8_t idx) +{ + const stm32_switch_t* def = boardGetSwitchDef(idx); + uint32_t state = _io_fs_switches.state; + if ((state & def->Pin_high) == 0) { + return SWITCH_HW_DOWN; + } else { + return SWITCH_HW_UP; + } +} + +SwitchHwPos boardSwitchGetPosition(uint8_t idx) +{ + if (idx < 6) + return _get_switch_pos(idx); + return _get_fs_switch_pos(idx); +} diff --git a/radio/src/targets/tx15/bsp_io.h b/radio/src/targets/tx15/bsp_io.h new file mode 100644 index 000000000..dafcc45ba --- /dev/null +++ b/radio/src/targets/tx15/bsp_io.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "drivers/pca95xx.h" +#include "hal/switch_driver.h" + +// Port expander 1 (0x74) +#define SWITCH_A_L PCA95XX_P14 +#define SWITCH_A_H PCA95XX_P15 +#define SWITCH_B_L PCA95XX_P12 +#define SWITCH_B_H PCA95XX_P13 +#define SWITCH_C_L PCA95XX_P10 +#define SWITCH_C_H PCA95XX_P11 +#define SWITCH_D_L PCA95XX_P6 +#define SWITCH_D_H PCA95XX_P7 +#define SWITCH_E_L PCA95XX_P16 +#define SWITCH_E_H PCA95XX_P17 +#define SWITCH_F_H PCA95XX_P4 +#define SWITCH_F_L PCA95XX_P5 + +// Port expander 2 (0x75) +#define BSP_TR1U PCA95XX_PIN_13 +#define BSP_TR1D PCA95XX_PIN_12 + +#define BSP_TR2U PCA95XX_PIN_11 +#define BSP_TR2D PCA95XX_PIN_10 + +#define BSP_TR3L PCA95XX_PIN_15 +#define BSP_TR3R PCA95XX_PIN_14 + +#define BSP_TR4L PCA95XX_PIN_9 +#define BSP_TR4R PCA95XX_PIN_8 + +#define FSSWITCH_1 PCA95XX_P0 +#define FSSWITCH_2 PCA95XX_P1 +#define FSSWITCH_3 PCA95XX_P2 +#define FSSWITCH_4 PCA95XX_P3 +#define FSSWITCH_5 PCA95XX_P4 +#define FSSWITCH_6 PCA95XX_P5 + +extern uint8_t isSwitch3Pos(uint8_t idx); + +int bsp_io_init(); +uint32_t bsp_io_read_switches(); +uint32_t bsp_io_read_fs_switches(); +uint32_t bsp_get_fs_switches(); + +uint16_t bsp_input_get(); +struct stm32_switch_t; diff --git a/radio/src/targets/tx15/extflash_driver.cpp b/radio/src/targets/tx15/extflash_driver.cpp new file mode 100644 index 000000000..2354db468 --- /dev/null +++ b/radio/src/targets/tx15/extflash_driver.cpp @@ -0,0 +1,154 @@ +#include "extflash_driver.h" + +#include "stm32_hal.h" +#include "stm32_qspi.h" + +#include "board.h" + +#define QSPI_NOR_PAGE_SIZE 256 + +int32_t ExtFLASH_Init() +{ + if (stm32_qspi_nor_init() != 0) { + return -1; + } + + return stm32_qspi_nor_memory_mapped(); +} + +void ExtFLASH_InitRuntime() { stm32_qspi_no_init_runtime(); } + +// +// ETX HAL flash interface +// + +static uint32_t extflash_get_size_kb() +{ + return QSPI_FLASH_SIZE / 1024; +} + +static uint32_t extflash_get_sector(uint32_t address) +{ + address -= QSPI_BASE; + return address / (64 * 1024); +} + +static uint32_t extflash_get_sector_size(uint32_t sector) +{ + (void)sector; + return (64 * 1024); +} + +static int extflash_erase_sector(uint32_t address) +{ + address -= QSPI_BASE; + return stm32_qspi_nor_erase_sector(address); +} + +static int extflash_program(uint32_t address, void* data, uint32_t len) +{ + address -= QSPI_BASE; + + int ret = 0; + while (len > 0) { + uint32_t size = (len > QSPI_NOR_PAGE_SIZE) ? QSPI_NOR_PAGE_SIZE : len; + ret = stm32_qspi_nor_program(address, data, size); + if (ret != 0) break; + len -= size; + address += size; + data = (uint8_t*)data + size; + } + + return ret; +} + +static int extflash_read(uint32_t address, void* data, uint32_t len) +{ + address -= QSPI_BASE; + return stm32_qspi_nor_read(address, data, len); +} + +const etx_flash_driver_t extflash_driver = { + .get_size_kb = extflash_get_size_kb, + .get_sector = extflash_get_sector, + .get_sector_size = extflash_get_sector_size, + .erase_sector = extflash_erase_sector, + .program = extflash_program, + .read = extflash_read, +}; + +#if defined(BOOT) +// +// USB DFU interface +// +#include "usbd_dfu.h" + +#define MEDIA_DESC_STR "@External Flash /0x90000000/128*064Kg" + +static uint16_t extflash_dfu_init() +{ + return USBD_OK; +} + +static uint16_t extflash_dfu_deinit() +{ + return USBD_OK; +} + +static uint16_t extflash_dfu_erase(uint32_t address) +{ + if (extflash_erase_sector(address) < 0) { + return USBD_FAIL; + } + return USBD_OK; +} + +static uint16_t extflash_dfu_write(uint8_t* src, uint8_t* dest, uint32_t len) +{ + if (extflash_program((intptr_t)dest, (void*)src, len) < 0) { + return USBD_FAIL; + } + return USBD_OK; +} + +static uint8_t* extflash_dfu_read(uint8_t* src, uint8_t* dest, uint32_t len) +{ + if (extflash_read((intptr_t)src, (void*)dest, len) < 0) { + return nullptr; + } + return dest; +} + +static uint16_t extflash_dfu_get_status(uint32_t address, uint8_t cmd, uint8_t *buffer) +{ + switch (cmd) { + case DFU_MEDIA_PROGRAM: + buffer[1] = 1; + buffer[2] = 0; + buffer[3] = 0; + break; + + case DFU_MEDIA_ERASE: + default: + buffer[1] = 20; + buffer[2] = 0; + buffer[3] = 0; + break; + } + + return USBD_OK; +} + +const USBD_DFU_MediaTypeDef _extflash_dfu_media = { + .pStrDesc = (uint8_t*)MEDIA_DESC_STR, + .Init = extflash_dfu_init, + .DeInit = extflash_dfu_deinit, + .Erase = extflash_dfu_erase, + .Write = extflash_dfu_write, + .Read = extflash_dfu_read, + .GetStatus = extflash_dfu_get_status, +}; + +const void* extflash_dfu_media = &_extflash_dfu_media; + +#endif // BOOT diff --git a/radio/src/targets/tx15/extflash_driver.h b/radio/src/targets/tx15/extflash_driver.h new file mode 100644 index 000000000..c51d6a3aa --- /dev/null +++ b/radio/src/targets/tx15/extflash_driver.h @@ -0,0 +1,33 @@ +/* + * 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 "hal/flash_driver.h" + +extern const etx_flash_driver_t extflash_driver; +extern const void* extflash_dfu_media; + +// init external FLASH hardware +int32_t ExtFLASH_Init(); + +// init only runtime structures +void ExtFLASH_InitRuntime(); diff --git a/radio/src/targets/tx15/hal.h b/radio/src/targets/tx15/hal.h new file mode 100644 index 000000000..83b70b7eb --- /dev/null +++ b/radio/src/targets/tx15/hal.h @@ -0,0 +1,593 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/* + +STM32H750 + +SDMMC uses own DMA controler + +DMA1 +Stream0: LED_STRIP_TIMER_DMA_STREAM +Stream1: INTMODULE_DMA_STREAM +Stream2: FLYSKY_HALL_DMA_Stream_RX +Stream3: TELEMETRY_DMA_Stream_RX +Stream4: I2S_DMA_Stream +Stream5: INTMODULE_RX_DMA_STREAM +Stream6: AUX2_SERIAL_DMA_RX_STREAM +Stream7: TELEMETRY_DMA_Stream_TX + +DMA2 +Stream0: ADC_EXT_DMA_STREAM (do not move) +Stream1: AUX_SERIAL_DMA_TX_STREAM +Stream2: AUX_SERIAL_DMA_RX_STREAM +Stream3: EXTMODULE_TIMER_DMA_STREAM +Stream4: ADC_DMA_STREAM (do not move) +Stream5: EXTMODULE_USART_RX_DMA_STREAM +Stream6: EXTMODULE_USART_TX_DMA_STREAM +Stream7: AUX2_SERIAL_DMA_TX_STREAM + +TIM1: BACKLIGHT_TIMER +TIM2: LED_STRIP_TIMER +TIM3: HAPTIC_GPIO_TIMER +TIM4: +TIM5: EXTMODULE_TIMER +TIM7: +TIM8: TRAINER_TIMER +TIM12: MIXER_SCHEDULER_TIMER +TIM14: MS_TIMER +TIM15: PDM +TIM16: +TIM17: ROTARY_ENCODER_TIMER + */ + +#ifndef _HAL_H_ +#define _HAL_H_ + +#define CPU_FREQ 480000000 + +#define PERI1_FREQUENCY 120000000 +#define PERI2_FREQUENCY 120000000 +#define TIMER_MULT_APB1 2 +#define TIMER_MULT_APB2 2 + +// Keys +#define KEYS_GPIO_REG_PAGEDN GPIOA +#define KEYS_GPIO_PIN_PAGEDN LL_GPIO_PIN_8 // PA.08 +#define KEYS_GPIO_REG_PAGEUP GPIOG +#define KEYS_GPIO_PIN_PAGEUP LL_GPIO_PIN_7 // PG.07 +#define KEYS_GPIO_REG_SYS GPIOB +#define KEYS_GPIO_PIN_SYS LL_GPIO_PIN_2 // PB.02 +#define KEYS_GPIO_REG_ENTER GPIOG +#define KEYS_GPIO_PIN_ENTER LL_GPIO_PIN_12 // PG.12 +#define KEYS_GPIO_REG_MDL GPIOE +#define KEYS_GPIO_PIN_MDL LL_GPIO_PIN_3 // PE.03 +#define KEYS_GPIO_REG_EXIT GPIOG +#define KEYS_GPIO_PIN_EXIT LL_GPIO_PIN_3 // PG.03 +#define KEYS_GPIO_REG_TELE GPIOG +#define KEYS_GPIO_PIN_TELE LL_GPIO_PIN_2 // PG.02 + +// Trims +#define TRIMS_GPIO_REG_LHL +#define TRIMS_GPIO_PIN_LHL + +#define TRIMS_GPIO_REG_LHR +#define TRIMS_GPIO_PIN_LHR + +#define TRIMS_GPIO_REG_LVD +#define TRIMS_GPIO_PIN_LVD + +#define TRIMS_GPIO_REG_LVU +#define TRIMS_GPIO_PIN_LVU + +#define TRIMS_GPIO_REG_RHL +#define TRIMS_GPIO_PIN_RHL + +#define TRIMS_GPIO_REG_RHR +#define TRIMS_GPIO_PIN_RHR + +#define TRIMS_GPIO_REG_RVD +#define TRIMS_GPIO_PIN_RVD + +#define TRIMS_GPIO_REG_RVU +#define TRIMS_GPIO_PIN_RVU + + +// function switches +//SW1 +#define SWITCHES_GPIO_REG_G +#define SWITCHES_GPIO_PIN_G PCA95XX_P0 +#define SWITCHES_G_CFS_IDX 0 +#define FUNCTION_SWITCH_1 SG +//SW2 +#define SWITCHES_GPIO_REG_H +#define SWITCHES_GPIO_PIN_H PCA95XX_P1 +#define SWITCHES_H_CFS_IDX 1 +#define FUNCTION_SWITCH_2 SH +//SW3 +#define SWITCHES_GPIO_REG_I +#define SWITCHES_GPIO_PIN_I PCA95XX_P2 +#define SWITCHES_I_CFS_IDX 2 +#define FUNCTION_SWITCH_3 SI +//SW4 +#define SWITCHES_GPIO_REG_J +#define SWITCHES_GPIO_PIN_J PCA95XX_P3 +#define SWITCHES_J_CFS_IDX 3 +#define FUNCTION_SWITCH_4 SJ +//SW5 +#define SWITCHES_GPIO_REG_K +#define SWITCHES_GPIO_PIN_K PCA95XX_P4 +#define SWITCHES_K_CFS_IDX 4 +#define FUNCTION_SWITCH_5 SK +//SW6 +#define SWITCHES_GPIO_REG_L +#define SWITCHES_GPIO_PIN_L PCA95XX_P5 +#define SWITCHES_L_CFS_IDX 5 +#define FUNCTION_SWITCH_6 SL + +// Extender Switches +#define SWITCHES_A_3POS +#define SWITCHES_GPIO_REG_A_H +#define SWITCHES_GPIO_PIN_A_H PCA95XX_P15 +#define SWITCHES_GPIO_REG_A_L +#define SWITCHES_GPIO_PIN_A_L PCA95XX_P14 +#define SWITCHES_B_3POS +#define SWITCHES_GPIO_REG_B_H +#define SWITCHES_GPIO_PIN_B_H PCA95XX_P13 +#define SWITCHES_GPIO_REG_B_L +#define SWITCHES_GPIO_PIN_B_L PCA95XX_P12 +#define SWITCHES_C_3POS +#define SWITCHES_GPIO_REG_C_H +#define SWITCHES_GPIO_PIN_C_H PCA95XX_P11 +#define SWITCHES_GPIO_REG_C_L +#define SWITCHES_GPIO_PIN_C_L PCA95XX_P10 +#define SWITCHES_D_3POS +#define SWITCHES_GPIO_REG_D_H +#define SWITCHES_GPIO_PIN_D_H PCA95XX_P7 +#define SWITCHES_GPIO_REG_D_L +#define SWITCHES_GPIO_PIN_D_L PCA95XX_P6 +#define SWITCHES_E_2POS +#define SWITCHES_GPIO_REG_E +#define SWITCHES_GPIO_PIN_E PCA95XX_P16 +#define SWITCHES_F_2POS +#define SWITCHES_GPIO_REG_F +#define SWITCHES_GPIO_PIN_F PCA95XX_P4 + + +// Expanders +#define IO_INT_GPIO GPIO_PIN(GPIOD, 3) +#define IO_RESET_GPIO GPIO_PIN(GPIOG, 10) +#if !defined(USE_EXTI3_IRQ) +#define USE_EXTI3_IRQ +#define EXTI3_IRQ_Priority 9 +#endif + +// ADC +#define ADC_GPIO_PIN_STICK_LH LL_GPIO_PIN_6 // PA.06 +#define ADC_GPIO_PIN_STICK_LV LL_GPIO_PIN_3 // PC.03 +#define ADC_GPIO_PIN_STICK_RV LL_GPIO_PIN_4 // PC.04 +#define ADC_GPIO_PIN_STICK_RH LL_GPIO_PIN_5 // PC.05 + +#define ADC_GPIO_PIN_POT1 LL_GPIO_PIN_1 // PC.01 POT1 +#define ADC_GPIO_PIN_POT2 LL_GPIO_PIN_1 // PB.01 POT2 +#define ADC_GPIO_PIN_BATT LL_GPIO_PIN_3 // PH.03 + +#define ADC_GPIOA_PINS (ADC_GPIO_PIN_STICK_LH) +#define ADC_GPIOB_PINS (ADC_GPIO_PIN_POT2) +#define ADC_GPIOC_PINS (ADC_GPIO_PIN_STICK_RV | ADC_GPIO_PIN_STICK_LV | ADC_GPIO_PIN_STICK_RH | ADC_GPIO_PIN_POT1) +#define ADC_GPIOH_PINS (ADC_GPIO_PIN_BATT) + + +#define ADC_CHANNEL_STICK_LH LL_ADC_CHANNEL_3 // ADC12_INP3 +#define ADC_CHANNEL_STICK_LV LL_ADC_CHANNEL_13 // ADC12_INP13 +#define ADC_CHANNEL_STICK_RV LL_ADC_CHANNEL_4 // ADC12_INP4 +#define ADC_CHANNEL_STICK_RH LL_ADC_CHANNEL_8 // ADC12_INP8 + +// Each ADC cannot map more than 8 channels, otherwise it will cause problems +#define ADC_CHANNEL_POT1 LL_ADC_CHANNEL_11 // ADC12_INP11 +#define ADC_CHANNEL_POT2 LL_ADC_CHANNEL_5 // ADC12_INP5 +#define ADC_CHANNEL_BATT LL_ADC_CHANNEL_14 // ADC3_INP14 +#define ADC_CHANNEL_RTC_BAT LL_ADC_CHANNEL_VBAT // ADC3_INP17 + +#define ADC_MAIN ADC1 +#define ADC_DMA DMA2 +#define ADC_DMA_CHANNEL LL_DMAMUX1_REQ_ADC1 +#define ADC_DMA_STREAM LL_DMA_STREAM_4 +#define ADC_DMA_STREAM_IRQ DMA2_Stream4_IRQn +#define ADC_DMA_STREAM_IRQHandler DMA2_Stream4_IRQHandler +#define ADC_SAMPTIME LL_ADC_SAMPLINGTIME_8CYCLES_5 + +#define ADC_EXT ADC3 +#define ADC_EXT_CHANNELS { ADC_CHANNEL_BATT , ADC_CHANNEL_RTC_BAT } +#define ADC_EXT_DMA DMA2 +#define ADC_EXT_DMA_CHANNEL LL_DMAMUX1_REQ_ADC3 +#define ADC_EXT_DMA_STREAM LL_DMA_STREAM_0 +#define ADC_EXT_DMA_STREAM_IRQ DMA2_Stream0_IRQn +#define ADC_EXT_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler +#define ADC_EXT_SAMPTIME LL_ADC_SAMPLINGTIME_64CYCLES_5 // No hardware oversampling on ADC3 + +#define ADC_VREF_PREC2 330 + +#define ADC_DIRECTION { \ + 0,-1,0,-1, /* gimbals */ \ + -1,-1, /* pots */ \ + 0,0, /* sliders */ \ + 0, /* vbat */ \ + 0, /* rtc_bat */ \ + 0, /* SWA */ \ + 0, /* SWB */ \ + 0, /* SWC */ \ + 0, /* SWD */ \ + 0, /* SWE */ \ + 0 /* SWF */ \ + } + +// Serial gimbal sync port +#define HALL_SYNC GPIO_PIN(GPIOH, 11) + +#define USE_EXTI9_5_IRQ // used for I2C port extender interrupt +#define EXTI9_5_IRQ_Priority 5 + +// Power +#define PWR_SWITCH_GPIO GPIO_PIN(GPIOA, 4) +#define PWR_ON_GPIO GPIO_PIN(GPIOH, 12) + +// Chargers (USB and wireless) +#define UCHARGER_GPIO GPIO_PIN(GPIOD, 11) +#define UCHARGER_EN_GPIO GPIO_PIN(GPIOB, 12) + +// TODO! Check IOLL1 to PI.01 connectivity! + +// S.Port update connector +#define HAS_SPORT_UPDATE_CONNECTOR() (false) + +// Telemetry + #define TELEMETRY_SET_INPUT 0 + #define TELEMETRY_TX_GPIO GPIO_PIN(GPIOA, 9) + #define TELEMETRY_RX_GPIO GPIO_UNDEF + #define TELEMETRY_USART USART1 + #define TELEMETRY_USART_IRQn USART1_IRQn + #define TELEMETRY_DMA DMA1 + #define TELEMETRY_DMA_Stream_TX LL_DMA_STREAM_7 + #define TELEMETRY_DMA_Channel_TX LL_DMAMUX1_REQ_USART1_TX + #define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream7_IRQn + #define TELEMETRY_DMA_TX_IRQHandler DMA1_Stream7_IRQHandler + #define TELEMETRY_DMA_Stream_RX LL_DMA_STREAM_3 + #define TELEMETRY_DMA_Channel_RX LL_DMAMUX1_REQ_USART1_RX + #define TELEMETRY_USART_IRQHandler USART1_IRQHandler + +// Software IRQ (Prio 5 -> FreeRTOS compatible) +#define TELEMETRY_USE_CUSTOM_EXTI +#define CUSTOM_EXTI_IRQ_NAME ETH_WKUP_IRQ +#define ETH_WKUP_IRQ_Priority 5 +#define CUSTOM_EXTI_IRQ_LINE 86 +#define TELEMETRY_RX_FRAME_EXTI_LINE CUSTOM_EXTI_IRQ_LINE + +// USB +#define USB_GPIO GPIOA +#define USB_GPIO_VBUS GPIO_PIN(GPIOH, 5) +#define USB_GPIO_DM GPIO_PIN(GPIOA, 11) +#define USB_GPIO_DP GPIO_PIN(GPIOA, 12) +#define USB_GPIO_AF GPIO_AF10 + +// LCD +#define LCD_SPI_CS_GPIO GPIOA +#define LCD_SPI_CS_GPIO_PIN LL_GPIO_PIN_7 +#define LCD_SPI_SCK_GPIO GPIOB +#define LCD_SPI_SCK_GPIO_PIN LL_GPIO_PIN_0 +#define LCD_SPI_MOSI_GPIO GPIOI +#define LCD_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_9 + +#define LTDC_IRQ_PRIO 4 +#define DMA_SCREEN_IRQ_PRIO 6 + +#define LCD_RESET_GPIO GPIOJ +#define LCD_RESET_GPIO_PIN LL_GPIO_PIN_12 + +#define LCD_TE_GPIO GPIOI +#define LCD_TE_GPIO_PIN LL_GPIO_PIN_4 + +// Backlight +#define BACKLIGHT_GPIO GPIO_PIN(GPIOA, 10) // TIM1_CH3 +#define BACKLIGHT_TIMER TIM1 +#define BACKLIGHT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 +#define BACKLIGHT_GPIO_AF GPIO_AF1 +#define BACKLIGHT_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +// QSPI Flash +#define QSPI_CLK_GPIO GPIO_PIN(GPIOF, 10) +#define QSPI_CLK_GPIO_AF GPIO_AF9 +#define QSPI_CS_GPIO GPIO_PIN(GPIOG, 6) +#define QSPI_CS_GPIO_AF GPIO_AF10 +#define QSPI_MISO_GPIO GPIO_PIN(GPIOF, 9) +#define QSPI_MISO_GPIO_AF GPIO_AF10 +#define QSPI_MOSI_GPIO GPIO_PIN(GPIOF, 8) +#define QSPI_MOSI_GPIO_AF GPIO_AF10 +#define QSPI_WP_GPIO GPIO_PIN(GPIOF, 7) +#define QSPI_WP_GPIO_AF GPIO_AF9 +#define QSPI_HOLD_GPIO GPIO_PIN(GPIOF, 6) +#define QSPI_HOLD_GPIO_AF GPIO_AF9 +#define QSPI_FLASH_SIZE 0x1000000 + + +// SD +#define SD_SDIO SDMMC1 +#define SD_SDIO_CLK_DIV(fq) (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) / (2 * fq)) +#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz + +#define SD2_PRESENT_GPIO GPIO_PIN(GPIOH, 8) // PH.08 +#define SD2_SDIO SDMMC2 +#define SD2_SDIO_PIN_D0 GPIO_PIN(GPIOB, 14) +#define SD2_SDIO_AF_D0 GPIO_AF9 +#define SD2_SDIO_PIN_D1 GPIO_PIN(GPIOB, 15) +#define SD2_SDIO_AF_D1 GPIO_AF9 +#define SD2_SDIO_PIN_D2 GPIO_PIN(GPIOG, 11) +#define SD2_SDIO_AF_D2 GPIO_AF10 +#define SD2_SDIO_PIN_D3 GPIO_PIN(GPIOB, 4) +#define SD2_SDIO_AF_D3 GPIO_AF9 +#define SD2_SDIO_PIN_CLK GPIO_PIN(GPIOD, 6) +#define SD2_SDIO_AF_CLK GPIO_AF11 +#define SD2_SDIO_PIN_CMD GPIO_PIN(GPIOD, 7) +#define SD2_SDIO_AF_CMD GPIO_AF11 +#define SD2_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz + +#define STORAGE_USE_SDIO + + +// AUDIO +#define AUDIO_SPI SPI2 +#define AUDIO_RESET_PIN GPIO_PIN(GPIOH, 10) +#define AUDIO_HP_DETECT_PIN GPIO_PIN(GPIOA, 5) +#define I2S_DMA DMA1 +#define I2S_DMA_Stream LL_DMA_STREAM_4 +#define I2S_DMA_Stream_Request LL_DMAMUX1_REQ_SPI2_TX +#define I2S_DMA_Stream_IRQn DMA1_Stream4_IRQn +#define I2S_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + + +// I2C Bus +#define I2C_B1 I2C4 +#define I2C_B1_SDA_GPIO GPIO_PIN(GPIOD, 13) // PD.13 +#define I2C_B1_SCL_GPIO GPIO_PIN(GPIOD, 12) // PD.12 +#define I2C_B1_GPIO_AF LL_GPIO_AF_4 +#define I2C_B1_CLK_RATE 400000 + +// Haptic: TIM3_CH2 +#define HAPTIC_PWM +#define HAPTIC_GPIO GPIO_PIN(GPIOC, 7) +#define HAPTIC_GPIO_TIMER TIM3 +#define HAPTIC_GPIO_AF GPIO_AF2 +#define HAPTIC_TIMER_OUTPUT_ENABLE TIM_CCER_CC2E | TIM_CCER_CC2NE; +#define HAPTIC_TIMER_MODE TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE +#define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR2 + +// Flysky Hall Stick +#define FLYSKY_HALL_SERIAL_USART UART4 +#define FLYSKY_HALL_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 9) +#define FLYSKY_HALL_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 8) +#define FLYSKY_HALL_SERIAL_USART_IRQn UART4_IRQn + +#define FLYSKY_HALL_SERIAL_DMA DMA1 +#define FLYSKY_HALL_DMA_Stream_RX LL_DMA_STREAM_2 +#define FLYSKY_HALL_DMA_Channel LL_DMAMUX1_REQ_UART4_RX + +// LED Strip +#define LED_STRIP_LENGTH 26 // 6POS + 2 rings of 10 +#define BLING_LED_STRIP_START 6 +#define BLING_LED_STRIP_LENGTH 20 +#define CFS_LED_STRIP_START 0 +#define CFS_LED_STRIP_LENGTH 6 +#define CFS_LEDS_PER_SWITCH 1 +#define LED_STRIP_GPIO GPIO_PIN(GPIOA, 0) // PA.00 / TIM2_CH1 +#define LED_STRIP_GPIO_AF LL_GPIO_AF_1 // TIM1/2/16/17 +#define LED_STRIP_TIMER TIM2 +#define LED_STRIP_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define LED_STRIP_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 +#define LED_STRIP_TIMER_DMA DMA1 +#define LED_STRIP_TIMER_DMA_CHANNEL LL_DMAMUX1_REQ_TIM2_UP +#define LED_STRIP_TIMER_DMA_STREAM LL_DMA_STREAM_0 +#define LED_STRIP_TIMER_DMA_IRQn DMA1_Stream0_IRQn +#define LED_STRIP_TIMER_DMA_IRQHandler DMA1_Stream0_IRQHandler +#define LED_STRIP_REFRESH_PERIOD 50 //ms + +#define STATUS_LEDS +#define GPIO_LED_GPIO_ON gpio_clear +#define GPIO_LED_GPIO_OFF gpio_set +#define LED_RED_GPIO GPIO_PIN(GPIOI, 8) // PI.08 +#define LED_GREEN_GPIO GPIO_PIN(GPIOI, 11) // PI.11 +#define LED_BLUE_GPIO GPIO_PIN(GPIOI, 10) // PI.10 + +// Internal Module +#define INTMODULE_PWR_GPIO GPIO_PIN(GPIOB, 13) // PB.13 +#define INTMODULE_BOOTCMD_GPIO GPIO_PIN(GPIOH, 9) // PH.09 +#define INTMODULE_BOOTCMD_DEFAULT 0 +#define INTMODULE_TX_GPIO GPIO_PIN(GPIOG, 14) +#define INTMODULE_RX_GPIO GPIO_PIN(GPIOG, 9) +#define INTMODULE_USART USART6 +#define INTMODULE_GPIO_AF LL_GPIO_AF_7 +#define INTMODULE_USART_IRQn USART6_IRQn +#define INTMODULE_USART_IRQHandler USART6_IRQHandler +#define INTMODULE_DMA DMA1 +#define INTMODULE_DMA_STREAM LL_DMA_STREAM_1 +#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn +#define INTMODULE_DMA_FLAG_TC DMA_FLAG_TCIF1 +#define INTMODULE_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_TX +#define INTMODULE_RX_DMA DMA1 +#define INTMODULE_RX_DMA_STREAM LL_DMA_STREAM_5 +#define INTMODULE_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_RX +#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream5_IRQn +#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream5_IRQHandler + +// External Module +#define EXTMODULE +#define EXTMODULE_PULSES +#define EXTMODULE_PWR_GPIO GPIO_PIN(GPIOD, 4) // PD.04 +#define EXTMODULE_TX_GPIO GPIO_PIN(GPIOA, 2) // TIM2_CH3, TIM5_CH3,TIM15_CH1, +#define EXTMODULE_RX_GPIO GPIO_PIN(GPIOA, 3) +#define EXTMODULE_TX_GPIO_AF LL_GPIO_AF_3 // TIM5_CH3 +#define EXTMODULE_TIMER TIM5 +#define EXTMODULE_TIMER_32BITS +#define EXTMODULE_TIMER_Channel LL_TIM_CHANNEL_CH3 +#define EXTMODULE_TIMER_IRQn TIM5_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM5_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF LL_GPIO_AF_2 + + +//USART +#define EXTMODULE_USART USART2 +#define EXTMODULE_USART_TX_DMA DMA2 +#define EXTMODULE_USART_TX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_TX +#define EXTMODULE_USART_TX_DMA_STREAM LL_DMA_STREAM_6 +#define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_RX +#define EXTMODULE_USART_RX_DMA_STREAM LL_DMA_STREAM_5 +#define EXTMODULE_USART_IRQHandler USART2_IRQHandler +#define EXTMODULE_USART_IRQn USART2_IRQn + +//TIMER +#define EXTMODULE_TIMER_DMA_CHANNEL LL_DMAMUX1_REQ_TIM5_UP +#define EXTMODULE_TIMER_DMA DMA2 +#define EXTMODULE_TIMER_DMA_STREAM LL_DMA_STREAM_6 +#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream6_IRQn +#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream6_IRQHandler + +// Trainer Port +#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA) + +#define TRAINER_IN_GPIO GPIO_PIN(GPIOI, 6) +#define TRAINER_IN_TIMER_Channel LL_TIM_CHANNEL_CH2 + +#define TRAINER_OUT_GPIO GPIO_PIN(GPIOI, 5) +#define TRAINER_OUT_TIMER_Channel LL_TIM_CHANNEL_CH1 + +#define TRAINER_TIMER TIM8 +#define TRAINER_TIMER_IRQn TIM8_CC_IRQn +#define TRAINER_TIMER_IRQHandler TIM8_CC_IRQHandler +#define TRAINER_GPIO_AF LL_GPIO_AF_3 +#define TRAINER_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) + +#define TRAINER_DETECT_GPIO GPIO_PIN(GPIOH, 4) + +// AUX ports +#define AUX_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 6) // PB.06 +#define AUX_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 5) // PB.05 +#define AUX_SERIAL_USART UART5 +#define AUX_SERIAL_USART_IRQHandler UART5_IRQHandler +#define AUX_SERIAL_USART_IRQn UART5_IRQn +#define AUX_SERIAL_DMA_TX DMA2 +#define AUX_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_1 +#define AUX_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_UART5_TX +#define AUX_SERIAL_DMA_RX DMA2 +#define AUX_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_2 +#define AUX_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_UART5_RX +#define AUX_SERIAL_PWR_GPIO GPIO_PIN(GPIOB, 7) // PB.07 + +#define AUX2_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 10) // PB.10 +#define AUX2_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 11) // PB.11 +#define AUX2_SERIAL_USART USART3 +#define AUX2_SERIAL_USART_IRQHandler USART3_IRQHandler +#define AUX2_SERIAL_USART_IRQn USART3_IRQn +#define AUX2_SERIAL_DMA_TX DMA2 +#define AUX2_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_7 +#define AUX2_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_USART3_TX +#define AUX2_SERIAL_DMA_RX DMA1 +#define AUX2_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_6 +#define AUX2_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_USART3_RX +#define AUX2_SERIAL_PWR_GPIO GPIO_PIN(GPIOC, 13) // PC.13 + +// Touch +#define TOUCH_I2C_BUS I2C_Bus_1 +#define TOUCH_INT_GPIO GPIO_PIN(GPIOE, 2) // PE.02 +#define TOUCH_RST_GPIO GPIO_PIN(GPIOJ, 13) // PJ.13 + +// TOUCH_INT_EXTI IRQ +#if !defined(USE_EXTI2_IRQ) +#define USE_EXTI2_IRQ +#define EXTI2_IRQ_Priority 9 +#endif + +// IMU +#define IMU_I2C_BUS I2C_Bus_1 +#define IMU_I2C_ADDRESS 0x6A +#define IMU_INT_GPIO GPIO_PIN(GPIOG, 13) // PG.13 +// IMU_INT_EXTI IRQ +#if !defined(USE_EXTI15_10_IRQ) + #define USE_EXTI15_10_IRQ + #define EXTI15_10_IRQ_Priority 6 +#endif + + +//ROTARY emulation for trims as buttons +#define ROTARY_ENCODER_NAVIGATION +// Rotary Encoder +#define ROTARY_ENCODER_INVERTED +#define ROTARY_ENCODER_GPIO_A GPIOI +#define ROTARY_ENCODER_GPIO_PIN_A LL_GPIO_PIN_7 +#define ROTARY_ENCODER_GPIO_B GPIOJ +#define ROTARY_ENCODER_GPIO_PIN_B LL_GPIO_PIN_8 +#define ROTARY_ENCODER_POSITION() (((ROTARY_ENCODER_GPIO_A->IDR >> 7) & 0x01)|((ROTARY_ENCODER_GPIO_B->IDR >> 7) & 0x02)) +#define ROTARY_ENCODER_EXTI_LINE1 LL_EXTI_LINE_7 +#define ROTARY_ENCODER_EXTI_LINE2 LL_EXTI_LINE_8 +#if !defined(USE_EXTI7_IRQ) + #define USE_EXTI7_IRQ + #define EXTI7_IRQ_Priority 5 +#endif +#if !defined(USE_EXTI8_IRQ) + #define USE_EXTI8_IRQ + #define EXTI8_IRQ_Priority 5 +#endif +#define ROTARY_ENCODER_EXTI_PORT_A LL_SYSCFG_EXTI_PORTI +#define ROTARY_ENCODER_EXTI_PORT_B LL_SYSCFG_EXTI_PORTJ +#define ROTARY_ENCODER_EXTI_SYS_LINE1 LL_SYSCFG_EXTI_LINE7 +#define ROTARY_ENCODER_EXTI_SYS_LINE2 LL_SYSCFG_EXTI_LINE8 +#define ROTARY_ENCODER_TIMER TIM17 +#define ROTARY_ENCODER_TIMER_IRQn TIM17_IRQn +#define ROTARY_ENCODER_TIMER_IRQHandler TIM17_IRQHandler + +// Millisecond timer +#define MS_TIMER TIM14 +#define MS_TIMER_IRQn TIM8_TRG_COM_TIM14_IRQn +#define MS_TIMER_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler + +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER TIM12 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler + +#define LANDSCAPE_LCD true +#define PORTRAIT_LCD false +#define LANDSCAPE_LCD_SML false +#define LANDSCAPE_LCD_STD true +#define LANDSCAPE_LCD_LRG false + +#define LCD_W 480 +#define LCD_H 320 + +#define LCD_PHYS_W LCD_H +#define LCD_PHYS_H LCD_W + +#define LCD_DEPTH 16 + +#define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH + +#endif // _HAL_H_ diff --git a/radio/src/targets/tx15/haptic_driver.cpp b/radio/src/targets/tx15/haptic_driver.cpp new file mode 100644 index 000000000..148312717 --- /dev/null +++ b/radio/src/targets/tx15/haptic_driver.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/gpio.h" +#include "stm32_gpio.h" +#include "stm32_timer.h" +#include "stm32_pulse_driver.h" + +#include "board.h" + +#define HAPTIC_TIMER_FREQ 200000000 + +static const stm32_pulse_timer_t _haptic_timer = { + .GPIO = (gpio_t)HAPTIC_GPIO, + .GPIO_Alternate = HAPTIC_GPIO_AF, + .TIMx = HAPTIC_GPIO_TIMER, + .TIM_Freq = HAPTIC_TIMER_FREQ, + .TIM_Channel = LL_TIM_CHANNEL_CH2, + .TIM_IRQn = (IRQn_Type)-1, + .DMAx = nullptr, + .DMA_Stream = 0, + .DMA_Channel = 0, + .DMA_IRQn = (IRQn_Type)-1, + .DMA_TC_CallbackPtr = nullptr, +}; + +void hapticInit(void) +{ + stm32_pulse_init(&_haptic_timer, 10000); + stm32_pulse_config_output(&_haptic_timer, true, LL_TIM_OCMODE_PWM1, 0); + LL_TIM_SetAutoReload(_haptic_timer.TIMx, 100); + LL_TIM_EnableCounter(_haptic_timer.TIMx); +} + +void hapticOn(uint32_t pwmPercent) +{ + if (pwmPercent > 100) pwmPercent = 100; + stm32_pulse_set_cmp_val(&_haptic_timer, pwmPercent); +} + +void hapticOff(void) { hapticOn(0); } +void hapticDone(void) { hapticOff(); } diff --git a/radio/src/targets/tx15/key_driver.cpp b/radio/src/targets/tx15/key_driver.cpp new file mode 100644 index 000000000..4205a0d76 --- /dev/null +++ b/radio/src/targets/tx15/key_driver.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/key_driver.h" + +#include "bsp_io.h" +#include "stm32_hal_ll.h" +#include "stm32_gpio_driver.h" +#include "stm32_i2c_driver.h" + +#include "hal.h" +#include "delays_driver.h" +#include "keys.h" + +#include "stm32_keys.inc" +#include "debug.h" + +void keysInit() { _init_keys(); } + +uint32_t readKeys() { return _read_keys(); } + +/* The output bit-order has to be: + 0 LHL TR3L (Left equals down) + 1 LHR TR3R + 2 LVD TR1D + 3 LVU TR1U + 4 RVD TR2D + 5 RVU TR2U + 6 RHL TR4L + 7 RHR TR4R +*/ + +enum PhysicalTrims { + TR3L = 0, + TR3R, + TR1D = 2, + TR1U, + TR2D = 4, + TR2U, + TR4L = 6, + TR4R +}; + +uint32_t readTrims() +{ + uint32_t result = 0; + uint32_t keys = bsp_get_fs_switches(); + +#define _TRIM(t) \ + if ((keys & BSP_##t) == 0) result |= 1 << t; + + _TRIM(TR1U); + _TRIM(TR1D); + _TRIM(TR3L); + _TRIM(TR3R); + _TRIM(TR2U); + _TRIM(TR2D); + _TRIM(TR4L); + _TRIM(TR4R); + +#undef _TRIM + + return result; +} diff --git a/radio/src/targets/tx15/lcd_driver.cpp b/radio/src/targets/tx15/lcd_driver.cpp new file mode 100644 index 000000000..accfdcdbf --- /dev/null +++ b/radio/src/targets/tx15/lcd_driver.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stm32_hal_ll.h" +#include "stm32_hal.h" +#include "edgetx_types.h" +#include "dma2d.h" +#include "hal.h" +#include "delays_driver.h" +#include "debug.h" +#include "lcd.h" +#include "lcd_driver.h" +#include "board.h" + +#include "hal/gpio.h" +#include "stm32_gpio.h" +#include "stm32_qspi.h" + +uint8_t TouchControllerType = 0; // 0: other; 1: CST836U +static volatile uint16_t lcd_phys_w = LCD_PHYS_W; +static volatile uint16_t lcd_phys_h = LCD_PHYS_H; + +static LTDC_HandleTypeDef hltdc; +static void* initialFrameBuffer = nullptr; + +static volatile uint8_t _frame_addr_reloaded = 0; + +static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, + const rect_t ©_area) +{ + (void)disp_drv; + (void)copy_area; + + // given the data cache size, this is probably + // faster than cleaning by address + SCB_CleanDCache(); + + LTDC_Layer1->CFBAR = (uint32_t)buffer; + // reload shadow registers on vertical blank + _frame_addr_reloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI); + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait + while(_frame_addr_reloaded == 0); +} + +lcdSpiInitFucPtr lcdInitFunction; +lcdSpiInitFucPtr lcdOffFunction; +lcdSpiInitFucPtr lcdOnFunction; +uint32_t lcdPixelClock; + +volatile uint8_t LCD_ReadBuffer[24] = { 0, 0 }; + +static void LCD_Delay(void) { + volatile unsigned int i; + + for (i = 0; i < 20; i++) { + ; + } +} + +gpio_t _lcd_af_gpios[] = { + GPIO_PIN(GPIOI, 12), GPIO_PIN(GPIOI, 13), GPIO_PIN(GPIOI, 14), + + GPIO_PIN(GPIOJ, 1), GPIO_PIN(GPIOJ, 2), GPIO_PIN(GPIOJ, 3), + GPIO_PIN(GPIOJ, 4), GPIO_PIN(GPIOJ, 5), GPIO_PIN(GPIOJ, 6), + GPIO_PIN(GPIOJ, 9), GPIO_PIN(GPIOJ, 10), GPIO_PIN(GPIOJ, 11), + GPIO_PIN(GPIOJ, 14), GPIO_PIN(GPIOJ, 15), + + GPIO_PIN(GPIOK, 0), GPIO_PIN(GPIOK, 1), GPIO_PIN(GPIOK, 2), + GPIO_PIN(GPIOK, 3), GPIO_PIN(GPIOK, 4), GPIO_PIN(GPIOK, 5), + GPIO_PIN(GPIOK, 6), GPIO_PIN(GPIOK, 7), +}; + +static void LCD_AF_GPIOConfig(void) +{ + for (unsigned i = 0; i < sizeof(_lcd_af_gpios) / sizeof(_lcd_af_gpios[0]); i++) { + gpio_init_af(_lcd_af_gpios[i], GPIO_AF14, GPIO_SPEED_FREQ_LOW); + } +} + +static void lcdSpiConfig(void) +{ + LL_GPIO_InitTypeDef GPIO_InitStructure; + LL_GPIO_StructInit(&GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_SPI_MOSI_GPIO_PIN; + GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Mode = LL_GPIO_MODE_OUTPUT; + GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStructure.Pull = LL_GPIO_PULL_NO; + LL_GPIO_Init(LCD_SPI_MOSI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_SPI_SCK_GPIO_PIN; + LL_GPIO_Init(LCD_SPI_SCK_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_SPI_CS_GPIO_PIN; + LL_GPIO_Init(LCD_SPI_CS_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.Pin = LCD_RESET_GPIO_PIN; + LL_GPIO_Init(LCD_RESET_GPIO, &GPIO_InitStructure); + + LCD_CS_HIGH(); +} + +void lcdDelay() { + delay_01us(1); +} + +static void lcdReset() { + LCD_CS_HIGH(); + + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET(); + delay_ms(10); + + LCD_NRST_HIGH(); + delay_ms(100); +} + +unsigned char LCD_ReadByteOnFallingEdge(void) { + unsigned int i; + unsigned char ReceiveData = 0; + + LCD_MOSI_HIGH(); + LCD_MOSI_AS_INPUT(); + + for (i = 0; i < 8; i++) { + LCD_DELAY(); + LCD_SCK_HIGH(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + + LCD_SCK_LOW(); + LCD_DELAY(); + LCD_DELAY(); + if (LCD_READ_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + + LCD_MOSI_AS_OUTPUT(); + + return (ReceiveData); +} + +static void lcdWriteByte(uint8_t data_enable, uint8_t byte) { + + LCD_SCK_LOW(); + lcdDelay(); + + if (data_enable) { + LCD_MOSI_HIGH(); + } else { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + lcdDelay(); + + for (int i = 0; i < 8; i++) { + LCD_SCK_LOW(); + lcdDelay(); + + if (byte & 0x80) { + LCD_MOSI_HIGH(); + } else { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + byte <<= 1; + + lcdDelay(); + } + + LCD_SCK_LOW(); +} + +unsigned char LCD_ReadByte(void) { + unsigned int i; + unsigned char ReceiveData = 0; + + LCD_MOSI_HIGH(); + LCD_MOSI_AS_INPUT(); + for (i = 0; i < 8; i++) { + LCD_SCK_LOW(); + lcdDelay(); + ReceiveData <<= 1; + LCD_SCK_HIGH(); + lcdDelay(); + if (LCD_READ_DATA_PIN()) { + ReceiveData |= 0x01; + } + } + LCD_SCK_LOW(); + LCD_MOSI_AS_OUTPUT(); + return (ReceiveData); +} + +unsigned char LCD_ReadRegister(unsigned char Register) { + unsigned char ReadData = 0; + LCD_CS_LOW(); + lcdDelay(); + + lcdWriteByte(0, Register); + lcdDelay(); + lcdDelay(); + ReadData = LCD_ReadByte(); + LCD_CS_HIGH(); + return (ReadData); +} + +void lcdWriteCommand(uint8_t command) { + lcdWriteByte(0, command); +} + +void lcdWriteData(uint8_t data) { + lcdWriteByte(1, data); +} + +void LCD_ST7365_On(void) { + LCD_CS_LOW(); + delay_ms(1); + lcdWriteCommand(0x29); + LCD_CS_HIGH(); +} + +void LCD_ST7365_Init(void) { + + LCD_CS_LOW(); + delay_ms(1); + lcdWriteCommand( 0x11 ); + delay_ms(120); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0xC3 ); + + lcdWriteCommand( 0xF0 ); + lcdWriteData( 0x96 ); + + + lcdWriteCommand(0x11); + + delay_ms(120); + + lcdWriteCommand(0xF0); // Command Set Control + lcdWriteData(0xC3); + + lcdWriteCommand(0xF0); + lcdWriteData(0x96); + + lcdWriteCommand(0x36); // Memory Data Access Control MY,MX~~ + lcdWriteData(0xE8); + + lcdWriteCommand(0x2A);//480-1 + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0xDF); + + lcdWriteCommand(0x2B); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x3F); //320-1 + + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); //55:RGB565 66:RGB666 77:RGB888 + + lcdWriteCommand(0xB4); + lcdWriteData(0x01); + + lcdWriteCommand(0xB6); //RGB + lcdWriteData(0x20); + lcdWriteData(0x02); //02 + lcdWriteData(0x3B); + + lcdWriteCommand(0xB7); + lcdWriteData(0xC6); + + lcdWriteCommand(0xC0); + lcdWriteData(0x80); + lcdWriteData(0x45); + + lcdWriteCommand(0xC1); + lcdWriteData(0x0F ); //18 //00 + + lcdWriteCommand(0xC2); + lcdWriteData(0xA7); //A7 + + lcdWriteCommand(0xC5); + lcdWriteData(0x0A); + + lcdWriteCommand(0xE8); + lcdWriteData(0x40); + lcdWriteData(0x8A); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x19); + lcdWriteData(0xA5); + lcdWriteData(0x33); + + lcdWriteCommand(0xE0); + lcdWriteData(0xD0); + lcdWriteData(0x08); + lcdWriteData(0x0F); + lcdWriteData(0x06); + lcdWriteData(0x06); + lcdWriteData(0x33); + lcdWriteData(0x30); + lcdWriteData(0x33); + lcdWriteData(0x47); + lcdWriteData(0x17); + lcdWriteData(0x13); + lcdWriteData(0x13); + lcdWriteData(0x2B); + lcdWriteData(0x31); + + lcdWriteCommand(0xE1); + lcdWriteData(0xD0); + lcdWriteData(0x0A); + lcdWriteData(0x11); + lcdWriteData(0x0B); + lcdWriteData(0x09); + lcdWriteData(0x07); + lcdWriteData(0x2F); + lcdWriteData(0x33); + lcdWriteData(0x47); + lcdWriteData(0x38); + lcdWriteData(0x15); + lcdWriteData(0x16); + lcdWriteData(0x2C); + lcdWriteData(0x32); + + + lcdWriteCommand(0xF0); + lcdWriteData(0x3C); + + lcdWriteCommand(0xF0); + lcdWriteData(0x69); + + delay_ms(120); + + lcdWriteCommand(0x21); + LCD_CS_HIGH(); + delay_ms(1); + LCD_ST7365_On(); +} + +void LCD_ST7365_Off(void) { + LCD_CS_LOW(); + delay_ms(1); + lcdWriteCommand(0x28); + LCD_CS_HIGH(); +} + +void LCD_Init_LTDC() { + hltdc.Instance = LTDC; + + /* Configure PLLSAI prescalers for LCD */ + /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ + /* PLLSAI_VCO Output = PLLSAI_VCO Input * lcdPixelclock * 16 = XX Mhz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLL_LTDC = PLLSAI_VCO/4 = YY Mhz */ + /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = YY/4 = lcdPixelClock Mhz */ +// uint32_t clock = (lcdPixelClock*16) / 1000000; // clock*16 in MHz +// RCC_PeriphCLKInitTypeDef clkConfig; +// clkConfig.PeriphClockSelection = RCC_PERIPHCLK_LTDC; +// clkConfig.PLLI2S +// clkConfig.PLLSAI.PLLSAIN = clock; +// clkConfig.PLLSAI.PLLSAIR = 4; +// clkConfig.PLLSAIDivQ = 6; +// clkConfig.PLLSAIDivR = RCC_PLLSAIDIVR_4; +// HAL_RCCEx_PeriphCLKConfig(&clkConfig); + + /* LTDC Configuration *********************************************************/ + /* Polarity configuration */ + /* Initialize the horizontal synchronization polarity as active low */ + hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL; + /* Initialize the vertical synchronization polarity as active low */ + hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL; + /* Initialize the data enable polarity as active low */ + hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL; + /* Initialize the pixel clock polarity as input pixel clock */ + hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IIPC; + + /* Configure R,G,B component values for LCD background color */ + hltdc.Init.Backcolor.Red = 0; + hltdc.Init.Backcolor.Green = 0; + hltdc.Init.Backcolor.Blue = 0; + + /* Configure horizontal synchronization width */ + hltdc.Init.HorizontalSync = HSW-1; + /* Configure vertical synchronization height */ + hltdc.Init.VerticalSync = VSH-1; + /* Configure accumulated horizontal back porch */ + hltdc.Init.AccumulatedHBP = HSW+HBP-1; + /* Configure accumulated vertical back porch */ + hltdc.Init.AccumulatedVBP = VSH+VBP-1; + /* Configure accumulated active width */ + hltdc.Init.AccumulatedActiveW = lcd_phys_w + HBP+HSW-1; + /* Configure accumulated active height */ + hltdc.Init.AccumulatedActiveH = lcd_phys_h + VBP+VSH-1; + /* Configure total width */ + hltdc.Init.TotalWidth = lcd_phys_w + HBP + HFP + HSW -1; + /* Configure total height */ + hltdc.Init.TotalHeigh = lcd_phys_h + VBP + VFP+VSH-1; + + HAL_LTDC_Init(&hltdc); + + // Configure IRQ (line) + NVIC_SetPriority(LTDC_IRQn, LTDC_IRQ_PRIO); + NVIC_EnableIRQ(LTDC_IRQn); + + // Trigger on last line + HAL_LTDC_ProgramLineEvent(&hltdc, lcd_phys_h); + __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI); +} + +void LCD_LayerInit() { + auto& layer = hltdc.LayerCfg[0]; + + /* Windowing configuration */ + layer.WindowX0 = 0; + layer.WindowX1 = lcd_phys_w; + layer.WindowY0 = 0; + layer.WindowY1 = lcd_phys_h; + + /* Pixel Format configuration*/ + layer.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; + + /* Alpha constant (255 totally opaque) */ + layer.Alpha = 255; + + /* Default Color configuration (configure A,R,G,B component values) */ + layer.Backcolor.Blue = 0; + layer.Backcolor.Green = 0; + layer.Backcolor.Red = 0; + layer.Alpha0 = 0; + + /* Configure blending factors */ + layer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; + layer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; + + layer.ImageWidth = lcd_phys_w; + layer.ImageHeight = lcd_phys_h; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + layer.FBStartAdress = (intptr_t)initialFrameBuffer; + + /* Initialize LTDC layer 1 */ + HAL_LTDC_ConfigLayer(&hltdc, &hltdc.LayerCfg[0], 0); + + /* dithering activation */ + HAL_LTDC_EnableDither(&hltdc); +} + +extern "C" +void lcdSetInitalFrameBuffer(void* fbAddress) +{ + initialFrameBuffer = fbAddress; +} + +const char* boardLcdType = ""; + +extern "C" +void lcdInit(void) +{ +#if defined(LCD_SPI_CONFLICTS_WITH_QSPI) + stm32_qspi_nor_deinit(); +#endif + + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + /* Reset the LCD --------------------------------------------------------*/ + lcdReset(); + + /* Configure the LCD Control pins */ + LCD_AF_GPIOConfig(); + + /* Send LCD initialization commands */ + TRACE("LCD INIT (default): ST7365"); + boardLcdType = "ST7365 (Default)"; + lcdInitFunction = LCD_ST7365_Init; + lcdOffFunction = LCD_ST7365_Off; + lcdOnFunction = LCD_ST7365_On; + + __HAL_RCC_LTDC_CLK_ENABLE(); + lcdInitFunction(); + + LCD_Init_LTDC(); + LCD_LayerInit(); + + // Enable LCD display + __HAL_LTDC_ENABLE(&hltdc); + + lcdSetFlushCb(startLcdRefresh); + +#if defined(LCD_SPI_CONFLICTS_WITH_QSPI) + stm32_qspi_nor_init(); + stm32_qspi_nor_memory_mapped(); +#endif +} + +extern "C" void LTDC_IRQHandler(void) +{ + __HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_LI); + __HAL_LTDC_DISABLE_IT(&hltdc, LTDC_IT_LI); + _frame_addr_reloaded = 1; +} + diff --git a/radio/src/targets/tx15/lcd_driver.h b/radio/src/targets/tx15/lcd_driver.h new file mode 100644 index 000000000..19c8b3362 --- /dev/null +++ b/radio/src/targets/tx15/lcd_driver.h @@ -0,0 +1,69 @@ +/* + * 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 "bsp_io.h" + +#define HBP ( 20 ) +#define VBP ( 10 ) + +#define HSW ( 6 ) +#define VSH ( 2 ) + +#define HFP ( 40 ) +#define VFP ( 10 ) + +#define LCD_DELAY() LCD_Delay() + +typedef void (*lcdSpiInitFucPtr)(void); +typedef unsigned int LcdReadIDFucPtr( void ); + +extern lcdSpiInitFucPtr lcdInitFunction; +extern lcdSpiInitFucPtr lcdOffFunction; +extern lcdSpiInitFucPtr lcdOnFunction; + +#define SET_IO_INPUT( PORT, PIN ) LL_GPIO_SetPinMode( PORT, PIN, LL_GPIO_MODE_INPUT ) +#define SET_IO_OUTPUT( PORT, PIN ) LL_GPIO_SetPinMode( PORT, PIN, LL_GPIO_MODE_OUTPUT ) + +#define LCD_NRST_HIGH() LL_GPIO_SetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN) +#define LCD_NRST_LOW() LL_GPIO_ResetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN) + +#define LCD_CS_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_CS_GPIO, LCD_SPI_CS_GPIO_PIN) +#define LCD_CS_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_CS_GPIO, LCD_SPI_CS_GPIO_PIN) + +#define LCD_SCK_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_SCK_GPIO, LCD_SPI_SCK_GPIO_PIN) +#define LCD_SCK_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_SCK_GPIO, LCD_SPI_SCK_GPIO_PIN) + +#define LCD_MOSI_HIGH() LL_GPIO_SetOutputPin(LCD_SPI_MOSI_GPIO, LCD_SPI_MOSI_GPIO_PIN) +#define LCD_MOSI_LOW() LL_GPIO_ResetOutputPin(LCD_SPI_MOSI_GPIO, LCD_SPI_MOSI_GPIO_PIN) + +#define LCD_MOSI_AS_INPUT() SET_IO_INPUT( LCD_SPI_MOSI_GPIO, LCD_SPI_MOSI_GPIO_PIN ) +#define LCD_MOSI_AS_OUTPUT() SET_IO_OUTPUT( LCD_SPI_MOSI_GPIO, LCD_SPI_MOSI_GPIO_PIN ) + +#define LCD_READ_DATA_PIN() LL_GPIO_IsInputPinSet(LCD_SPI_MOSI_GPIO, LCD_SPI_MOSI_GPIO_PIN) + +#endif + + + + diff --git a/radio/src/targets/tx15/led_driver.cpp b/radio/src/targets/tx15/led_driver.cpp new file mode 100644 index 000000000..8690ec2f8 --- /dev/null +++ b/radio/src/targets/tx15/led_driver.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/gpio.h" +#include "hal/rgbleds.h" +#include "stm32_gpio.h" +#include "stm32_ws2812.h" +#include "boards/generic_stm32/rgb_leds.h" +#include "board.h" + +#define GET_RED(color) (((color) & 0xFF0000) >>16) +#define GET_GREEN(color) (((color) & 0x00FF00) >> 8) +#define GET_BLUE(color) ((color) & 0x0000FF) + +#if defined(FUNCTION_SWITCHES) && !defined(FUNCTION_SWITCHES_RGB_LEDS) +static const uint32_t fsLeds[] = {FSLED_GPIO_PIN_1, FSLED_GPIO_PIN_2, + FSLED_GPIO_PIN_3, FSLED_GPIO_PIN_4, + FSLED_GPIO_PIN_5, FSLED_GPIO_PIN_6}; +#endif + +void ledInit() +{ +#if defined(LED_GREEN_GPIO) + gpio_init(LED_GREEN_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); +#endif + +#if defined(LED_RED_GPIO) + gpio_init(LED_RED_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); +#endif + +#if defined(LED_BLUE_GPIO) + gpio_init(LED_BLUE_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); +#endif + +#if defined(FUNCTION_SWITCHES) && !defined(FUNCTION_SWITCHES_RGB_LEDS) + for (size_t i = 0; i < DIM(fsLeds); i++) { + gpio_init(fsLeds[i], GPIO_OUT, GPIO_PIN_SPEED_LOW); + } +#endif +} + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) +void fsLedRGB(uint8_t index, uint32_t color) +{ + ws2812_set_color(index + CFS_LED_STRIP_START, GET_RED(color), GET_GREEN(color), + GET_BLUE(color)); +} + +uint32_t fsGetLedRGB(uint8_t index) +{ + return rgbGetLedColor(index + CFS_LED_STRIP_START); +} + +uint8_t getRGBColorIndex(uint32_t color) +{ + for (uint8_t i = 0; i < DIM(colorTable); i++) { + if (color == colorTable[i]) + return(i + 1); + } + return 0; // Custom value set with Companion +} +#elif defined(FUNCTION_SWITCHES) +void fsLedOff(uint8_t index) +{ + gpio_clear(fsLeds[index]); +} + +void fsLedOn(uint8_t index) +{ + gpio_set(fsLeds[index]); +} + +bool fsLedState(uint8_t index) +{ + return gpio_read(fsLeds[index]) ? true : false; +} +#endif + +void ledOff() +{ +#if defined(LED_RED_GPIO) + GPIO_LED_GPIO_OFF(LED_RED_GPIO); +#endif +#if defined(LED_BLUE_GPIO) + GPIO_LED_GPIO_OFF(LED_BLUE_GPIO); +#endif +#if defined(LED_GREEN_GPIO) + GPIO_LED_GPIO_OFF(LED_GREEN_GPIO); +#endif +} + +void ledRed() +{ +#if defined(LED_RED_GPIO) + GPIO_LED_GPIO_ON(LED_RED_GPIO); +#endif +} + +void ledGreen() +{ +#if defined(LED_GREEN_GPIO) + GPIO_LED_GPIO_ON(LED_GREEN_GPIO); +#endif +} + +void ledBlue() +{ +#if defined(LED_BLUE_GPIO) + GPIO_LED_GPIO_ON(LED_BLUE_GPIO); +#endif +} diff --git a/radio/src/targets/tx15/sdram_driver.cpp b/radio/src/targets/tx15/sdram_driver.cpp new file mode 100644 index 000000000..b643af154 --- /dev/null +++ b/radio/src/targets/tx15/sdram_driver.cpp @@ -0,0 +1,215 @@ +/* + * 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 "hal/gpio.h" +#include "stm32_gpio.h" +#include "stm32_hal_ll.h" + +#include "board.h" + +#define SDRAM_MEMORY_WIDTH FMC_SDRAM_MEM_BUS_WIDTH_16 + +#define SDRAM_CAS_LATENCY FMC_SDRAM_CAS_LATENCY_3 + +#define SDCLOCK_PERIOD FMC_SDRAM_CLOCK_PERIOD_2 + +#define SDRAM_TIMEOUT ((uint32_t)0xFFFF) + +#define SDRAM_READBURST FMC_SDRAM_RBURST_DISABLE + +#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) + +#define GPIO_AF_FMC GPIO_AF12 + +extern "C" void SDRAM_GPIOConfig(void) +{ + /*-- GPIOs Configuration -----------------------------------------------------*/ + /* + */ + + /* GPIOC configuration */ + gpio_init_af(GPIO_PIN(GPIOC, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + /* GPIOD configuration */ + gpio_init_af(GPIO_PIN(GPIOD, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 9), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 10), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOD, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + + /* GPIOE configuration */ + gpio_init_af(GPIO_PIN(GPIOE, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 7), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 9), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 10), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 11), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 12), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 13), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOE, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + + /* GPIOF configuration */ + gpio_init_af(GPIO_PIN(GPIOF, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 2), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 3), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 4), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 5), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 11), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 12), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 13), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOF, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + + /* GPIOG configuration */ + gpio_init_af(GPIO_PIN(GPIOG, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOG, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOG, 4), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOG, 5), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOG, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOG, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + + + gpio_init_af(GPIO_PIN(GPIOH, 6), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(GPIO_PIN(GPIOH, 7), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); + +} + +extern "C" void SDRAM_InitSequence(void) +{ + FMC_SDRAM_CommandTypeDef FMC_SDRAMCommandStructure; + uint32_t tmpr = 0; + + /* Step 3 --------------------------------------------------------------------*/ + /* Configure a clock configuration enable command */ + FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; + FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; + FMC_SDRAMCommandStructure.AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0; + /* Send the command */ + FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10); + + /* Step 4 --------------------------------------------------------------------*/ + /* Insert 1 ms delay */ + delay_ms(100); + + /* Step 5 --------------------------------------------------------------------*/ + /* Configure a PALL (precharge all) command */ + FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_PALL; + FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; + FMC_SDRAMCommandStructure.AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0; + /* Send the command */ + FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10); + + /* Step 6 --------------------------------------------------------------------*/ + /* Configure a Auto-Refresh command */ + FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; + FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; + FMC_SDRAMCommandStructure.AutoRefreshNumber = 8; + FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0; + /* Send the command */ + FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10); + + /* 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.CommandMode = FMC_SDRAM_CMD_LOAD_MODE; + FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; + FMC_SDRAMCommandStructure.AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.ModeRegisterDefinition = tmpr; + /* Send the command */ + FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10); + + /* Step 8 --------------------------------------------------------------------*/ + /* Set the refresh rate counter */ + /* (15.62 us x Freq) - 20 */ + /* Set the device refresh counter */ + FMC_SDRAM_SetAutoRefreshNumber(FMC_Bank5_6_R, 15); + FMC_SDRAM_ProgramRefreshRate(FMC_Bank5_6_R, 1855); +} + +extern "C" void SDRAM_Init(void) +{ + //delay funcion needed + delaysInit(); + + /* GPIO configuration for FMC SDRAM bank */ + SDRAM_GPIOConfig(); + + /* Enable FMC clock */ + LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_FMC); + + /* FMC Configuration ---------------------------------------------------------*/ + FMC_SDRAM_InitTypeDef FMC_SDRAMInitStructure; + FMC_SDRAM_TimingTypeDef FMC_SDRAMTimingInitStructure; + + /* FMC SDRAM Bank configuration */ + /* Timing configuration for 80 Mhz of SD clock */ + FMC_SDRAMTimingInitStructure.LoadToActiveDelay = 2; + FMC_SDRAMTimingInitStructure.ExitSelfRefreshDelay = 7; + FMC_SDRAMTimingInitStructure.SelfRefreshTime = 4; + FMC_SDRAMTimingInitStructure.RowCycleDelay = 7; + FMC_SDRAMTimingInitStructure.WriteRecoveryTime = 3; + FMC_SDRAMTimingInitStructure.RPDelay = 2; + FMC_SDRAMTimingInitStructure.RCDDelay = 2; + + /* FMC SDRAM control configuration */ + FMC_SDRAMInitStructure.SDBank = FMC_SDRAM_BANK2; + FMC_SDRAMInitStructure.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; + FMC_SDRAMInitStructure.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; + FMC_SDRAMInitStructure.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; + FMC_SDRAMInitStructure.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; + FMC_SDRAMInitStructure.CASLatency = FMC_SDRAM_CAS_LATENCY_3; + FMC_SDRAMInitStructure.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; + FMC_SDRAMInitStructure.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_3; + FMC_SDRAMInitStructure.ReadBurst = FMC_SDRAM_RBURST_ENABLE; + FMC_SDRAMInitStructure.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0; + + /* FMC SDRAM bank initialization */ + FMC_SDRAM_Init(FMC_Bank5_6_R, &FMC_SDRAMInitStructure); + FMC_SDRAM_Timing_Init(FMC_Bank5_6_R, &FMC_SDRAMTimingInitStructure, FMC_SDRAM_BANK2); + + __FMC_ENABLE(); + + /* FMC SDRAM device initialization sequence */ + SDRAM_InitSequence(); +} diff --git a/radio/src/targets/tx15/system_clock.c b/radio/src/targets/tx15/system_clock.c new file mode 100644 index 000000000..2c32220da --- /dev/null +++ b/radio/src/targets/tx15/system_clock.c @@ -0,0 +1,111 @@ +/* + * 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 "stm32h7xx_ll_pwr.h" +#include "stm32h7xx_ll_rcc.h" +#include "stm32h7xx_ll_system.h" +#include "stm32h7xx_ll_bus.h" + +#define BOOTSTRAP __attribute__((section(".bootstrap"))) + +BOOTSTRAP void SystemClock_Config(void) +{ + /* Power Configuration */ + LL_PWR_ConfigSupply(LL_PWR_LDO_SUPPLY); + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); + while (LL_PWR_IsActiveFlag_VOS() == 0){ + } + + /* Enable HSE oscillator */ + LL_RCC_HSE_Enable(); + while (LL_RCC_HSE_IsReady() != 1) { + } + + /* Set FLASH latency */ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_5); + + /* Main PLL configuration and activation */ + LL_RCC_PLL_SetSource(LL_RCC_PLLSOURCE_HSE); + LL_RCC_PLL1P_Enable(); + LL_RCC_PLL1Q_Enable(); + LL_RCC_PLL1R_Enable(); + LL_RCC_PLL1FRACN_Disable(); + LL_RCC_PLL1_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4); + LL_RCC_PLL1_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); + LL_RCC_PLL1_SetM(3); + LL_RCC_PLL1_SetN(60); + LL_RCC_PLL1_SetP(2); + LL_RCC_PLL1_SetQ(20); + LL_RCC_PLL1_SetR(2); + LL_RCC_PLL1_Enable(); + while (LL_RCC_PLL1_IsReady() != 1) { + } + + /* Set Sys & AHB & APB1 & APB2 & APB4 prescaler */ + LL_RCC_SetSysPrescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2); + LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_2); + + /* Set PLL1 as System Clock Source */ + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) { + } + + /* Config & activate PLL2 */ + LL_RCC_PLL2P_Enable(); + LL_RCC_PLL2FRACN_Disable(); + LL_RCC_PLL2_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4); + LL_RCC_PLL2_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); + LL_RCC_PLL2_SetM(24); + LL_RCC_PLL2_SetN(96); + LL_RCC_PLL2_SetP(17); // 11.294 MHz + LL_RCC_PLL2_Enable(); + while (LL_RCC_PLL2_IsReady() != 1) { + } + + /* Config & activate PLL3 */ + LL_RCC_PLL3P_Enable(); + LL_RCC_PLL3Q_Enable(); + LL_RCC_PLL3R_Enable(); + LL_RCC_PLL3FRACN_Disable(); + LL_RCC_PLL3_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4); + LL_RCC_PLL3_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); + LL_RCC_PLL3_SetM(1); + LL_RCC_PLL3_SetN(4); + LL_RCC_PLL3_SetP(2); + LL_RCC_PLL3_SetQ(2); + LL_RCC_PLL3_SetR(18); + LL_RCC_PLL3_Enable(); + while (LL_RCC_PLL3_IsReady() != 1) { + } + + /* Enable SRAM1, SRAM2 & SRAM3 */ + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_D2SRAM1 | + LL_AHB2_GRP1_PERIPH_D2SRAM2 | + LL_AHB2_GRP1_PERIPH_D2SRAM3); + + /* Set periph clock sources */ + LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL1Q); + LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL1Q); + LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP); +} diff --git a/radio/src/targets/tx15/system_clock.h b/radio/src/targets/tx15/system_clock.h new file mode 100644 index 000000000..10b69a118 --- /dev/null +++ b/radio/src/targets/tx15/system_clock.h @@ -0,0 +1,23 @@ +/* + * 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. + */ + +void SystemClock_Config(); + diff --git a/radio/src/targets/tx15/touch_driver.h b/radio/src/targets/tx15/touch_driver.h new file mode 100644 index 000000000..6b0fb01a8 --- /dev/null +++ b/radio/src/targets/tx15/touch_driver.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "touch.h" + +extern void touchPanelDeInit(); +extern bool touchPanelInit(); + +struct TouchState touchPanelRead(); +bool touchPanelEventOccured(); +struct TouchState getInternalTouchState(); diff --git a/radio/src/targets/tx15/tp_gt911.cpp b/radio/src/targets/tx15/tp_gt911.cpp new file mode 100644 index 000000000..833fc83f3 --- /dev/null +++ b/radio/src/targets/tx15/tp_gt911.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "hal/gpio.h" +#include "hal/i2c_driver.h" +#include "stm32_gpio.h" +#include "stm32_hal_ll.h" +#include "stm32_hal.h" +#include "stm32_i2c_driver.h" +#include "stm32_gpio_driver.h" +#include "stm32_exti_driver.h" + +#include "hal.h" +#include "timers_driver.h" +#include "tp_gt911.h" +#include "delays_driver.h" + +#include "os/sleep.h" +#include "edgetx_types.h" +#include "debug.h" + +#include +#include + +#define TP_GT911_ID "911" + + +bool touchGT911Flag = false; +volatile static bool touchEventOccured = false; +struct TouchData touchData; +uint16_t touchGT911fwver = 0; +uint32_t touchGT911hiccups = 0; + +static tmr10ms_t downTime = 0; +static tmr10ms_t tapTime = 0; +static short tapCount = 0; + +static TouchState internalTouchState = {}; + +static void _gt911_exti_isr(void) +{ + touchEventOccured = true; +} + +void I2C_Init_Radio(void) +{ + TRACE("GT911 I2C Init"); + + if (i2c_init(TOUCH_I2C_BUS) < 0) { + TRACE("GT911 ERROR: i2c_init failed"); + return; + } +} + +bool I2C_GT911_WriteRegister(uint16_t reg, uint8_t *buf, uint8_t len) +{ + uint8_t uAddrAndBuf[258]; + 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 (stm32_i2c_master_tx(TOUCH_I2C_BUS, GT911_I2C_ADDR, uAddrAndBuf, len + 2, + 100) < 0) { + TRACE("I2C B1 ERROR: WriteRegister failed"); + return false; + } + return true; +} + +bool I2C_GT911_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 (stm32_i2c_master_tx(TOUCH_I2C_BUS, GT911_I2C_ADDR, uRegAddr, 2, 10) < 0) { + TRACE("I2C B1 ERROR: ReadRegister write reg address failed"); + return false; + } + + if (stm32_i2c_master_rx(TOUCH_I2C_BUS, GT911_I2C_ADDR, buf, len, 100) < 0) { + TRACE("I2C B1 ERROR: ReadRegister read reg address failed"); + return false; + } + return true; +} + +void touchPanelDeInit(void) +{ + gpio_int_disable(TOUCH_INT_GPIO); + touchGT911Flag = false; +} + +uint8_t tp_gt911_cfgVer = GT911_CFG_NUMBER; + +bool touchPanelInit(void) +{ + uint8_t tmp[4] = {0}; + + if (touchGT911Flag) { + gpio_init_int(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_RISING, _gt911_exti_isr); + return true; + } else { + TRACE("Touchpanel init start ..."); + + gpio_init(TOUCH_RST_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); + gpio_init(TOUCH_INT_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); + I2C_Init_Radio(); + + gpio_clear(TOUCH_RST_GPIO); + gpio_set(TOUCH_INT_GPIO); + delay_us(200); + + gpio_set(TOUCH_RST_GPIO); + delay_ms(6); + + gpio_clear(TOUCH_INT_GPIO); + delay_ms(55); + + gpio_init(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_PIN_SPEED_LOW); + + delay_ms(50); + + TRACE("Reading Touch registry"); + if (!I2C_GT911_ReadRegister(GT911_PRODUCT_ID_REG, tmp, 4)) { + TRACE("GT911 ERROR: Product ID read failed"); + } + + if (strcmp((char *)tmp, TP_GT911_ID) == 0) { + TRACE("GT911 chip detected"); + tmp[0] = 0X02; + if (!I2C_GT911_WriteRegister(GT911_COMMAND_REG, tmp, 1)) { + TRACE("GT911 ERROR: write to control register failed"); + } + if (!I2C_GT911_ReadRegister(GT911_CONFIG_REG, tmp, 1)) { + TRACE("GT911 ERROR: configuration register read failed"); + } + + TRACE("Chip config Ver:%x", tmp[0]); + if ((tp_gt911_cfgVer == 0) || (tmp[0] < tp_gt911_cfgVer)) { // Config ver + TRACE("Sending new config %d", GT911_CFG_NUMBER); + if (!I2C_GT911_ReadRegister(GT911_CONFIG_REG, tmp, 1)) { + TRACE("GT911 ERROR: configuration register read failed"); + } + tp_gt911_cfgVer = tmp[0]; + } + + if (!I2C_GT911_ReadRegister(GT911_FIRMWARE_VERSION_REG, tmp, 2)) { + TRACE("GT911 ERROR: reading firmware version failed"); + } else { + touchGT911fwver = (tmp[1] << 8) + tmp[0]; + TRACE("GT911 FW version: %u", touchGT911fwver); + } + + delay_ms(10); + tmp[0] = 0X00; + if (!I2C_GT911_WriteRegister(GT911_COMMAND_REG, tmp, 1)) { // end reset + TRACE("GT911 ERROR: write to command register failed"); + } + touchGT911Flag = true; + + gpio_init_int(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_RISING, _gt911_exti_isr); + + return true; + } + TRACE("GT911 chip NOT FOUND"); + return false; + } +} + +bool I2C_ReInit(void) +{ + TRACE("I2C B1 ReInit"); + touchPanelDeInit(); + if (stm32_i2c_deinit(TOUCH_I2C_BUS) < 0) + 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() +{ + uint8_t state = 0; + + if (!touchEventOccured) return internalTouchState; + + touchEventOccured = false; + + uint32_t startReadStatus = timersGetMsTick(); + do { + if (!I2C_GT911_ReadRegister(GT911_READ_XY_REG, &state, 1)) { + // ledRed(); + touchGT911hiccups++; + TRACE("GT911 I2C read XY error"); + if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed"); + return internalTouchState; + } + + if (state & 0x80u) { + // ready + break; + } + sleep_ms(1); + } while (timersGetMsTick() - startReadStatus < GT911_TIMEOUT); + + internalTouchState.deltaX = 0; + internalTouchState.deltaY = 0; + TRACE("touch state = 0x%x", state); + if (state & 0x80u) { + uint8_t pointsCount = (state & 0x0Fu); + uint32_t now = timersGetMsTick(); + internalTouchState.tapCount = 0; + + if (pointsCount > 0 && pointsCount <= GT911_MAX_TP) { + if (!I2C_GT911_ReadRegister(GT911_READ_XY_REG + 1, touchData.data, + pointsCount * sizeof(TouchPoint))) { + // ledRed(); + touchGT911hiccups++; + TRACE("GT911 I2C data read error"); + if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed"); + return internalTouchState; + } + + if (internalTouchState.event == TE_NONE || + internalTouchState.event == TE_UP || + internalTouchState.event == TE_SLIDE_END) { + internalTouchState.event = TE_DOWN; + internalTouchState.startX = internalTouchState.x = + touchData.points[0].x; + internalTouchState.startY = internalTouchState.y = + touchData.points[0].y; + downTime = now; + } else { + internalTouchState.deltaX = + touchData.points[0].x - internalTouchState.x; + internalTouchState.deltaY = + touchData.points[0].y - internalTouchState.y; + if (internalTouchState.event == TE_SLIDE || + abs(internalTouchState.deltaX) >= SLIDE_RANGE || + abs(internalTouchState.deltaY) >= SLIDE_RANGE) { + internalTouchState.event = TE_SLIDE; + internalTouchState.x = touchData.points[0].x; + internalTouchState.y = touchData.points[0].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 <= GT911_TAP_TIME) { + if (now - tapTime > GT911_TAP_TIME) + tapCount = 1; + else + tapCount++; + internalTouchState.tapCount = tapCount; + tapTime = now; + } + } else { + internalTouchState.event = TE_NONE; + } + } + } + + uint8_t zero = 0; + if (!I2C_GT911_WriteRegister(GT911_READ_XY_REG, &zero, 1)) { + TRACE("GT911 ERROR: clearing XY register failed"); + } + + TRACE("touch event = %s", event2str(internalTouchState.event)); + return internalTouchState; +} + +bool touchPanelEventOccured() +{ + return touchEventOccured; +} + +TouchState getInternalTouchState() +{ + return internalTouchState; +} + diff --git a/radio/src/targets/tx15/tp_gt911.h b/radio/src/targets/tx15/tp_gt911.h new file mode 100644 index 000000000..2e7f2c818 --- /dev/null +++ b/radio/src/targets/tx15/tp_gt911.h @@ -0,0 +1,83 @@ +/* + * 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" +#include "definitions.h" + +#define HAS_TOUCH_PANEL() (touchGT911Flag == true) + +extern bool touchGT911Flag; +extern uint16_t touchGT911fwver; +extern uint32_t touchGT911hiccups; + +extern void touchPanelDeInit(); +extern bool touchPanelInit(); + +struct TouchState touchPanelRead(); +bool touchPanelEventOccured(); +struct TouchState getInternalTouchState(); + +#define GT911_TIMEOUT 3 // 3ms +#define GT911_TAP_TIME 250 // 250 ms + +#define GT911_MAX_TP 5 + +#define GT911_CFG_NUMBER 0x6D + +//I2C +#define GT911_I2C_ADDR 0x14 + +//#define GT_CMD_WR 0x28 +//#define GT_CMD_RD 0x29 +//#define I2C_TIMEOUT_MAX 1000 + +#define GT911_COMMAND_REG 0x8040 +#define GT911_CONFIG_REG 0x8047 +#define GT911_COORDINATE_REG 0x804D +#define GT911_CONFIG_CHECKSUM_REG 0x80FF + +#define GT911_PRODUCT_ID_REG 0x8140 +#define GT911_FIRMWARE_VERSION_REG 0x8144 +#define GT911_VENDOR_ID_REG 0x814A +#define GT911_READ_XY_REG 0x814E +#define GT911_TP1_REG 0x8150 +#define GT911_TP2_REG 0x8158 +#define GT911_TP3_REG 0x8160 +#define GT911_TP4_REG 0x8168 +#define GT911_TP5_REG 0x8170 + +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[GT911_MAX_TP]; + uint8_t data[GT911_MAX_TP * sizeof(TouchPoint)]; + }; +}); diff --git a/radio/src/targets/st16/extmodule_helper.cpp b/radio/src/targets/tx15/usb_charger_driver.cpp similarity index 51% rename from radio/src/targets/st16/extmodule_helper.cpp rename to radio/src/targets/tx15/usb_charger_driver.cpp index dbb02e90b..5e01d8723 100644 --- a/radio/src/targets/st16/extmodule_helper.cpp +++ b/radio/src/targets/tx15/usb_charger_driver.cpp @@ -19,31 +19,22 @@ * GNU General Public License for more details. */ +#include "hal/gpio.h" +#include "stm32_gpio.h" + #include "board.h" +#include "hal/usb_driver.h" -void EXTERNAL_MODULE_ON() +//TODO: charger enable control + +void usbChargerInit() { - GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + gpio_init(UCHARGER_GPIO, GPIO_IN_PU, GPIO_PIN_SPEED_LOW); + gpio_init(UCHARGER_EN_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW); + gpio_clear(UCHARGER_EN_GPIO); } -void EXTERNAL_MODULE_OFF() +bool usbChargerLed() { - GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); -} - -void extModuleInit() -{ - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = EXTMODULE_RX_INVERT_GPIO_PIN; - GPIO_Init(EXTMODULE_RX_INVERT_GPIO, &GPIO_InitStructure); - - EXTMODULE_TX_INVERTED(); - EXTMODULE_RX_INVERTED(); + return (gpio_read(UCHARGER_GPIO) && usbPlugged()); } diff --git a/radio/src/targets/tx15/usb_descriptor.h b/radio/src/targets/tx15/usb_descriptor.h new file mode 100644 index 000000000..1edca8594 --- /dev/null +++ b/radio/src/targets/tx15/usb_descriptor.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _USB_DESCRIPTOR_H_ +#define _USB_DESCRIPTOR_H_ + +#define USB_NAME "TX15" +#define USB_MANUFACTURER 'R', 'M', ' ', ' ', ' ', ' ', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'T', 'X', '1', '5', ' ', ' ', ' ', ' ' /* 8 Bytes */ + +#endif // _USB_DESCRIPTOR_H_ diff --git a/radio/src/tests/mixer.cpp b/radio/src/tests/mixer.cpp index 17e66df64..0957d0f77 100644 --- a/radio/src/tests/mixer.cpp +++ b/radio/src/tests/mixer.cpp @@ -125,33 +125,33 @@ TEST_F(TrimsTest, throttleTrim) { g_model.thrTrim = 1; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); // stick min + trim max - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(512)); // stick min + trim mid - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(256)); // stick min + trim min - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS); #if defined(SURFACE_RADIO) // stick bellow 0 + trim max :no trim in reverse - anaSetFiltered(THR_STICK, -512); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -512); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -512); @@ -159,27 +159,27 @@ TEST_F(TrimsTest, throttleTrim) // now the same tests with extended Trims g_model.extendedTrims = 1; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); // stick min + trim max - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(2048)); // stick min + trim mid - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(1024)); // stick min + trim min - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS); @@ -190,12 +190,12 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrim) g_model.throttleReversed = 1; g_model.thrTrim = 1; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024); // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); #if defined(SURFACE_RADIO) @@ -204,7 +204,7 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrim) EXPECT_EQ(channelOutputs[2], -1024+256); #endif // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); #if defined(SURFACE_RADIO) @@ -213,7 +213,7 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrim) EXPECT_EQ(channelOutputs[THR_CHAN], -1024+512); #endif // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); #if defined(SURFACE_RADIO) @@ -222,12 +222,12 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrim) EXPECT_EQ(channelOutputs[THR_CHAN], -1024+512); #endif // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); @@ -235,40 +235,40 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrim) // now the same tests with extended Trims g_model.extendedTrims = 1; // stick max + trim max : no effect since we are in reverse mode - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024); #if defined(SURFACE_RADIO) // stick max + trim mid: no effect since we are in reverse mode - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024); #else // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024+1024); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024+2048); #endif // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); // stick min + trim mid - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); @@ -281,32 +281,32 @@ TEST_F(TrimsTest, throttleTrimWithZeroWeightOnThrottle) ExpoData *expo = expoAddress(THR_STICK); expo->weight = makeSourceNumVal(0); // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 256); // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_LE(abs(channelOutputs[THR_CHAN] - 128), 1); //can't use precise comparison here because of lower precision math on 9X // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 256); // stick min + trim mid - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_LE(abs(channelOutputs[THR_CHAN] - 128), 1); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); @@ -315,31 +315,31 @@ TEST_F(TrimsTest, throttleTrimWithZeroWeightOnThrottle) g_model.extendedTrims = 1; // trim min + various stick positions = should always be same value setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, -300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, +300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); // trim max + various stick positions = should always be same value setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, -300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, +300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); } @@ -352,32 +352,32 @@ TEST_F(TrimsTest, invertedThrottlePlusthrottleTrimWithZeroWeightOnThrottle) ExpoData *expo = expoAddress(THR_STICK); expo->weight = makeSourceNumVal(0); // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_LE(abs(channelOutputs[THR_CHAN] - 128), 1); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 256); // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); // stick min + trim mid - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, 0); evalMixes(1); EXPECT_LE(abs(channelOutputs[THR_CHAN] - 128), 1); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, THR_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 256); @@ -386,38 +386,38 @@ TEST_F(TrimsTest, invertedThrottlePlusthrottleTrimWithZeroWeightOnThrottle) g_model.extendedTrims = 1; // trim min + various stick positions = should always be same value setTrimValue(0, THR_STICK, TRIM_EXTENDED_MIN); - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, -300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, +300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); // trim max + various stick positions = should always be same value setTrimValue(0, THR_STICK, TRIM_EXTENDED_MAX); - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, -300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, +300); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +300); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 0); } TEST_F(TrimsTest, CopyTrimsToOffset) { - setTrimValue(0, ELE_STICK, -100); // -100 on elevator/steering + setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, -100); // -100 on elevator/steering evalFunctions(g_model.customFn, modelFunctionsContext); // it disables all safety channels copyTrimsToOffset(ELE_CHAN); EXPECT_EQ(getTrimValue(0, ELE_STICK), -100); // unchanged @@ -426,7 +426,7 @@ TEST_F(TrimsTest, CopyTrimsToOffset) TEST_F(TrimsTest, CopySticksToOffset) { - anaSetFiltered(ELE_STICK, -100); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), -100); evalMixes(1); copySticksToOffset(ELE_CHAN); #if defined(STICK_DEAD_ZONE) @@ -440,7 +440,7 @@ TEST_F(TrimsTest, MoveTrimsToOffsets) { // No trim idle only g_model.thrTrim = 0; - anaSetFiltered(THR_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), 0); setTrimValue(0, MIXSRC_TRIMTHR - MIXSRC_FIRST_TRIM, 100); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, -100); evalMixes(1); @@ -462,7 +462,7 @@ TEST_F(TrimsTest, MoveTrimsToOffsetsWithTrimIdle) { // Trim idle only g_model.thrTrim = 1; - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); // Min stick + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); // Min stick g_model.limitData[THR_CHAN].offset = 0; g_model.limitData[ELE_CHAN].offset = 0; setTrimValue(0, MIXSRC_TRIMTHR - MIXSRC_FIRST_TRIM, 100); @@ -505,8 +505,8 @@ TEST_F(TrimsTest, MoveTrimsToOffsetsWithCrossTrims) expo = expoAddress(ELE_CHAN); expo->trimSource = THR_TRIM_SOURCE; - anaSetFiltered(THR_STICK, 0); - anaSetFiltered(ELE_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), 0); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), 0); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 100); setTrimValue(0, MIXSRC_TRIMTHR - MIXSRC_FIRST_TRIM, -100); evalMixes(1); @@ -537,7 +537,7 @@ TEST_F(TrimsTest, MoveTrimsToOffsetsWithCrosstrimsAndTrimIdle) expo->trimSource = ELE_TRIM_SOURCE; expo = expoAddress(ELE_CHAN); expo->trimSource = THR_TRIM_SOURCE; - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); // Min throttle + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); // Min throttle setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 100); setTrimValue(0, MIXSRC_TRIMTHR - MIXSRC_FIRST_TRIM, -100); evalMixes(1); @@ -927,7 +927,7 @@ TEST_F(TrimsTest, throttleTrimEle) { g_model.thrTrim = 1; // checks ELE sticks are not affected by throttleTrim // stick max + trim min - anaSetFiltered(ELE_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), +1024); setTrimValue(0, ELE_STICK, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024 - 256); @@ -965,9 +965,9 @@ TEST(Heli, BasicTest) g_model.mixData[2].mltpx = MLTPX_ADD; g_model.mixData[2].srcRaw = MIXSRC_CYC3; g_model.mixData[2].weight = makeSourceNumVal(100); - anaSetFiltered(THR_STICK, 0); - anaSetFiltered(ELE_STICK, 1024); - anaSetFiltered(AIL_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), 0); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), 1024); + anaSetFiltered(inputMappingConvertMode(AIL_STICK), 0); evalFlightModeMixes(e_perout_mode_normal, 0); EXPECT_EQ(chans[0], -CHANNEL_MAX); EXPECT_EQ(chans[1], CHANNEL_MAX/2); @@ -1001,9 +1001,9 @@ TEST(Heli, Mode2Test) g_model.mixData[2].mltpx = MLTPX_ADD; g_model.mixData[2].srcRaw = MIXSRC_CYC3; g_model.mixData[2].weight = makeSourceNumVal(100); - anaSetFiltered(THR_STICK, 0); - anaSetFiltered(ELE_STICK, 1024); - anaSetFiltered(AIL_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), 0); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), 1024); + anaSetFiltered(inputMappingConvertMode(AIL_STICK), 0); evalFlightModeMixes(e_perout_mode_normal, 0); EXPECT_EQ(chans[0], -CHANNEL_MAX); EXPECT_EQ(chans[1], CHANNEL_MAX/2); @@ -1093,32 +1093,32 @@ TEST_F(TrimsTest, throttleTrimWithCrossTrims) expo->trimSource = THR_TRIM_SOURCE; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); - anaSetFiltered(ELE_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), 0); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim max - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(512)); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim mid - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(256)); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim min - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS); @@ -1127,31 +1127,31 @@ TEST_F(TrimsTest, throttleTrimWithCrossTrims) // now the same tests with extended Trims g_model.extendedTrims = 1; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], 1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim max - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024+2048); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim mid - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 0); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS + TRIM_SCALE(1024)); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim min - anaSetFiltered(THR_STICK, THR_STICK_MIN_THR_POS); + anaSetFiltered(inputMappingConvertMode(THR_STICK), THR_STICK_MIN_THR_POS); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], THR_STICK_MIN_THR_POS); @@ -1169,14 +1169,14 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) expo->trimSource = THR_TRIM_SOURCE; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); - anaSetFiltered(ELE_STICK, 0); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); + anaSetFiltered(inputMappingConvertMode(ELE_STICK), 0); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 0); evalMixes(1); #if defined(SURFACE_RADIO) @@ -1186,7 +1186,7 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) #endif EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MIN); evalMixes(1); #if defined(SURFACE_RADIO) @@ -1196,13 +1196,13 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) #endif EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); @@ -1211,13 +1211,13 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) // now the same tests with extended Trims g_model.extendedTrims = 1; // stick max + trim max - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], -1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim mid - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, 0); evalMixes(1); #if defined(SURFACE_RADIO) @@ -1227,7 +1227,7 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) #endif EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick max + trim min - anaSetFiltered(THR_STICK, +1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), +1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); evalMixes(1); #if defined(SURFACE_RADIO) @@ -1237,13 +1237,13 @@ TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) #endif EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim max - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); EXPECT_EQ(channelOutputs[ELE_CHAN], 0); // stick min + trim min - anaSetFiltered(THR_STICK, -1024); + anaSetFiltered(inputMappingConvertMode(THR_STICK), -1024); setTrimValue(0, MIXSRC_TRIMELE - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); evalMixes(1); EXPECT_EQ(channelOutputs[THR_CHAN], +1024); diff --git a/radio/util/hw_defs/legacy_names.py b/radio/util/hw_defs/legacy_names.py index 5af58e37a..d9ee464da 100644 --- a/radio/util/hw_defs/legacy_names.py +++ b/radio/util/hw_defs/legacy_names.py @@ -90,7 +90,7 @@ LEGACY_NAMES = [ } }, { - "targets": {"t15"}, + "targets": {"t15", "tx15"}, "inputs": { "LH": { "yaml": "Rud", diff --git a/radio/util/hw_defs/pot_config.py b/radio/util/hw_defs/pot_config.py index 5cee112c3..174a9f7cf 100644 --- a/radio/util/hw_defs/pot_config.py +++ b/radio/util/hw_defs/pot_config.py @@ -142,6 +142,10 @@ POT_CONFIG = { "SL1": {"default": "SLIDER"}, "SL2": {"default": "SLIDER"} }, + "tx15": { + "P1": {"default": "POT"}, + "P2": {"default": "POT"}, + }, "tx16s": { "P1": {"default": "POT_CENTER"}, "P2": {"default": "MULTIPOS"}, diff --git a/radio/util/hw_defs/switch_config.py b/radio/util/hw_defs/switch_config.py index 6cd0061d9..f12011f91 100644 --- a/radio/util/hw_defs/switch_config.py +++ b/radio/util/hw_defs/switch_config.py @@ -382,6 +382,21 @@ SWITCH_CONFIG = { "SF": {"default": "3POS", "display": [1, 2]}, "SH": {"default": "NONE", "display": [1, 3]} }, + "tx15": { + "SA": {"default": "3POS"}, + "SB": {"default": "3POS"}, + "SC": {"default": "3POS"}, + "SD": {"default": "3POS"}, + "SE": {"default": "2POS"}, + "SF": {"default": "TOGGLE"}, + # custom switches + "SG": {"default": "2POS"}, + "SH": {"default": "2POS"}, + "SI": {"default": "2POS"}, + "SJ": {"default": "2POS"}, + "SK": {"default": "2POS"}, + "SL": {"default": "2POS"}, + }, "tx16s": { "SA": {"default": "3POS"}, "SB": {"default": "3POS"}, diff --git a/tools/build-common.sh b/tools/build-common.sh index 763e5802f..40d2d088e 100644 --- a/tools/build-common.sh +++ b/tools/build-common.sh @@ -116,6 +116,9 @@ get_target_build_options() { t18) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=T18" ;; + tx15) + BUILD_OPTIONS+="-DPCB=TX15 -DNANO=NO" + ;; tx16s) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=TX16S" ;; diff --git a/tools/build-companion.sh b/tools/build-companion.sh index 5411cc575..10bb1892d 100755 --- a/tools/build-companion.sh +++ b/tools/build-companion.sh @@ -208,7 +208,7 @@ declare -a simulator_plugins=( t8 t12 t12max tx12 tx12mk2 t15 t16 t18 t20 t20v2 xlite xlites x10 x10express x12s - zorro tx16s + zorro tx16s tx15 commando8 boxer pocket mt12 gx12 tlite tpro tprov2 tpros bumblebee lr3pro t14 nv14 el18 pl18 pl18ev pl18u st16 pa01 diff --git a/tools/build-gh.sh b/tools/build-gh.sh index 9655dd43b..ff1f58e42 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -86,8 +86,7 @@ do rm -f CMakeCache.txt arm-none-eabi/CMakeCache.txt - #if [ -f arm-none-eabi/firmware.uf2 ]; then - if [ "$target_name" = "st16" ]; then + if [ -f "arm-none-eabi/firmware.uf2" ]; then mv arm-none-eabi/firmware.uf2 "../${fw_name}.uf2" else mv arm-none-eabi/firmware.bin "../${fw_name}.bin" diff --git a/tools/generate-hw-defs.sh b/tools/generate-hw-defs.sh index 4075506e8..9fc027acf 100755 --- a/tools/generate-hw-defs.sh +++ b/tools/generate-hw-defs.sh @@ -9,7 +9,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" : "${SRCDIR:=$(dirname "$(pwd)/$0")/..}" -: ${FLAVOR:="nv14;el18;pl18;pl18ev;nb4p;st16;t12;t12max;t15;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v14;v12"} +: ${FLAVOR:="nv14;el18;pl18;pl18ev;nb4p;st16;t12;t12max;t15;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v14;v12;tx15"} : ${COMMON_OPTIONS:="-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_RULE_MESSAGES=OFF -Wno-dev -DCMAKE_MESSAGE_LOG_LEVEL=WARNING"} # wipe build directory clean diff --git a/tools/generate-yaml.sh b/tools/generate-yaml.sh index 5508c775a..f9ddbb72d 100755 --- a/tools/generate-yaml.sh +++ b/tools/generate-yaml.sh @@ -11,7 +11,7 @@ if [[ -n ${GCC_ARM} ]] ; then export PATH=${GCC_ARM}:$PATH fi -: ${FLAVOR:="t15;tx16s;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;pa01"} +: ${FLAVOR:="t15;tx16s;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;pa01;tx15"} : ${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"} diff --git a/tools/msys2_fetch_and_build_all.sh b/tools/msys2_fetch_and_build_all.sh index a575d14e8..e11d2c1e0 100644 --- a/tools/msys2_fetch_and_build_all.sh +++ b/tools/msys2_fetch_and_build_all.sh @@ -9,7 +9,7 @@ # ----------------------------------------------------------------------------- export BRANCH_NAME="main" # main|2.9|... -export RADIO_TYPE="tx16s" # tx16s|x10|x10express|x12s|x9d|x9dp|x9lite|x9lites|x7|x7access|t12|t12max|tx12|tx12mk2|mt12|gx12|boxer|t8|zorro|pocket|tlite|tpro|t20|t20v2|t14|lr3pro|xlite|xlites|x9dp2019|x9e|x9e-hall|t15|t16|t18|nv14|commando8 +export RADIO_TYPE="tx16s" # tx16s|x10|x10express|x12s|x9d|x9dp|x9lite|x9lites|x7|x7access|t12|t12max|tx12|tx12mk2|mt12|gx12|boxer|t8|zorro|pocket|tlite|tpro|t20|t20v2|t14|lr3pro|xlite|xlites|x9dp2019|x9e|x9e-hall|t15|t16|t18|nv14|commando8|tx15 export BUILD_OPTIONS="-DDEFAULT_MODE=2 -DGVARS=YES" @@ -114,6 +114,9 @@ case $RADIO_TYPE in t18) BUILD_OPTIONS+=" -DPCB=X10 -DPCBREV=T18" ;; + tx15) + BUILD_OPTIONS+=" -DPCB=TX15" + ;; tx16s) BUILD_OPTIONS+=" -DPCB=X10 -DPCBREV=TX16S" ;;