From 7ffd3f5a33ec0fe5bef407ce2adc8c8e162a04a3 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 8 Jun 2021 13:14:48 +0800 Subject: [PATCH 01/45] Added build-flysky script. --- tools/build-flysky.py | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tools/build-flysky.py diff --git a/tools/build-flysky.py b/tools/build-flysky.py new file mode 100644 index 000000000..c36e6c25a --- /dev/null +++ b/tools/build-flysky.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 + +import argparse +import datetime +import os +from builtins import NotADirectoryError +import shutil +import tempfile + + +boards = { + "NV14": { + "PCB": "NV14", + "DEFAULT_MODE": "1", + }, +} + +translations = [ + "EN", +] + + +def timestamp(): + return datetime.datetime.now().strftime("%y%m%d") + + +def build(board, translation, srcdir): + cmake_options = " ".join(["-D%s=%s" % (key, value) for key, value in boards[board].items()]) + cwd = os.getcwd() + if not os.path.exists("output"): + os.mkdir("output") + path = tempfile.mkdtemp() + os.chdir(path) + command = "cmake %s -DTRANSLATIONS=%s %s" % (cmake_options, translation, srcdir) + print(command) + os.system(command) + os.system("make firmware -j16") + os.chdir(cwd) + index = 0 + while 1: + suffix = "" if index == 0 else "_%d" % index + filename = "output/firmware_%s_%s_%s%s.bin" % (board.lower(), translation.lower(), timestamp(), suffix) + if not os.path.exists(filename): + shutil.copy("%s/firmware.bin" % path, filename) + break + index += 1 + shutil.rmtree(path) + + +def dir_path(string): + if os.path.isdir(string): + return string + else: + raise NotADirectoryError(string) + + +def main(): + parser = argparse.ArgumentParser(description="Build Flysky firmware") + parser.add_argument("-b", "--boards", action="append", help="Destination boards", required=True) + parser.add_argument("-t", "--translations", action="append", help="Translations", required=True) + parser.add_argument("srcdir", type=dir_path) + + args = parser.parse_args() + + for board in (boards.keys() if "ALL" in args.boards else args.boards): + for translation in (translations if "ALL" in args.translations else args.translations): + build(board, translation, args.srcdir) + + +if __name__ == "__main__": + main() From cc92f2504616d4b44f594a69a889fd4a2cac2bc8 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 9 Jun 2021 16:10:45 +0800 Subject: [PATCH 02/45] Fixed NV14 build, added flysky build script and target in build-gh.sh --- radio/src/datastructs.h | 2 +- radio/src/gui/colorlcd/model_curves.cpp | 10 ++- radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 2 + radio/src/pulses/afhds3.h | 3 + .../src/storage/conversions/datastructs_219.h | 6 +- radio/src/targets/nv14/CMakeLists.txt | 66 ++++++++++++++++--- radio/src/targets/nv14/adc_driver.cpp | 4 +- radio/src/targets/nv14/board.cpp | 9 ++- radio/src/targets/nv14/board.h | 19 +++++- .../src/targets/nv14/bootloader/boot_menu.cpp | 4 +- radio/src/targets/nv14/extmodule_driver.cpp | 2 +- radio/src/targets/nv14/hal.h | 51 +++++++++++++- radio/src/targets/nv14/lcd_driver.cpp | 62 ++++++++++++++++- radio/src/targets/nv14/libopenui_config.h | 4 +- radio/src/targets/nv14/pwr_driver.cpp | 20 +++--- radio/src/targets/nv14/touch_driver.cpp | 3 +- tools/build-gh.sh | 3 + 18 files changed, 226 insertions(+), 45 deletions(-) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index eeccb77b6..08afe8281 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -582,7 +582,7 @@ PACK(struct CustomScreenData { #define TOPBAR_DATA #endif -#if defined(PCBHORUS) || defined(PCBTARANIS) +#if defined(PCBHORUS) || defined(PCBTARANIS) || defined(PCBNV14) #define SCRIPT_DATA \ NOBACKUP(ScriptData scriptsData[MAX_SCRIPTS]); #else diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index fdf2295c0..085bdf3b2 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -168,7 +168,9 @@ class CurveEditWindow : public Page curve.type = newValue; } SET_DIRTY(); - curveEdit->update(); + curveEdit->updatePreview(); + curveDataEdit->clear(); + curveDataEdit->update(); } }); @@ -190,7 +192,9 @@ class CurveEditWindow : public Page } curve.points = newValue; SET_DIRTY(); - curveEdit->update(); + curveEdit->updatePreview(); + curveDataEdit->clear(); + curveDataEdit->update(); } }); edit->setSuffix(STR_PTS); @@ -202,7 +206,7 @@ class CurveEditWindow : public Page [=](int32_t newValue) { g_model.curves[index].smooth = newValue; SET_DIRTY(); - curveEdit->update(); + curveEdit->updatePreview(); }); grid.nextLine(); } diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index c1cd80a12..cab6bcd4f 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -33,4 +33,5 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(tx12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_TX12) add_lua_export_target(t8 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T8) add_lua_export_target(t16 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10 -DRADIO_T16) + add_lua_export_target(nv14 ${LUA_INCLUDES} -DPCBFLYSKY -DPCBNV14) endif() diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 9ef7577b2..aa7a5a643 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -64,6 +64,8 @@ #include "lua/lua_exports_x9d+2019.inc" #elif defined(PCBTARANIS) #include "lua/lua_exports_x9d.inc" +#elif defined(PCBNV14) + #include "lua/lua_exports_nv14.inc" #endif #if defined(SIMU) diff --git a/radio/src/pulses/afhds3.h b/radio/src/pulses/afhds3.h index 5d687bc0e..c3edc33cd 100644 --- a/radio/src/pulses/afhds3.h +++ b/radio/src/pulses/afhds3.h @@ -93,7 +93,10 @@ struct Data void reset() { +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) +#else pulsesSize = 0; +#endif } #if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) diff --git a/radio/src/storage/conversions/datastructs_219.h b/radio/src/storage/conversions/datastructs_219.h index ef83d2471..3f30c69b3 100644 --- a/radio/src/storage/conversions/datastructs_219.h +++ b/radio/src/storage/conversions/datastructs_219.h @@ -27,7 +27,7 @@ #define NUM_SWITCHES_219 10 #elif defined(PCBXLITE) ||defined(PCBXLITES) #define NUM_SWITCHES_219 6 -#elif defined(RADIO_TLITE) || defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_T8) +#elif defined(RADIO_TLITE) || defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_T8) || defined(PCBNV14) #define NUM_SWITCHES_219 8 #elif defined(PCBX7) || defined(PCBX7ACCESS) #define NUM_SWITCHES_219 8 @@ -49,7 +49,7 @@ #elif defined(PCBX9LITE) #define NUM_POTS_219 1 #define NUM_SLIDERS_219 0 -#elif defined(RADIO_T8) || defined(RADIO_TLITE) || defined(PCBXLITE) || defined(PCBX7) +#elif defined(RADIO_T8) || defined(RADIO_TLITE) || defined(PCBXLITE) || defined(PCBX7) || defined(PCBNV14) #define NUM_POTS_219 2 #define NUM_SLIDERS_219 0 #elif defined(PCBX9E) @@ -113,7 +113,7 @@ #define MAX_CURVE_POINTS_219 512 #endif -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined(PCBNV14) #define MAX_MODELS_219 60 #define MAX_OUTPUT_CHANNELS_219 32 // number of real output channels CH1-CH32 #define MAX_FLIGHT_MODES_219 9 diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 2bf624a70..10c476d1b 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -14,6 +14,8 @@ set(STORAGE_FORMAT BIN) set(HAPTIC YES) set(GUI_DIR colorlcd) set(BITMAPS_DIR 480x272) +set(HARDWARE_EXTERNAL_MODULE YES) +set(AUX_SERIAL ON) # set(NAVIGATION_TYPE touch) set(TARGET_DIR nv14) add_definitions(-DHARDWARE_TRAINER_JACK) @@ -42,17 +44,19 @@ add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME set(TARGET_SRC ${TARGET_SRC} audio_spi_driver.cpp + ../common/arm/stm32/mixer_scheduler_driver.cpp ) +#set(FIRMWARE_TARGET_SRC +# ${FIRMWARE_TARGET_SRC} +# x12s_adc_driver.cpp +# ) + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} - x12s_adc_driver.cpp - ) - -set(FIRMWARE_TARGET_SRC - ${FIRMWARE_TARGET_SRC} + adc_driver.cpp battery_driver.cpp - hallStick_driver.cpp + ../horus/flyskyHallStick_driver.cpp ) set(BITMAPS_TARGET nv14_bitmaps) @@ -73,7 +77,9 @@ add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) add_definitions(-DCROSSFIRE_NATIVE) add_definitions(-DFLYSKY_HALL_STICKS_REVERSE) -add_definitions(-DHALL_STICKS) +add_definitions(-DFLYSKY_HALL_STICKS) +add_definitions(-DHARDWARE_EXTERNAL_MODULE) +add_definitions(-DAUX_SERIAL) #add_definitions(-DFLYSKY_AUTO_POWER_DOWN) if(STICKS_DEAD_ZONE) @@ -149,12 +155,52 @@ set(STM32LIB_SRC STM32F4xx_StdPeriph_Driver/src/stm32f4xx_syscfg.c ) +MACRO(GET_GCC_INCLUDE_PATH gcc_include_path) + if (WIN32) + execute_process(COMMAND arm-none-eabi-gcc -v -x c -E NUL ERROR_VARIABLE _gcc_output OUTPUT_QUIET) + else() + execute_process(COMMAND arm-none-eabi-gcc -v -x c -E - INPUT_FILE /dev/null ERROR_VARIABLE _gcc_output OUTPUT_QUIET) + endif() + + # Build an array of string from the GCC output + string(REPLACE "\n" ";" _gcc_output "${_gcc_output}") + + set(_capture_include FALSE) + set(_include_path "") + + # Go through the lines and capture between '"#include <...> search starts here:"' and 'End of search list.' + foreach(_line ${_gcc_output}) + if(${_line} STREQUAL "End of search list.") + set(_capture_include FALSE) + endif() + + if(_capture_include) + # Remove the leading and trailing empty characters + string(REPLACE "\r" "" _line ${_line}) + string(SUBSTRING "${_line}" 1 -1 _line) + set(_include_path "${_include_path}" "-I${_line}") + endif() + + if(${_line} STREQUAL "#include <...> search starts here:") + set(_capture_include TRUE) + endif() + endforeach() + set(${gcc_include_path} ${_include_path}) +ENDMACRO() + if(PYTHONINTERP_FOUND) - add_custom_target(datacopy + GET_GCC_INCLUDE_PATH(arm_gcc_include_path) + + add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp WORKING_DIRECTORY ${RADIO_DIRECTORY}/src - COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs.h -DPCBFLYSKY -DPCBNV14 -DCPUARM -DCOLORLCD -DBACKUP > storage/datacopy.cpp + COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs.h -DPCBFLYSKY -DPCBNV14 -DCPUARM -DSTM32F4 -DSTM32F429_439xx -DCOLORLCD -DBACKUP -DSIMU -I. -Igui/colorlcd -Itargets/horus -Itargets/common/arm/stm32 -I${THIRDPARTY_DIR} -I${THIRDPARTY_DIR}/libopenui/src -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/STM32F4xx_StdPeriph_Driver/inc -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Include ${arm_gcc_include_path} -Wno-pragma-once-outside-header > ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py - ) + ) + + add_custom_target(datacopy + DEPENDS ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp + ) endif() set(LUA_EXPORT lua_export_nv14) diff --git a/radio/src/targets/nv14/adc_driver.cpp b/radio/src/targets/nv14/adc_driver.cpp index 60cc4a00b..be68f6559 100644 --- a/radio/src/targets/nv14/adc_driver.cpp +++ b/radio/src/targets/nv14/adc_driver.cpp @@ -68,7 +68,7 @@ #define FIRST_ANALOG_ADC 0 #define NUM_MAIN_ANALOGS_ADC 10 #define NUM_MAIN_ANALOGS_ADC_EXT (NUM_MAIN_ANALOGS - 10) -#elif defined(PCBNV14) && defined(HALL_STICKS) +#elif defined(PCBNV14) && defined(FLYSKY_HALL_STICKS) #define FIRST_ANALOG_ADC 4 #define NUM_MAIN_ANALOGS_ADC 9 #else @@ -147,7 +147,7 @@ void adcInit() ADC_MAIN->SQR2 = (ADC_CHANNEL_SWE<<0) + (ADC_CHANNEL_SWF<<5) + (ADC_CHANNEL_LIBATT<<10) + (ADC_CHANNEL_DRYBATT<<15); // conversions 7 and more ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWB<<15) + (ADC_CHANNEL_SWC<<20) + (ADC_CHANNEL_SWD<<25); // conversions 1 to 6 #elif defined(PCBNV14) - #if !defined(HALL_STICKS) + #if !defined(FLYSKY_HALL_STICKS) ADC_MAIN->SQR1 |= (ADC_CHANNEL_BATT <<0 ); ADC_MAIN->SQR2 = (ADC_CHANNEL_SWA << 0) + (ADC_CHANNEL_SWC << 5) + (ADC_CHANNEL_SWE << 10) + (ADC_CHANNEL_SWF << 15) + (ADC_CHANNEL_SWG << 20) + (ADC_CHANNEL_SWH << 25); // conversions 7 and more ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 65ea598e4..809c8c62b 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -29,6 +29,9 @@ extern "C" { } #endif +extern void flysky_hall_stick_init( void ); +extern void flysky_hall_stick_loop( void ); + HardwareOptions hardwareOptions; void watchdogInit(unsigned int duration) @@ -84,7 +87,7 @@ void interrupt1ms() if (pre_scale == 10) { pre_scale = 0; #if !defined(SIMU) - if (boardState == BOARD_STARTED) hall_stick_loop(); + if (boardState == BOARD_STARTED) flysky_hall_stick_loop(); #endif DEBUG_TIMER_START(debugTimerPer10ms); DEBUG_TIMER_SAMPLE(debugTimerPer10msPeriod); @@ -144,7 +147,7 @@ void delay_self(int count) #define RCC_APB1PeriphOther (TELEMETRY_RCC_APB1Periph |\ TRAINER_RCC_APB1Periph |\ INTMODULE_RCC_APB1Periph |\ - HALL_RCC_APB1Periph |\ + FLYSKY_HALL_RCC_APB1Periph |\ EXTMODULE_RCC_APB1Periph |\ INTMODULE_RCC_APB1Periph |\ AUX_SERIAL_RCC_APB1Periph \ @@ -229,7 +232,7 @@ void boardInit() backlightInit(); lcdInit(); #if defined(FLYSKY_HALL_STICKS) - hall_stick_init(FLYSKY_HALL_BAUDRATE); + flysky_hall_stick_init(); #endif usbInit(); hapticInit(); diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index db90d841d..56d04e499 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -34,7 +34,7 @@ #endif #include "touch_driver.h" -#include "hallStick_driver.h" +//#include "hallStick_driver.h" #include "lcd_driver.h" #include "battery_driver.h" @@ -128,7 +128,8 @@ void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendNextFrame(); -void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +//void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +void extmoduleSerialStart(); void extmoduleSendNextFrame(); void extmoduleSendInvertedByte(uint8_t byte); @@ -146,6 +147,7 @@ enum EnumKeys KEY_PGUP, KEY_PGDN, KEY_UP, + KEY_MODEL = KEY_UP, // TODO: just fixed for compile error, seems this is quite different from horus definition. KEY_DOWN, KEY_RIGHT, KEY_LEFT, @@ -332,6 +334,8 @@ enum CalibratedAnalogs { CALIBRATED_STICK4, CALIBRATED_POT1, CALIBRATED_POT2, + CALIBRATED_SLIDER_REAR_LEFT, + CALIBRATED_SLIDER_REAR_RIGHT, CALIBRATED_SWA, CALIBRATED_SWB, CALIBRATED_SWC, @@ -374,6 +378,11 @@ void pwrSoftReboot(); void pwrOff(); void pwrResetHandler(); bool pwrPressed(); +#if defined(PWR_EXTRA_SWITCH_GPIO) + bool pwrForcePressed(); +#else + #define pwrForcePressed() false +#endif uint32_t pwrPressedDuration();; #if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) #define UNEXPECTED_SHUTDOWN() (false) @@ -493,9 +502,13 @@ void hapticOff(); void hapticOn(uint32_t pwmPercent); // Second serial port driver -#define AUX_SERIAL +//#define AUX_SERIAL #define DEBUG_BAUDRATE 115200 +#define LUA_DEFAULT_BAUDRATE 115200 extern uint8_t auxSerialMode; +#if defined __cplusplus +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); +#endif void auxSerialInit(unsigned int mode, unsigned int protocol); void auxSerialPutc(char c); #define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp index 41d48140a..a689cacab 100644 --- a/radio/src/targets/nv14/bootloader/boot_menu.cpp +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -79,8 +79,8 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawText(center, 200, "for mass storage", CENTERED | DEFAULT_COLOR); bootloaderDrawFooter(); - lcd->drawText(center, LCD_H - DOUBLE_PADDING, "Current Firmware:", CENTERED | DEFAULT_COLOR); - lcd->drawText(center, LCD_H - DEFAULT_PADDING, getOtherVersion(nullptr), CENTERED | DEFAULT_COLOR); + lcd->drawText(center, LCD_H - DOUBLE_PADDING, "Current Firmware:", CENTERED | DEFAULT_COLOR); + lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | DEFAULT_COLOR); } else if (st == ST_USB) { //lcd->drawBitmap(136, 98, &BMP_USB_PLUGGED); diff --git a/radio/src/targets/nv14/extmodule_driver.cpp b/radio/src/targets/nv14/extmodule_driver.cpp index aacaf9cfd..489f105f4 100644 --- a/radio/src/targets/nv14/extmodule_driver.cpp +++ b/radio/src/targets/nv14/extmodule_driver.cpp @@ -148,7 +148,7 @@ void extmodulePpmStart() NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); } -void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted) +void extmoduleSerialStart() { } diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index a7a57b872..eba39e872 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -99,7 +99,7 @@ // ADC #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_DMA2) #define ADC_RCC_APB2Periph (RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC3) -#if !defined(HALL_STICKS) +#if !defined(FLYSKY_HALL_STICKS) #define ADC_GPIO_PIN_STICK_LH GPIO_Pin_2 // PA.02 #define ADC_GPIO_PIN_STICK_LV GPIO_Pin_3 // PA.03 #define ADC_GPIO_PIN_STICK_RH GPIO_Pin_4 // PA.04 @@ -127,7 +127,7 @@ #define ADC_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_5) #define ADC_GPIOF_PINS (GPIO_Pin_8 | GPIO_Pin_10) -#if !defined(HALL_STICKS) +#if !defined(FLYSKY_HALL_STICKS) #define ADC_CHANNEL_STICK_LH ADC_Channel_2 // ADC123_IN2 -> ADC1_IN2 #define ADC_CHANNEL_STICK_LV ADC_Channel_3 // ADC123_IN3 -> ADC1_IN3 #define ADC_CHANNEL_STICK_RH ADC_Channel_4 // ADC12_IN4 -> ADC1_IN4 @@ -160,7 +160,8 @@ // Power #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOI -#define PWR_GPIO GPIOI +#define PWR_ON_GPIO GPIOI +#define PWR_SWITCH_GPIO GPIOI #define PWR_SWITCH_GPIO_REG PWR_GPIO->IDR #define PWR_SWITCH_GPIO_PIN GPIO_Pin_11 // PI.11 #define PWR_ON_GPIO_PIN GPIO_Pin_14 // PI.14 @@ -205,6 +206,11 @@ //used in BOOTLOADER #define SERIAL_RCC_AHB1Periph 0 #define SERIAL_RCC_APB1Periph 0 +#define AUX2_SERIAL_RCC_AHB1Periph 0 +#define AUX2_SERIAL_RCC_APB1Periph 0 +#define AUX2_SERIAL_RCC_APB2Periph 0 +#define KEYS_BACKLIGHT_RCC_AHB1Periph 0 + // Telemetry #define TELEMETRY_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_DMA1) #define TELEMETRY_RCC_APB1Periph RCC_APB1Periph_USART2 @@ -357,6 +363,37 @@ #define HAPTIC_TIMER_MODE TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE #define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR1 +// Flysky Hall Stick +#if defined(FLYSKY_HALL_STICKS) + #if defined (RADIO_TX16S) + #define FLYSKY_HALL_STICKS_REVERSE + #endif + + #define FLYSKY_HALL_SERIAL_USART UART4 + #define FLYSKY_HALL_SERIAL_GPIO GPIOA + #define FLYSKY_HALL_DMA_Channel DMA_Channel_4 + #define FLYSKY_HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 + #define FLYSKY_HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 + #define FLYSKY_HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 + #define FLYSKY_HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 + #define FLYSKY_HALL_SERIAL_GPIO_AF GPIO_AF_UART4 + + #define FLYSKY_HALL_RCC_AHB1Periph RCC_AHB1Periph_DMA1 + #define FLYSKY_HALL_RCC_APB1Periph RCC_APB1Periph_UART4 + + #define FLYSKY_HALL_SERIAL_USART_IRQHandler UART4_IRQHandler + #define FLYSKY_HALL_SERIAL_USART_IRQn UART4_IRQn + #define FLYSKY_HALL_SERIAL_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn + #define FLYSKY_HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn + #define FLYSKY_HALL_DMA_Stream_RX DMA1_Stream2 + #define FLYSKY_HALL_DMA_Stream_TX DMA1_Stream4 + #define FLYSKY_HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 + + #define FLYSKY_HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler + #define FLYSKY_HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + +#endif + // Internal Module #define HARDWARE_INTERNAL_MODULE #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) @@ -399,6 +436,7 @@ #define EXTMODULE_PULSES #define EXTMODULE_PWR_GPIO GPIOD #define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_11 +#define EXTERNAL_MODULE_PWR_OFF() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) #define EXTMODULE_PWR_FIX_GPIO GPIOA #define EXTMODULE_PWR_FIX_GPIO_PIN GPIO_Pin_2 // PA.02 #define EXTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_DMA2) @@ -519,4 +557,11 @@ #define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 #define TIMER_2MHz_TIMER TIM7 +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13 +#define MIXER_SCHEDULER_TIMER TIM13 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler + #endif // _HAL_H_ diff --git a/radio/src/targets/nv14/lcd_driver.cpp b/radio/src/targets/nv14/lcd_driver.cpp index c1b19906f..007d7e446 100644 --- a/radio/src/targets/nv14/lcd_driver.cpp +++ b/radio/src/targets/nv14/lcd_driver.cpp @@ -23,6 +23,7 @@ uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; +uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t currentLayer = LCD_FIRST_LAYER; lcdSpiInitFucPtr lcdInitFunction; @@ -1273,7 +1274,8 @@ BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *) LCD_FIRST_FRAME_BUFFER); BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *) LCD_SECOND_FRAME_BUFFER); -BitmapBuffer * lcd = &lcdBuffer1; +BitmapBuffer * lcdFront = &lcdBuffer1; +BitmapBuffer * lcd = &lcdBuffer2; void LCD_SetLayer(uint32_t layer) { if (layer == LCD_FIRST_LAYER) { @@ -1455,6 +1457,54 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } +// same as DMACopyAlphaBitmap(), but with an 8 bit mask for each pixel (used by fonts) +void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color) +{ + TRACE("++++++"); + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_BLEND; + DMA2D_InitStruct.DMA2D_CMode = CM_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_A8; // 8 bit inputs every time + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FG_InitStruct.DMA2D_FGC_RED = GET_RED(bg_color); // 8 bit red + DMA2D_FG_InitStruct.DMA2D_FGC_GREEN = GET_GREEN(bg_color); // 8 bit green + DMA2D_FG_InitStruct.DMA2D_FGC_BLUE = GET_BLUE(bg_color); // 8 bit blue + + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + DMA2D_BG_InitTypeDef DMA2D_BG_InitStruct; + DMA2D_BG_StructInit(&DMA2D_BG_InitStruct); + DMA2D_BG_InitStruct.DMA2D_BGMA = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_BG_InitStruct.DMA2D_BGO = destw - w; + DMA2D_BG_InitStruct.DMA2D_BGCM = CM_RGB565; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_VALUE = 0; + DMA2D_BGConfig(&DMA2D_BG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) { DMA2D_DeInit(); @@ -1532,6 +1582,16 @@ int lcdRestoreBackupBuffer() return 1; } +uint16_t* lcdGetBackupBuffer() +{ + return (uint16_t*)LCD_BACKUP_FRAME_BUFFER; +} + +uint16_t* lcdGetScratchBuffer() +{ + return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; +} + void lcdRefresh() { if (currentLayer == LCD_FIRST_LAYER) { diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h index 50a1606d5..a24c6fef9 100644 --- a/radio/src/targets/nv14/libopenui_config.h +++ b/radio/src/targets/nv14/libopenui_config.h @@ -86,7 +86,7 @@ constexpr uint32_t ALERT_BUTTON_TOP = 300; constexpr uint32_t PAGE_TITLE_TOP = 4; constexpr uint32_t PAGE_TITLE_LEFT = 70; -enum LcdColorIndex +/*enum LcdColorIndex { DEFAULT_COLOR_INDEX, DEFAULT_BGCOLOR_INDEX, @@ -130,7 +130,7 @@ enum LcdColorIndex TABLE_HEADER_BGCOLOR_INDEX, TABLE_BGCOLOR_INDEX, -}; +};*/ enum FontIndex { diff --git a/radio/src/targets/nv14/pwr_driver.cpp b/radio/src/targets/nv14/pwr_driver.cpp index b78cf7f3f..65f653b39 100644 --- a/radio/src/targets/nv14/pwr_driver.cpp +++ b/radio/src/targets/nv14/pwr_driver.cpp @@ -32,13 +32,13 @@ void pwrInit() GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(PWR_GPIO, &GPIO_InitStructure); + GPIO_Init(PWR_ON_GPIO, &GPIO_InitStructure); // TODO move this elsewhere! // Init Module PWR - //GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN); - //GPIO_InitStructure.GPIO_Pin = INTMODULE_PWR_GPIO_PIN; - //GPIO_Init(INTMODULE_PWR_GPIO, &GPIO_InitStructure); + //GPIO_ResetBits(INTMODULE_PWR_ON_GPIO, INTMODULE_PWR_ON_GPIO_PIN); + //GPIO_InitStructure.GPIO_Pin = INTMODULE_PWR_ON_GPIO_PIN; + //GPIO_Init(INTMODULE_PWR_ON_GPIO, &GPIO_InitStructure); //Bluetooth GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN); @@ -48,7 +48,7 @@ void pwrInit() // Init PWR SWITCH PIN GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_Pin = PWR_SWITCH_GPIO_PIN; - GPIO_Init(PWR_GPIO, &GPIO_InitStructure); + GPIO_Init(PWR_ON_GPIO, &GPIO_InitStructure); // Init SD-DETECT PIN GPIO_ResetBits(SD_PRESENT_GPIO, SD_PRESENT_GPIO_PIN); @@ -56,16 +56,16 @@ void pwrInit() GPIO_Init(SD_PRESENT_GPIO, &GPIO_InitStructure); //turn power off - GPIO_ResetBits(PWR_GPIO, PWR_ON_GPIO_PIN); + GPIO_ResetBits(PWR_ON_GPIO, PWR_ON_GPIO_PIN); //turn Bluetooth off - GPIO_ResetBits(PWR_GPIO, BLUETOOTH_ON_GPIO_PIN); + GPIO_ResetBits(PWR_ON_GPIO, BLUETOOTH_ON_GPIO_PIN); boardState = BOARD_POWER_OFF; } void pwrOn() { - GPIO_SetBits(PWR_GPIO, PWR_ON_GPIO_PIN); + GPIO_SetBits(PWR_ON_GPIO, PWR_ON_GPIO_PIN); boardState = BOARD_POWER_ON; } @@ -78,12 +78,12 @@ void pwrOff() // Shutdown the Haptic hapticDone(); boardState = BOARD_POWER_OFF; - GPIO_ResetBits(PWR_GPIO, PWR_ON_GPIO_PIN); + GPIO_ResetBits(PWR_ON_GPIO, PWR_ON_GPIO_PIN); } bool pwrPressed() { - return GPIO_ReadInputDataBit(PWR_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET; + return GPIO_ReadInputDataBit(PWR_ON_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET; } void pwrResetHandler() diff --git a/radio/src/targets/nv14/touch_driver.cpp b/radio/src/targets/nv14/touch_driver.cpp index 72234dd0d..07a81996e 100644 --- a/radio/src/targets/nv14/touch_driver.cpp +++ b/radio/src/targets/nv14/touch_driver.cpp @@ -473,7 +473,8 @@ void handleTouch() touchState.startY = touchState.y; touchState.event = TE_DOWN; if (g_eeGeneral.backlightMode & e_backlight_mode_keys) - backlightOn(); // TODO is that the best place ? + backlightEnable(BACKLIGHT_LEVEL_MAX); + // backlightOn(); // TODO is that the best place ? } else if (touchState.event == TE_DOWN) { if (dx >= SLIDE_RANGE || dx <= -SLIDE_RANGE || dy >= SLIDE_RANGE || dy <= -SLIDE_RANGE) { diff --git a/tools/build-gh.sh b/tools/build-gh.sh index 2ccc0f914..d97eff033 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -133,6 +133,9 @@ case $FLAVOR in TX16S_FS) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=TX16S -DFLYSKY_HALL_STICKS=ON" ;; + NV14) + BUILD_OPTIONS+="-DPCB=NV14" + ;; esac cmake ${BUILD_OPTIONS} "${SRCDIR}" From e36e41579bbbcc866288a83d719945dc1b3174c2 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 11 Jun 2021 11:14:59 +0800 Subject: [PATCH 03/45] Fixed typo bug in jumper build script. --- tools/build-jumper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build-jumper.py b/tools/build-jumper.py index b6dd3b8b8..436b2b23d 100755 --- a/tools/build-jumper.py +++ b/tools/build-jumper.py @@ -43,7 +43,7 @@ boards = { "PCBREV": "T18", "INTERNAL_MODULE_MULTI": "YES", "DEFAULT_MODE": "2", - } + }, "T18_FS": { "PCB": "X10", "PCBREV": "T18", From a79b317a397bb59e00852f7e6169c746b936566c Mon Sep 17 00:00:00 2001 From: qba667 Date: Sun, 20 Jun 2021 09:45:47 +0200 Subject: [PATCH 04/45] Fixed LCD backligh on touch --- radio/src/opentx.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 4c421e52e..10552b7a2 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -537,6 +537,11 @@ void checkBacklight() resetBacklightTimeout(); } } + #if defined(HARDWARE_TOUCH) + if (touchPanelEventOccured() && (g_eeGeneral.backlightMode & e_backlight_mode_keys)) { + resetBacklightTimeout(); + } + #endif if (requiredBacklightBright == BACKLIGHT_FORCED_ON) { currentBacklightBright = g_eeGeneral.backlightBright; @@ -550,7 +555,6 @@ void checkBacklight() if (flashCounter) { backlightOn = !backlightOn; } - if (backlightOn) { currentBacklightBright = requiredBacklightBright; BACKLIGHT_ENABLE(); @@ -2083,7 +2087,7 @@ uint32_t pwrCheck() LED_ERROR_BEGIN(); WDG_RESET(); - lcdRefresh(); + (); if (warningResult) { pwr_check_state = PWR_CHECK_OFF; From b621c90640cad1c1290045dcbe768635b554dae2 Mon Sep 17 00:00:00 2001 From: qba667 Date: Sun, 20 Jun 2021 09:46:57 +0200 Subject: [PATCH 05/45] Removed not existing debug define --- radio/src/targets/horus/flyskyHallStick_driver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/targets/horus/flyskyHallStick_driver.cpp b/radio/src/targets/horus/flyskyHallStick_driver.cpp index 0aa6f3168..6d4b1d0c4 100644 --- a/radio/src/targets/horus/flyskyHallStick_driver.cpp +++ b/radio/src/targets/horus/flyskyHallStick_driver.cpp @@ -534,7 +534,6 @@ void flysky_hall_stick_loop(void) if (log) { - TRACE_NOCRLF("Hall(%0d):", FLYSKY_FLYSKY_HALL_BAUDRATE); for (int idx = 0; idx < HallProtocol.length + 5; idx++) { TRACE_NOCRLF(" %02X", *((uint8_t*)&HallProtocol + idx)); From 0b365adaf94ba69e607503699401dc75a9e72315 Mon Sep 17 00:00:00 2001 From: qba667 Date: Sun, 20 Jun 2021 09:48:04 +0200 Subject: [PATCH 06/45] Fixed LCD display for NV14 --- radio/src/targets/nv14/battery_driver.cpp | 1 - radio/src/targets/nv14/board.h | 3 + .../src/targets/nv14/bootloader/boot_menu.cpp | 6 +- radio/src/targets/nv14/lcd_driver.cpp | 130 ++++++++---------- radio/src/targets/nv14/lcd_driver.h | 3 - 5 files changed, 65 insertions(+), 78 deletions(-) diff --git a/radio/src/targets/nv14/battery_driver.cpp b/radio/src/targets/nv14/battery_driver.cpp index 6ef43f042..ca6b11854 100644 --- a/radio/src/targets/nv14/battery_driver.cpp +++ b/radio/src/targets/nv14/battery_driver.cpp @@ -182,7 +182,6 @@ void handle_battery_charge(uint32_t last_press_time) lcdOn(); } updateTime = get_tmr10ms(); - lcdNextLayer(); lcd->clear(); drawChargingInfo(chargeState); lcdRefresh(); diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index 56d04e499..b1901434a 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -390,6 +390,9 @@ uint32_t pwrPressedDuration();; #define UNEXPECTED_SHUTDOWN() (powerupReason == DIRTY_SHUTDOWN) #endif +#define AUX_SERIAL_POWER_ON() +#define AUX_SERIAL_POWER_OFF() + // LCD driver #define LCD_W 320 #define LCD_H 480 diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp index a689cacab..907f8d460 100644 --- a/radio/src/targets/nv14/bootloader/boot_menu.cpp +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -56,9 +56,6 @@ static void bootloaderDrawFooter() void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { -#if defined(COLORLCD) - lcdNextLayer(); -#endif // clear screen lcd->drawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, DEFAULT_BGCOLOR); int center = LCD_W/2; @@ -162,6 +159,9 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit"); } } +#if defined(COLORLCD) + lcdRefresh(); +#endif } void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) diff --git a/radio/src/targets/nv14/lcd_driver.cpp b/radio/src/targets/nv14/lcd_driver.cpp index 007d7e446..6606a7af7 100644 --- a/radio/src/targets/nv14/lcd_driver.cpp +++ b/radio/src/targets/nv14/lcd_driver.cpp @@ -19,12 +19,12 @@ */ #include "opentx.h" - +#define LTDC_DOUBLELAYER uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -uint8_t currentLayer = LCD_FIRST_LAYER; +bool secondFrame = false; lcdSpiInitFucPtr lcdInitFunction; lcdSpiInitFucPtr lcdOffFunction; @@ -1179,24 +1179,37 @@ void LCD_Init_LTDC() { LTDC_Init(<DC_InitStruct); -#if 0 + //Configure IRQ + // LTDC_ITConfig(LTDC_IER_RRIE, ENABLE); + // NVIC_InitTypeDef NVIC_InitStructure; + // NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; + // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; + // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + // NVIC_Init(&NVIC_InitStructure); + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pre-emption priority. */; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init( &NVIC_InitStructure ); + NVIC_Init(&NVIC_InitStructure); + LTDC_LIPConfig(LCD_H); +#if 0 DMA2D_ITConfig(DMA2D_CR_TCIE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA2D_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA_SCREEN_IRQ_PRIO; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); - DMA2D->IFCR = (unsigned long)DMA2D_IFSR_CTCIF; #endif + + // Enable LTDC IRQ in NVIC + NVIC_EnableIRQ(LTDC_IRQn); + NVIC_SetPriority(LTDC_IRQn, 7); } void LCD_LayerInit() { @@ -1247,25 +1260,29 @@ void LCD_LayerInit() { /* Initialize LTDC layer 1 */ LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); - /* Configure Layer 2 */ +#if defined(LTDC_DOUBLELAYER) + /* Configure Layer 2 */ LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA; LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA; /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ - LTDC_Layer_InitStruct.LTDC_CFBStartAdress = - (uint32_t) LCD_SECOND_FRAME_BUFFER; - + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t) LCD_SECOND_FRAME_BUFFER; + /* Initialize LTDC layer 2 */ LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct); - +#endif /* LTDC configuration reload */ - LTDC_ReloadConfig (LTDC_IMReload); - - LTDC_LayerCmd(LTDC_Layer1, ENABLE); - LTDC_LayerCmd(LTDC_Layer2, ENABLE); - LTDC_ReloadConfig(LTDC_IMReload); + LTDC_LayerCmd(LTDC_Layer1, ENABLE); + LTDC_LayerAlpha(LTDC_Layer1, 255); + +#if defined(LTDC_DOUBLELAYER) + LTDC_LayerCmd(LTDC_Layer2, ENABLE); + LTDC_LayerAlpha(LTDC_Layer2, 0); +#endif + + LTDC_ReloadConfig(LTDC_IMReload); /* dithering activation */ LTDC_DitherCmd(ENABLE); } @@ -1277,27 +1294,11 @@ BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, BitmapBuffer * lcdFront = &lcdBuffer1; BitmapBuffer * lcd = &lcdBuffer2; -void LCD_SetLayer(uint32_t layer) { - if (layer == LCD_FIRST_LAYER) { - lcd = &lcdBuffer1; - } else { - lcd = &lcdBuffer2; - } - currentLayer = layer; -} - -void LCD_SetTransparency(uint8_t transparency) -{ - if (currentLayer == LCD_FIRST_LAYER) { - LTDC_LayerAlpha(LTDC_Layer1, transparency); - } else { - LTDC_LayerAlpha(LTDC_Layer2, transparency); - } - LTDC_ReloadConfig (LTDC_IMReload); -} - extern void loadFonts(); void lcdInit(void) { + // Clear buffers first + memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); + memset(LCD_SECOND_FRAME_BUFFER, 0, sizeof(LCD_SECOND_FRAME_BUFFER)); loadFonts(); /* Configure the LCD SPI+RESET pins */ @@ -1308,7 +1309,7 @@ void lcdInit(void) { /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); - + /* Send LCD initializaiton commands */ if (LCD_ILI9481_ReadID() == LCD_ILI9481_ID) { TRACE("LCD INIT: ILI9481"); @@ -1339,23 +1340,11 @@ void lcdInit(void) { } lcdInitFunction(); - + LCD_Init_LTDC(); - LCD_LayerInit(); - - /* Enable LCD display */ LTDC_Cmd(ENABLE); - - /* Set Background layer */ - LCD_SetLayer (LCD_FIRST_LAYER); - // lcdClear(); - LCD_SetTransparency(0); - - /* Set Foreground layer */ - LCD_SetLayer (LCD_SECOND_LAYER); - lcd->clear(); - LCD_SetTransparency(255); + LTDC_ReloadConfig(LTDC_IMReload); } void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) @@ -1460,7 +1449,6 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ // same as DMACopyAlphaBitmap(), but with an 8 bit mask for each pixel (used by fonts) void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color) { - TRACE("++++++"); DMA2D_DeInit(); DMA2D_InitTypeDef DMA2D_InitStruct; @@ -1592,27 +1580,27 @@ uint16_t* lcdGetScratchBuffer() return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; } +static volatile uint8_t refreshRequested = 0; + +extern "C" void LTDC_IRQHandler(void) +{ + LTDC_ClearFlag(LTDC_ICR_CLIF); + if (refreshRequested) { +#if defined(LTDC_DOUBLELAYER) + LTDC_LayerAlpha(LTDC_Layer1, (lcd == &lcdBuffer2) ? 255 : 0); + LTDC_LayerAlpha(LTDC_Layer2, (lcd == &lcdBuffer1) ? 255 : 0); +#else + LTDC_Layer1->CFBAR = (uint32_t)((lcd == &lcdBuffer2) ? &lcdBuffer1 : &lcdBuffer2)->getData(); +#endif + LTDC_ReloadConfig(LTDC_IMReload); + refreshRequested = false; + } +} + void lcdRefresh() { - if (currentLayer == LCD_FIRST_LAYER) { - LTDC_LayerAlpha(LTDC_Layer1, 255); - LTDC_LayerAlpha(LTDC_Layer2, 0); - } - else { - LTDC_LayerAlpha(LTDC_Layer1, 0); - LTDC_LayerAlpha(LTDC_Layer2, 255); - } - LTDC_ReloadConfig(LTDC_IMReload); + while(refreshRequested) {} //block if last frame was not painted yet + lcd = (lcd == &lcdBuffer2) ? &lcdBuffer1 : &lcdBuffer2; + refreshRequested = true; } -void lcdNextLayer() -{ - if (currentLayer == LCD_FIRST_LAYER) { - LCD_SetLayer(LCD_SECOND_LAYER); - } - else { - LCD_SetLayer(LCD_FIRST_LAYER); - } -} - - diff --git a/radio/src/targets/nv14/lcd_driver.h b/radio/src/targets/nv14/lcd_driver.h index bc348984e..32b9d7079 100644 --- a/radio/src/targets/nv14/lcd_driver.h +++ b/radio/src/targets/nv14/lcd_driver.h @@ -32,9 +32,6 @@ #define HFP ( 140 - HBP ) #define VFP ( 22 - VBP ) -#define LCD_FIRST_LAYER ( 0 ) -#define LCD_SECOND_LAYER ( 1 ) - #define PORT_LCD_CS ( GPIOE ) #define LCD_CS_PIN ( GPIO_Pin_4 ) From 865a8204b6b2a2cfb9200863895db916320ac096 Mon Sep 17 00:00:00 2001 From: qba667 Date: Sun, 20 Jun 2021 09:49:26 +0200 Subject: [PATCH 07/45] Fixed transistion form TE_SLIDE_END to TE_DOWN --- radio/src/targets/nv14/touch_driver.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/targets/nv14/touch_driver.cpp b/radio/src/targets/nv14/touch_driver.cpp index 07a81996e..b11767c20 100644 --- a/radio/src/targets/nv14/touch_driver.cpp +++ b/radio/src/targets/nv14/touch_driver.cpp @@ -468,13 +468,10 @@ void handleTouch() touchState.x = touchX; touchState.y = touchY; - if (touchState.event == TE_NONE || touchState.event == TE_UP) { + if (touchState.event == TE_NONE || touchState.event == TE_UP || touchState.event == TE_SLIDE_END) { touchState.startX = touchState.x; touchState.startY = touchState.y; touchState.event = TE_DOWN; - if (g_eeGeneral.backlightMode & e_backlight_mode_keys) - backlightEnable(BACKLIGHT_LEVEL_MAX); - // backlightOn(); // TODO is that the best place ? } else if (touchState.event == TE_DOWN) { if (dx >= SLIDE_RANGE || dx <= -SLIDE_RANGE || dy >= SLIDE_RANGE || dy <= -SLIDE_RANGE) { From 35776c5f4a69810c4adfd2f953719bf2e3ad02dd Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 22 Jun 2021 17:09:09 +0800 Subject: [PATCH 08/45] Use default theme for nv14, may customize the theme later. --- radio/src/gui/colorlcd/themes/480_default.cpp | 2 +- radio/src/gui/colorlcd/themes/flysky.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 5d5907536..77b5ac252 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -460,7 +460,7 @@ BitmapBuffer * Theme480::currentMenuBackground = nullptr; Theme480 Theme480; -#if LCD_W == 480 +#if LCD_W == 480 || LCD_H == 480 OpenTxTheme * defaultTheme = &Theme480; Theme * theme = &Theme480; #endif diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index a8c519d41..618a988ad 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -454,9 +454,9 @@ BitmapBuffer * FlyskyTheme::menuIconNormal[MENUS_ICONS_COUNT] = { nullptr }; BitmapBuffer * FlyskyTheme::menuIconSelected[MENUS_ICONS_COUNT] = { nullptr }; BitmapBuffer * FlyskyTheme::currentMenuBackground = nullptr; -FlyskyTheme flyskyTheme; +/*FlyskyTheme flyskyTheme; #if defined(PCBFLYSKY) OpenTxTheme * defaultTheme = &flyskyTheme; Theme * theme = &flyskyTheme; -#endif +#endif*/ From fac40096213312b9a6e21e2e628728875a68ba13 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 30 Jun 2021 15:07:15 +0800 Subject: [PATCH 09/45] Audio driver fix. --- radio/src/audio.cpp | 6 ++++++ radio/src/targets/nv14/audio_spi_driver.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index da1998e4b..bbe59ff34 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -531,6 +531,12 @@ void audioTask(void * pdata) #if defined(PCBX12S) || defined(RADIO_TX16S) // The audio amp needs ~2s to start RTOS_WAIT_MS(1000); // 1s +#elif defined(PCBNV14) + while(!isAudioReady()) + { + audioChipReset(); + RTOS_WAIT_MS(1000); + } #endif if (!globalData.unexpectedShutdown) { diff --git a/radio/src/targets/nv14/audio_spi_driver.cpp b/radio/src/targets/nv14/audio_spi_driver.cpp index f21b5c46f..60ec589d9 100644 --- a/radio/src/targets/nv14/audio_spi_driver.cpp +++ b/radio/src/targets/nv14/audio_spi_driver.cpp @@ -276,7 +276,8 @@ uint8_t audioHardReset(void) XDCS_HIGH(); CS_HIGH(); RST_HIGH(); - while (READ_DREQ() == 0 && retry < 200) { + while (READ_DREQ() == 0 && retry < 200) + { retry++; delay_us(50); } From fe532f32fe3ed734e1ce6a83f93c4ece2cfd197c Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 30 Jun 2021 15:10:45 +0800 Subject: [PATCH 10/45] Some more fixes. --- radio/src/keys.h | 2 +- radio/src/lua/CMakeLists.txt | 2 +- radio/src/targets/nv14/board.cpp | 2 +- radio/src/targets/nv14/libopenui_config.h | 36 ++++++++++++----------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/radio/src/keys.h b/radio/src/keys.h index f8d30f1e5..bbe71e09b 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -32,7 +32,7 @@ constexpr event_t EVT_ENTRY_UP = 0x1002; constexpr event_t EVT_ROTARY_LEFT = 0x1003; constexpr event_t EVT_ROTARY_RIGHT = 0x1004; -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined (PCBNV14) constexpr event_t _MSK_KEY_BREAK = 0x0200; constexpr event_t _MSK_KEY_REPT = 0x0400; constexpr event_t _MSK_KEY_FIRST = 0x0600; diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index cab6bcd4f..8325bb2aa 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -33,5 +33,5 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(tx12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_TX12) add_lua_export_target(t8 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T8) add_lua_export_target(t16 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10 -DRADIO_T16) - add_lua_export_target(nv14 ${LUA_INCLUDES} -DPCBFLYSKY -DPCBNV14) + add_lua_export_target(nv14 ${LUA_INCLUDES} -I${RADIO_SRC_DIR}/targets/nv14 -DPCBNV14) endif() diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 809c8c62b..8608806ea 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -134,7 +134,7 @@ void delay_self(int count) AUDIO_RCC_AHB1Periph |\ HAPTIC_RCC_AHB1Periph |\ INTMODULE_RCC_AHB1Periph |\ - INTMODULE_RCC_AHB1Periph|\ + FLYSKY_HALL_RCC_AHB1Periph |\ EXTMODULE_RCC_AHB1Periph\ ) #define RCC_AHB3PeriphMinimum (SDRAM_RCC_AHB3Periph) diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h index a24c6fef9..502856879 100644 --- a/radio/src/targets/nv14/libopenui_config.h +++ b/radio/src/targets/nv14/libopenui_config.h @@ -18,19 +18,34 @@ * GNU General Public License for more details. */ -#ifndef _LIBOPENUI_CONFIG_H_ -#define _LIBOPENUI_CONFIG_H_ +#pragma once #include "debug.h" #include "libopenui_defines.h" + #include "colors.h" #include "board.h" #include "keys.h" +enum FontIndex +{ + FONT_STD_INDEX, +#if !defined(BOLD) + FONT_BOLD_INDEX, + FONT_XXS_INDEX, + FONT_XS_INDEX, + FONT_L_INDEX, + FONT_XL_INDEX, + FONT_XXL_INDEX, +#endif + + FONTS_COUNT +}; + typedef uint16_t pixel_t; constexpr bool WRAP_FORM_FIELDS_WITHIN_PAGE = true; -constexpr uint32_t SLIDE_SPEED_REDUCTION = 5; +constexpr short SLIDE_SPEED_REDUCTION = 5; constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 60; constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = MENU_HEADER_BUTTON_WIDTH; @@ -132,19 +147,6 @@ constexpr uint32_t PAGE_TITLE_LEFT = 70; TABLE_BGCOLOR_INDEX, };*/ -enum FontIndex -{ - FONT_STD_INDEX, -#if !defined(BOLD) - FONT_BOLD_INDEX, - FONT_XXS_INDEX, - FONT_XS_INDEX, - FONT_L_INDEX, - FONT_XL_INDEX, - FONT_XXL_INDEX, -#endif - FONTS_COUNT -}; constexpr uint32_t INPUT_EDIT_LABELS_WIDTH = 80; constexpr coord_t INPUT_EDIT_CURVE_WIDTH = LCD_W; @@ -175,4 +177,4 @@ constexpr LcdFlags TABLE_BODY_FONT = FONT(STD); constexpr int CJK_FIRST_LETTER_INDEX = 128 - 32 + 21; constexpr coord_t CHAR_SPACING = 0; -#endif // _LIBOPENUI_CONFIG_H_ +#define ROTARY_ENCODER_SPEED() rotencSpeed From 88cbc5378635d098e30a0b8b0be7f77875f11c53 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 30 Jun 2021 17:18:14 +0800 Subject: [PATCH 11/45] Stick loop should be called per 1ms. --- radio/src/targets/nv14/board.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 8608806ea..67c1301b9 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -83,12 +83,11 @@ void interrupt1ms() DEBUG_TIMER_STOP(debugTimerHaptic); } #endif - - if (pre_scale == 10) { - pre_scale = 0; #if !defined(SIMU) if (boardState == BOARD_STARTED) flysky_hall_stick_loop(); #endif + if (pre_scale == 10) { + pre_scale = 0; DEBUG_TIMER_START(debugTimerPer10ms); DEBUG_TIMER_SAMPLE(debugTimerPer10msPeriod); per10ms(); From 3a9571eeb846a456e574f3200d40ee0d508f5f38 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 5 Jul 2021 16:55:34 +0800 Subject: [PATCH 12/45] Fixed SWB/SWD problem. --- radio/src/targets/nv14/adc_driver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/nv14/adc_driver.cpp b/radio/src/targets/nv14/adc_driver.cpp index be68f6559..8d414cc6c 100644 --- a/radio/src/targets/nv14/adc_driver.cpp +++ b/radio/src/targets/nv14/adc_driver.cpp @@ -71,6 +71,7 @@ #elif defined(PCBNV14) && defined(FLYSKY_HALL_STICKS) #define FIRST_ANALOG_ADC 4 #define NUM_MAIN_ANALOGS_ADC 9 + #define NUM_SUB_ANALOGS_ADC 2 #else #define FIRST_ANALOG_ADC 0 #define FIRST_SUB_ANALOG_ADC 0 @@ -173,7 +174,7 @@ void adcInit() ADC_MAIN_DMA_Stream->NDTR = NUM_MAIN_ANALOGS_ADC; ADC_MAIN_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; -#if NUM_SUB_ANALOGS > 0 +#if NUM_SUB_ANALOGS_ADC > 0 ADC_SUB->SMPR1 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24); ADC_SUB->SMPR2 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24) + (ADC_SAMPTIME<<27); From 2a55a286ed9122fbbd507469f25d31afae07a38e Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 5 Jul 2021 18:51:16 +0800 Subject: [PATCH 13/45] Fixed stick problem, the 1ms timer in nv14 target not working properly. --- radio/src/targets/nv14/CMakeLists.txt | 3 +- radio/src/targets/nv14/board.cpp | 71 ++------------------------- radio/src/targets/nv14/hal.h | 5 -- 3 files changed, 6 insertions(+), 73 deletions(-) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 10c476d1b..50aa59734 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -44,7 +44,9 @@ add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME set(TARGET_SRC ${TARGET_SRC} audio_spi_driver.cpp + adc_driver.cpp ../common/arm/stm32/mixer_scheduler_driver.cpp + ../common/arm/stm32/timers_driver.cpp ) #set(FIRMWARE_TARGET_SRC @@ -54,7 +56,6 @@ set(TARGET_SRC set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} - adc_driver.cpp battery_driver.cpp ../horus/flyskyHallStick_driver.cpp ) diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 67c1301b9..15fc3ab5a 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -30,7 +30,6 @@ extern "C" { #endif extern void flysky_hall_stick_init( void ); -extern void flysky_hall_stick_loop( void ); HardwareOptions hardwareOptions; @@ -44,68 +43,6 @@ void watchdogInit(unsigned int duration) IWDG->KR = 0xCCCC; // start } -// Start TIMER7 at 2000000Hz -void init2MhzTimer() -{ - TIMER_2MHz_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 2000000 - 1; // 0.5 uS, 2 MHz - TIMER_2MHz_TIMER->ARR = 65535; - TIMER_2MHz_TIMER->CR2 = 0; - TIMER_2MHz_TIMER->CR1 = TIM_CR1_CEN; -} - -// Starts TIMER at 1000Hz -void init1msTimer() -{ - INTERRUPT_xMS_TIMER->ARR = 999; // 1mS in uS - INTERRUPT_xMS_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS - INTERRUPT_xMS_TIMER->CCER = 0; - INTERRUPT_xMS_TIMER->CCMR1 = 0; - INTERRUPT_xMS_TIMER->EGR = 0; - INTERRUPT_xMS_TIMER->CR1 = 5; - INTERRUPT_xMS_TIMER->DIER |= 1; - NVIC_EnableIRQ(INTERRUPT_xMS_IRQn); - NVIC_SetPriority(INTERRUPT_xMS_IRQn, 7); -} -void interrupt1ms() -{ - static uint8_t pre_scale; // Used to get 10 Hz counter - - ++pre_scale; - -#if defined(DEBUG) && !defined(SIMU) - debugCounter1ms++; -#endif - -#if defined(HAPTIC) && !defined(BOOT) - if (pre_scale == 5 || pre_scale == 10) { - DEBUG_TIMER_START(debugTimerHaptic); - HAPTIC_HEARTBEAT(); - DEBUG_TIMER_STOP(debugTimerHaptic); - } -#endif -#if !defined(SIMU) - if (boardState == BOARD_STARTED) flysky_hall_stick_loop(); -#endif - if (pre_scale == 10) { - pre_scale = 0; - DEBUG_TIMER_START(debugTimerPer10ms); - DEBUG_TIMER_SAMPLE(debugTimerPer10msPeriod); - per10ms(); - DEBUG_TIMER_STOP(debugTimerPer10ms); - } - - DEBUG_TIMER_START(debugTimerRotEnc); - DEBUG_TIMER_STOP(debugTimerRotEnc); -} -#if !defined(BOOT) -extern "C" void INTERRUPT_xMS_IRQHandler() -{ - INTERRUPT_xMS_TIMER->SR &= ~TIM_SR_UIF; - interrupt1ms(); - DEBUG_INTERRUPT(INT_1MS); -} -#endif - #if defined(SEMIHOSTING) extern "C" void initialise_monitor_handles(); #endif @@ -187,8 +124,11 @@ void boardInit() pwrInit(); extModuleInit(); battery_charge_init(); +#if defined(FLYSKY_HALL_STICKS) + flysky_hall_stick_init(); +#endif init2MhzTimer(); - init1msTimer(); + init5msTimer(); TouchInit(); uint32_t press_start = 0; @@ -230,9 +170,6 @@ void boardInit() adcInit(); backlightInit(); lcdInit(); -#if defined(FLYSKY_HALL_STICKS) - flysky_hall_stick_init(); -#endif usbInit(); hapticInit(); boardState = BOARD_STARTED; diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index eba39e872..4e4a73d73 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -365,10 +365,6 @@ // Flysky Hall Stick #if defined(FLYSKY_HALL_STICKS) - #if defined (RADIO_TX16S) - #define FLYSKY_HALL_STICKS_REVERSE - #endif - #define FLYSKY_HALL_SERIAL_USART UART4 #define FLYSKY_HALL_SERIAL_GPIO GPIOA #define FLYSKY_HALL_DMA_Channel DMA_Channel_4 @@ -391,7 +387,6 @@ #define FLYSKY_HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler #define FLYSKY_HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler - #endif // Internal Module From 1d81db01133255d78483422c028510067d9a5c64 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 7 Jul 2021 10:39:16 +0800 Subject: [PATCH 14/45] Updated timer driver to use 1ms loop to compatible with nv14. --- .../common/arm/stm32/timers_driver.cpp | 33 +++++++++++-------- radio/src/targets/horus/board.cpp | 2 +- radio/src/targets/horus/board.h | 2 +- radio/src/targets/nv14/board.cpp | 2 +- radio/src/targets/nv14/board.h | 2 +- radio/src/targets/taranis/board.cpp | 2 +- radio/src/targets/taranis/board.h | 2 +- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/timers_driver.cpp b/radio/src/targets/common/arm/stm32/timers_driver.cpp index b44d0b051..7ac4881e3 100644 --- a/radio/src/targets/common/arm/stm32/timers_driver.cpp +++ b/radio/src/targets/common/arm/stm32/timers_driver.cpp @@ -31,10 +31,10 @@ void init2MhzTimer() TIMER_2MHz_TIMER->CR1 = TIM_CR1_CEN; } -// Start TIMER at 200Hz -void init5msTimer() +// Start TIMER at 1000Hz +void init1msTimer() { - INTERRUPT_xMS_TIMER->ARR = 4999; // 5mS in uS + INTERRUPT_xMS_TIMER->ARR = 999; // 1mS in uS INTERRUPT_xMS_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS INTERRUPT_xMS_TIMER->CCER = 0; INTERRUPT_xMS_TIMER->CCMR1 = 0; @@ -45,30 +45,37 @@ void init5msTimer() NVIC_SetPriority(INTERRUPT_xMS_IRQn, 7); } -void stop5msTimer() +void stop1msTimer() { INTERRUPT_xMS_TIMER->CR1 = 0; // stop timer NVIC_DisableIRQ(INTERRUPT_xMS_IRQn); } // TODO use the same than board_sky9x.cpp -void interrupt5ms() +void interrupt1ms() { static uint8_t pre_scale; // Used to get 10 Hz counter ++pre_scale; -#if defined(HAPTIC) - DEBUG_TIMER_START(debugTimerHaptic); - HAPTIC_HEARTBEAT(); - DEBUG_TIMER_STOP(debugTimerHaptic); -#endif - + // 1 ms loop #if defined(FLYSKY_HALL_STICKS) flysky_hall_stick_loop(); // TODO: need to put in 1ms timer loop for best performance #endif - if (pre_scale == 2) { + // 5ms loop + if (pre_scale == 5 || pre_scale == 10) + { +#if defined(HAPTIC) + DEBUG_TIMER_START(debugTimerHaptic); + HAPTIC_HEARTBEAT(); + DEBUG_TIMER_STOP(debugTimerHaptic); +#endif + } + + // 10ms loop + if (pre_scale == 10) + { pre_scale = 0; DEBUG_TIMER_START(debugTimerPer10ms); DEBUG_TIMER_SAMPLE(debugTimerPer10msPeriod); @@ -80,6 +87,6 @@ void interrupt5ms() extern "C" void INTERRUPT_xMS_IRQHandler() { INTERRUPT_xMS_TIMER->SR &= ~TIM_SR_UIF; - interrupt5ms(); + interrupt1ms(); DEBUG_INTERRUPT(INT_1MS); } diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index f5a9d0978..f24f8f1bf 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -206,7 +206,7 @@ void boardInit() #endif init2MhzTimer(); - init5msTimer(); + init1msTimer(); usbInit(); hapticInit(); diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index bc1be0d09..504a4d216 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -75,7 +75,7 @@ void boardOff(); // Timers driver void init2MhzTimer(); -void init5msTimer(); +void init1msTimer(); // PCBREV driver enum { diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp index 15fc3ab5a..627b175a2 100644 --- a/radio/src/targets/nv14/board.cpp +++ b/radio/src/targets/nv14/board.cpp @@ -128,7 +128,7 @@ void boardInit() flysky_hall_stick_init(); #endif init2MhzTimer(); - init5msTimer(); + init1msTimer(); TouchInit(); uint32_t press_start = 0; diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index b1901434a..d33bba5a9 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -69,7 +69,7 @@ void boardOff(); // Timers driver void init2MhzTimer(); -void init5msTimer(); +void init1msTimer(); // CPU Unique ID #define LEN_CPU_UID (3*8+2) diff --git a/radio/src/targets/taranis/board.cpp b/radio/src/targets/taranis/board.cpp index 69e36d5e8..03b6c4c84 100644 --- a/radio/src/targets/taranis/board.cpp +++ b/radio/src/targets/taranis/board.cpp @@ -163,7 +163,7 @@ void boardInit() lcdInit(); // delaysInit() must be called before audioInit(); init2MhzTimer(); - init5msTimer(); + init1msTimer(); __enable_irq(); i2cInit(); usbInit(); diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 8e583be73..c01c83046 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -62,7 +62,7 @@ void boardOff(); // Timers driver void init2MhzTimer(); -void init5msTimer(); +void init1msTimer(); // PCBREV driver enum { From cf161368ab0076d1156877cd4e4312482cd96a65 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 7 Jul 2021 11:40:44 +0800 Subject: [PATCH 15/45] Clean up something. --- radio/src/targets/nv14/board.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index d33bba5a9..38efa567f 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -399,20 +399,19 @@ uint32_t pwrPressedDuration();; #define LCD_DEPTH 16 #define LCD_CONTRAST_DEFAULT 20 void lcdInit(); -void lcdNextLayer(); void lcdRefresh(); void lcdCopy(void * dest, void * src); void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format); +void lcdStoreBackupBuffer(); +int lcdRestoreBackupBuffer(); void lcdSetContrast(); void lcdOff(); void lcdOn(); #define lcdSetRefVolt(...) #define lcdRefreshWait(...) -void lcdStoreBackupBuffer(); -int lcdRestoreBackupBuffer(); // Backlight driver void backlightInit(); From 6df1f8bdbe0a51383217142b0083669690184252 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 7 Jul 2021 12:14:21 +0800 Subject: [PATCH 16/45] Fixed bootloader. --- .../src/targets/nv14/bootloader/boot_menu.cpp | 151 ++++++++++++------ 1 file changed, 100 insertions(+), 51 deletions(-) diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp index 907f8d460..ed3b18044 100644 --- a/radio/src/targets/nv14/bootloader/boot_menu.cpp +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -1,12 +1,26 @@ #include "opentx.h" #include "../../common/arm/stm32/bootloader/boot.h" #include "../../common/arm/stm32/bootloader/bin_files.h" + #define SELECTED_COLOR (INVERS | DEFAULT_COLOR) #define DEFAULT_PADDING 28 #define DOUBLE_PADDING 56 #define MESSAGE_TOP (LCD_H - (2*DOUBLE_PADDING)) -//#include "bmp_plug_usb.lbm" -//#include "bmp_usb_plugged.lbm" + +const uint8_t __bmp_plug_usb_rle[] { +#include "bmp_plug_usb.lbm" +}; +RLEBitmap BMP_PLUG_USB(BMP_ARGB4444, __bmp_plug_usb_rle); + +const uint8_t __bmp_usb_plugged_rle[] { +#include "bmp_usb_plugged.lbm" +}; +RLEBitmap BMP_USB_PLUGGED(BMP_ARGB4444, __bmp_usb_plugged_rle); + +const uint8_t __bmp_background_rle[] { +#include "bmp_background.lbm" +}; +RLEBitmap BMP_BACKGROUND(BMP_ARGB4444, __bmp_background_rle); const uint8_t LBM_FLASH[] = { #include "icon_flash.lbm" @@ -32,105 +46,143 @@ const uint8_t LBM_OK[] = { #include "icon_ok.lbm" }; +#define BL_GREEN COLOR2FLAGS(RGB(73, 219, 62)) +#define BL_RED COLOR2FLAGS(RED) +#define BL_BACKGROUND COLOR2FLAGS(BLACK) +#define BL_FOREGROUND COLOR2FLAGS(WHITE) +#define BL_SELECTED COLOR2FLAGS(RGB(11, 65, 244)) // deep blue + void bootloaderInitScreen() { - lcdColorTable[DEFAULT_COLOR_INDEX] = BLACK; - lcdColorTable[DEFAULT_BGCOLOR_INDEX] = WHITE; - lcdColorTable[LINE_COLOR_INDEX] = RED; - lcdColorTable[BARGRAPH1_COLOR_INDEX] = RED; - lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(73, 219, 62); // green +// lcdColorTable[DEFAULT_COLOR_INDEX] = WHITE; +// lcdColorTable[DEFAULT_BGCOLOR_INDEX] = BLACK; +// lcdColorTable[LINE_COLOR_INDEX] = RED; +// lcdColorTable[BARGRAPH1_COLOR_INDEX] = RED; +// lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(73, 219, 62); // green backlightEnable(BACKLIGHT_LEVEL_MAX); + + // TODO: load/decompress bitmaps + extern void loadFonts(); + loadFonts(); + } static void bootloaderDrawTitle(const char* text) { - lcd->drawText(LCD_W/2, DEFAULT_PADDING, text, CENTERED | DEFAULT_COLOR); - lcd->drawSolidFilledRect(DEFAULT_PADDING, DOUBLE_PADDING, LCD_W - DOUBLE_PADDING, 2, DEFAULT_COLOR); + lcd->drawText(LCD_W/2, DEFAULT_PADDING, text, CENTERED | BL_FOREGROUND); + lcd->drawSolidFilledRect(DEFAULT_PADDING, DOUBLE_PADDING, LCD_W - DOUBLE_PADDING, 2, BL_FOREGROUND); } static void bootloaderDrawFooter() { - lcd->drawSolidFilledRect(DEFAULT_PADDING, LCD_H - (DOUBLE_PADDING + 4), LCD_W - DOUBLE_PADDING, 2, DEFAULT_COLOR); + lcd->drawSolidFilledRect(DEFAULT_PADDING, LCD_H - (DOUBLE_PADDING + 4), LCD_W - DOUBLE_PADDING, 2, BL_FOREGROUND); +} + +static void bootloaderDrawBackground() +{ + // we have plenty of memory, let's cache that background + static BitmapBuffer* _background = nullptr; + + if (!_background) { + _background = new BitmapBuffer(BMP_RGB565, LCD_W, LCD_H); + + for (int i=0; idrawBitmap(i, j, &BMP_BACKGROUND); + } + } + _background->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, + COLOR2FLAGS(BLACK), OPACITY(4)); + } + + if (_background) { + lcd->drawBitmap(0, 0, _background); + } + else { + lcd->clear(BL_BACKGROUND); + } } void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { // clear screen - lcd->drawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, DEFAULT_BGCOLOR); +// bootloaderDrawBackground(); + + lcd->drawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, BL_BACKGROUND); int center = LCD_W/2; if (st == ST_START) { - bootloaderDrawTitle("NV14 BOOTLOADER"); + bootloaderDrawTitle(BOOTLOADER_TITLE); - lcd->drawBitmapPattern(50, 72, LBM_FLASH, DEFAULT_COLOR); - lcd->drawText(84, 75, "Write Firmware"); + lcd->drawBitmapPattern(50, 72, LBM_FLASH, BL_FOREGROUND); + lcd->drawText(84, 75, "Write Firmware", BL_FOREGROUND); - lcd->drawBitmapPattern(50, 107, LBM_EXIT, DEFAULT_COLOR); - lcd->drawText(84, 110, "Exit"); + lcd->drawBitmapPattern(50, 107, LBM_EXIT, BL_FOREGROUND); + lcd->drawText(84, 110, "Exit", BL_FOREGROUND); - lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, LCD_W - 79 - 28, 26, 2, LINE_COLOR); + lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, LCD_W - 79 - 28, 26, 2, BL_SELECTED); - //lcd->drawBitmap(60, 166, &BMP_PLUG_USB); - lcd->drawText(center, 175, "Or plug in a USB cable", CENTERED | DEFAULT_COLOR); - lcd->drawText(center, 200, "for mass storage", CENTERED | DEFAULT_COLOR); +// lcd->drawBitmap(0, 166, &BMP_PLUG_USB); + lcd->drawText(center, 175, "Or plug in a USB cable", CENTERED | BL_FOREGROUND); + lcd->drawText(center, 200, "for mass storage", CENTERED | BL_FOREGROUND); bootloaderDrawFooter(); - lcd->drawText(center, LCD_H - DOUBLE_PADDING, "Current Firmware:", CENTERED | DEFAULT_COLOR); - lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | DEFAULT_COLOR); + lcd->drawText(center, LCD_H - DOUBLE_PADDING, "Current Firmware:", CENTERED | BL_FOREGROUND); + lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); } else if (st == ST_USB) { - //lcd->drawBitmap(136, 98, &BMP_USB_PLUGGED); - lcd->drawText(center, 128, "USB Connected", CENTERED | DEFAULT_COLOR); +// lcd->drawBitmap(0, 98, &BMP_USB_PLUGGED); + lcd->drawText(center, 128, "USB Connected", CENTERED | BL_FOREGROUND); } else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || st == ST_FLASH_CHECK || st == ST_FLASHING || st == ST_FLASH_DONE) { bootloaderDrawTitle("SD>FIRMWARE"); - lcd->drawBitmapPattern(DEFAULT_PADDING, 16, LBM_SD, DEFAULT_COLOR); + lcd->drawBitmapPattern(DEFAULT_PADDING, 16, LBM_SD, BL_FOREGROUND); if (st == ST_FLASHING || st == ST_FLASH_DONE) { - LcdFlags color = BARGRAPH1_COLOR; // red + LcdFlags color = BL_RED; // red if (st == ST_FLASH_DONE) { - color = BARGRAPH2_COLOR/* green */; + color = BL_GREEN/* green */; opt = 100; // Completed > 100% } - lcd->drawRect(DEFAULT_PADDING, 120, LCD_W - DOUBLE_PADDING, 31, 2); + lcd->drawRect(DEFAULT_PADDING, 120, LCD_W - DOUBLE_PADDING, 31, 2, SOLID, BL_SELECTED); lcd->drawSolidFilledRect(DEFAULT_PADDING+4, 124, ((LCD_W - DOUBLE_PADDING - 8) * opt) / 100, 23, color); } else if (st == ST_DIR_CHECK) { if (opt == FR_NO_PATH) { - lcd->drawText(20, MESSAGE_TOP, "Directory is missing"); + lcd->drawText(20, MESSAGE_TOP, "Directory is missing", BL_FOREGROUND); } else { - lcd->drawText(20, MESSAGE_TOP, "Directory is empty"); + lcd->drawText(20, MESSAGE_TOP, "Directory is empty", BL_FOREGROUND); } - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BARGRAPH1_COLOR); + lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); } else if (st == ST_FLASH_CHECK) { bootloaderDrawFilename(str, 0, true); if (opt == FC_ERROR) { - lcd->drawText(20, MESSAGE_TOP, STR_INVALID_FIRMWARE); - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BARGRAPH1_COLOR); + lcd->drawText(20, MESSAGE_TOP, STR_INVALID_FIRMWARE, BL_FOREGROUND); + lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_ERROR, BL_RED); } else if (opt == FC_OK) { VersionTag tag; extractFirmwareVersion(&tag); - lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP, "Version:", RIGHT); - lcd->drawText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP, tag.version); + lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP, "Version:", RIGHT | BL_FOREGROUND); + lcd->drawText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP, tag.version, BL_FOREGROUND); - lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP + DEFAULT_PADDING, "Radio:", RIGHT); - lcd->drawText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP + DEFAULT_PADDING, tag.flavour); + lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP + DEFAULT_PADDING, "Radio:", RIGHT | BL_FOREGROUND); + lcd->drawText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP + DEFAULT_PADDING, tag.flavour, BL_FOREGROUND); - lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_OK, BARGRAPH2_COLOR); + lcd->drawBitmapPattern(LCD_W - DOUBLE_PADDING, MESSAGE_TOP-10, LBM_OK, BL_GREEN); } } @@ -138,38 +190,35 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) if ( st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { - lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, LBM_FLASH, DEFAULT_COLOR); + lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DOUBLE_PADDING - 2, LBM_FLASH, BL_FOREGROUND); if (st == ST_FILE_LIST) { - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[R TRIM] to select file"); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "[R TRIM] to select file", BL_FOREGROUND); } else if (st == ST_FLASH_CHECK && opt == FC_OK) { - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to flash"); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Hold [R TRIM] long to flash", BL_FOREGROUND); } else if (st == ST_FLASHING) { - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Writing Firmware ..."); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Writing Firmware ...", BL_FOREGROUND); } else if (st == ST_FLASH_DONE) { - lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Writing Completed"); + lcd->drawText(DOUBLE_PADDING, LCD_H - DOUBLE_PADDING, "Writing Completed", BL_FOREGROUND); } } if (st != ST_FLASHING) { - lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING - 2, LBM_EXIT, DEFAULT_COLOR); - lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit"); + lcd->drawBitmapPattern(DEFAULT_PADDING, LCD_H - DEFAULT_PADDING - 2, LBM_EXIT, BL_FOREGROUND); + lcd->drawText(DOUBLE_PADDING, LCD_H - DEFAULT_PADDING, "[L TRIM] to exit", BL_FOREGROUND); } } -#if defined(COLORLCD) - lcdRefresh(); -#endif } void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) { - lcd->drawBitmapPattern(DEFAULT_PADDING, 76 + (line * 25), LBM_FILE, DEFAULT_COLOR); - lcd->drawText(DEFAULT_PADDING + 30, 75 + (line * 25), str); + lcd->drawBitmapPattern(DEFAULT_PADDING, 76 + (line * 25), LBM_FILE, BL_FOREGROUND); + lcd->drawText(DEFAULT_PADDING + 30, 75 + (line * 25), str, BL_FOREGROUND); if (selected) { - lcd->drawSolidRect(DEFAULT_PADDING + 25, 72 + (line * 25), LCD_W - (DEFAULT_PADDING + 25) - 28, 26, 2, LINE_COLOR); + lcd->drawSolidRect(DEFAULT_PADDING + 25, 72 + (line * 25), LCD_W - (DEFAULT_PADDING + 25) - 28, 26, 2, BL_SELECTED); } } From e9bb2122ad5a545a9cba9ad96dd4849062807872 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 7 Jul 2021 12:47:38 +0800 Subject: [PATCH 17/45] Fixed lcd driver. --- radio/src/targets/nv14/lcd_driver.cpp | 95 ++++++++++++++++++++------- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/radio/src/targets/nv14/lcd_driver.cpp b/radio/src/targets/nv14/lcd_driver.cpp index 6606a7af7..5f5f89343 100644 --- a/radio/src/targets/nv14/lcd_driver.cpp +++ b/radio/src/targets/nv14/lcd_driver.cpp @@ -19,12 +19,21 @@ */ #include "opentx.h" -#define LTDC_DOUBLELAYER + +#define LCD_FIRST_LAYER 0 +#define LCD_SECOND_LAYER 1 + uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; uint8_t LCD_SCRATCH_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(pixel_t)] __SDRAM; -bool secondFrame = false; +uint8_t currentLayer = LCD_FIRST_LAYER; + +BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); +BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); + +BitmapBuffer * lcdFront = &lcdBuffer1; +BitmapBuffer * lcd = &lcdBuffer2; lcdSpiInitFucPtr lcdInitFunction; lcdSpiInitFucPtr lcdOffFunction; @@ -1287,14 +1296,21 @@ void LCD_LayerInit() { LTDC_DitherCmd(ENABLE); } -BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, - (uint16_t *) LCD_FIRST_FRAME_BUFFER); -BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, - (uint16_t *) LCD_SECOND_FRAME_BUFFER); -BitmapBuffer * lcdFront = &lcdBuffer1; -BitmapBuffer * lcd = &lcdBuffer2; - extern void loadFonts(); + +void LCD_SetLayer(uint32_t layer) +{ + if (layer == LCD_FIRST_LAYER) { + lcdFront = &lcdBuffer1; + lcd = &lcdBuffer2; + } + else { + lcdFront = &lcdBuffer2; + lcd = &lcdBuffer1; + } + currentLayer = layer; +} + void lcdInit(void) { // Clear buffers first memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); @@ -1367,6 +1383,10 @@ void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, ui /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1400,6 +1420,10 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1442,6 +1466,10 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1489,6 +1517,10 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1522,6 +1554,10 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1555,6 +1591,10 @@ void lcdCopy(void * dest, void * src) /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -1580,27 +1620,36 @@ uint16_t* lcdGetScratchBuffer() return (uint16_t*)LCD_SCRATCH_FRAME_BUFFER; } -static volatile uint8_t refreshRequested = 0; +//static volatile uint8_t refreshRequested = 0; +static volatile uint8_t _frameBufferAddressReloaded = 0; extern "C" void LTDC_IRQHandler(void) { LTDC_ClearFlag(LTDC_ICR_CLIF); - if (refreshRequested) { -#if defined(LTDC_DOUBLELAYER) - LTDC_LayerAlpha(LTDC_Layer1, (lcd == &lcdBuffer2) ? 255 : 0); - LTDC_LayerAlpha(LTDC_Layer2, (lcd == &lcdBuffer1) ? 255 : 0); -#else - LTDC_Layer1->CFBAR = (uint32_t)((lcd == &lcdBuffer2) ? &lcdBuffer1 : &lcdBuffer2)->getData(); -#endif - LTDC_ReloadConfig(LTDC_IMReload); - refreshRequested = false; + _frameBufferAddressReloaded = 1; +} + +static void lcdSwitchLayers() +{ + if (currentLayer == LCD_FIRST_LAYER) { + LTDC_Layer1->CFBAR = (uint32_t)LCD_SECOND_FRAME_BUFFER; + LCD_SetLayer(LCD_SECOND_LAYER); } + else { + LTDC_Layer1->CFBAR = (uint32_t)LCD_FIRST_FRAME_BUFFER; + LCD_SetLayer(LCD_FIRST_LAYER); + } + + // reload shadow registers on vertical blank + _frameBufferAddressReloaded = 0; + LTDC->SRCR = LTDC_SRCR_VBR; + + // wait for reload + // TODO: replace through some smarter mechanism without busy wait + while(_frameBufferAddressReloaded == 0); } void lcdRefresh() { - while(refreshRequested) {} //block if last frame was not painted yet - lcd = (lcd == &lcdBuffer2) ? &lcdBuffer1 : &lcdBuffer2; - refreshRequested = true; + lcdSwitchLayers(); } - From e2842550eb9cb622b80bbfef3b68612edca819ae Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 7 Jul 2021 13:25:00 +0800 Subject: [PATCH 18/45] Finalized boot loader for NV14. --- radio/src/targets/nv14/bootloader/boot_menu.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp index ed3b18044..d6e521850 100644 --- a/radio/src/targets/nv14/bootloader/boot_menu.cpp +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -107,9 +107,8 @@ static void bootloaderDrawBackground() void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { // clear screen -// bootloaderDrawBackground(); + bootloaderDrawBackground(); - lcd->drawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, BL_BACKGROUND); int center = LCD_W/2; if (st == ST_START) { @@ -123,17 +122,17 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) lcd->drawSolidRect(79, (opt == 0) ? 72 : 107, LCD_W - 79 - 28, 26, 2, BL_SELECTED); -// lcd->drawBitmap(0, 166, &BMP_PLUG_USB); - lcd->drawText(center, 175, "Or plug in a USB cable", CENTERED | BL_FOREGROUND); - lcd->drawText(center, 200, "for mass storage", CENTERED | BL_FOREGROUND); + lcd->drawBitmap(center - 55, 165, &BMP_PLUG_USB); + lcd->drawText(center, 250, "Or plug in a USB cable", CENTERED | BL_FOREGROUND); + lcd->drawText(center, 275, "for mass storage", CENTERED | BL_FOREGROUND); bootloaderDrawFooter(); lcd->drawText(center, LCD_H - DOUBLE_PADDING, "Current Firmware:", CENTERED | BL_FOREGROUND); lcd->drawText(center, LCD_H - DEFAULT_PADDING, getFirmwareVersion(nullptr), CENTERED | BL_FOREGROUND); } else if (st == ST_USB) { -// lcd->drawBitmap(0, 98, &BMP_USB_PLUGGED); - lcd->drawText(center, 128, "USB Connected", CENTERED | BL_FOREGROUND); + lcd->drawBitmap(center - 26, 98, &BMP_USB_PLUGGED); + lcd->drawText(center, 168, "USB Connected", CENTERED | BL_FOREGROUND); } else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || st == ST_FLASH_CHECK || st == ST_FLASHING || st == ST_FLASH_DONE) { @@ -174,8 +173,12 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) } else if (opt == FC_OK) { VersionTag tag; + memset(&tag, 0, sizeof(tag)); extractFirmwareVersion(&tag); + lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP - DEFAULT_PADDING, "Fork:", RIGHT | BL_FOREGROUND); + lcd->drawSizedText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP - DEFAULT_PADDING, tag.fork, 6, BL_FOREGROUND); + lcd->drawText(LCD_W/4 + DEFAULT_PADDING, MESSAGE_TOP, "Version:", RIGHT | BL_FOREGROUND); lcd->drawText(LCD_W/4 + 6 + DEFAULT_PADDING, MESSAGE_TOP, tag.version, BL_FOREGROUND); From e54f2f4c0a6ed6a6b34ad99547ce7127d7fe2290 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 14 Jul 2021 15:14:29 +0800 Subject: [PATCH 19/45] Fixed build problems after merging from 2.4 --- radio/src/gui/colorlcd/radio_diaganas.cpp | 2 +- radio/src/gui/colorlcd/view_text.cpp | 11 +++++++++-- radio/src/targets/nv14/libopenui_config.h | 1 - tools/build-gh.sh | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio_diaganas.cpp index 939b113b2..9539bfb36 100644 --- a/radio/src/gui/colorlcd/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio_diaganas.cpp @@ -59,7 +59,7 @@ class RadioAnalogsDiagsWindow: public Window { dc->drawLine(touchState.x - 10, touchState.y - 8 - parent->top(), touchState.x + 10, touchState.y + 8 - parent->top(), SOLID, 0); dc->drawLine(touchState.x - 10, touchState.y + 8 - parent->top(), touchState.x + 10, touchState.y - 8- parent->top(), SOLID, 0); } -#if !defined(SIMU) +#if !defined(SIMU) && !defined(PCBNV14) constexpr coord_t y1 = MENU_CONTENT_TOP + 6 * FH; coord_t x1 = MENUS_MARGIN_LEFT; x1 = dc->drawText(x1, y1, "Touch GT911 FW ver:") + 8; diff --git a/radio/src/gui/colorlcd/view_text.cpp b/radio/src/gui/colorlcd/view_text.cpp index c3381508d..af2536453 100644 --- a/radio/src/gui/colorlcd/view_text.cpp +++ b/radio/src/gui/colorlcd/view_text.cpp @@ -96,12 +96,15 @@ void ViewTextWindow::checkEvents() coord_t deltaY; event_t event = getWindowEvent(); +#if defined(ROTARY_ENCODER_NAVIGATION) if (event == EVT_ROTARY_LEFT || event == EVT_ROTARY_RIGHT) { deltaY = ROTARY_ENCODER_SPEED() * step; } else { deltaY = step; } - +#else + deltaY = step; +#endif switch (event) { CASE_EVT_KEY_NEXT_LINE: currentPos += deltaY; @@ -259,12 +262,16 @@ void ViewTextWindow::checkEvents() coord_t deltaY; event_t event = getWindowEvent(); +#if defined(ROTARY_ENCODER_NAVIGATION) if (event == EVT_ROTARY_LEFT || event == EVT_ROTARY_RIGHT) { deltaY = ROTARY_ENCODER_SPEED() * step; } else { deltaY = step; } - +#else + deltaY = step; +#endif + int lineStep = deltaY / step; if(lineStep > (maxScreenLines >> 1)) lineStep = maxScreenLines >> 1; /* diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h index 502856879..0a00fe51b 100644 --- a/radio/src/targets/nv14/libopenui_config.h +++ b/radio/src/targets/nv14/libopenui_config.h @@ -177,4 +177,3 @@ constexpr LcdFlags TABLE_BODY_FONT = FONT(STD); constexpr int CJK_FIRST_LETTER_INDEX = 128 - 32 + 21; constexpr coord_t CHAR_SPACING = 0; -#define ROTARY_ENCODER_SPEED() rotencSpeed diff --git a/tools/build-gh.sh b/tools/build-gh.sh index 457742b97..78e71f6f2 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -122,7 +122,7 @@ case $FLAVOR in BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=T16 -DINTERNAL_MODULE_MULTI=ON" ;; T16_FS) - BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=T16 -DFLYSKY_HALL_STICKS=ON" + BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=T16 -DINTERNAL_MODULE_MULTI=ON -DFLYSKY_HALL_STICKS=ON" ;; T18) BUILD_OPTIONS+="-DPCB=X10 -DPCBREV=T18" From 078f9d1338978bdf4fac0ba3b91cde0caf3bf9ae Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 22 Jul 2021 12:41:05 +0800 Subject: [PATCH 20/45] Fixed trainer mode selection problem for NV14. --- radio/src/dataconstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 86070e89b..3a0c85554 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -183,7 +183,7 @@ enum TrainerMode { #endif TRAINER_MODE_MASTER_TRAINER_JACK, TRAINER_MODE_SLAVE, -#if defined(PCBTARANIS) +#if defined(PCBTARANIS) || defined(PCBNV14) TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE, TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE, #endif @@ -210,7 +210,7 @@ enum TrainerMode { #define TRAINER_MODE_MAX() TRAINER_MODE_MULTI #elif defined(BLUETOOTH) #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE_BLUETOOTH -#elif defined(PCBX7) || defined(PCBXLITE) +#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBNV14) #define TRAINER_MODE_MAX() TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE #elif defined(PCBTARANIS) #define TRAINER_MODE_MAX() (IS_TRAINER_AUX_SERIAL() ? TRAINER_MODE_MASTER_BATTERY_COMPARTMENT : TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE) From 383985f73c87c13100f3f119f8c57d3d640dda3f Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 22 Jul 2021 12:43:57 +0800 Subject: [PATCH 21/45] Try to fix internal module. --- radio/src/targets/nv14/CMakeLists.txt | 18 +++++--- radio/src/targets/nv14/hal.h | 59 ++++++++++++++++++--------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 50aa59734..26c646dfe 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -4,6 +4,8 @@ option(FLYSKY_HALL_STICKS "sticks for FlySky hall sticks" ON) option(FLYSKY_HALL_STICKS_REVERSE "Reverse sticks for FlySky hall sticks" OFF) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) option(AFHDS2 "Support for AFHDS2" ON) +option(PXX1 "PXX1 protocol support" ON) +option(PXX2 "PXX2 protocol support" OFF) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F4) @@ -14,8 +16,8 @@ set(STORAGE_FORMAT BIN) set(HAPTIC YES) set(GUI_DIR colorlcd) set(BITMAPS_DIR 480x272) -set(HARDWARE_EXTERNAL_MODULE YES) -set(AUX_SERIAL ON) +#set(HARDWARE_EXTERNAL_MODULE YES) +#set(AUX_SERIAL ON) # set(NAVIGATION_TYPE touch) set(TARGET_DIR nv14) add_definitions(-DHARDWARE_TRAINER_JACK) @@ -38,7 +40,8 @@ set(PCB_RF_BAUD 921600 CACHE STRING "INTERNAL_MODULE_BAUDRATE: ${RF_BAUD_RATE}") set_property(CACHE PCB_RF_BAUD PROPERTY STRINGS ${RF_BAUD_RATE}) set(FLAVOUR nv14) -add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) +#add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) +add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_SERIAL -DAFHDS2_BAUDRATE=${PCB_RF_BAUD}) add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME set(TARGET_SRC @@ -79,8 +82,9 @@ add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) add_definitions(-DCROSSFIRE_NATIVE) add_definitions(-DFLYSKY_HALL_STICKS_REVERSE) add_definitions(-DFLYSKY_HALL_STICKS) +add_definitions(-DAFHDS2) add_definitions(-DHARDWARE_EXTERNAL_MODULE) -add_definitions(-DAUX_SERIAL) +#add_definitions(-DAUX_SERIAL) #add_definitions(-DFLYSKY_AUTO_POWER_DOWN) if(STICKS_DEAD_ZONE) @@ -105,7 +109,7 @@ if(DISK_CACHE) add_definitions(-DDISK_CACHE) endif() -set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) +#set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) set(SRC ${SRC} @@ -116,7 +120,9 @@ set(GVAR_SCREEN model_gvars.cpp) set(TARGET_SRC ${TARGET_SRC} - extmodule_driver.cpp +# extmodule_driver.cpp + extmodule_helper.cpp + ../horus/extmodule_driver.cpp trainer_driver.cpp ../common/arm/stm32/intmodule_serial_driver.cpp ) diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index 4e4a73d73..e610ad797 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -394,9 +394,10 @@ #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) #define INTMODULE_PWR_GPIO GPIOH #define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 -#define INTMODULE_TX_GPIO GPIOF +#define INTMODULE_GPIO GPIOF +//#define INTMODULE_TX_GPIO GPIOF #define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 -#define INTMODULE_RX_GPIO GPIOF +//#define INTMODULE_RX_GPIO GPIOF #define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 #define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 #define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 @@ -404,28 +405,28 @@ #define INTMODULE_GPIO_AF GPIO_AF_UART7 #define INTMODULE_USART_IRQn UART7_IRQn #define INTMODULE_USART_IRQHandler UART7_IRQHandler -#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 +#define INTMODULE_DMA_STREAM DMA1_Stream1 +#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn +#define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream7_IRQHandler +#define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 +#define INTMODULE_DMA_CHANNEL DMA_Channel_5 + +/*#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 #define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn #define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream3_IRQHandler #define INTMODULE_TX_DMA_STREAM DMA1_Stream1 #define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn #define INTMODULE_TX_DMA_Stream_IRQHandler DMA1_Stream1_IRQHandler -#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1 -//TX new implementation -#define INTMODULE_DMA_STREAM DMA1_Stream1 -#define INTMODULE_DMA_CHANNEL DMA_Channel_5 -#define INTMODULE_GPIO INTMODULE_TX_GPIO -//#define INTMODULE_DMA_STREAM_IRQ INTMODULE_RX_DMA_Stream_IRQHandler -#define INTMODULE_DMA_STREAM_IRQ INTMODULE_TX_DMA_Stream_IRQn - -#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) +#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1*/ +#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7 | RCC_APB1Periph_TIM3) #define INTMODULE_RCC_APB2Periph 0 #define INTMODULE_TIMER TIM3 #define INTMODULE_TIMER_IRQn TIM3_IRQn #define INTMODULE_TIMER_IRQHandler TIM3_IRQHandler #define INTMODULE_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + // External Module #define EXTMODULE #define EXTMODULE_PULSES @@ -449,9 +450,14 @@ #define EXTMODULE_RX_GPIO_AF_USART GPIO_AF_USART6 #define EXTMODULE_TIMER TIM8 #define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_CC_IRQn TIM8_CC_IRQn #define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler #define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) +#define EXTMODULE_TIMER_TX_GPIO_AF GPIO_AF_TIM8 //USART +#define EXTMODULE_USART_GPIO GPIOC +#define EXTMODULE_USART_GPIO_AF GPIO_AF_USART6 + #define EXTMODULE_USART_TX_DMA_CHANNEL DMA_Channel_5 #define EXTMODULE_USART_TX_DMA_STREAM DMA2_Stream7 #define EXTMODULE_USART_TX_DMA_IRQn DMA2_Stream7_IRQn @@ -460,26 +466,39 @@ #define EXTMODULE_USART_RX_DMA_CHANNEL DMA_Channel_5 #define EXTMODULE_USART_RX_DMA_STREAM DMA2_Stream2 -#define EXTMODULE_USART_RX_DMA_IRQn DMA2_Stream5_IRQn -#define EXTMODULE_USART_RX_DMA_IRQHandler DMA2_Stream5_IRQHandler -#define EXTMODULE_USART_RX_DMA_FLAG_TC DMA_IT_TCIF5 +#define EXTMODULE_USART_RX_DMA_IRQn DMA2_Stream2_IRQn +#define EXTMODULE_USART_RX_DMA_IRQHandler DMA2_Stream2_IRQHandler +#define EXTMODULE_USART_RX_DMA_FLAG_TC DMA_IT_TCIF2 #define EXTMODULE_USART_IRQHandler USART6_IRQHandler #define EXTMODULE_USART_IRQn USART6_IRQn +#define EXTMODULE_TIMER_DMA_SIZE (DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0) //TIMER #define EXTMODULE_DMA_CHANNEL DMA_Channel_7 #define EXTMODULE_DMA_STREAM DMA2_Stream1 #define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn #define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler #define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 -#define EXTMODULE_USART USART6 -#define EXTMODULE_TX_INVERT_GPIO GPIOE -#define EXTMODULE_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 -#define EXTMODULE_RX_INVERT_GPIO GPIOI -#define EXTMODULE_RX_INVERT_GPIO_PIN GPIO_Pin_15 // PI.15 +#define EXTMODULE_TIMER_DMA_CHANNEL DMA_Channel_7 +#define EXTMODULE_TIMER_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream1_IRQHandler +#define EXTMODULE_TIMER_DMA_FLAG_TC DMA_IT_TCIF1 +//#define EXTMODULE_USART USART6 + +#define EXTMODULE_TX_INVERT_GPIO GPIOE +#define EXTMODULE_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 +#define EXTMODULE_RX_INVERT_GPIO GPIOI +#define EXTMODULE_RX_INVERT_GPIO_PIN GPIO_Pin_15 // PI.15 + + +#define EXTMODULE_TX_NORMAL() EXTMODULE_TX_INVERT_GPIO->BSRRH = EXTMODULE_TX_INVERT_GPIO_PIN +#define EXTMODULE_TX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_TX_INVERT_GPIO_PIN +#define EXTMODULE_RX_NORMAL() EXTMODULE_TX_INVERT_GPIO->BSRRH = EXTMODULE_RX_INVERT_GPIO_PIN +#define EXTMODULE_RX_INVERTED() EXTMODULE_TX_INVERT_GPIO->BSRRL = EXTMODULE_RX_INVERT_GPIO_PIN // Heartbeat (not used) #define HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD From 31b819c83b437041da01d6fb8ac69a5870793831 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 22 Jul 2021 12:44:26 +0800 Subject: [PATCH 22/45] Try to fix external module. --- radio/src/targets/nv14/extmodule_helper.cpp | 63 +++++++++++++++++++++ radio/src/targets/nv14/telemetry_driver.cpp | 4 +- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 radio/src/targets/nv14/extmodule_helper.cpp diff --git a/radio/src/targets/nv14/extmodule_helper.cpp b/radio/src/targets/nv14/extmodule_helper.cpp new file mode 100644 index 000000000..49c2d7d0d --- /dev/null +++ b/radio/src/targets/nv14/extmodule_helper.cpp @@ -0,0 +1,63 @@ +/* + * 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 "opentx.h" + +void EXTERNAL_MODULE_ON() +{ + GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_ResetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); +} + +void EXTERNAL_MODULE_OFF() +{ + GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); +} + +void extModuleInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); + + //for additional transistor to ensuring module is completely disabled + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + //pin must be pulled to V+ (voltage of board - VCC is not enough to fully close transistor) + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_FIX_GPIO_PIN; + GPIO_SetBits(EXTMODULE_PWR_FIX_GPIO, EXTMODULE_PWR_FIX_GPIO_PIN); + GPIO_Init(EXTMODULE_PWR_FIX_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_INVERT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(EXTMODULE_TX_INVERT_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = EXTMODULE_RX_INVERT_GPIO_PIN; + GPIO_Init(EXTMODULE_RX_INVERT_GPIO, &GPIO_InitStructure); + + GPIO_ResetBits(EXTMODULE_TX_INVERT_GPIO, EXTMODULE_TX_INVERT_GPIO_PIN); + GPIO_ResetBits(EXTMODULE_RX_INVERT_GPIO, EXTMODULE_RX_INVERT_GPIO_PIN); +} diff --git a/radio/src/targets/nv14/telemetry_driver.cpp b/radio/src/targets/nv14/telemetry_driver.cpp index 2e440380c..b8cce7e8b 100644 --- a/radio/src/targets/nv14/telemetry_driver.cpp +++ b/radio/src/targets/nv14/telemetry_driver.cpp @@ -94,10 +94,10 @@ void telemetryPortInvertedInit(uint32_t baudrate) #warning "TODO telemetryPortInvertedInit"; } -void extmoduleSendInvertedByte(uint8_t byte) +/*void extmoduleSendInvertedByte(uint8_t byte) { #warning "TODO extmoduleSendInvertedByte"; -} +}*/ void telemetryPortSetDirectionOutput() { From 3a754126caf061b2a2aeda6e063aadcbedc0845f Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 22 Jul 2021 12:46:11 +0800 Subject: [PATCH 23/45] Try to fix external module. --- radio/src/targets/horus/extmodule_driver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 878e3ffea..9565e900c 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -167,7 +167,8 @@ void extmoduleSerialStart() EXTMODULE_TIMER->EGR = 1; // Restart EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0; #else - EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (inverted ? 0 : TIM_CCER_CC1P); +// EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (inverted ? 0 : TIM_CCER_CC1P); + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; // Inverted should be false here? EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs EXTMODULE_TIMER->CCR1 = 0; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high From 78a7e507f35a16355c44654aa55ce9cbd776a97a Mon Sep 17 00:00:00 2001 From: Richard Li Date: Thu, 22 Jul 2021 13:14:53 +0800 Subject: [PATCH 24/45] Fixed wrong target directory for NV14 for datacopy --- radio/src/targets/nv14/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 26c646dfe..0f96ee6e1 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -201,7 +201,7 @@ if(PYTHONINTERP_FOUND) add_custom_command( OUTPUT ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp WORKING_DIRECTORY ${RADIO_DIRECTORY}/src - COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs.h -DPCBFLYSKY -DPCBNV14 -DCPUARM -DSTM32F4 -DSTM32F429_439xx -DCOLORLCD -DBACKUP -DSIMU -I. -Igui/colorlcd -Itargets/horus -Itargets/common/arm/stm32 -I${THIRDPARTY_DIR} -I${THIRDPARTY_DIR}/libopenui/src -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/STM32F4xx_StdPeriph_Driver/inc -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Include ${arm_gcc_include_path} -Wno-pragma-once-outside-header > ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp + COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs.h -DPCBFLYSKY -DPCBNV14 -DCPUARM -DSTM32F4 -DSTM32F429_439xx -DCOLORLCD -DBACKUP -DSIMU -I. -Igui/colorlcd -Itargets/nv14 -Itargets/common/arm/stm32 -I${THIRDPARTY_DIR} -I${THIRDPARTY_DIR}/libopenui/src -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/STM32F4xx_StdPeriph_Driver/inc -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I${THIRDPARTY_DIR}/STM32F4xx_DSP_StdPeriph_Lib_V1.8.0/Libraries/CMSIS/Include ${arm_gcc_include_path} -Wno-pragma-once-outside-header > ${PROJECT_BINARY_DIR}/radio/src/datacopy.cpp DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py ) From 62458bdb55a6e394c1c0627ea8c065d213df5054 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 23 Jul 2021 14:08:38 +0800 Subject: [PATCH 25/45] Migrated NV14 ADC driver to use common driver. --- .../targets/common/arm/stm32/adc_driver.cpp | 74 +++++++++++++++++-- radio/src/targets/nv14/CMakeLists.txt | 2 +- radio/src/targets/nv14/hal.h | 16 +++- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index 6a1214207..4a70161a6 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -52,6 +52,17 @@ const int8_t adcDirection[NUM_ANALOGS] = {-1,1,-1,1, 1,1, 1}; #elif defined(PCBXLITE) const int8_t adcDirection[NUM_ANALOGS] = {1,-1,-1,1, -1,1, 1, 1}; +#elif defined(PCBNV14) + const int8_t adcDirection[NUM_ANALOGS] = { 0 /*STICK1*/, 0 /*STICK2*/, 0 /*STICK3*/, 0 /*STICK4*/, + 0 /*POT1*/, 0 /*POT2*/, 0 /*SWA*/, 0 /*SWB*/, + 0 /*SWC*/, -1 /*SWD*/, 0 /*SWE*/, -1 /*SWF*/, + 0 /*SWH*/, 0 /*SWG*/, + 0 /*TX_VOLTAGE*/ }; + const uint8_t adcMapping[NUM_ANALOGS] = { 0 /*STICK1*/, 1 /*STICK2*/, 2 /*STICK3*/, 3 /*STICK4*/, + 4 /*POT1*/, 5 /*POT2*/, 6 /*SWA*/, 13 /*SWB*/, + 7 /*SWC*/, 14 /*SWD*/, 8 /*SWE*/, 9 /*SWF*/, + 11/*SWH*/, 10/*SWG*/, + 12 /*TX_VOLTAGE*/ }; #endif #if NUM_PWMSTICKS > 0 @@ -61,6 +72,10 @@ #define FIRST_ANALOG_ADC 0 #define NUM_ANALOGS_ADC 11 #define NUM_ANALOGS_ADC_EXT (NUM_ANALOGS - NUM_ANALOGS_ADC) +#elif defined(PCBNV14) && defined(FLYSKY_HALL_STICKS) + #define FIRST_ANALOG_ADC 4 + #define NUM_ANALOGS_ADC 9 + #define NUM_ANALOGS_ADC_SUB 2 #else #define FIRST_ANALOG_ADC 0 #define NUM_ANALOGS_ADC NUM_ANALOGS @@ -104,6 +119,12 @@ void adcInit() ADC_MAIN->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; ADC_MAIN->SQR1 = (NUM_ANALOGS_ADC - 1) << 20; // bits 23:20 = number of conversions +#if NUM_ANALOGS_ADC_SUB > 0 + ADC_SUB->CR1 = ADC_CR1_SCAN; + ADC_SUB->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; + ADC_SUB->SQR1 = (NUM_ANALOGS_ADC_SUB - 1) << 20; // bits 23:20 = number of conversions +#endif + #if defined(PCBX10) if (STICKS_PWM_ENABLED()) { ADC_MAIN->SQR2 = (ADC_CHANNEL_SLIDER2 << 0) + (ADC_CHANNEL_BATT << 5); @@ -137,6 +158,10 @@ void adcInit() #elif defined(PCBX9D) || defined(PCBX9DP) ADC_MAIN->SQR2 = (ADC_CHANNEL_POT3 << 0) + (ADC_CHANNEL_SLIDER1 << 5) + (ADC_CHANNEL_SLIDER2 << 10) + (ADC_CHANNEL_BATT << 15) + (ADC_Channel_Vbat << 20); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); +#elif defined(PCBNV14) + ADC_MAIN->SQR2 = (ADC_CHANNEL_SWG << 0) + (ADC_CHANNEL_SWH << 5) + (ADC_CHANNEL_BATT << 10); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWC<<15) + (ADC_CHANNEL_SWE<<20) + (ADC_CHANNEL_SWF<<25); // conversions 1 to 6 + ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 #endif ADC_MAIN->SMPR1 = ADC_MAIN_SMPR1; @@ -150,6 +175,19 @@ void adcInit() ADC_DMA_Stream->NDTR = NUM_ANALOGS_ADC; ADC_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; +#if NUM_ANALOGS_ADC_SUB > 0 + ADC_SUB->SMPR1 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24); + ADC_SUB->SMPR2 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24) + (ADC_SAMPTIME<<27); + + ADC->CCR = 0; + + ADC_SUB_DMA_Stream->CR = DMA_SxCR_PL | ADC_SUB_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; + ADC_SUB_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_SUB->DR); + ADC_SUB_DMA_Stream->M0AR = CONVERT_PTR_UINT(&adcValues[SUB_ANALOG_POS]); + ADC_SUB_DMA_Stream->NDTR = NUM_ANALOGS_ADC_SUB; + ADC_SUB_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; +#endif + #if defined(PCBX10) ADC1->CR1 = ADC_CR1_SCAN; ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; @@ -191,6 +229,14 @@ void adcSingleRead() ADC_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA ADC_MAIN->CR2 |= (uint32_t)ADC_CR2_SWSTART; +#if NUM_ANALOGS_ADC_SUB > 0 + ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA + ADC_SUB->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); + ADC_SUB_SET_DMA_FLAGS(); + ADC_SUB_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA + ADC_SUB->CR2 |= (uint32_t) ADC_CR2_SWSTART; +#endif + #if defined(PCBX10) ADC1->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); ADC1->CR2 |= (uint32_t)ADC_CR2_SWSTART; @@ -214,11 +260,23 @@ void adcSingleRead() ADC_EXT_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA #else for (unsigned int i = 0; i < 10000; i++) { - if (ADC_TRANSFER_COMPLETE()) { +#if defined(PCBNV14) + if (ADC_TRANSFER_COMPLETE() && ADC_SUB_TRANSFER_COMPLETE()) + { break; } +#else + if (ADC_TRANSFER_COMPLETE()) + { + break; + } +#endif } ADC_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA +#if defined(PCBNV14) + ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA +#endif + #endif #if defined(PCBX10) @@ -256,17 +314,17 @@ void adcRead() #endif } +uint16_t getRTCBatteryVoltage() +{ #if defined(PCBX10) -uint16_t getRTCBatteryVoltage() -{ return (rtcBatteryVoltage * 2 * ADC_VREF_PREC2) / 2048; -} +#elif defined(PCBNV14) + #warning "TODO RTC voltage" + return 330; #else -uint16_t getRTCBatteryVoltage() -{ return (getAnalogValue(TX_RTC_VOLTAGE) * ADC_VREF_PREC2) / 2048; -} #endif +} #if !defined(SIMU) uint16_t getAnalogValue(uint8_t index) @@ -277,7 +335,7 @@ uint16_t getAnalogValue(uint8_t index) // which produces ghost readings on these inputs. return 0; } -#if defined(PCBX9E) +#if defined(PCBX9E) || defined(PCBNV14) index = adcMapping[index]; #endif if (adcDirection[index] < 0) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 0f96ee6e1..be2c2ec84 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -47,7 +47,7 @@ add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME set(TARGET_SRC ${TARGET_SRC} audio_spi_driver.cpp - adc_driver.cpp + ../common/arm/stm32/adc_driver.cpp ../common/arm/stm32/mixer_scheduler_driver.cpp ../common/arm/stm32/timers_driver.cpp ) diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index e610ad797..c6c639851 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -149,15 +149,23 @@ #define ADC_SUB ADC3 #define ADC_SAMPTIME 2 #define ADC_DMA DMA2 -#define ADC_MAIN_DMA_SxCR_CHSEL 0 // DMA_SxCR_CHSEL_1 +#define ADC_DMA_SxCR_CHSEL DMA_SxCR_CHSEL_1 +#define ADC_DMA_Stream DMA2_Stream4 +#define ADC_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) +#define ADC_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) +//#define ADC_MAIN_DMA_SxCR_CHSEL 0 // DMA_SxCR_CHSEL_1 #define ADC_SUB_DMA_SxCR_CHSEL DMA_Channel_2 -#define ADC_MAIN_DMA_Stream DMA2_Stream4 +//#define ADC_MAIN_DMA_Stream DMA2_Stream4 #define ADC_SUB_DMA_Stream DMA2_Stream0 -#define ADC_MAIN_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) +//#define ADC_MAIN_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) #define ADC_SUB_SET_DMA_FLAGS() ADC_DMA->LIFCR = (DMA_LIFCR_CTCIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CFEIF0) -#define ADC_MAIN_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) +//#define ADC_MAIN_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) #define ADC_SUB_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF0) +#define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); +#define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); + + // Power #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOI #define PWR_ON_GPIO GPIOI From 82c3f0b3dff956f3a3cbfbcb64b67778dbd15bf7 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 23 Jul 2021 17:59:22 +0800 Subject: [PATCH 26/45] NV14 reverted ADC driver change, not working properly for common driver. --- radio/src/targets/common/arm/stm32/adc_driver.cpp | 15 +++++++++++++-- radio/src/targets/nv14/CMakeLists.txt | 3 ++- radio/src/targets/nv14/hal.h | 14 ++++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index 4a70161a6..a70dced97 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -159,15 +159,26 @@ void adcInit() ADC_MAIN->SQR2 = (ADC_CHANNEL_POT3 << 0) + (ADC_CHANNEL_SLIDER1 << 5) + (ADC_CHANNEL_SLIDER2 << 10) + (ADC_CHANNEL_BATT << 15) + (ADC_Channel_Vbat << 20); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); #elif defined(PCBNV14) - ADC_MAIN->SQR2 = (ADC_CHANNEL_SWG << 0) + (ADC_CHANNEL_SWH << 5) + (ADC_CHANNEL_BATT << 10); // conversions 7 and more - ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWC<<15) + (ADC_CHANNEL_SWE<<20) + (ADC_CHANNEL_SWF<<25); // conversions 1 to 6 + #if !defined(FLYSKY_HALL_STICKS) + ADC_MAIN->SQR1 |= (ADC_CHANNEL_BATT <<0 ); + ADC_MAIN->SQR2 = (ADC_CHANNEL_SWA << 0) + (ADC_CHANNEL_SWC << 5) + (ADC_CHANNEL_SWE << 10) + (ADC_CHANNEL_SWF << 15) + (ADC_CHANNEL_SWG << 20) + (ADC_CHANNEL_SWH << 25); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 + #else + ADC_MAIN->SQR2 = (ADC_CHANNEL_SWG << 0) + (ADC_CHANNEL_SWH << 5) + (ADC_CHANNEL_BATT << 10); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWC<<15) + (ADC_CHANNEL_SWE<<20) + (ADC_CHANNEL_SWF<<25); // conversions 1 to 6 + ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 + #endif #endif ADC_MAIN->SMPR1 = ADC_MAIN_SMPR1; ADC_MAIN->SMPR2 = ADC_MAIN_SMPR2; +#if defined(PCBNV14) + ADC->CCR = 0; +#else ADC->CCR = ADC_CCR_VBATE; // Enable vbat sensor +#endif ADC_DMA_Stream->CR = DMA_SxCR_PL | ADC_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; ADC_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_MAIN->DR); diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index be2c2ec84..708d3957a 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -47,7 +47,8 @@ add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME set(TARGET_SRC ${TARGET_SRC} audio_spi_driver.cpp - ../common/arm/stm32/adc_driver.cpp + adc_driver.cpp +# ../common/arm/stm32/adc_driver.cpp ../common/arm/stm32/mixer_scheduler_driver.cpp ../common/arm/stm32/timers_driver.cpp ) diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index c6c639851..4d8f6f1b6 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -149,23 +149,25 @@ #define ADC_SUB ADC3 #define ADC_SAMPTIME 2 #define ADC_DMA DMA2 -#define ADC_DMA_SxCR_CHSEL DMA_SxCR_CHSEL_1 +#define ADC_DMA_SxCR_CHSEL 0 //DMA_SxCR_CHSEL_1 #define ADC_DMA_Stream DMA2_Stream4 #define ADC_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) #define ADC_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) -//#define ADC_MAIN_DMA_SxCR_CHSEL 0 // DMA_SxCR_CHSEL_1 + +// NV14 customized drivers uses this +#define ADC_MAIN_DMA_SxCR_CHSEL 0 // DMA_SxCR_CHSEL_1 +#define ADC_MAIN_DMA_Stream DMA2_Stream4 +#define ADC_MAIN_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) +#define ADC_MAIN_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) + #define ADC_SUB_DMA_SxCR_CHSEL DMA_Channel_2 -//#define ADC_MAIN_DMA_Stream DMA2_Stream4 #define ADC_SUB_DMA_Stream DMA2_Stream0 -//#define ADC_MAIN_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) #define ADC_SUB_SET_DMA_FLAGS() ADC_DMA->LIFCR = (DMA_LIFCR_CTCIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CFEIF0) -//#define ADC_MAIN_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) #define ADC_SUB_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF0) #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); - // Power #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOI #define PWR_ON_GPIO GPIOI From 185b201251baf0c61387381048f8f0079940b1ee Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 23 Jul 2021 18:07:54 +0800 Subject: [PATCH 27/45] Fixed a problem in B/W screen. --- radio/src/opentx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 77332df11..d2a5aa00c 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -2077,7 +2077,7 @@ uint32_t pwrCheck() LED_ERROR_BEGIN(); WDG_RESET(); - (); + lcdRefresh(); if (warningResult) { pwr_check_state = PWR_CHECK_OFF; From 3997116bb86219f0c536c6d9736d8363aedbdaec Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sat, 24 Jul 2021 20:24:19 +0800 Subject: [PATCH 28/45] Added NV14 test images. --- radio/src/tests/bitmap_320x480.png | Bin 0 -> 7251 bytes radio/src/tests/clipping_320x480.png | Bin 0 -> 5175 bytes radio/src/tests/darkmode_EN_320x480.png | Bin 0 -> 9735 bytes radio/src/tests/lines_320x480.png | Bin 0 -> 5767 bytes radio/src/tests/masks_320x480.png | Bin 0 -> 11321 bytes radio/src/tests/primitives_EN_320x480.png | Bin 0 -> 10163 bytes radio/src/tests/transparency_EN_320x480.png | Bin 0 -> 10170 bytes radio/src/tests/vline_320x480.png | Bin 0 -> 5300 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 radio/src/tests/bitmap_320x480.png create mode 100644 radio/src/tests/clipping_320x480.png create mode 100644 radio/src/tests/darkmode_EN_320x480.png create mode 100644 radio/src/tests/lines_320x480.png create mode 100644 radio/src/tests/masks_320x480.png create mode 100644 radio/src/tests/primitives_EN_320x480.png create mode 100644 radio/src/tests/transparency_EN_320x480.png create mode 100644 radio/src/tests/vline_320x480.png diff --git a/radio/src/tests/bitmap_320x480.png b/radio/src/tests/bitmap_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..f9396282f9b20525e61ebbef82046876b4cda6b1 GIT binary patch literal 7251 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 R;Lk!RC?ITZt)FDWKLJ-kF&+Q_ literal 0 HcmV?d00001 diff --git a/radio/src/tests/clipping_320x480.png b/radio/src/tests/clipping_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..1e74f147890057b5d226f3467fc50e37ddb3a92d GIT binary patch literal 5175 zcmeI0Z%7ki9LI0xx#rw-Y0*6YZDpfgm`9y|Ftxeqbcqqtn+$9g+QgDx2(D1_<}#z% zoCb-7SZBS;=*3=Rr3V~!N89J7W5&1Xc3>o` zY&kwi;I*`#aF{xq$ek9mjl{ZJ6z;|V^I|uSs^}C4x!&FFw<}aeh5MzSe%wN0mgWFM z5;#ih{X}hy$KYf%TD7dkWb?%p57j{oc9e6mELWbQjF(j!g&Ok!Mu5!8&|gh=V&wHJ4}K29&P; z$Y8r5KN$Hu2@U`7J%}_$RXm6Nun((?f@xMNxlEiX5i=4ey-6tD5ny~J5FEyTDfmOZ z|55z_fm5VfmJl01va~1T>WDYnmv@FdrJB~*fSU2X3;HhSMq~crK~3qg%gxw^x3385 zKvmPs8%o*BxQn$s0tppno8p|`4qdtrT^b4%-00$JKFcBki!rbWh0L7`BB0xjvAo1J z0)rdL4V!wq+7Rtn;p_j`ua+y0D$i<#SS>W}i zzNvYd%EQsFEcmG0kdwpwO!IIZ_Qg{SKIpm=JKS78;Vq;ew*kkyh`peL>-51RB&O37 zg*~}LNwA$Lw=2qo#k;buC2zmAp;f4lucQ^n$1v#{4lX2esf=QF7E~l`+YJ|ZZ zz8@^LrMFg3qdtu*?$Sk&g4m7_*Tx!!JMUUW@)HmXjDl#APYdl3CNF8UGoZw8Ag+g8 zy9d6&D_|k3) zM0U+Yq$=Zl{L?ZY7oAkr&uiGWf{ll~eH65u=^)2E-F{61SB7>F8Q68uktqe&t*PE{ zpA}DzFt}9jIeX{jC3u0hhj;2JASu-9FxSXB>$d}m4_F)jok(%AGLB+O=IphZYARZ| zqGUV5Q5W%Gi59N4J2V9o1)zh@x@9Y7`3}PCSizxJ0aut~f*pIu4fQ0-0)%zj6-0?+ zzMlNrqmuP@I#W{(Zuul!K!*7{!bJ2o6V{5g$Be}-c%_koi|Uig648{TvPfkSD-iLf z6?TM4w1~Bm$|BK%qknJ@ltd(nNUXqrA|eEFO%!d^W!)%(KLinL`M$E-CY$pYGqtwh literal 0 HcmV?d00001 diff --git a/radio/src/tests/darkmode_EN_320x480.png b/radio/src/tests/darkmode_EN_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..7868be171f05575cf5689d7b1de86018e3d5323e GIT binary patch literal 9735 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 KPC2N>*8dZDmIZPE literal 0 HcmV?d00001 diff --git a/radio/src/tests/masks_320x480.png b/radio/src/tests/masks_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..77bdb219193b1f20df0badd4d98f4f5e4b115c05 GIT binary patch literal 11321 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%0A3T5FHUgNCjL_tIA77 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 literal 0 HcmV?d00001 diff --git a/radio/src/tests/primitives_EN_320x480.png b/radio/src/tests/primitives_EN_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..46c993e0139b00fccff7c67ab012283543466fd1 GIT binary patch literal 10163 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 MR#foeKzZ)}2EYDaO#lD@ literal 0 HcmV?d00001 diff --git a/radio/src/tests/vline_320x480.png b/radio/src/tests/vline_320x480.png new file mode 100644 index 0000000000000000000000000000000000000000..9ff6dcf16d53049e71f2056363ac512cf0da00da GIT binary patch literal 5300 zcmeHLZA@EL7{0w+;gZaCYztPgKW+WM`afs30$Ir?~}*J*sxLvCa6eb+z7_a}@t|O|8GdeS^2JFgo2wBHo8a?_sjGb7utQC224^505_-Cv=?FGGFsJ&2Fh=B$Qu^ z`TYh!>kbTK z-LRzM$T85=cKdWLSvz@sQdpq$or7)14hUwOckFNzv#WQP0C)QG*LW9Dr{aY%o}Z89M(Qc$Pj1yj69dL)4FPc)gpqGdlkY zY#M|h!in#=eHUeVcP1dU0bO_=24=!c>63_NeDGP?dlx|OLoF@}Vg;_Y!&FEMG4E#O z0{I0X50!~=;ydo>;_cZ-xscbxvs*`M{>b;BjdAqwsD@&|HanTk!aw90~S8gVoSH z_O9BSwk2+^1iih9Bf-$frl+&Wz==pWxHoZgCFt!<+8x*j^}wP>DATV{Fef-FQFA35 zC+$r(&j03HIZEV+% xPVsa0uYXP9%jJd-?BtEp{||hs>)iygukT#>XQc)2z<(o&nrG^(#thbuKLCkgxz+#x literal 0 HcmV?d00001 From f99389c4a2fb5468c444dba2a858a548460545ad Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sun, 25 Jul 2021 14:28:49 +0800 Subject: [PATCH 29/45] Fixed problematic test images. --- radio/src/tests/bitmap_480x272.png | Bin 6441 -> 6427 bytes radio/src/tests/clipping_480x272.png | Bin 1197 -> 4374 bytes radio/src/tests/lines_480x272.png | Bin 4962 -> 4980 bytes radio/src/tests/masks_480x272.png | Bin 13120 -> 14063 bytes radio/src/tests/primitives_EN_480x272.png | Bin 8855 -> 9364 bytes radio/src/tests/transparency_EN_480x272.png | Bin 9025 -> 9421 bytes radio/src/tests/vline_480x272.png | Bin 1294 -> 4451 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/tests/bitmap_480x272.png b/radio/src/tests/bitmap_480x272.png index a9148109cfc13c066a3ac61a69c3f7f00872260c..f546ecd6ce9c4881e63698277a40aa2191858be7 100644 GIT binary patch delta 3251 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 delta 3264 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 TqgG-Z_={b#Eb9A6*}ne)>q|A| diff --git a/radio/src/tests/clipping_480x272.png b/radio/src/tests/clipping_480x272.png index 5a90991cb50cde470ce699ffe72b0a0c0359708d..c7284536501b8d90867f7cb4e62c3c749a3f6651 100644 GIT binary patch literal 4374 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 OSjh_(CN(E!ZT%Mm8iQK^ diff --git a/radio/src/tests/masks_480x272.png b/radio/src/tests/masks_480x272.png index e6a8c0873b25dd49e61d591bef11a883a3efbe69..e602c58a728a0084e2fc8f54f133b6f4004d2c16 100644 GIT binary patch literal 14063 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 literal 13120 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

ii(mdIO>-nue^xSdI=tK%0Dq{64b3en`!5>+b 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 I=@?A=f0)5ORR910 delta 5457 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�p{Z#X6AcZA!u)A;R}0 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 delta 5946 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 literal 1294 zcmeAS@N?(olHy`uVBq!ia0y~yV0-|?0vt?0k>@G~w}BLAfk$L90|S2|2s5s*U!KXp zz;eLT#WAE}&f7cIL3aW~92^(OZ1{gw$fBTt^FSI8^Di0CyPaNLB6F+0*7fuA^Xu=A zmt*LtoOS&3`scO(nH%=o?5&-c+0eJV-u|SC!$I~&IV0W;>t}HbW%DN+g5@myaf zWH#vD6?~z1nc?;h>kH1y7_WUh>%c#gE#iDXSPiegckB_M?CC05$I~VbXLoDz&18G= zBY`h!86#Jop3o_i1N-c59tEnX{&~sFdM4YAd$PY5q)H`tm&=C$?a!_Gtg_IDdCgmQ z_q{+-Tkbm`wb>C0AiJh*jabm`!>p6~5M&jFH$0B0JcC$_i3ng4pX`GJy2PoJ|Co zzoW026(q;n4zlLK9X^l|2O?$dfVMS$w)6zqmR__9r19Lt7LZ1nyP3>DjTY-BvsdX% z1qN8G5=d?G&H#{F_d>9Rzx1Sl<~ZD33R0RO^dj7d_X@`sYa`wgjxX#fQW^p;W~WHq znC?*Tw5)Nj4VQq!WI+>$DGtmooTJoe5Dddq@R#w^i&@_-PtSP;EZ`VCUHx3vIVCg! E0Pz`yT>t<8 From 74f2445558d9b9fdb8eb5d2acfd9ab557fde841e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 24 Jul 2021 16:42:56 +0200 Subject: [PATCH 30/45] WiP: Simu and modules fixes --- radio/src/gui/colorlcd/curveedit.cpp | 4 ++-- radio/src/gui/colorlcd/model_setup.cpp | 10 ++++++++++ radio/src/gui/gui_common.cpp | 7 +++++++ radio/src/pulses/afhds3.h | 2 ++ radio/src/pulses/modules_constants.h | 6 ++++++ radio/src/pulses/modules_helpers.h | 15 ++++++++++++++- radio/src/pulses/pulses.cpp | 16 ++++++++++++++-- radio/src/pulses/pulses.h | 2 ++ .../targets/common/arm/stm32/timers_driver.cpp | 2 +- radio/src/targets/nv14/CMakeLists.txt | 1 + radio/src/targets/nv14/adc_driver.cpp | 2 +- radio/src/targets/nv14/board.h | 2 +- radio/src/targets/nv14/libopenui_config.h | 2 +- radio/src/targets/simu/simpgmspace.cpp | 2 +- radio/src/translations.h | 1 + radio/src/translations/untranslated.h | 6 +++--- 16 files changed, 67 insertions(+), 13 deletions(-) diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index 9de50061f..4d6412f2b 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -48,7 +48,7 @@ void CurveDataEdit::update() uint8_t curvePointsCount = 5 + curve.points; // Point number - for (uint8_t i = 0; i < curvePointsCount; i++) { + for (int i = 0; i < curvePointsCount; i++) { auto txt = new StaticText( this, {i * boxWidth + PAGE_PADDING, 10, boxWidth - PAGE_PADDING, boxHeight}, @@ -60,7 +60,7 @@ void CurveDataEdit::update() // x value if (curve.type == CURVE_TYPE_CUSTOM) { // Adjustable points for custom curves - for (uint8_t i = 0; i < curvePointsCount; i++) { + for (int i = 0; i < curvePointsCount; i++) { int8_t* points = curveAddress(index); auto pointEdit = new NumberEdit( this, diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index f045fabfc..2239080d0 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -622,6 +622,16 @@ class ModuleWindow : public FormGroup { rfChoice->setFocus(SET_FOCUS_DEFAULT); }); } + else if (isModuleFlySky(moduleIdx)) { + rfChoice = new Choice(this, grid.getFieldSlot(2, 1), STR_FLYSKY_PROTOCOLS, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_LR12, + GET_DEFAULT(g_model.moduleData[moduleIdx].subType), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].subType = newValue; + SET_DIRTY(); + update(); + rfChoice->setFocus(SET_FOCUS_DEFAULT); + }); + } #if defined(MULTIMODULE) else if (isModuleMultimodule(moduleIdx)) { Choice * mmSubProtocol = nullptr; diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 058c20765..2afe2e6b9 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -628,6 +628,13 @@ bool isInternalModuleAvailable(int moduleType) return true; #endif } + +#if (defined(AFHDS3) || defined(AFHDS2A)) && (defined(INTERNAL_MODULE_AFHDS2A) || defined(INTERNAL_MODULE_AFHDS3)) + if (moduleType == MODULE_TYPE_AFHDS3) { + return true; + } +#endif + return false; } #else diff --git a/radio/src/pulses/afhds3.h b/radio/src/pulses/afhds3.h index c3edc33cd..1cf262eee 100644 --- a/radio/src/pulses/afhds3.h +++ b/radio/src/pulses/afhds3.h @@ -102,7 +102,9 @@ struct Data #if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) void sendByte(uint8_t b) { +#if !defined(SIMU) *ptr++ = b; +#endif } const uint8_t* getData() diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index b6b7a6abc..4774c3dae 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -38,6 +38,7 @@ enum ModuleType { MODULE_TYPE_SBUS, MODULE_TYPE_XJT_LITE_PXX2, MODULE_TYPE_AFHDS3, //no more protocols possible because of 4 bits value + //MODULE_TYPE_AFHDS2A, MODULE_TYPE_COUNT, MODULE_TYPE_MAX = MODULE_TYPE_COUNT - 1 }; @@ -222,6 +223,11 @@ enum ModuleSubtypeDSM2 { DSM2_PROTO_DSMX, }; +enum ModuleSubtypeFlysky { + FLYSKY_SUBTYPE_AFHDS3=0, + FLYSKY_SUBTYPE_AFHDS2A +}; + enum FailsafeModes { FAILSAFE_NOT_SET, FAILSAFE_HOLD, diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 37a8c45c2..23eca3f9f 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -319,9 +319,22 @@ inline bool isModuleSBUS(uint8_t moduleIdx) return g_model.moduleData[moduleIdx].type == MODULE_TYPE_SBUS; } +inline bool isModuleFlySky(uint8_t idx) +{ + return + (g_model.moduleData[idx].type == MODULE_TYPE_AFHDS3); +} + +inline bool isModuleAFHDS2A(uint8_t idx) +{ + return isModuleFlySky(idx) + && (g_model.moduleData[idx].subType == FLYSKY_SUBTYPE_AFHDS2A); +} + inline bool isModuleAFHDS3(uint8_t idx) { - return g_model.moduleData[idx].type == MODULE_TYPE_AFHDS3; + return isModuleFlySky(idx) + && (g_model.moduleData[idx].subType == FLYSKY_SUBTYPE_AFHDS3); } // order is the same as in enum Protocols in myeeprom.h (none, ppm, pxx, pxx2, dsm, crossfire, multi, r9m, r9m2, sbus) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index d1f067d53..ee93e7a21 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -187,9 +187,13 @@ uint8_t getRequiredProtocol(uint8_t module) break; #endif -#if defined(AFHDS3) +#if defined(AFHDS3) || defined(AFHDS2) case MODULE_TYPE_AFHDS3: - protocol = PROTOCOL_CHANNELS_AFHDS3; + if (isModuleAFHDS3(module)) { + protocol = PROTOCOL_CHANNELS_AFHDS3; + } else if (isModuleAFHDS2A(module)) { + protocol = PROTOCOL_CHANNELS_AFHDS2A; + } break; #endif @@ -505,6 +509,14 @@ static void enablePulsesInternalModule(uint8_t protocol) break; #endif +#if defined(AFHDS2) + case PROTOCOL_CHANNELS_AFHDS2A: + extmodulePulsesData.afhds2.init(INTERNAL_MODULE); + extmoduleSerialStart(); + mixerSchedulerSetPeriod(INTERNAL_MODULE, 2 * 1000 /* us */); + break; +#endif + default: // internal module stopped, set internal period to 0 and start the scheduler mixerSchedulerSetPeriod(INTERNAL_MODULE, 0); diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 679bfe419..76d489737 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -273,6 +273,7 @@ union InternalModulePulsesData { #if defined(INTERNAL_MODULE_PPM) PpmPulsesData ppm; #endif + } __ALIGNED(4); union ExternalModulePulsesData { @@ -389,6 +390,7 @@ enum ChannelsProtocols { PROTOCOL_CHANNELS_SBUS, PROTOCOL_CHANNELS_PXX2_LOWSPEED, PROTOCOL_CHANNELS_PXX2_HIGHSPEED, + PROTOCOL_CHANNELS_AFHDS2A, PROTOCOL_CHANNELS_AFHDS3, PROTOCOL_CHANNELS_GHOST }; diff --git a/radio/src/targets/common/arm/stm32/timers_driver.cpp b/radio/src/targets/common/arm/stm32/timers_driver.cpp index 7ac4881e3..bae659483 100644 --- a/radio/src/targets/common/arm/stm32/timers_driver.cpp +++ b/radio/src/targets/common/arm/stm32/timers_driver.cpp @@ -59,7 +59,7 @@ void interrupt1ms() ++pre_scale; // 1 ms loop -#if defined(FLYSKY_HALL_STICKS) +#if defined(FLYSKY_HALL_STICKS) && !defined(SIMU) flysky_hall_stick_loop(); // TODO: need to put in 1ms timer loop for best performance #endif diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index 708d3957a..be14ca93d 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -43,6 +43,7 @@ set(FLAVOUR nv14) #add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_SERIAL -DAFHDS2_BAUDRATE=${PCB_RF_BAUD}) add_definitions(-DBATTERY_CHARGE) # -DSOFTWARE_VOLUME +add_definitions(-DINTERNAL_MODULE_AFHDS2A) set(TARGET_SRC ${TARGET_SRC} diff --git a/radio/src/targets/nv14/adc_driver.cpp b/radio/src/targets/nv14/adc_driver.cpp index 8d414cc6c..203ee1f34 100644 --- a/radio/src/targets/nv14/adc_driver.cpp +++ b/radio/src/targets/nv14/adc_driver.cpp @@ -297,13 +297,13 @@ void adcRead() #endif } +#if !defined(SIMU) uint16_t getRTCBatteryVoltage() { #warning "TODO RTC voltage" return 330; } -#if !defined(SIMU) uint16_t getAnalogValue(uint8_t index) { if (IS_POT(index) && !IS_POT_SLIDER_AVAILABLE(index)) { diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index 38efa567f..dc1228e1c 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -483,7 +483,7 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode); void telemetryPortSetDirectionOutput(); void telemetryPortSetDirectionInput(); void sportSendBuffer(uint8_t * buffer, uint32_t count); -uint8_t telemetryGetByte(uint8_t * byte); +bool telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); void sportSendByte(uint8_t byte); extern uint32_t telemetryErrors; diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h index 0a00fe51b..f23ad216b 100644 --- a/radio/src/targets/nv14/libopenui_config.h +++ b/radio/src/targets/nv14/libopenui_config.h @@ -60,7 +60,7 @@ constexpr uint32_t MENU_FOOTER_TOP = LCD_H - MENU_FOOTER_HEIGHT; constexpr uint32_t MENU_BODY_HEIGHT = MENU_FOOTER_TOP - MENU_BODY_TOP; constexpr uint32_t MENUS_MARGIN_LEFT = 6; -constexpr uint32_t PAGE_PADDING = 8; +constexpr coord_t PAGE_PADDING = 8; constexpr uint32_t PAGE_LINE_HEIGHT = 26; constexpr uint32_t PAGE_LINE_SPACING = 6; constexpr uint32_t PAGE_INDENT_WIDTH = 10; diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index a32bed11d..cb0757e78 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -471,7 +471,7 @@ void sportSendByte(uint8_t byte) { } -void sportSendBuffer(const uint8_t * buffer, uint32_t count) +void sportSendBuffer(uint8_t * buffer, uint32_t count) { } diff --git a/radio/src/translations.h b/radio/src/translations.h index 84fa3f02d..74f3d3399 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -166,6 +166,7 @@ extern const char STR_XJT_ACCST_RF_PROTOCOLS[]; extern const char STR_ISRM_RF_PROTOCOLS[]; extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; +extern const char STR_FLYSKY_PROTOCOLS[]; extern const char STR_CRSF_BAUDRATE[]; extern const char STR_PPM_POL[]; extern const char STR_SBUS_INVERSION_VALUES[]; diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index a734e63c6..cb85b7825 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -111,10 +111,10 @@ #endif #define LEN_EXTERNAL_MODULE_PROTOCOLS "\014" -#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3\0 " +#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""FLYSKY\0 " -#define LEN_FLYSKY_PROTOCOLS "\006" -#define TR_FLYSKY_PROTOCOLS +#define LEN_FLYSKY_PROTOCOLS "\007" +#define TR_FLYSKY_PROTOCOLS "AFHDS3\0""AFHDS2A" #define LEN_INTERNAL_MODULE_PROTOCOLS LEN_EXTERNAL_MODULE_PROTOCOLS #define TR_INTERNAL_MODULE_PROTOCOLS TR_EXTERNAL_MODULE_PROTOCOLS From 30fe8013444e038b93dce4b4c246a1b1893dda34 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sun, 25 Jul 2021 16:55:20 +0800 Subject: [PATCH 31/45] Fixed nv14 build. --- radio/src/pulses/pulses.cpp | 2 +- radio/src/targets/nv14/board.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index ee93e7a21..db2480f9a 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -511,7 +511,7 @@ static void enablePulsesInternalModule(uint8_t protocol) #if defined(AFHDS2) case PROTOCOL_CHANNELS_AFHDS2A: - extmodulePulsesData.afhds2.init(INTERNAL_MODULE); +/* extmodulePulsesData.afhds2.init(INTERNAL_MODULE);*/ extmoduleSerialStart(); mixerSchedulerSetPeriod(INTERNAL_MODULE, 2 * 1000 /* us */); break; diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index dc1228e1c..38efa567f 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -483,7 +483,7 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode); void telemetryPortSetDirectionOutput(); void telemetryPortSetDirectionInput(); void sportSendBuffer(uint8_t * buffer, uint32_t count); -bool telemetryGetByte(uint8_t * byte); +uint8_t telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); void sportSendByte(uint8_t byte); extern uint32_t telemetryErrors; From 9e044b6f97d6f6e3bb3a43b4e4c5bc050e310444 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Sun, 25 Jul 2021 21:15:20 +0800 Subject: [PATCH 32/45] Fixed NV14 build error. --- radio/src/pulses/pulses.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index db2480f9a..5d4df0b06 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -511,8 +511,8 @@ static void enablePulsesInternalModule(uint8_t protocol) #if defined(AFHDS2) case PROTOCOL_CHANNELS_AFHDS2A: -/* extmodulePulsesData.afhds2.init(INTERNAL_MODULE);*/ - extmoduleSerialStart(); +/* extmodulePulsesData.afhds2.init(INTERNAL_MODULE); + extmoduleSerialStart();*/ mixerSchedulerSetPeriod(INTERNAL_MODULE, 2 * 1000 /* us */); break; #endif From 0dc0d07b2792ca36078708eade0027c88c2c7eee Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 26 Jul 2021 22:28:57 +0800 Subject: [PATCH 33/45] Fixed simu build problem. --- radio/src/targets/simu/simpgmspace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index cb0757e78..a32bed11d 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -471,7 +471,7 @@ void sportSendByte(uint8_t byte) { } -void sportSendBuffer(uint8_t * buffer, uint32_t count) +void sportSendBuffer(const uint8_t * buffer, uint32_t count) { } From 353996fab8457d1a364130b4d0bda1e3c4e3db35 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 26 Jul 2021 23:31:53 +0800 Subject: [PATCH 34/45] Updated failed images. --- radio/src/tests/primitives_CN_480x272.png | Bin 9147 -> 9554 bytes radio/src/tests/transparency_CN_480x272.png | Bin 9228 -> 9582 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/tests/primitives_CN_480x272.png b/radio/src/tests/primitives_CN_480x272.png index b99ee6ff7ea7691da5924527958ff665ab5bfe65..94e283babd63e1e1d605c0c3061b5f44441e2414 100644 GIT binary patch delta 6213 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 delta 5940 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 Ue|**5TEO@%iCh+OJRB|jKc2YotpET3 diff --git a/radio/src/tests/transparency_CN_480x272.png b/radio/src/tests/transparency_CN_480x272.png index a6ee82449dfaf6e0943823681abd701adf67dcd9..f35e221306354dab630b78d740097b6caa9aac87 100644 GIT binary patch delta 6685 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 delta 6364 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 From 6cd3ee557a5f5d2eb007ab34a3754c31e595baef Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 01:44:39 +0800 Subject: [PATCH 35/45] Fixed simu build failure for NV14. --- radio/src/targets/nv14/board.h | 2 +- radio/src/targets/nv14/telemetry_driver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index 38efa567f..dc1228e1c 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -483,7 +483,7 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode); void telemetryPortSetDirectionOutput(); void telemetryPortSetDirectionInput(); void sportSendBuffer(uint8_t * buffer, uint32_t count); -uint8_t telemetryGetByte(uint8_t * byte); +bool telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); void sportSendByte(uint8_t byte); extern uint32_t telemetryErrors; diff --git a/radio/src/targets/nv14/telemetry_driver.cpp b/radio/src/targets/nv14/telemetry_driver.cpp index b8cce7e8b..3ad7bca4f 100644 --- a/radio/src/targets/nv14/telemetry_driver.cpp +++ b/radio/src/targets/nv14/telemetry_driver.cpp @@ -197,7 +197,7 @@ extern "C" void TELEMETRY_USART_IRQHandler(void) } // TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp -uint8_t telemetryGetByte(uint8_t * byte) +bool telemetryGetByte(uint8_t * byte) { #if defined(PCBX12S) if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) From 86d787756e30f6f11fcd759b99ab6514b62cd3fe Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 02:03:47 +0800 Subject: [PATCH 36/45] Fixed NV14 firmware and simu build error. --- radio/src/targets/nv14/board.h | 2 +- radio/src/targets/nv14/keys_driver.cpp | 8 ++++---- radio/src/targets/nv14/telemetry_driver.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index dc1228e1c..f27b02a9a 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -482,7 +482,7 @@ int32_t getVolume(); void telemetryPortInit(uint32_t baudrate, uint8_t mode); void telemetryPortSetDirectionOutput(); void telemetryPortSetDirectionInput(); -void sportSendBuffer(uint8_t * buffer, uint32_t count); +void sportSendBuffer(const uint8_t * buffer, uint32_t count); bool telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); void sportSendByte(uint8_t byte); diff --git a/radio/src/targets/nv14/keys_driver.cpp b/radio/src/targets/nv14/keys_driver.cpp index 98512df67..3e4f530b3 100644 --- a/radio/src/targets/nv14/keys_driver.cpp +++ b/radio/src/targets/nv14/keys_driver.cpp @@ -24,9 +24,9 @@ uint32_t readKeys() { uint32_t result = 0; bool getKeys = true; -#if defined(LUA) +/*#if defined(LUA) getKeys = (luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT); -#endif +#endif*/ //TODO: not sure why has LUA condition here, this construct is not in horus target if (getKeys) { if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) result |= 1 << KEY_PGDN; @@ -57,11 +57,11 @@ uint32_t readTrims() { uint32_t result = 0; bool getTrim = true; -#if defined(LUA) +/*#if defined(LUA) if(luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT) { getTrim = false; } -#endif +#endif*/ //TODO: not sure why has LUA condition here, this construct is not in horus target if(!getTrim) return result; if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) result |= 1 << (TRM_LH_DWN - TRM_BASE); diff --git a/radio/src/targets/nv14/telemetry_driver.cpp b/radio/src/targets/nv14/telemetry_driver.cpp index 3ad7bca4f..adf82c752 100644 --- a/radio/src/targets/nv14/telemetry_driver.cpp +++ b/radio/src/targets/nv14/telemetry_driver.cpp @@ -117,7 +117,7 @@ void sportSendByte(uint8_t byte) USART_SendData(TELEMETRY_USART, byte); } -void sportSendBuffer(uint8_t * buffer, uint32_t count) +void sportSendBuffer(const uint8_t * buffer, uint32_t count) { telemetryPortSetDirectionOutput(); From fc3fe63a2c60ff082124a74033d0e1f37af43c26 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 02:16:44 +0800 Subject: [PATCH 37/45] Added NV14 in workflow action. --- .github/workflows/actions.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index a951d29b3..9c017603d 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -26,6 +26,7 @@ jobs: strategy: matrix: target: + - NV14 - T12 - T16 - T18 From 15e19a52c4faf52c6fe6d354404b214d8bcd0cb1 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 08:29:25 +0800 Subject: [PATCH 38/45] Updated new definitions about RAM BACKUP. --- radio/src/targets/nv14/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index be14ca93d..d7b12236b 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -28,7 +28,7 @@ else() set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) endif() -set(RAMBACKUP YES) +set(RTC_BACKUP_RAM YES) set(PPM_LIMITS_SYMETRICAL YES) # for size report script set(CPU_TYPE_FULL STM32F429xI) From 7700df705c3009b3d88fc7d387e9534d5e19a1ac Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 27 Jul 2021 08:09:04 +0200 Subject: [PATCH 39/45] Removed useless "powerupReason" --- radio/src/targets/nv14/board.h | 62 +++++++++++++++++---------- radio/src/targets/nv14/pwr_driver.cpp | 5 --- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index f27b02a9a..186856ab0 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -238,13 +238,6 @@ enum EnumSwitchesPositions }; #define STORAGE_NUM_SWITCHES_POSITIONS (STORAGE_NUM_SWITCHES * 3) -enum EnumPowerupState -{ - BOARD_POWER_OFF = 0xCAFEDEAD, - BOARD_POWER_ON = 0xDEADBEEF, - BOARD_STARTED = 0xBAADF00D, - BOARD_REBOOT = 0xC00010FF, -}; void monitorInit(); void keysInit(); @@ -261,15 +254,6 @@ uint32_t readTrims(); #define DBLKEYS_PRESSED_RGT_UP(in) (false) #define DBLKEYS_PRESSED_LFT_DWN(in) (false) -// WDT driver -extern uint32_t powerupReason; -extern uint32_t boardState; - -#define SHUTDOWN_REQUEST 0xDEADBEEF -#define NO_SHUTDOWN_REQUEST ~SHUTDOWN_REQUEST -#define DIRTY_SHUTDOWN 0xCAFEDEAD -#define NORMAL_POWER_OFF ~DIRTY_SHUTDOWN - #define WDG_DURATION 500 /*ms*/ void watchdogInit(unsigned int duration); #if defined(SIMU) @@ -361,6 +345,45 @@ uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps #define BATTERY_MIN 35 // 3.5V #define BATTERY_MAX 42 // 4.2V +enum EnumPowerupState +{ + BOARD_POWER_OFF = 0xCAFEDEAD, + BOARD_POWER_ON = 0xDEADBEEF, + BOARD_STARTED = 0xBAADF00D, + BOARD_REBOOT = 0xC00010FF, +}; + +extern uint32_t boardState; + +#if defined(__cplusplus) +enum PowerReason { + SHUTDOWN_REQUEST = 0xDEADBEEF, + SOFTRESET_REQUEST = 0xCAFEDEAD, +}; + +constexpr uint32_t POWER_REASON_SIGNATURE = 0x0178746F; + +inline bool UNEXPECTED_SHUTDOWN() +{ +#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) + return false; +#else + if (WAS_RESET_BY_WATCHDOG()) + return true; + else if (WAS_RESET_BY_SOFTWARE()) + return RTC->BKP0R != SOFTRESET_REQUEST; + else + return RTC->BKP1R == POWER_REASON_SIGNATURE && RTC->BKP0R != SHUTDOWN_REQUEST; +#endif +} + +inline void SET_POWER_REASON(uint32_t value) +{ + RTC->BKP0R = value; + RTC->BKP1R = POWER_REASON_SIGNATURE; +} +#endif + #if defined(__cplusplus) && !defined(SIMU) extern "C" { #endif @@ -384,12 +407,7 @@ bool pwrPressed(); #define pwrForcePressed() false #endif uint32_t pwrPressedDuration();; -#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) - #define UNEXPECTED_SHUTDOWN() (false) -#else - #define UNEXPECTED_SHUTDOWN() (powerupReason == DIRTY_SHUTDOWN) -#endif - + #define AUX_SERIAL_POWER_ON() #define AUX_SERIAL_POWER_OFF() diff --git a/radio/src/targets/nv14/pwr_driver.cpp b/radio/src/targets/nv14/pwr_driver.cpp index 65f653b39..16c447ff3 100644 --- a/radio/src/targets/nv14/pwr_driver.cpp +++ b/radio/src/targets/nv14/pwr_driver.cpp @@ -21,7 +21,6 @@ #include "pwr.h" #include "board.h" -uint32_t powerupReason __NOINIT; // Stores power up reason beyond initialization for emergency mode activation uint32_t boardState __NOINIT; void pwrInit() @@ -89,10 +88,6 @@ bool pwrPressed() void pwrResetHandler() { if (boardState != BOARD_POWER_OFF) { - powerupReason = boardState != BOARD_REBOOT && WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() ? DIRTY_SHUTDOWN : ~DIRTY_SHUTDOWN; RCC->CSR |= RCC_CSR_RMVF; //clear all flags } - else { - powerupReason = ~DIRTY_SHUTDOWN; - } } From 67bf2a4e2b4a2a83d468f1fa130257e470d6fb27 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 14:16:06 +0800 Subject: [PATCH 40/45] NV14 fixed firmware and simu build. --- radio/src/targets/nv14/CMakeLists.txt | 5 ++-- radio/src/targets/nv14/board.h | 43 ++++++++++++++++++++++----- radio/src/targets/nv14/pwr_driver.cpp | 4 +-- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index d7b12236b..461c9e63f 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -68,7 +68,7 @@ set(FIRMWARE_TARGET_SRC set(BITMAPS_TARGET nv14_bitmaps) set(FONTS_TARGET x12_fonts) set(LCD_DRIVER lcd_driver.cpp) -set(LUA_EXPORT lua_export_x10) +set(LUA_EXPORT lua_export_nv14) set(TOUCH_DRIVER touch_driver.cpp) set(HARDWARE_TOUCH YES) @@ -77,6 +77,8 @@ set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET} truetype_fonts) set(HARDWARE_TOUCH ON) set(SOFTWARE_KEYBOARD ON) +set(FIRMWARE_DEPENDENCIES datacopy) + add_definitions(-DSTM32F429_439xx -DSDRAM -DCOLORLCD -DLIBOPENUI -DHARDWARE_TOUCH -DSOFTWARE_KEYBOARD) add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) @@ -212,4 +214,3 @@ if(PYTHONINTERP_FOUND) ) endif() -set(LUA_EXPORT lua_export_nv14) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index f27b02a9a..fc02cfcee 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -21,10 +21,10 @@ #ifndef _BOARD_H_ #define _BOARD_H_ -#include "../definitions.h" -#include "../opentx_constants.h" -#include "hal.h" +#include "definitions.h" +#include "opentx_constants.h" #include "board_common.h" +#include "hal.h" #if !defined(LUA_EXPORT_GENERATION) #include "stm32f4xx_sdio.h" @@ -265,10 +265,10 @@ uint32_t readTrims(); extern uint32_t powerupReason; extern uint32_t boardState; -#define SHUTDOWN_REQUEST 0xDEADBEEF +/*#define SHUTDOWN_REQUEST 0xDEADBEEF #define NO_SHUTDOWN_REQUEST ~SHUTDOWN_REQUEST #define DIRTY_SHUTDOWN 0xCAFEDEAD -#define NORMAL_POWER_OFF ~DIRTY_SHUTDOWN +#define NORMAL_POWER_OFF ~DIRTY_SHUTDOWN*/ #define WDG_DURATION 500 /*ms*/ void watchdogInit(unsigned int duration); @@ -361,6 +361,35 @@ uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps #define BATTERY_MIN 35 // 3.5V #define BATTERY_MAX 42 // 4.2V +#if defined(__cplusplus) +enum PowerReason { + SHUTDOWN_REQUEST = 0xDEADBEEF, + SOFTRESET_REQUEST = 0xCAFEDEAD, +}; + +constexpr uint32_t POWER_REASON_SIGNATURE = 0x0178746F; + +inline bool UNEXPECTED_SHUTDOWN() +{ +#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) + return false; +#else + if (WAS_RESET_BY_WATCHDOG()) + return true; + else if (WAS_RESET_BY_SOFTWARE()) + return RTC->BKP0R != SOFTRESET_REQUEST; + else + return RTC->BKP1R == POWER_REASON_SIGNATURE && RTC->BKP0R != SHUTDOWN_REQUEST; +#endif +} + +inline void SET_POWER_REASON(uint32_t value) +{ + RTC->BKP0R = value; + RTC->BKP1R = POWER_REASON_SIGNATURE; +} +#endif + #if defined(__cplusplus) && !defined(SIMU) extern "C" { #endif @@ -384,11 +413,11 @@ bool pwrPressed(); #define pwrForcePressed() false #endif uint32_t pwrPressedDuration();; -#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) +/*#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) #define UNEXPECTED_SHUTDOWN() (false) #else #define UNEXPECTED_SHUTDOWN() (powerupReason == DIRTY_SHUTDOWN) -#endif +#endif*/ #define AUX_SERIAL_POWER_ON() #define AUX_SERIAL_POWER_OFF() diff --git a/radio/src/targets/nv14/pwr_driver.cpp b/radio/src/targets/nv14/pwr_driver.cpp index 65f653b39..f042fb120 100644 --- a/radio/src/targets/nv14/pwr_driver.cpp +++ b/radio/src/targets/nv14/pwr_driver.cpp @@ -89,10 +89,10 @@ bool pwrPressed() void pwrResetHandler() { if (boardState != BOARD_POWER_OFF) { - powerupReason = boardState != BOARD_REBOOT && WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() ? DIRTY_SHUTDOWN : ~DIRTY_SHUTDOWN; + powerupReason = boardState != BOARD_REBOOT && WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() ? SOFTRESET_REQUEST : SHUTDOWN_REQUEST; RCC->CSR |= RCC_CSR_RMVF; //clear all flags } else { - powerupReason = ~DIRTY_SHUTDOWN; + powerupReason = SHUTDOWN_REQUEST; } } From 4e112f690329b1c5acda09f598212fe0c66699f3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 27 Jul 2021 08:37:40 +0200 Subject: [PATCH 41/45] Fixed datacopy dependency --- radio/src/targets/nv14/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt index d7b12236b..fc0548b79 100644 --- a/radio/src/targets/nv14/CMakeLists.txt +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -73,6 +73,7 @@ set(TOUCH_DRIVER touch_driver.cpp) set(HARDWARE_TOUCH YES) set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET} truetype_fonts) +set(FIRMWARE_DEPENDENCIES datacopy) set(HARDWARE_TOUCH ON) set(SOFTWARE_KEYBOARD ON) From aede763f4cbb0ffbc1045f3c7a8cb25fb5865bd2 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 14:38:54 +0800 Subject: [PATCH 42/45] Merged remove useless powerup reason. --- radio/src/targets/nv14/board.h | 33 +++++++++------------------ radio/src/targets/nv14/pwr_driver.cpp | 5 ---- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h index fc02cfcee..d4bc4e37c 100644 --- a/radio/src/targets/nv14/board.h +++ b/radio/src/targets/nv14/board.h @@ -238,13 +238,6 @@ enum EnumSwitchesPositions }; #define STORAGE_NUM_SWITCHES_POSITIONS (STORAGE_NUM_SWITCHES * 3) -enum EnumPowerupState -{ - BOARD_POWER_OFF = 0xCAFEDEAD, - BOARD_POWER_ON = 0xDEADBEEF, - BOARD_STARTED = 0xBAADF00D, - BOARD_REBOOT = 0xC00010FF, -}; void monitorInit(); void keysInit(); @@ -261,15 +254,6 @@ uint32_t readTrims(); #define DBLKEYS_PRESSED_RGT_UP(in) (false) #define DBLKEYS_PRESSED_LFT_DWN(in) (false) -// WDT driver -extern uint32_t powerupReason; -extern uint32_t boardState; - -/*#define SHUTDOWN_REQUEST 0xDEADBEEF -#define NO_SHUTDOWN_REQUEST ~SHUTDOWN_REQUEST -#define DIRTY_SHUTDOWN 0xCAFEDEAD -#define NORMAL_POWER_OFF ~DIRTY_SHUTDOWN*/ - #define WDG_DURATION 500 /*ms*/ void watchdogInit(unsigned int duration); #if defined(SIMU) @@ -361,6 +345,16 @@ uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps #define BATTERY_MIN 35 // 3.5V #define BATTERY_MAX 42 // 4.2V +enum EnumPowerupState +{ + BOARD_POWER_OFF = 0xCAFEDEAD, + BOARD_POWER_ON = 0xDEADBEEF, + BOARD_STARTED = 0xBAADF00D, + BOARD_REBOOT = 0xC00010FF, +}; + +extern uint32_t boardState; + #if defined(__cplusplus) enum PowerReason { SHUTDOWN_REQUEST = 0xDEADBEEF, @@ -413,12 +407,7 @@ bool pwrPressed(); #define pwrForcePressed() false #endif uint32_t pwrPressedDuration();; -/*#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) - #define UNEXPECTED_SHUTDOWN() (false) -#else - #define UNEXPECTED_SHUTDOWN() (powerupReason == DIRTY_SHUTDOWN) -#endif*/ - + #define AUX_SERIAL_POWER_ON() #define AUX_SERIAL_POWER_OFF() diff --git a/radio/src/targets/nv14/pwr_driver.cpp b/radio/src/targets/nv14/pwr_driver.cpp index f042fb120..16c447ff3 100644 --- a/radio/src/targets/nv14/pwr_driver.cpp +++ b/radio/src/targets/nv14/pwr_driver.cpp @@ -21,7 +21,6 @@ #include "pwr.h" #include "board.h" -uint32_t powerupReason __NOINIT; // Stores power up reason beyond initialization for emergency mode activation uint32_t boardState __NOINIT; void pwrInit() @@ -89,10 +88,6 @@ bool pwrPressed() void pwrResetHandler() { if (boardState != BOARD_POWER_OFF) { - powerupReason = boardState != BOARD_REBOOT && WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() ? SOFTRESET_REQUEST : SHUTDOWN_REQUEST; RCC->CSR |= RCC_CSR_RMVF; //clear all flags } - else { - powerupReason = SHUTDOWN_REQUEST; - } } From a752651cda085d18a486cef995985092db065457 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 15:15:46 +0800 Subject: [PATCH 43/45] Revert common adc driver changes for NV14. --- .../targets/common/arm/stm32/adc_driver.cpp | 87 ++----------------- 1 file changed, 9 insertions(+), 78 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index a70dced97..6a1214207 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -52,17 +52,6 @@ const int8_t adcDirection[NUM_ANALOGS] = {-1,1,-1,1, 1,1, 1}; #elif defined(PCBXLITE) const int8_t adcDirection[NUM_ANALOGS] = {1,-1,-1,1, -1,1, 1, 1}; -#elif defined(PCBNV14) - const int8_t adcDirection[NUM_ANALOGS] = { 0 /*STICK1*/, 0 /*STICK2*/, 0 /*STICK3*/, 0 /*STICK4*/, - 0 /*POT1*/, 0 /*POT2*/, 0 /*SWA*/, 0 /*SWB*/, - 0 /*SWC*/, -1 /*SWD*/, 0 /*SWE*/, -1 /*SWF*/, - 0 /*SWH*/, 0 /*SWG*/, - 0 /*TX_VOLTAGE*/ }; - const uint8_t adcMapping[NUM_ANALOGS] = { 0 /*STICK1*/, 1 /*STICK2*/, 2 /*STICK3*/, 3 /*STICK4*/, - 4 /*POT1*/, 5 /*POT2*/, 6 /*SWA*/, 13 /*SWB*/, - 7 /*SWC*/, 14 /*SWD*/, 8 /*SWE*/, 9 /*SWF*/, - 11/*SWH*/, 10/*SWG*/, - 12 /*TX_VOLTAGE*/ }; #endif #if NUM_PWMSTICKS > 0 @@ -72,10 +61,6 @@ #define FIRST_ANALOG_ADC 0 #define NUM_ANALOGS_ADC 11 #define NUM_ANALOGS_ADC_EXT (NUM_ANALOGS - NUM_ANALOGS_ADC) -#elif defined(PCBNV14) && defined(FLYSKY_HALL_STICKS) - #define FIRST_ANALOG_ADC 4 - #define NUM_ANALOGS_ADC 9 - #define NUM_ANALOGS_ADC_SUB 2 #else #define FIRST_ANALOG_ADC 0 #define NUM_ANALOGS_ADC NUM_ANALOGS @@ -119,12 +104,6 @@ void adcInit() ADC_MAIN->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; ADC_MAIN->SQR1 = (NUM_ANALOGS_ADC - 1) << 20; // bits 23:20 = number of conversions -#if NUM_ANALOGS_ADC_SUB > 0 - ADC_SUB->CR1 = ADC_CR1_SCAN; - ADC_SUB->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; - ADC_SUB->SQR1 = (NUM_ANALOGS_ADC_SUB - 1) << 20; // bits 23:20 = number of conversions -#endif - #if defined(PCBX10) if (STICKS_PWM_ENABLED()) { ADC_MAIN->SQR2 = (ADC_CHANNEL_SLIDER2 << 0) + (ADC_CHANNEL_BATT << 5); @@ -158,27 +137,12 @@ void adcInit() #elif defined(PCBX9D) || defined(PCBX9DP) ADC_MAIN->SQR2 = (ADC_CHANNEL_POT3 << 0) + (ADC_CHANNEL_SLIDER1 << 5) + (ADC_CHANNEL_SLIDER2 << 10) + (ADC_CHANNEL_BATT << 15) + (ADC_Channel_Vbat << 20); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); -#elif defined(PCBNV14) - #if !defined(FLYSKY_HALL_STICKS) - ADC_MAIN->SQR1 |= (ADC_CHANNEL_BATT <<0 ); - ADC_MAIN->SQR2 = (ADC_CHANNEL_SWA << 0) + (ADC_CHANNEL_SWC << 5) + (ADC_CHANNEL_SWE << 10) + (ADC_CHANNEL_SWF << 15) + (ADC_CHANNEL_SWG << 20) + (ADC_CHANNEL_SWH << 25); // conversions 7 and more - ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 - ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 - #else - ADC_MAIN->SQR2 = (ADC_CHANNEL_SWG << 0) + (ADC_CHANNEL_SWH << 5) + (ADC_CHANNEL_BATT << 10); // conversions 7 and more - ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWC<<15) + (ADC_CHANNEL_SWE<<20) + (ADC_CHANNEL_SWF<<25); // conversions 1 to 6 - ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 - #endif #endif ADC_MAIN->SMPR1 = ADC_MAIN_SMPR1; ADC_MAIN->SMPR2 = ADC_MAIN_SMPR2; -#if defined(PCBNV14) - ADC->CCR = 0; -#else ADC->CCR = ADC_CCR_VBATE; // Enable vbat sensor -#endif ADC_DMA_Stream->CR = DMA_SxCR_PL | ADC_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; ADC_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_MAIN->DR); @@ -186,19 +150,6 @@ void adcInit() ADC_DMA_Stream->NDTR = NUM_ANALOGS_ADC; ADC_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; -#if NUM_ANALOGS_ADC_SUB > 0 - ADC_SUB->SMPR1 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24); - ADC_SUB->SMPR2 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24) + (ADC_SAMPTIME<<27); - - ADC->CCR = 0; - - ADC_SUB_DMA_Stream->CR = DMA_SxCR_PL | ADC_SUB_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; - ADC_SUB_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_SUB->DR); - ADC_SUB_DMA_Stream->M0AR = CONVERT_PTR_UINT(&adcValues[SUB_ANALOG_POS]); - ADC_SUB_DMA_Stream->NDTR = NUM_ANALOGS_ADC_SUB; - ADC_SUB_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; -#endif - #if defined(PCBX10) ADC1->CR1 = ADC_CR1_SCAN; ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; @@ -240,14 +191,6 @@ void adcSingleRead() ADC_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA ADC_MAIN->CR2 |= (uint32_t)ADC_CR2_SWSTART; -#if NUM_ANALOGS_ADC_SUB > 0 - ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA - ADC_SUB->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); - ADC_SUB_SET_DMA_FLAGS(); - ADC_SUB_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA - ADC_SUB->CR2 |= (uint32_t) ADC_CR2_SWSTART; -#endif - #if defined(PCBX10) ADC1->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); ADC1->CR2 |= (uint32_t)ADC_CR2_SWSTART; @@ -271,23 +214,11 @@ void adcSingleRead() ADC_EXT_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA #else for (unsigned int i = 0; i < 10000; i++) { -#if defined(PCBNV14) - if (ADC_TRANSFER_COMPLETE() && ADC_SUB_TRANSFER_COMPLETE()) - { + if (ADC_TRANSFER_COMPLETE()) { break; } -#else - if (ADC_TRANSFER_COMPLETE()) - { - break; - } -#endif } ADC_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA -#if defined(PCBNV14) - ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA -#endif - #endif #if defined(PCBX10) @@ -325,17 +256,17 @@ void adcRead() #endif } +#if defined(PCBX10) uint16_t getRTCBatteryVoltage() { -#if defined(PCBX10) return (rtcBatteryVoltage * 2 * ADC_VREF_PREC2) / 2048; -#elif defined(PCBNV14) - #warning "TODO RTC voltage" - return 330; -#else - return (getAnalogValue(TX_RTC_VOLTAGE) * ADC_VREF_PREC2) / 2048; -#endif } +#else +uint16_t getRTCBatteryVoltage() +{ + return (getAnalogValue(TX_RTC_VOLTAGE) * ADC_VREF_PREC2) / 2048; +} +#endif #if !defined(SIMU) uint16_t getAnalogValue(uint8_t index) @@ -346,7 +277,7 @@ uint16_t getAnalogValue(uint8_t index) // which produces ghost readings on these inputs. return 0; } -#if defined(PCBX9E) || defined(PCBNV14) +#if defined(PCBX9E) index = adcMapping[index]; #endif if (adcDirection[index] < 0) From c4b9c26fcc2232d369f56d25282bb7234da03ab1 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 15:16:27 +0800 Subject: [PATCH 44/45] Added NV14 specific definitions. --- radio/src/opentx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 42827f98e..0bb88ac0f 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -538,11 +538,11 @@ void checkBacklight() resetBacklightTimeout(); } } - #if defined(HARDWARE_TOUCH) +#if defined(HARDWARE_TOUCH) && defined(PCBNV14) if (touchPanelEventOccured() && (g_eeGeneral.backlightMode & e_backlight_mode_keys)) { resetBacklightTimeout(); } - #endif +#endif if (requiredBacklightBright == BACKLIGHT_FORCED_ON) { currentBacklightBright = g_eeGeneral.backlightBright; From afe6245ae81dbc8cc0957cf250dba512073997d9 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 27 Jul 2021 15:25:53 +0800 Subject: [PATCH 45/45] Updated extmodule change to only specific to NV14 and will not affect other radios. --- radio/src/targets/horus/extmodule_driver.cpp | 7 +++++-- tools/build-flysky.py | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 3f963a4bc..ce7c0e98e 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -167,8 +167,11 @@ void extmoduleSerialStart() EXTMODULE_TIMER->EGR = 1; // Restart EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0; #else -// EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (inverted ? 0 : TIM_CCER_CC1P); - EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; // Inverted should be false here? +#if defined(PCBNV14) + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; // inverted breaks NV14 build, inverted should be false here? +#else + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (inverted ? 0 : TIM_CCER_CC1P); +#endif EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs EXTMODULE_TIMER->CCR1 = 0; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high diff --git a/tools/build-flysky.py b/tools/build-flysky.py index c36e6c25a..0ac9936b2 100644 --- a/tools/build-flysky.py +++ b/tools/build-flysky.py @@ -35,6 +35,8 @@ def build(board, translation, srcdir): print(command) os.system(command) os.system("make firmware -j16") + os.system("make simu -j16") + os.system("make tests-radio -j16") os.chdir(cwd) index = 0 while 1: