1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-25 17:25:18 +03:00

Merge branch 'master' into dzikuvx-mag-gain-calibration

This commit is contained in:
Pawel Spychalski (DzikuVx) 2020-08-14 14:59:30 +02:00
commit d13ba61d07
138 changed files with 5066 additions and 31316 deletions

9
.gitignore vendored
View file

@ -12,10 +12,11 @@ startup_stm32f10x_md_gcc.s
.vagrant/
.vscode/
cov-int*
obj/
patches/
tools/
downloads/
/build/
/obj/
/patches/
/tools/
/downloads/
# script-generated files
docs/Manual.pdf

51
CMakeLists.txt Normal file
View file

@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.17)
set(TOOLCHAIN_OPTIONS "arm-none-eabi")
set(TOOLCHAIN "arm-none-eabi" CACHE STRING "Toolchain to use. Available: ${TOOLCHAIN_OPTIONS}")
set_property(CACHE TOOLCHAIN PROPERTY STRINGS ${TOOLCHAIN_OPTIONS})
if (NOT ${TOOLCHAIN} IN_LIST TOOLCHAIN_OPTIONS)
message(FATAL_ERROR "Invalid toolchain ${TOOLCHAIN}")
endif()
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TOOLCHAIN}.cmake")
project(INAV VERSION 2.5.0)
ENABLE_LANGUAGE(ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_program(RUBY_EXECUTABLE ruby)
if (NOT RUBY_EXECUTABLE)
message(FATAL_ERROR "Could not find ruby")
endif()
execute_process(COMMAND git rev-parse --short HEAD
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE NO_GIT_HASH
OUTPUT_VARIABLE GIT_SHORT_HASH)
if (NO_GIT_HASH)
message(FATAL_ERROR "Could not find git revision. Is git installed?")
endif()
set(INAV_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(INAV_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib")
set(INAV_UTILS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/utils")
set(INAV_MAIN_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/main")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(settings)
include(inav)
include(stm32)
add_subdirectory(src)
collect_targets()
message("-- Build type: ${CMAKE_BUILD_TYPE}")

View file

@ -465,7 +465,7 @@ clean:
## clean_test : clean up all temporary / machine-generated files (tests)
clean_test:
$(V0) cd src/test && $(MAKE) clean
$(V0) $(RM) -r src/test/build
## clean_<TARGET> : clean up one specific target
$(CLEAN_TARGETS) :
@ -533,7 +533,7 @@ help: Makefile
## test : run the cleanflight test suite
test:
$(V0) cd src/test && $(MAKE) test
$(V0) mkdir -p src/test/build && cd src/test/build && cmake .. && $(MAKE) check
# rebuild everything when makefile changes
# Make the generated files and the build stamp order only prerequisites,

63
cmake/arm-none-eabi.cmake Normal file
View file

@ -0,0 +1,63 @@
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
if(WIN32)
set(TOOL_EXECUTABLE_SUFFIX ".exe")
endif()
set(TARGET_TRIPLET "arm-none-eabi")
set(gcc "${TARGET_TRIPLET}-gcc${TOOL_EXECUTABLE_SUFFIX}")
find_program(GCC "${gcc}")
if (NOT GCC)
message(FATAL_ERROR "Could not find ${gcc}")
endif()
set(ARM_NONE_EABI_GCC_VERSION 9.2.1)
execute_process(COMMAND "${GCC}" -dumpversion
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE GCC_VERSION)
if (NOT ${ARM_NONE_EABI_GCC_VERSION} STREQUAL ${GCC_VERSION})
# TODO: Show how to override on cmdline or install builtin compiler
message(FATAL_ERROR "Expecting gcc version ${ARM_NONE_EABI_GCC_VERSION}, but found ${GCC_VERSION}")
endif()
get_filename_component(TOOLCHAIN_BIN_DIR "${GCC}" DIRECTORY)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_ASM_COMPILER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-gcc${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "asm compiler")
set(CMAKE_C_COMPILER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-gcc${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "c compiler")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-g++${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "c++ compiler")
set(CMAKE_OBJCOPY "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-objcopy${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "objcopy tool")
set(CMAKE_OBJDUMP "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-objdump${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "objdump tool")
set(CMAKE_SIZE "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-size${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "size tool")
set(CMAKE_DEBUGER "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-gdb${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "debuger")
set(CMAKE_CPPFILT "${TOOLCHAIN_BIN_DIR}/${TARGET_TRIPLET}-c++filt${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "c++filt")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_EXECUTABLE_SUFFIX ".elf")
if(NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo)
endif()
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build Type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_CONFIGURATION_TYPES})
set(arm_none_eabi_debug "-Og -g")
set(arm_none_eabi_release "-O2 -DNDEBUG -flto -fuse-linker-plugin")
set(arm_none_eabi_relwithdebinfo "-ggdb3 ${arm_none_eabi_release}")
SET(CMAKE_C_FLAGS_DEBUG ${arm_none_eabi_debug} CACHE INTERNAL "c compiler flags debug")
SET(CMAKE_CXX_FLAGS_DEBUG ${arm_none_eabi_debug} CACHE INTERNAL "c++ compiler flags debug")
SET(CMAKE_ASM_FLAGS_DEBUG ${arm_none_eabi_debug} CACHE INTERNAL "asm compiler flags debug")
SET(CMAKE_C_FLAGS_RELEASE ${arm_none_eabi_release} CACHE INTERNAL "c compiler flags release")
SET(CMAKE_CXX_FLAGS_RELEASE ${arm_none_eabi_release} CACHE INTERNAL "cxx compiler flags release")
SET(CMAKE_ASM_FLAGS_RELEASE ${arm_none_eabi_release} CACHE INTERNAL "asm compiler flags release")
SET(CMAKE_C_FLAGS_RELWITHDEBINFO ${arm_none_eabi_relwithdebinfo} CACHE INTERNAL "c compiler flags release")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${arm_none_eabi_relwithdebinfo} CACHE INTERNAL "cxx compiler flags release")
SET(CMAKE_ASM_FLAGS_RELWITHDEBINFO ${arm_none_eabi_relwithdebinfo} CACHE INTERNAL "asm compiler flags release")

65
cmake/inav.cmake Normal file
View file

@ -0,0 +1,65 @@
set(INAV_INCLUDE_DIRS
"${INAV_LIB_DIR}"
"${INAV_MAIN_SRC_DIR}"
"${INAV_LIB_DIR}/main/MAVLink"
)
# TODO: We need a way to override HSE_VALUE
set(INAV_DEFINITIONS
__FORKNAME__=inav
__REVISION__="${GIT_SHORT_HASH}"
HSE_VALUE=8000000
)
set(INAV_COMPILE_OPTIONS
-Wall
-Wextra
-Wunsafe-loop-optimizations
-Wdouble-promotion
-Wstrict-prototypes
-Werror=switch
)
macro(main_sources) # list-var
list(TRANSFORM ${ARGV0} PREPEND "${INAV_MAIN_SRC_DIR}/")
endmacro()
macro(exclude_basenames) # list-var excludes-var
set(_filtered "")
foreach(item ${${ARGV0}})
get_filename_component(basename ${item} NAME)
if (NOT ${basename} IN_LIST ${ARGV1})
list(APPEND _filtered ${item})
endif()
endforeach()
set(${ARGV0} ${_filtered})
endmacro()
macro(glob_except) # var-name pattern excludes-var
file(GLOB ${ARGV0} ${ARGV1})
exclude_basenames(${ARGV0} ${ARGV2})
endmacro()
function(setup_firmware_target name)
target_compile_options(${name} PRIVATE ${INAV_COMPILE_OPTIONS})
target_include_directories(${name} PRIVATE ${INAV_INCLUDE_DIRS})
target_compile_definitions(${name} PRIVATE ${INAV_DEFINITIONS} __TARGET__="${name}")
enable_settings(${name})
# XXX: Don't make SETTINGS_GENERATED_C part of the build,
# since it's compiled via #include in settings.c. This will
# change once we move off PGs
target_sources(${name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${name}/${SETTINGS_GENERATED_H}")
set_target_properties(${name} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
set_property(GLOBAL PROPERTY VALID_TARGETS "${targets} ${name}")
endfunction()
function(collect_targets)
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
list(SORT targets)
add_custom_target("targets"
COMMAND cmake -E echo "Valid targets: ${targets}")
set_property(TARGET "targets" PROPERTY TARGET_MESSAGES OFF)
endfunction()

25
cmake/settings.cmake Normal file
View file

@ -0,0 +1,25 @@
set(SETTINGS_GENERATED "settings_generated")
set(SETTINGS_GENERATED_C "${SETTINGS_GENERATED}.c")
set(SETTINGS_GENERATED_H "${SETTINGS_GENERATED}.h")
set(SETTINGS_FILE "${INAV_MAIN_SRC_DIR}/fc/settings.yaml")
set(SETTINGS_GENERATOR "${INAV_UTILS_DIR}/settings.rb")
function(enable_settings target)
set(dir "${CMAKE_CURRENT_BINARY_DIR}/${target}")
target_include_directories(${target} PRIVATE ${dir})
get_target_property(options ${target} COMPILE_OPTIONS)
get_target_property(includes ${target} INCLUDE_DIRECTORIES)
list(TRANSFORM includes PREPEND "-I")
get_target_property(defs ${target} COMPILE_DEFINITIONS)
list(TRANSFORM defs PREPEND "-D")
list(APPEND cflags ${options})
list(APPEND cflags ${includes})
list(APPEND cflags ${defs})
add_custom_command(
OUTPUT ${dir}/${SETTINGS_GENERATED_H} ${dir}/${SETTINGS_GENERATED_C}
COMMAND
${CMAKE_COMMAND} -E env CFLAGS="${cflags}" TARGET=${target}
${RUBY_EXECUTABLE} ${SETTINGS_GENERATOR} ${INAV_DIR} ${SETTINGS_FILE} -o "${dir}"
DEPENDS ${SETTINGS_GENERATOR} ${SETTINGS_FILE}
)
endfunction()

65
cmake/stm32-usb.cmake Normal file
View file

@ -0,0 +1,65 @@
set(STM32_STDPERIPH_USBOTG_DIR "${INAV_LIB_DIR}/main/STM32_USB_OTG_Driver")
set(STM32_STDPERIPH_USBCORE_DIR "${INAV_LIB_DIR}/main/STM32_USB_Device_Library/Core")
set(STM32_STDPERIPH_USBCDC_DIR "${INAV_LIB_DIR}/main/STM32_USB_Device_Library/Class/cdc")
set(STM32_STDPERIPH_USBHID_DIR "${INAV_LIB_DIR}/main/STM32_USB_Device_Library/Class/hid")
set(STM32_STDPERIPH_USBWRAPPER_DIR "${INAV_LIB_DIR}/main/STM32_USB_Device_Library/Class/hid_cdc_wrapper")
set(STM32_STDPERIPH_USBMSC_DIR "${INAV_LIB_DIR}/main/STM32_USB_Device_Library/Class/msc")
set(STM32_STDPERIPH_USBFS_DIR "${INAV_LIB_DIR}/main/STM32_USB-FS-Device_Driver")
set(STM32_STDPERIPH_USB_INCLUDE_DIRS
"${STM32_STDPERIPH_USBOTG_DIR}/inc"
"${STM32_STDPERIPH_USBCORE_DIR}/inc"
"${STM32_STDPERIPH_USBCDC_DIR}/inc"
"${STM32_STDPERIPH_USBHID_DIR}/inc"
"${STM32_STDPERIPH_USBWRAPPER_DIR}/inc"
"${STM32_STDPERIPH_USBMSC_DIR}/inc"
"${STM32_STDPERIPH_USBFS_DIR}/inc"
)
SET(STM32_STDPERIPH_USBOTG_SRC_EXCLUDES
usb_bsp_template.c
usb_conf_template.c
usb_hcd_int.c
usb_hcd.c
usb_otg.c
)
set(STM32_STDPERIPH_USBOTG_SRC
usb_core.c
usb_dcd.c
usb_dcd_int.c
)
list(TRANSFORM STM32_STDPERIPH_USBOTG_SRC PREPEND "${STM32_STDPERIPH_USBOTG_DIR}/src/")
set(STM32_STDPERIPH_USBCORE_SRC
usbd_core.c
usbd_ioreq.c
usbd_req.c
)
list(TRANSFORM STM32_STDPERIPH_USBCORE_SRC PREPEND "${STM32_STDPERIPH_USBCORE_DIR}/src/")
set(STM32_STDPERIPH_USBCDC_SRC
"${STM32_STDPERIPH_USBCDC_DIR}/src/usbd_cdc_core.c"
)
set(STM32_STDPERIPH_USBHID_SRC
"${STM32_STDPERIPH_USBHID_DIR}/src/usbd_hid_core.c"
)
set(STM32_STDPERIPH_USBWRAPPER_SRC
"${STM32_STDPERIPH_USBWRAPPER_DIR}/src/usbd_hid_cdc_wrapper.c"
)
set(STM32_STDPERIPH_USBMSC_SRC
usbd_msc_bot.c
usbd_msc_core.c
usbd_msc_data.c
usbd_msc_scsi.c
)
list(TRANSFORM STM32_STDPERIPH_USBMSC_SRC PREPEND "${STM32_STDPERIPH_USBMSC_DIR}/src/")
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBOTG_SRC})
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBCORE_SRC})
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBCDC_SRC})
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBHID_SRC})
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBWRAPPER_SRC})
list(APPEND STM32_STDPERIPH_USB_SRC ${STM32_STDPERIPH_USBMSC_SRC})

263
cmake/stm32.cmake Normal file
View file

@ -0,0 +1,263 @@
include(arm-none-eabi)
include(stm32-usb)
set(CMSIS_DIR "${INAV_LIB_DIR}/main/CMSIS")
set(CMSIS_INCLUDE_DIR "${CMSIS_DIR}/Core/Include")
set(CMSIS_DSP_DIR "${INAV_LIB_DIR}/main/CMSIS/DSP")
set(CMSIS_DSP_INCLUDE_DIR "${CMSIS_DSP_DIR}/Include")
set(CMSIS_DSP_SRC
BasicMathFunctions/arm_mult_f32.c
TransformFunctions/arm_rfft_fast_f32.c
TransformFunctions/arm_cfft_f32.c
TransformFunctions/arm_rfft_fast_init_f32.c
TransformFunctions/arm_cfft_radix8_f32.c
TransformFunctions/arm_bitreversal2.S
CommonTables/arm_common_tables.c
ComplexMathFunctions/arm_cmplx_mag_f32.c
StatisticsFunctions/arm_max_f32.c
)
list(TRANSFORM CMSIS_DSP_SRC PREPEND "${CMSIS_DSP_DIR}/Source/")
set(STM32_STARTUP_DIR "${INAV_MAIN_SRC_DIR}/startup")
set(STM32_VCP_SRC
drivers/serial_usb_vcp.c
drivers/usb_io.c
)
main_sources(STM32_VCP_SRC)
set(STM32_MSC_SRC
msc/usbd_msc_desc.c
msc/usbd_storage.c
)
main_sources(STM32_MSC_SRC)
set(STM32_MSC_FLASH_SRC
msc/usbd_storage_emfat.c
msc/emfat.c
msc/emfat_file.c
)
main_sources(STM32_MSC_FLASH_SRC)
set(STM32F4_STDPERIPH_DIR "${INAV_LIB_DIR}/main/STM32F4/Drivers/STM32F4xx_StdPeriph_Driver")
set(STM32F4_CMSIS_DEVICE_DIR "${INAV_LIB_DIR}/main/STM32F4/Drivers/CMSIS/Device/ST/STM32F4xx")
set(STM32F4_CMSIS_DRIVERS_DIR "${INAV_LIB_DIR}/main/STM32F4/Drivers/CMSIS")
set(STM32F4_VCP_DIR "${INAV_MAIN_SRC_DIR}/vcpf4")
set(STM32F4_STDPERIPH_SRC_EXCLUDES
stm32f4xx_can.c
stm32f4xx_cec.c
stm32f4xx_crc.c
stm32f4xx_cryp.c
stm32f4xx_cryp_aes.c
stm32f4xx_cryp_des.c
stm32f4xx_cryp_tdes.c
stm32f4xx_dbgmcu.c
stm32f4xx_dsi.c
stm32f4xx_flash_ramfunc.c
stm32f4xx_fmpi2c.c
stm32f4xx_fmc.c
stm32f4xx_hash.c
stm32f4xx_hash_md5.c
stm32f4xx_hash_sha1.c
stm32f4xx_lptim.c
stm32f4xx_qspi.c
stm32f4xx_sai.c
stm32f4xx_spdifrx.c
)
set(STM32F4_STDPERIPH_SRC_DIR "${STM32F4_STDPERIPH_DIR}/Src")
glob_except(STM32F4_STDPERIPH_SRC "${STM32F4_STDPERIPH_SRC_DIR}/*.c" STM32F4_STDPERIPH_SRC_EXCLUDES)
set(STM32F4_VCP_SRC
stm32f4xx_it.c
usb_bsp.c
usbd_desc.c
usbd_usr.c
usbd_cdc_vcp.c
)
list(TRANSFORM STM32F4_VCP_SRC PREPEND "${STM32F4_VCP_DIR}/")
set(STM32F4_MSC_SRC
drivers/usb_msc_f4xx.c
)
main_sources(STM32F4_MSC_SRC)
set(STM32F4_INCLUDE_DIRS
"${CMSIS_INCLUDE_DIR}"
"${CMSIS_DSP_INCLUDE_DIR}"
"${STM32F4_STDPERIPH_DIR}/inc"
"${STM32F4_CMSIS_DEVICE_DIR}"
"${STM32F4_CMSIS_DRIVERS_DIR}"
"${STM32F4_VCP_DIR}"
)
set(STM32_INCLUDE_DIRS
"${INAV_MAIN_SRC_DIR}/target"
)
set(STM32_LINKER_DIR "${INAV_MAIN_SRC_DIR}/target/link")
#if(SEMIHOSTING)
# set(SEMIHOSTING_DEFINITIONS "SEMIHOSTING")
# set(SEMIHOSTING_LDFLAGS
# --specs=rdimon.specs
# -lc
# -lrdimon
# )
#else()
# set(SYS)
#endif()
#ifneq ($(SEMIHOSTING),)
#SEMIHOSTING_CFLAGS = -DSEMIHOSTING
#SEMIHOSTING_LDFLAGS = --specs=rdimon.specs -lc -lrdimon
#SYSLIB :=
#else
#SEMIHOSTING_LDFLAGS =
#SEMIHOSTING_CFLAGS =
#SYSLIB := -lnosys
#endif
set(STM32_LINK_LIBRARIES
-lm
-lc
)
set(STM32_LINK_OPTIONS
-nostartfiles
--specs=nano.specs
-static
-Wl,-gc-sections,-Map,target.map
-Wl,-L${STM32_LINKER_DIR}
-Wl,--cref
-Wl,--no-wchar-size-warning
-Wl,--print-memory-usage
)
set(STM32F4_SRC
target/system_stm32f4xx.c
drivers/accgyro/accgyro.c
drivers/accgyro/accgyro_mpu.c
drivers/adc_stm32f4xx.c
drivers/adc_stm32f4xx.c
drivers/bus_i2c_stm32f40x.c
drivers/serial_softserial.c
drivers/serial_uart_stm32f4xx.c
drivers/system_stm32f4xx.c
drivers/timer.c
drivers/timer_impl_stdperiph.c
drivers/timer_stm32f4xx.c
drivers/uart_inverter.c
drivers/dma_stm32f4xx.c
drivers/sdcard/sdmmc_sdio_f4xx.c
)
main_sources(STM32F4_SRC)
set(STM32F4_DEFINITIONS
STM32F4
USE_STDPERIPH_DRIVER
ARM_MATH_MATRIX_CHECK
ARM_MATH_ROUNDING
__FPU_PRESENT=1
UNALIGNED_SUPPORT_DISABLE
ARM_MATH_CM4
)
set(STM32F4_COMMON_OPTIONS
-mthumb
-mcpu=cortex-m4
-march=armv7e-m
-mfloat-abi=hard
-mfpu=fpv4-sp-d16
-fsingle-precision-constant
)
set(STM32F4_COMPILE_OPTIONS
)
set(SETM32F4_LINK_OPTIONS
)
set(STM32F411_STDPERIPH_SRC_EXCLUDES "stm32f4xx_fsmc.c")
set(STM32F411_COMPILE_DEFINITIONS
FLASH_SIZE=512
)
macro(get_stm32_target_features) # out-var dir
file(READ "${ARGV1}/target.h" _contents)
string(REGEX MATCH "#define[\t ]+USE_VCP" HAS_VCP ${_contents})
if(HAS_VCP)
list(APPEND ${ARGV0} VCP)
endif()
string(REGEX MATCH "define[\t ]+USE_FLASHFS" HAS_FLASHFS ${_contents})
if(HAS_FLASHFS)
list(APPEND ${ARGV0} FLASHFS)
endif()
if (HAS_FLASHFS) # || SDCARD
list(APPEND ${ARGV0} MSC)
endif()
endmacro()
function(target_stm32 name)
# Main .elf target
add_executable(${name} ${COMMON_SRC} ${CMSIS_DSP_SRC})
file(GLOB target_c_sources "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
file(GLOB target_h_sources "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
target_sources(${name} PRIVATE ${target_c_sources} ${target_h_sources})
target_include_directories(${name} PRIVATE . ${STM32_INCLUDE_DIRS})
target_link_libraries(${name} PRIVATE ${STM32_LINK_LIBRARIES})
target_link_options(${name} PRIVATE ${STM32_LINK_OPTIONS})
get_stm32_target_features(features "${CMAKE_CURRENT_SOURCE_DIR}")
set_property(TARGET ${name} PROPERTY FEATURES ${features})
if(VCP IN_LIST features)
target_sources(${name} PRIVATE ${STM32_VCP_SRC})
endif()
if(MSC IN_LIST features)
target_sources(${name} PRIVATE ${STM32_MSC_SRC})
if (FLASHFS IN_LIST features)
target_sources(${name} PRIVATE ${STM32_MSC_FLASH_SRC})
endif()
endif()
# Generate .hex
set(hexdir "${CMAKE_BINARY_DIR}/hex")
set(hex "${hexdir}/$<TARGET_FILE_PREFIX:${name}>.hex")
add_custom_command(TARGET ${name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${hexdir}"
COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:${name}> "${hex}")
# clean_<target>
set(clean_target "clean_${name}")
add_custom_target(${clean_target}
COMMAND cmake -E rm -r "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Removeng intermediate files for ${name}")
set_property(TARGET ${clean_target} PROPERTY TARGET_MESSAGES OFF)
endfunction()
function(target_stm32f4xx name)
target_stm32(${name})
target_sources(${name} PRIVATE ${STM32F4_SRC})
target_compile_options(${name} PRIVATE ${STM32F4_COMMON_OPTIONS} ${STM32F4_COMPILE_OPTIONS})
target_include_directories(${name} PRIVATE ${STM32_STDPERIPH_USB_INCLUDE_DIRS} ${STM32F4_INCLUDE_DIRS})
target_compile_definitions(${name} PRIVATE ${STM32F4_DEFINITIONS})
target_link_options(${name} PRIVATE ${STM32F4_COMMON_OPTIONS} ${STM32F4_LINK_OPTIONS})
get_property(features TARGET ${name} PROPERTY FEATURES)
if(VCP IN_LIST features)
target_sources(${name} PRIVATE ${STM32_STDPERIPH_USB_SRC} ${STM32F4_VCP_SRC})
endif()
if(MSC IN_LIST features)
target_sources(${name} PRIVATE ${STM32F4_MSC_SRC})
endif()
endfunction()
function(target_stm32f411 name)
target_stm32f4xx(${name})
set(STM32F411_STDPERIPH_SRC ${STM32F4_STDPERIPH_SRC})
exclude_basenames(STM32F411_STDPERIPH_SRC STM32F411_STDPERIPH_SRC_EXCLUDES)
target_sources(${name} PRIVATE "${STM32_STARTUP_DIR}/startup_stm32f411xe.s" ${STM32F411_STDPERIPH_SRC})
target_link_options(${name} PRIVATE "-T${STM32_LINKER_DIR}/stm32_flash_f411.ld")
target_compile_definitions(${name} PRIVATE STM32F411xE ${STM32F411_COMPILE_DEFINITIONS})
setup_firmware_target(${name})
endfunction()

33
docs/Board - FLYWOOF411.md Executable file
View file

@ -0,0 +1,33 @@
# Board - FLYWOOF411
![Banggood](https://img.staticbg.com/thumb/view/oaupload/banggood/images/A5/01/79d28a2c-ef4b-4e4f-bab6-14edf66bbb23.jpg)
![Banggood](https://img.staticbg.com/images/oaupload/banggood/images/2E/C5/c14a1e86-4e58-4bc8-85de-8e344cb382b9.jpg)
![Banggood](https://img.staticbg.com/images/oaupload/banggood/images/42/F7/45a68ade-9be1-4fff-afec-bbdd45f0331d.jpg)
*Note:* This board used to be sold as a 'NOX F4' but is now an updated version similar to a Flywoo F411
## Banggood Specification:
* Model: Upgrade Betaflight F4 Noxe V1
* Version: Acro Version / Deluxe Version
* Acro Version: Without Barometer and Blackbox
* Deluxe Version: With Barometer and Blackbox
* CPU: STM32F411C
* MPU: MPU6000
* Input Voltage: Support 2-6S Lipo Input
* Built-In Betaflight OSD
* Built-in 5V @ 2.5A BEC & 8V @ 3A BEC
* 3.3V
* 4.5V powered by USB
* DShot, Proshot ESC
* Support Spektrum 1024 /2048 , SBUS, IBUS, PPM
* Built in flash for blackbox 16MB
* Support WS2812 LED strip
* Size: 27x27mm
* Mounting Hole: 20x20mm , M2.5
* Weight: 4.3g
* DSM / IBUS/SBUS Receiver, choose UART RX2
* PPM Receiver, don't need choose UART Port
## Where to buy:
* [Banggood](https://inavflight.com/shop/s/bg/1310419)

View file

@ -1,6 +1,6 @@
# Board - FOXEERF722DUAL
# Board - FOXEERF722DUAL, Foxeer F722 V2 and Foxeer F722 Mini
The FOXEERF722DUAL described here:
The Foxeer F722 DUAL, Foxeer F722 V2 and Foxeer F722 Mini described here:
This board use the STM32F722RET6 microcontroller and have the following features:
* High-performance and DSP with FPU, ARM Cortex-M7 MCU with 512 Kbytes Flash

View file

@ -123,6 +123,17 @@ A shorter form is also supported to enable and disable functions using `serial <
`serial` can also be used without any argument to print the current configuration of all the serial ports.
## Flash chip management
For targets that have a flash data chip, typically used for blackbox logs, the following additional comamnds are provided.
| Command | Effect |
| ------- | ------ |
| `flash_erase` | Erases the flash chip |
| `flash_info` | Displays flash chip information (used, free etc.) |
| `flash_read <length> <address>` | Reads `length` bytes from `address` |
| `flash_write <address> <data>` | Writes `data` to `address` |
## CLI Variable Reference
See [Settings.md](Settings.md).

View file

@ -95,9 +95,20 @@ Just connect the S.Port wire from the receiver to the TX pad of a free UART on y
#### Configuration
For INAV 2.6 and newer versions, the default configuration should just work. However, if you're
upgrading from a previous version you might need to set the following settings to their
default values:
```
set serialrx_inverted = true
set serialrx_halfduplex = true
set serialrx_inverted = OFF
set serialrx_halfduplex = AUTO
```
For INAV versions prior to 2.6, you need to change the following settings:
```
set serialrx_inverted = ON
set serialrx_halfduplex = ON
```
### XBUS

70
docs/Safehomes.md Normal file
View file

@ -0,0 +1,70 @@
# INav - Safehomes
## Introduction
The "Home" position is used for the landing point when landing is enabled or in an emergency situation. It is usually determined by the GPS location where the aircraft is armed.
For airplanes, the landing procedure is explained very well by Pawel Spychalski [here.](https://quadmeup.com/inav-1-8-automated-landing-for-fixed-wings/)
<img src="https://quadmeup.com/wp-content/uploads/2017/06/fixed-wing-landing-1024x683.png" width="600">
One potential risk when landing is that there might be buildings, trees and other obstacles in the way as the airplance circles lower toward the ground at the arming point. Most people don't go the middle of the field when arming their airplanes.
## Safehome
Safehomes are a list of GPS coordinates that identify safe landing points. When the flight controller is armed, it checks the list of safehomes. The first one that is enabled and within 200m of the current position will be selected. Otherwise, it reverts to the old behaviour of using your current GPS position as home.
You can define up to 8 safehomes for different locations you fly at.
When you are choosing safehome locations, ensure that the location is clear of obstructions for a radius more than 50m (`nav_fw_loiter_radius`). As the plane descends, the circles aren't always symmetrical, as wind direction could result in some wider or tighter turns. Also, the direction and length of the final landing stage is also unknown. You want to choose a point that has provides a margin for variation and the final landing.
## OSD Message when Armed
When the aircraft is armed, the OSD briefly shows `ARMED` and the current GPS position and current date and time.
If a safehome is selected, an additional message appears:
```
H - DIST -> SAFEHOME n <- New message
n is the Safehome index (0-7)
ARMED DIST is the distance from
GPS LATITUDE your current position to this safehome
GPS LONGITUDE
GPS PLUS CODE
CURRENT DATE
CURRENT TIME
```
The GPS details are those of the selected safehome.
To draw your attention to "HOME" being replaced, the message flashes and stays visible longer.
## CLI command `safehome` to manage safehomes
`safehome` - List all safehomes
`safehome reset` - Clears all safehomes.
`safehome <n> <enabled> <lat> <lon>` - Set the parameters of a safehome with index `<n>`.
Parameters:
* `<enabled>` - 0 is disabled, 1 is enabled.
* `<lat>` - Latitude (WGS84), in degrees * 1E7 (for example 123456789 means 12.3456789).
* `<lon>` - Longitude.
Safehomes are saved along with your regular settings and will appear in `diff` and `dump` output. Use `save` to save any changes, as with other settings.
### `safehome` example
```
# safehome
safehome 0 1 543533193 -45179273
safehome 1 1 435464846 -78654544
safehome 2 0 0 0
safehome 3 0 0 0
safehome 4 0 0 0
safehome 5 0 0 0
safehome 6 0 0 0
safehome 7 0 0 0
```

View file

@ -33,6 +33,7 @@
| antigravity_accelerator | 1 | |
| antigravity_cutoff_lpf_hz | 15 | Antigravity cutoff frequenct for Throtte filter. Antigravity is based on the difference between actual and filtered throttle input. The bigger is the difference, the bigger Antigravity gain |
| antigravity_gain | 1 | Max Antigravity gain. `1` means Antigravity is disabled, `2` means Iterm is allowed to double during rapid throttle movements |
| applied_defaults | 0 | Internal (configurator) hint. Should not be changed manually |
| baro_cal_tolerance | 150 | Baro calibration tolerance in cm. The default should allow the noisiest baro to complete calibration [cm]. |
| baro_hardware | AUTO | Selection of baro hardware. See Wiki Sensor auto detect and hardware failure detection for more info |
| baro_median_filter | ON | 3-point median filtering for barometer readouts. No reason to change this setting |
@ -52,6 +53,7 @@
| current_meter_scale | 400 | This sets the output voltage to current scaling for the current sensor in 0.1 mV/A steps. 400 is 40mV/A such as the ACS756 sensor outputs. 183 is the setting for the uberdistro with a 0.25mOhm shunt. |
| current_meter_type | ADC | ADC , VIRTUAL, NONE. The virtual current sensor, once calibrated, estimates the current value from throttle position. |
| deadband | 5 | These are values (in us) by how much RC input can be different before it's considered valid. For transmitters with jitter on outputs, this value can be increased. Defaults are zero, but can be increased up to 10 or so if rc inputs twitch while idle. |
| debug_mode | NONE | Defines debug values exposed in debug variables (developer / debugging setting) |
| disarm_kill_switch | ON | Disarms the motors independently of throttle value. Setting to OFF reverts to the old behaviour of disarming only when the throttle is low. Only applies when arming and disarming with an AUX channel. |
| display_force_sw_blink | OFF | OFF = OSD hardware blink / ON = OSD software blink. If OSD warning text/values are invisible, try setting this to ON |
| dterm_lpf2_hz | 0 | Cutoff frequency for stage 2 D-term low pass filter |
@ -135,6 +137,7 @@
| imu_dcm_ki_mag | 0 | Inertial Measurement Unit KI Gain for compass measurements |
| imu_dcm_kp | 2500 | Inertial Measurement Unit KP Gain for accelerometer measurements |
| imu_dcm_kp_mag | 10000 | Inertial Measurement Unit KP Gain for compass measurements |
| inav_allow_dead_reckoning | OFF | Defines if inav will dead-reckon over short GPS outages. May also be useful for indoors OPFLOW navigation |
| inav_auto_mag_decl | ON | Automatic setting of magnetic declination based on GPS position. When used manual magnetic declination is ignored. |
| inav_baro_epv | 100.000 | Uncertainty value for barometric sensor [cm] |
| inav_gravity_cal_tolerance | 5 | Unarmed gravity calibration tolerance level. Won't finish the calibration until estimated gravity error falls below this value. |
@ -153,6 +156,8 @@
| inav_w_z_res_v | 0.500 | Decay coefficient for estimated climb rate when baro/GPS reference for altitude is lost |
| iterm_windup | 50 | Used to prevent Iterm accumulation on during maneuvers. Iterm will be dampened when motors are reaching it's limit (when requested motor correction range is above percentage specified by this parameter) |
| ledstrip_visual_beeper | OFF | |
| log_level | `ERROR` | Defines serial debugging log level. See `docs/development/serial_printf_debugging.md` for usage. |
| log_topics | 0 | Defines serial debugging log topic. See `docs/development/serial_printf_debugging.md` for usage. |
| looptime | 1000 | This is the main loop time (in us). Changing this affects PID effect with some PID controllers (see PID section for details). A very conservative value of 3500us/285Hz should work for everyone. Setting it to zero does not limit loop time, so it will go as fast as possible. |
| ltm_update_rate | NORMAL | Defines the LTM update rate (use of bandwidth [NORMAL/MEDIUM/SLOW]). See Telemetry.md, LTM section for details. |
| mag_calibration_time | 30 | Adjust how long time the Calibration of mag will last. |
@ -199,6 +204,7 @@
| motor_direction_inverted | OFF | Use if you need to inverse yaw motor direction. |
| motor_pwm_protocol | STANDARD | Protocol that is used to send motor updates to ESCs. Possible values - STANDARD, ONESHOT125, ONESHOT42, MULTISHOT, DSHOT150, DSHOT300, DSHOT600, DSHOT1200, BRUSHED |
| motor_pwm_rate | 400 | Output frequency (in Hz) for motor pins. Default is 400Hz for motor with motor_pwm_protocol set to STANDARD. For *SHOT (e.g. ONESHOT125) values of 1000 and 2000 have been tested by the development team and are supported. It may be possible to use higher values. For BRUSHED values of 8000 and above should be used. Setting to 8000 will use brushed mode at 8kHz switching frequency. Up to 32kHz is supported for brushed. Default is 16000 for boards with brushed motors. Note, that in brushed mode, minthrottle is offset to zero. For brushed mode, set max_throttle to 2000. |
| msp_override_channels | 0 | Mask of RX channels that may be overridden by MSP `SET_RAW_RC`. Note that this requires custom firmware with `USE_RX_MSP` and `USE_MSP_RC_OVERRIDE` compile options and the `MSP RC Override` flight mode. |
| name | Empty string | Craft name |
| nav_auto_climb_rate | 500 | Maximum climb/descent rate that UAV is allowed to reach during navigation modes. [cm/s] |
| nav_auto_speed | 300 | Maximum velocity firmware is allowed in full auto modes (RTH, WP) [cm/s] [Multirotor only] |
@ -247,7 +253,7 @@
| nav_landing_speed | 200 | Vertical descent velocity during the RTH landing phase. [cm/s] |
| nav_manual_climb_rate | 200 | Maximum climb/descent rate firmware is allowed when processing pilot input for ALTHOLD control mode [cm/s] |
| nav_manual_speed | 500 | Maximum velocity firmware is allowed when processing pilot input for POSHOLD/CRUISE control mode [cm/s] [Multirotor only] |
| nav_mc_auto_disarm_delay | 2000 | |
| nav_mc_auto_disarm_delay | 2000 | Delay before multi-rotor disarms when `nav_disarm_on_landing` is set (m/s) |
| nav_mc_bank_angle | 30 | Maximum banking angle (deg) that multicopter navigation is allowed to set. Machine must be able to satisfy this angle without loosing altitude |
| nav_mc_braking_bank_angle | 40 | max angle that MR is allowed to bank in BOOST mode |
| nav_mc_braking_boost_disengage_speed | 100 | BOOST will be disabled when speed goes below this value |
@ -264,6 +270,9 @@
| nav_mc_pos_xy_p | 65 | Controls how fast the drone will fly towards the target position. This is a multiplier to convert displacement to target velocity |
| nav_mc_pos_z_p | 50 | P gain of altitude PID controller (Multirotor) |
| nav_mc_vel_xy_d | 100 | D gain of Position-Rate (Velocity to Acceleration) PID controller. It can damp P and I. Increasing D might help when drone overshoots target. |
| nav_mc_vel_xy_dterm_attenuation | 90 | Maximum D-term attenution percentage for horizontal velocity PID controller (Multirotor). It allows to smooth the PosHold CRUISE, WP and RTH when Multirotor is traveling at full speed. Dterm is not attenuated at low speeds, breaking and accelerating. |
| nav_mc_vel_xy_dterm_attenuation_end | 60 | A point (in percent of both target and current horizontal velocity) where nav_mc_vel_xy_dterm_attenuation reaches maximum |
| nav_mc_vel_xy_dterm_attenuation_start | 10" | A point (in percent of both target and current horizontal velocity) where nav_mc_vel_xy_dterm_attenuation begins |
| nav_mc_vel_xy_i | 15 | I gain of Position-Rate (Velocity to Acceleration) PID controller. Used for drift compensation (caused by wind for example). Higher I means stronger response to drift. Too much I gain might cause target overshot |
| nav_mc_vel_xy_p | 40 | P gain of Position-Rate (Velocity to Acceleration) PID controller. Higher P means stronger response when position error occurs. Too much P might cause "nervous" behavior and oscillations |
| nav_mc_vel_z_d | 10 | D gain of velocity PID controller |
@ -279,35 +288,55 @@
| nav_rth_climb_first | ON | If set to ON drone will climb to nav_rth_altitude first and head home afterwards. If set to OFF drone will head home instantly and climb on the way. |
| nav_rth_climb_ignore_emerg | OFF | If set to ON, aircraft will execute initial climb regardless of position sensor (GPS) status. |
| nav_rth_home_altitude | 0 | Aircraft will climb/descend to this altitude after reaching home if landing is not enabled. Set to 0 to stay at `nav_rth_altitude` (default) [cm] |
| nav_rth_home_offset_direction | 0 | Direction offset from GPS established home to "safe" position used for RTH (degrees, 0=N, 90=E, 180=S, 270=W, requires non-zero offset distance) |
| nav_rth_home_offset_distance | 0 | Distance offset from GPS established home to "safe" position used for RTH (cm, 0 disables) |
| nav_rth_tail_first | OFF | If set to ON drone will return tail-first. Obviously meaningless for airplanes. |
| nav_use_fw_yaw_control | OFF | Enables or Disables the use of the heading PID controller on fixed wing. Heading PID controller is always enabled for rovers and boats |
| nav_use_midthr_for_althold | OFF | If set to OFF, the FC remembers your throttle stick position when enabling ALTHOLD and treats it as a neutral midpoint for holding altitude |
| nav_user_control_mode | ATTI | Defines how Pitch/Roll input from RC receiver affects flight in POSHOLD mode: ATTI - pitch/roll controls attitude like in ANGLE mode; CRUISE - pitch/roll controls velocity in forward and right direction. |
| nav_wp_radius | 100 | Waypoint radius [cm]. Waypoint would be considered reached if machine is within this radius |
| nav_wp_safe_distance | 10000 | First waypoint in the mission should be closer than this value [cm]. A value of 0 disables this check. |
| opflow_hardware | | Selection of OPFLOW hardware. |
| osd_ahi_bordered | OFF | Shows a border/corners around the AHI region (pixel OSD only) |
| osd_ahi_height | 162 | AHI height in pixels (pixel OSD only) |
| osd_ahi_style | DEFAULT | Sets OSD Artificial Horizon style "DEFAULT" or "LINE" for the FrSky Graphical OSD. |
| osd_ahi_vertical_offset | 0 | AHI vertical offset from center (pixel OSD only) |
| osd_ahi_width | 132 | AHI width in pixels (pixel OSD only) |
| osd_alt_alarm | 100 | Value above which to make the OSD relative altitude indicator blink (meters) |
| osd_artificial_horizon_max_pitch | 20 | Max pitch, in degrees, for OSD artificial horizon |
| osd_baro_temp_alarm_max | 600 | Temperature above which the baro temperature OSD element will start blinking (decidegrees centigrade) |
| osd_baro_temp_alarm_min | -200 | Temperature under which the baro temperature OSD element will start blinking (decidegrees centigrade) |
| osd_camera_fov_h | 135 | Horizontal field of view for the camera in degres |
| osd_camera_fov_v | 85 | Vertical field of view for the camera in degres |
| osd_camera_uptilt | 0 | Set the camera uptilt for the FPV camera in degres, positive is up, negative is down, relative to the horizontal |
| osd_crosshairs_style | DEFAULT | To set the visual type for the crosshair |
| osd_current_alarm | 0 | Value above which the OSD current consumption element will start blinking. Measured in full Amperes. |
| osd_dist_alarm | 1000 | Value above which to make the OSD distance from home indicator blink (meters) |
| osd_esc_temp_alarm_max | 900 | Temperature above which the IMU temperature OSD element will start blinking (decidegrees centigrade) |
| osd_esc_temp_alarm_min | -200 | Temperature under which the IMU temperature OSD element will start blinking (decidegrees centigrade) |
| osd_estimations_wind_compensation | ON | Use wind estimation for remaining flight time/distance estimation |
| osd_failsafe_switch_layout | OFF | If enabled the OSD automatically switches to the first layout during failsafe |
| osd_force_grid | OFF | Force OSD to work in grid mode even if the OSD device supports pixel level access (mainly used for development) |
| osd_gforce_alarm | 5 | Value above which the OSD g force indicator will blink (g) |
| osd_gforce_axis_alarm_max | 5 | Value above which the OSD axis g force indicators will blink (g) |
| osd_gforce_axis_alarm_min | -5 | Value under which the OSD axis g force indicators will blink (g) |
| osd_hud_wp_disp | OFF | Controls display of the next waypoints in the HUD. |
| osd_horizon_offset | 0 | To vertically adjust the whole OSD and AHI and scrolling bars |
| osd_hud_homepoint | 0 | To 3D-display the home point location in the hud |
| osd_hud_homing | 0 | To display little arrows around the crossair showing where the home point is in the hud |
| osd_hud_margin_h | 3 | Left and right margins for the hud area |
| osd_hud_margin_v | 3 | Top and bottom margins for the hud area |
| osd_hud_radar_disp | 0 | Maximum count of nearby aircrafts or points of interest to display in the hud, as sent from an ESP32 LoRa module. Set to 0 to disable (show nothing). The nearby aircrafts will appear as markers A, B, C, etc |
| osd_hud_radar_nearest | 0 | To display an extra bar of informations at the bottom of the hud area for the closest radar aircraft found, if closest than the set value, in meters. Shows relative altitude (meters or feet, with an up or down arrow to indicate if above or below), speed (in m/s or f/s), and absolute heading (in °, 0 is north, 90 is east, 180 is south, 270 is west). Set to 0 (zero) to disable. |
| osd_hud_radar_range_max | 4000" | In meters, radar aircrafts further away than this will not be displayed in the hud |
| osd_hud_radar_range_min | 3 | In meters, radar aircrafts closer than this will not be displayed in the hud |
| osd_hud_wp_disp | 0 | How many navigation waypoints are displayed, set to 0 (zero) to disable. As sample, if set to 2, and you just passed the 3rd waypoint of the mission, you'll see markers for the 4th waypoint (marked 1) and the 5th waypoint (marked 2) |
| osd_imu_temp_alarm_max | 600 | Temperature above which the IMU temperature OSD element will start blinking (decidegrees centigrade) |
| osd_imu_temp_alarm_min | -200 | Temperature under which the IMU temperature OSD element will start blinking (decidegrees centigrade) |
| osd_left_sidebar_scroll_step | 0 | How many units each sidebar step represents. 0 means the default value for the scroll type. |
| osd_main_voltage_decimals | 1 | Number of decimals for the battery voltages displayed in the OSD [1-2]. |
| osd_neg_alt_alarm | 5 | Value bellow which (negative altitude) to make the OSD relative altitude indicator blink (meters) |
| osd_right_sidebar_scroll_step | 0 | Same as left_sidebar_scroll_step, but for the right sidebar |
| osd_row_shiftdown | 0 | Number of rows to shift the OSD display (increase if top rows are cut off) |
| osd_rssi_alarm | 20 | Value bellow which to make the OSD RSSI indicator blink |
| osd_sidebar_horizontal_offset | 0 | Sidebar horizontal offset from default position. Positive values move the sidebars closer to the edges. |
| osd_stats_energy_unit | MAH | Unit used for the drawn energy in the OSD stats [MAH/WH] (milliAmpere hour/ Watt hour) |
| osd_temp_label_align | LEFT | Allows to chose between left and right alignment for the OSD temperature sensor labels. Valid values are `LEFT` and `RIGHT` |
| osd_time_alarm | 10 | Value above which to make the OSD flight time indicator blink (minutes) |
@ -317,8 +346,10 @@
| pidsum_limit | 500 | A limitation to overall amount of correction Flight PID can request on each axis (Roll/Pitch). If when doing a hard maneuver on one axis machine looses orientation on other axis - reducing this parameter may help |
| pidsum_limit_yaw | 400 | A limitation to overall amount of correction Flight PID can request on each axis (Yaw). If when doing a hard maneuver on one axis machine looses orientation on other axis - reducing this parameter may help |
| pitch_rate | 20 | Defines rotation rate on PITCH axis that UAV will try to archive on max. stick deflection. Rates are defined in tens of degrees (deca-degrees) per second [rate = dps/10]. That means, rate 20 represents 200dps rotation speed. Default 20 (200dps) is more less equivalent of old Cleanflight/Baseflight rate 0. Max. 180 (1800dps) is what gyro can measure. |
| pitot_hardware | NONE | Selection of pitot hardware. |
| platform_type | "MULTIROTOR" | Defines UAV platform type. Allowed values: "MULTIROTOR", "AIRPLANE", "HELICOPTER", "TRICOPTER", "ROVER", "BOAT". Currently only MULTIROTOR, AIRPLANE and TRICOPTER types are implemented |
| pos_hold_deadband | 20 | Stick deadband in [r/c points], applied after r/c deadband and expo |
| rangefinder_hardware | NONE | Selection of rangefinder hardware. |
| rangefinder_median_filter | OFF | 3-point median filtering for rangefinder readouts |
| rate_accel_limit_roll_pitch | 0 | Limits acceleration of ROLL/PITCH rotation speed that can be requested by stick input. In degrees-per-second-squared. Small and powerful UAV flies great with high acceleration limit ( > 5000 dps^2 and even > 10000 dps^2). Big and heavy multirotors will benefit from low acceleration limit (~ 360 dps^2). When set correctly, it greatly improves stopping performance. Value of 0 disables limiting. |
| rate_accel_limit_yaw | 10000 | Limits acceleration of YAW rotation speed that can be requested by stick input. In degrees-per-second-squared. Small and powerful UAV flies great with high acceleration limit ( > 10000 dps^2). Big and heavy multirotors will benefit from low acceleration limit (~ 180 dps^2). When set correctly, it greatly improves stopping performance and general stability during yaw turns. Value of 0 disables limiting. |
@ -326,6 +357,7 @@
| rc_filter_frequency | 50 | RC data biquad filter cutoff frequency. Lower cutoff frequencies result in smoother response at expense of command control delay. Practical values are 20-50. Set to zero to disable entirely and use unsmoothed RC stick values |
| rc_yaw_expo | 20 | Exposition value used for the YAW axis by all the stabilized flights modes (all but `MANUAL`) |
| reboot_character | 82 | Special character used to trigger reboot |
| receiver_type | `TARGET dependent` | Selection of receiver (RX) type. Additional configuration of a `serialrx_provider` and a UART will be needed for `SERIAL` |
| report_cell_voltage | OFF | S.Port, D-Series, and IBUS telemetry: Send the average cell voltage if set to ON |
| roll_rate | 20 | Defines rotation rate on ROLL axis that UAV will try to archive on max. stick deflection. Rates are defined in tens of degrees (deca-degrees) per second [rate = dps/10]. That means, rate 20 represents 200dps rotation speed. Default 20 (200dps) is more less equivalent of old Cleanflight/Baseflight rate 0. Max. 180 (1800dps) is what gyro can measure. |
| rpm_gyro_filter_enabled | `OFF` | Enables gyro RPM filtere. Set to `ON` only when ESC telemetry is working and rotation speed of the motors is correctly reported to INAV |
@ -341,13 +373,17 @@
| rx_max_usec | 2115 | Defines the longest pulse width value used when ensuring the channel value is valid. If the receiver gives a pulse value higher than this value then the channel will be marked as bad and will default to the value of mid_rc. |
| rx_min_usec | 885 | Defines the shortest pulse width value used when ensuring the channel value is valid. If the receiver gives a pulse value lower than this value then the channel will be marked as bad and will default to the value of mid_rc. |
| sdcard_detect_inverted | `TARGET dependent` | This setting drives the way SD card is detected in card slot. On some targets (AnyFC F7 clone) different card slot was used and depending of hardware revision ON or OFF setting might be required. If card is not detected, change this value. |
| serialrx_halfduplex | OFF | Allow serial receiver to operate on UART TX pin. With some receivers will allow control and telemetry over a single wire |
| serialrx_halfduplex | AUTO | Allow serial receiver to operate on UART TX pin. With some receivers will allow control and telemetry over a single wire |
| serialrx_inverted | OFF | Reverse the serial inversion of the serial RX protocol. When this value is OFF, each protocol will use its default signal (e.g. SBUS will use an inverted signal). Some OpenLRS receivers produce a non-inverted SBUS signal. This setting supports this type of receivers (including modified FrSKY). |
| serialrx_provider | SPEK1024 | When feature SERIALRX is enabled, this allows connection to several receivers which output data via digital interface resembling serial. See RX section. |
| servo_center_pulse | 1500 | Servo midpoint |
| servo_lpf_hz | 20 | Selects the servo PWM output cutoff frequency. Value is in [Hz] |
| servo_protocol | PWM | An option to chose the protocol/option that would be used to output servo data. Possible options `PWM` (FC servo outputs), `SERVO_DRIVER` (I2C PCA9685 peripheral), `SBUS` (S.Bus protocol output via a configured serial port) |
| servo_pwm_rate | 50 | Output frequency (in Hz) servo pins. When using tricopters or gimbal with digital servo, this rate can be increased. Max of 498Hz (for 500Hz pwm period), and min of 50Hz. Most digital servos will support for example 330Hz. |
| setpoint_kalman_enabled | OFF | Enable Kalman filter on the PID controller setpoint |
| setpoint_kalman_q | 100 | Quality factor of the setpoint Kalman filter. Higher values means less filtering and lower phase delay. On 3-7 inch multirotors can be usually increased to 200-300 or even higher of clean builds |
| setpoint_kalman_sharpness | 100 | Dynamic factor for the setpoint Kalman filter. In general, the higher the value, the more dynamic Kalman filter gets |
| setpoint_kalman_w | 4 | Window size for the setpoint Kalman filter. Wider the window, more samples are used to compute variance. In general, wider window results in smoother filter response |
| sim_ground_station_number | Empty string | Number of phone that is used to communicate with SIM module. Messages / calls from other numbers are ignored. If undefined, can be set by calling or sending a message to the module. |
| sim_low_altitude | 0 | Threshold for low altitude warning messages sent by SIM module when the 'L' transmit flag is set in `sim_transmit_flags`. |
| sim_pin | Empty string | PIN code for the SIM module |
@ -385,6 +421,7 @@
| vtx_channel | 1 | Channel to use within the configured `vtx_band`. Valid values are [1, 8]. |
| vtx_halfduplex | ON | Use half duplex UART to communicate with the VTX, using only a TX pin in the FC. |
| vtx_low_power_disarm | OFF | When the craft is disarmed, set the VTX to its lowest power. `ON` will set the power to its minimum value on startup, increase it to `vtx_power` when arming and change it back to its lowest setting after disarming. `UNTIL_FIRST_ARM` will start with minimum power, but once the craft is armed it will increase to `vtx_power` and it will never decrease until the craft is power cycled. |
| vtx_max_power_override | 0 | Some VTXes may report max power incorrectly (i.e. 200mW for a 600mW VTX). Use this to override max supported power. 0 to disable and use whatever VTX reports as its capabilities |
| vtx_power | 1 | VTX RF power level to use. The exact number of mw depends on the VTX hardware. |
| yaw_deadband | 5 | These are values (in us) by how much RC input can be different before it's considered valid. For transmitters with jitter on outputs, this value can be increased. Defaults are zero, but can be increased up to 10 or so if rc inputs twitch while idle. |
| yaw_lpf_hz | 30 | Yaw low pass filter cutoff frequency. Should be disabled (set to `0`) on small multirotors (7 inches and below) |

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,38 +0,0 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from gtest_main.cc\n");
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -1,6 +1,6 @@
RELEASE_TARGETS = AIRHEROF3 AIRHEROF3_QUAD
RELEASE_TARGETS += LUX_RACE FURYF3 FURYF3_SPIFLASH RCEXPLORERF3 RMDO SPARKY KFC32F3_INAV FALCORE MOTOLAB ANYFC BLUEJAYF4 COLIBRI F4BY DALRCF405 DALRCF722DUAL
RELEASE_TARGETS += LUX_RACE RCEXPLORERF3 RMDO SPARKY KFC32F3_INAV FALCORE MOTOLAB ANYFC BLUEJAYF4 COLIBRI F4BY DALRCF405 DALRCF722DUAL
RELEASE_TARGETS += QUANTON REVO SPARKY2 PIKOBLX CLRACINGF4AIR CLRACINGF4AIRV2 PIXRACER BEEROTORF4 ANYFCF7 ANYFCF7_EXTERNAL_BARO
RELEASE_TARGETS += ALIENFLIGHTNGF7
@ -8,7 +8,7 @@ RELEASE_TARGETS += BETAFLIGHTF3 BETAFLIGHTF4
RELEASE_TARGETS += FF_F35_LIGHTNING FF_FORTINIF4
RELEASE_TARGETS += KAKUTEF4 KAKUTEF4V2 KAKUTEF7 KAKUTEF7MINI KAKUTEF7HDV
RELEASE_TARGETS += SPRACINGF3 SPRACINGF3EVO SPRACINGF3EVO_1SS SPRACINGF3MINI SPRACINGF4EVO SPRACINGF7DUAL
RELEASE_TARGETS += SPRACINGF3 SPRACINGF3MINI SPRACINGF4EVO SPRACINGF7DUAL
RELEASE_TARGETS += AIRBOTF4 ASGARD32F4 ASGARD32F7 FIREWORKSV2 AIRBOTF7 OMNIBUSF7NANOV7
RELEASE_TARGETS += OMNIBUSF4 OMNIBUSF4PRO OMNIBUSF4PRO_LEDSTRIPM5 DYSF4PRO DYSF4PROV2
@ -19,9 +19,9 @@ RELEASE_TARGETS += OMNIBUSF7 OMNIBUSF7V2 OMNIBUSF7NXT YUPIF7
RELEASE_TARGETS += MATEKF405 MATEKF405_SERVOS6 MATEKF405OSD MATEKF722 MATEKF722_HEXSERVO MATEKF722SE MATEKF722MINI MATEKF405SE MATEKF411 MATEKF411_SFTSRL2 MATEKF411_FD_SFTSRL MATEKF411_RSSI MATEKF411SE MATEKF765 MATEKF722PX
RELEASE_TARGETS += MATEKF765
RELEASE_TARGETS += FOXEERF405 FOXEERF722DUAL
RELEASE_TARGETS += FOXEERF405 FOXEERF722DUAL FOXEERF722V2
RELEASE_TARGETS += SPEEDYBEEF4 FRSKYF3 FRSKYF4
RELEASE_TARGETS += SPEEDYBEEF4 SPEEDYBEEF7 FRSKYF3 FRSKYF4
RELEASE_TARGETS += NOX WINGFC
@ -35,3 +35,9 @@ RELEASE_TARGETS += IFLIGHTF7_TWING IFLIGHTF4_TWING IFLIGHTF4_SUCCEXD
RELEASE_TARGETS += AIKONF4
RELEASE_TARGETS += ZEEZF7 HGLRCF722
RELEASE_TARGETS += FLYWOOF7DUAL FLYWOOF411
RELEASE_TARGETS += ZEEZF7
RELEASE_TARGETS += FRSKYPILOT FRSKY_ROVERF7

View file

@ -50,6 +50,7 @@ MAIN_SRC = \
drivers/display.c \
drivers/display_canvas.c \
drivers/display_font_metadata.c \
drivers/display_widgets.c \
drivers/exti.c \
drivers/io_pca9685.c \
drivers/io_pcf8574.c \
@ -128,6 +129,7 @@ MAIN_SRC = \
rx/crsf.c \
rx/eleres.c \
rx/fport.c \
rx/fport2.c \
rx/ibus.c \
rx/jetiexbus.c \
rx/msp.c \

View file

@ -72,13 +72,13 @@ $(error Unknown target MCU specified.)
endif
GROUP_1_TARGETS := AIRHEROF3 AIRHEROF3_QUAD LUX_RACE SPARKY REVO SPARKY2 COLIBRI FALCORE FF_F35_LIGHTNING FF_FORTINIF4 FF_PIKOF4 FF_PIKOF4OSD
GROUP_2_TARGETS := SPRACINGF3 SPRACINGF3EVO SPRACINGF3EVO_1SS SPRACINGF3MINI SPRACINGF4EVO CLRACINGF4AIR CLRACINGF4AIRV2 BEEROTORF4 BETAFLIGHTF3 BETAFLIGHTF4 PIKOBLX
GROUP_2_TARGETS := SPRACINGF3 SPRACINGF3MINI SPRACINGF4EVO CLRACINGF4AIR CLRACINGF4AIRV2 BEEROTORF4 BETAFLIGHTF3 BETAFLIGHTF4 PIKOBLX
GROUP_3_TARGETS := AIRBOTF4 BLUEJAYF4 OMNIBUSF4 OMNIBUSF4PRO FIREWORKSV2 SPARKY2 MATEKF405 OMNIBUSF7 DYSF4PRO OMNIBUSF4PRO_LEDSTRIPM5 OMNIBUSF7NXT OMNIBUSF7V2 ASGARD32F4
GROUP_4_TARGETS := ANYFC ANYFCF7 ANYFCF7_EXTERNAL_BARO ALIENFLIGHTNGF7 PIXRACER YUPIF7 MATEKF405SE MATEKF411 MATEKF722 MATEKF405OSD MATEKF405_SERVOS6 NOX
GROUP_5_TARGETS := ASGARD32F7 CLRACINGF4AIRV3 DALRCF405 DALRCF722DUAL DYSF4PROV2 F4BY FISHDRONEF4 FOXEERF405 FOXEERF722DUAL FRSKYF3 FRSKYF4 FURYF3 FURYF3_SPIFLASH FURYF4OSD
GROUP_5_TARGETS := ASGARD32F7 CLRACINGF4AIRV3 DALRCF405 DALRCF722DUAL DYSF4PROV2 F4BY FISHDRONEF4 FOXEERF405 FOXEERF722DUAL FRSKYF3 FRSKYF4 FURYF4OSD
GROUP_6_TARGETS := MAMBAF405 OMNIBUSF4V3 OMNIBUSF4V3_S6_SS OMNIBUSF4V3_S5S6_SS OMNIBUSF4V3_S5_S6_2SS AIKONF4
GROUP_7_TARGETS := KAKUTEF4 KAKUTEF4V2 KAKUTEF7 KAKUTEF7MINI KFC32F3_INAV MATEKF411_RSSI MATEKF411_SFTSRL2 MATEKF722MINI MATEKF722SE MATEKF722_HEXSERVO
GROUP_8_TARGETS := MATEKF765 MATEKF722PX KAKUTEF7HDV ZEEZF7
GROUP_8_TARGETS := MATEKF765 MATEKF722PX KAKUTEF7HDV ZEEZF7 FOXEERF722V2 FRSKYPILOT FRSKY_ROVERF7
GROUP_OTHER_TARGETS := $(filter-out $(GROUP_1_TARGETS) $(GROUP_2_TARGETS) $(GROUP_3_TARGETS) $(GROUP_4_TARGETS) $(GROUP_5_TARGETS) $(GROUP_6_TARGETS) $(GROUP_7_TARGETS) $(GROUP_8_TARGETS), $(VALID_TARGETS))
## targets-group-1 : build some targets

276
src/main/CMakeLists.txt Normal file
View file

@ -0,0 +1,276 @@
set(COMMON_SRC
main.c
target/common_hardware.c
build/assert.c
build/build_config.c
build/debug.c
build/version.c
common/bitarray.c
common/calibration.c
common/colorconversion.c
common/crc.c
common/encoding.c
common/filter.c
common/gps_conversion.c
common/log.c
common/logic_condition.c
common/global_functions.c
common/global_variables.c
common/maths.c
common/memory.c
common/olc.c
common/printf.c
common/streambuf.c
common/string_light.c
common/time.c
common/typeconversion.c
common/uvarint.c
config/config_eeprom.c
config/config_streamer.c
config/feature.c
config/parameter_group.c
config/general_settings.c
drivers/adc.c
drivers/buf_writer.c
drivers/bus.c
drivers/bus_busdev_i2c.c
drivers/bus_busdev_spi.c
drivers/bus_i2c_soft.c
drivers/bus_spi.c
drivers/display.c
drivers/display_canvas.c
drivers/display_font_metadata.c
drivers/exti.c
drivers/io.c
drivers/io_pca9685.c
drivers/irlock.c
drivers/light_led.c
drivers/osd.c
drivers/persistent.c
drivers/resource.c
drivers/rx_nrf24l01.c
drivers/rx_spi.c
drivers/rx_xn297.c
drivers/pitotmeter_adc.c
drivers/pitotmeter_virtual.c
drivers/pwm_esc_detect.c
drivers/pwm_mapping.c
drivers/pwm_output.c
drivers/pinio.c
drivers/rcc.c
drivers/rx_pwm.c
drivers/serial.c
drivers/serial_uart.c
drivers/sound_beeper.c
drivers/stack_check.c
drivers/system.c
drivers/time.c
drivers/timer.c
drivers/usb_msc.c
drivers/lights_io.c
drivers/1-wire.c
drivers/1-wire/ds_crc.c
drivers/1-wire/ds2482.c
drivers/temperature/ds18b20.c
drivers/temperature/lm75.c
drivers/pitotmeter_ms4525.c
fc/cli.c
fc/config.c
fc/controlrate_profile.c
fc/fc_core.c
fc/fc_init.c
fc/fc_tasks.c
fc/fc_hardfaults.c
fc/fc_msp.c
fc/fc_msp_box.c
fc/rc_smoothing.c
fc/rc_adjustments.c
fc/rc_controls.c
fc/rc_curves.c
fc/rc_modes.c
fc/runtime_config.c
fc/settings.c
fc/stats.c
flight/failsafe.c
flight/hil.c
flight/imu.c
flight/mixer.c
flight/pid.c
flight/pid_autotune.c
flight/rth_estimator.c
flight/servos.c
flight/wind_estimator.c
flight/gyroanalyse.c
flight/rpm_filter.c
flight/dynamic_gyro_notch.c
io/beeper.c
io/esc_serialshot.c
io/servo_sbus.c
io/frsky_osd.c
io/osd_dji_hd.c
io/lights.c
io/piniobox.c
io/pwmdriver_i2c.c
io/serial.c
io/serial_4way.c
io/serial_4way_avrootloader.c
io/serial_4way_stk500v2.c
io/statusindicator.c
io/rcdevice.c
io/rcdevice_cam.c
msp/msp_serial.c
rx/crsf.c
rx/eleres.c
rx/fport.c
rx/ibus.c
rx/jetiexbus.c
rx/msp.c
rx/msp_override.c
rx/nrf24_cx10.c
rx/nrf24_inav.c
rx/nrf24_h8_3d.c
rx/nrf24_syma.c
rx/nrf24_v202.c
rx/pwm.c
rx/frsky_crc.c
rx/rx.c
rx/rx_spi.c
rx/sbus.c
rx/sbus_channels.c
rx/spektrum.c
rx/sumd.c
rx/sumh.c
rx/uib_rx.c
rx/xbus.c
scheduler/scheduler.c
sensors/acceleration.c
sensors/battery.c
sensors/boardalignment.c
sensors/compass.c
sensors/diagnostics.c
sensors/gyro.c
sensors/initialisation.c
sensors/esc_sensor.c
sensors/irlock.c
sensors/temperature.c
uav_interconnect/uav_interconnect_bus.c
uav_interconnect/uav_interconnect_rangefinder.c
blackbox/blackbox.c
blackbox/blackbox_encoding.c
blackbox/blackbox_io.c
cms/cms.c
cms/cms_menu_battery.c
cms/cms_menu_blackbox.c
cms/cms_menu_builtin.c
cms/cms_menu_imu.c
cms/cms_menu_ledstrip.c
cms/cms_menu_misc.c
cms/cms_menu_mixer_servo.c
cms/cms_menu_navigation.c
cms/cms_menu_osd.c
cms/cms_menu_saveexit.c
cms/cms_menu_vtx.c
drivers/accgyro/accgyro_mpu6000.c
drivers/accgyro/accgyro_mpu6500.c
drivers/barometer/barometer_bmp085.c
drivers/barometer/barometer_bmp280.c
drivers/barometer/barometer_ms56xx.c
drivers/compass/compass_ak8975.c
drivers/compass/compass_hmc5883l.c
drivers/compass/compass_mag3110.c
drivers/compass/compass_qmc5883l.c
drivers/compass/compass_ist8308.c
drivers/compass/compass_ist8310.c
drivers/compass/compass_lis3mdl.c
drivers/display_ug2864hsweg01.c
drivers/flash.c
drivers/flash_m25p16.c
drivers/light_ws2811strip.c
drivers/max7456.c
drivers/rangefinder/rangefinder_hcsr04.c
drivers/rangefinder/rangefinder_hcsr04_i2c.c
drivers/rangefinder/rangefinder_srf10.c
drivers/rangefinder/rangefinder_vl53l0x.c
drivers/rangefinder/rangefinder_virtual.c
drivers/opflow/opflow_fake.c
drivers/opflow/opflow_virtual.c
drivers/pitotmeter_adc.c
drivers/vtx_common.c
io/rangefinder_msp.c
io/rangefinder_benewake.c
io/opflow_cxof.c
io/opflow_msp.c
io/dashboard.c
io/displayport_frsky_osd.c
io/displayport_max7456.c
io/displayport_msp.c
io/displayport_oled.c
io/displayport_hott.c
io/flashfs.c
io/gps.c
io/gps_ublox.c
io/gps_nmea.c
io/gps_naza.c
io/ledstrip.c
io/osd.c
io/osd_canvas.c
io/osd_common.c
io/osd_grid.c
io/osd_hud.c
navigation/navigation.c
navigation/navigation_fixedwing.c
navigation/navigation_fw_launch.c
navigation/navigation_geo.c
navigation/navigation_multicopter.c
navigation/navigation_pos_estimator.c
navigation/navigation_pos_estimator_agl.c
navigation/navigation_pos_estimator_flow.c
navigation/navigation_rover_boat.c
sensors/barometer.c
sensors/pitotmeter.c
sensors/rangefinder.c
sensors/opflow.c
telemetry/crsf.c
telemetry/frsky.c
telemetry/frsky_d.c
telemetry/hott.c
telemetry/ibus_shared.c
telemetry/ibus.c
telemetry/ltm.c
telemetry/mavlink.c
telemetry/msp_shared.c
telemetry/smartport.c
telemetry/sim.c
telemetry/telemetry.c
io/vtx.c
io/vtx_string.c
io/vtx_smartaudio.c
io/vtx_tramp.c
io/vtx_ffpv24g.c
io/vtx_control.c
)
main_sources(COMMON_SRC)
add_subdirectory(target)

View file

@ -63,7 +63,6 @@ typedef enum {
DEBUG_REM_FLIGHT_TIME,
DEBUG_SMARTAUDIO,
DEBUG_ACC,
DEBUG_ITERM_RELAX,
DEBUG_ERPM,
DEBUG_RPM_FILTER,
DEBUG_RPM_FREQ,
@ -72,7 +71,8 @@ typedef enum {
DEBUG_DYNAMIC_FILTER_FREQUENCY,
DEBUG_IRLOCK,
DEBUG_CD,
DEBUG_KALMAN,
DEBUG_KALMAN_GAIN,
DEBUG_PID_MEASUREMENT,
DEBUG_SPM_CELLS, // Smartport master FLVSS
DEBUG_SPM_VS600, // Smartport master VS600 VTX
DEBUG_SPM_VARIO, // Smartport master variometer

View file

@ -1314,6 +1314,7 @@ void cmsUpdate(uint32_t currentTimeUs)
rcDelayMs = BUTTON_PAUSE; // Tends to overshoot if BUTTON_TIME
}
} else {
displayBeginTransaction(pCurrentDisplay, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
// Check if we're yielding and its's time to stop it
if (cmsYieldUntil > 0 && currentTimeMs > cmsYieldUntil) {
@ -1339,6 +1340,7 @@ void cmsUpdate(uint32_t currentTimeUs)
displayHeartbeat(pCurrentDisplay);
lastCmsHeartBeatMs = currentTimeMs;
}
displayCommitTransaction(pCurrentDisplay);
}
// Some key (command), notably flash erase, takes too long to use the

View file

@ -202,7 +202,7 @@ static const OSD_Entry cmsx_menuServoMixerEntries[] =
OSD_UINT8_CALLBACK_ENTRY("SERVO MIX", cmsx_menuServoMixerIndexOnChange, (&(const OSD_UINT8_t){ &tmpcurrentServoMixerIndex, 1, MAX_SERVO_RULES, 1})),
OSD_UINT8_DYN_ENTRY("SERVO", (&(const OSD_UINT8_t){ &tmpServoMixer.targetChannel, 0, MAX_SUPPORTED_SERVOS, 1})),
OSD_TAB_DYN_ENTRY("INPUT", (&(const OSD_TAB_t){ &tmpServoMixer.inputSource, SERVO_MIXER_INPUT_CMS_NAMES_COUNT - 1, servoMixerInputCmsNames})),
OSD_INT16_DYN_ENTRY("WEIGHT", (&(const OSD_INT16_t){&tmpServoMixer.rate, 0, 1000, 1})),
OSD_INT16_DYN_ENTRY("WEIGHT", (&(const OSD_INT16_t){&tmpServoMixer.rate, -1000, 1000, 1})),
OSD_UINT8_DYN_ENTRY("SPEED", (&(const OSD_UINT8_t){&tmpServoMixer.speed, 0, 255, 1})),
OSD_BACK_AND_END_ENTRY
};

View file

@ -75,7 +75,7 @@ static long cmsx_osdElementOnChange(displayPort_t *displayPort, const void *ptr)
{
UNUSED(ptr);
uint16_t *pos = &osdConfigMutable()->item_pos[osdCurrentLayout][osdCurrentItem];
uint16_t *pos = &osdLayoutsConfigMutable()->item_pos[osdCurrentLayout][osdCurrentItem];
*pos = OSD_POS(osdCurrentElementColumn, osdCurrentElementRow);
if (osdCurrentElementVisible) {
*pos |= OSD_VISIBLE_FLAG;
@ -125,7 +125,7 @@ static CMS_Menu cmsx_menuOsdElementActions = {
static long osdElemActionsOnEnter(const OSD_Entry *from)
{
osdCurrentItem = from->itemId;
uint16_t pos = osdConfig()->item_pos[osdCurrentLayout][osdCurrentItem];
uint16_t pos = osdLayoutsConfig()->item_pos[osdCurrentLayout][osdCurrentItem];
osdCurrentElementColumn = OSD_X(pos);
osdCurrentElementRow = OSD_Y(pos);
osdCurrentElementVisible = OSD_VISIBLE(pos) ? 1 : 0;

View file

@ -142,15 +142,6 @@ int32_t applyDeadband(int32_t value, int32_t deadband)
return value;
}
float fapplyDeadbandf(float value, float deadband)
{
if (fabsf(value) < deadband) {
return 0;
}
return value >= 0 ? value - deadband : value + deadband;
}
int constrain(int amt, int low, int high)
{
if (amt < low)

View file

@ -130,7 +130,6 @@ bool sensorCalibrationSolveForScale(sensorCalibrationState_t * state, float resu
int gcd(int num, int denom);
int32_t applyDeadband(int32_t value, int32_t deadband);
float fapplyDeadbandf(float value, float deadband);
int constrain(int amt, int low, int high);
float constrainf(float amt, float low, float high);

View file

@ -0,0 +1,56 @@
/*
* This file is part of iNav.
*
* iNav is free software. You can redistribute this software
* and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* iNav is distributed in the hope that they will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
// tristate_e represents something that can take a default AUTO
// value and two explicit ON and OFF values. To ease the transition
// from boolean settings (0 = OFF, 1 = ON), the 1 value has
// been picked as ON while OFF is represented by 2. AUTO is represented
// by 0.
typedef enum {
TRISTATE_AUTO = 0,
TRISTATE_ON = 1,
TRISTATE_OFF = 2,
} tristate_e;
// tristateWithDefaultOnIsActive returns false is tristate is TRISTATE_OFF
// and true otherwise.
static inline bool tristateWithDefaultOnIsActive(tristate_e tristate)
{
return tristate != TRISTATE_OFF;
}
// tristateWithDefaultOffIsActive returns true is tristate is TRISTATE_ON
// and false otherwise.
static inline bool tristateWithDefaultOffIsActive(tristate_e tristate)
{
return tristate == TRISTATE_ON;
}
// tristateWithDefaultIsActive() calls tristateWithDefaultOnIsActive() when
// def is true, and tristateWithDefaultOffIsActive() otherwise.
// See tristateWithDefaultOnIsActive() and tristateWithDefaultOffIsActive()
static inline bool tristateWithDefaultIsActive(tristate_e tristate, bool def)
{
return def ? tristateWithDefaultOnIsActive(tristate) : tristateWithDefaultOffIsActive(tristate);
}

View file

@ -112,7 +112,9 @@
#define PG_RPM_FILTER_CONFIG 1022
#define PG_GLOBAL_VARIABLE_CONFIG 1023
#define PG_SMARTPORT_MASTER_CONFIG 1024
#define PG_INAV_END 1024
#define PG_OSD_LAYOUTS_CONFIG 1025
#define PG_SAFE_HOME_CONFIG 1026
#define PG_INAV_END 1026
// OSD configuration (subject to change)
//#define PG_OSD_FONT_CONFIG 2047

View file

@ -273,3 +273,8 @@ void displayCanvasContextPop(displayCanvas_t *displayCanvas)
displayCanvas->vTable->contextPop(displayCanvas);
}
}
bool displayCanvasGetWidgets(displayWidgets_t *widgets, const displayCanvas_t *displayCanvas)
{
return displayCanvas && displayCanvas->vTable->getWidgets ? displayCanvas->vTable->getWidgets(widgets, displayCanvas) : false;
}

View file

@ -29,6 +29,8 @@
#include <stdbool.h>
#include <stdint.h>
typedef struct displayWidgets_s displayWidgets_t;
typedef enum {
DISPLAY_CANVAS_BITMAP_OPT_INVERT_COLORS = 1 << 0,
DISPLAY_CANVAS_BITMAP_OPT_SOLID_BACKGROUND = 1 << 1,
@ -100,8 +102,9 @@ typedef struct displayCanvasVTable_s {
void (*contextPush)(displayCanvas_t *displayCanvas);
void (*contextPop)(displayCanvas_t *displayCanvas);
} displayCanvasVTable_t;
bool (*getWidgets)(displayWidgets_t *widgets, const displayCanvas_t *displayCanvas);
} displayCanvasVTable_t;
void displayCanvasSetStrokeColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void displayCanvasSetFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
@ -141,3 +144,5 @@ void displayCanvasCtmRotate(displayCanvas_t *displayCanvas, float r);
void displayCanvasContextPush(displayCanvas_t *displayCanvas);
void displayCanvasContextPop(displayCanvas_t *displayCanvas);
bool displayCanvasGetWidgets(displayWidgets_t *widgets, const displayCanvas_t *displayCanvas);

View file

@ -0,0 +1,33 @@
#include "platform.h"
#if defined(USE_CANVAS)
#include "drivers/display_widgets.h"
int displayWidgetsSupportedInstances(displayWidgets_t *widgets, displayWidgetType_e widgetType)
{
return widgets->vTable->supportedInstances ? widgets->vTable->supportedInstances(widgets, widgetType) : 0;
}
bool displayWidgetsConfigureAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIConfiguration_t *config)
{
return widgets->vTable->configureAHI ? widgets->vTable->configureAHI(widgets, instance, config) : false;
}
bool displayWidgetsDrawAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIData_t *data)
{
return widgets->vTable->drawAHI ? widgets->vTable->drawAHI(widgets, instance, data) : false;
}
bool displayWidgetsConfigureSidebar(displayWidgets_t *widgets, unsigned instance, const widgetSidebarConfiguration_t *config)
{
return widgets->vTable->configureSidebar ? widgets->vTable->configureSidebar(widgets, instance, config) : false;
}
bool displayWidgetsDrawSidebar(displayWidgets_t *widgets, unsigned instance, int32_t data)
{
return widgets->vTable->drawSidebar ? widgets->vTable->drawSidebar(widgets, instance, data) : false;
}
#endif

View file

@ -0,0 +1,77 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "drivers/osd.h"
typedef struct widgetRect_s {
int x;
int y;
unsigned w;
unsigned h;
} widgetRect_t;
typedef enum {
DISPLAY_WIDGET_TYPE_AHI,
DISPLAY_WIDGET_TYPE_SIDEBAR,
} displayWidgetType_e;
typedef enum {
DISPLAY_WIDGET_AHI_STYLE_STAIRCASE = 0,
DISPLAY_WIDGET_AHI_STYLE_LINE = 1,
} widgetAHIStyle_e;
typedef enum {
DISPLAY_WIDGET_AHI_OPTION_SHOW_CORNERS = 1 << 0,
} widgetAHIOptions_t;
typedef struct widgetAHIConfiguration_s {
widgetRect_t rect;
widgetAHIStyle_e style;
widgetAHIOptions_t options;
unsigned crosshairMargin;
unsigned strokeWidth;
} widgetAHIConfiguration_t;
typedef struct widgetAHIData_s {
float pitch; // radians
float roll; // radians
} widgetAHIData_t;
typedef enum
{
DISPLAY_WIDGET_SIDEBAR_OPTION_LEFT = 1 << 0, // Display the sidebar oriented to the left. Default is to the right
DISPLAY_WIDGET_SIDEBAR_OPTION_REVERSE = 1 << 1, // Reverse the sidebar direction, so positive values move it down
DISPLAY_WIDGET_SIDEBAR_OPTION_UNLABELED = 1 << 2, // Don't display the central label with the value.
DISPLAY_WIDGET_SIDEBAR_OPTION_STATIC = 1 << 3, // The sidebar doesn't scroll nor display values along it.
} widgetSidebarOptions_t;
typedef struct widgetSidebarConfiguration_s {
widgetRect_t rect;
widgetSidebarOptions_t options;
uint8_t divisions; // How many divisions the sidebar will have
uint16_t counts_per_step; // How much the value increases/decreases per division BEFORE applying the unit scale
osdUnit_t unit; // The unit used to display the values in the sidebar
} widgetSidebarConfiguration_t;
typedef struct displayWidgetsVTable_s displayWidgetsVTable_t;
typedef struct displayWidgets_s {
const displayWidgetsVTable_t *vTable;
void *device;
} displayWidgets_t;
typedef struct displayWidgetsVTable_s {
int (*supportedInstances)(displayWidgets_t *widgets, displayWidgetType_e widgetType);
bool (*configureAHI)(displayWidgets_t *widgets, unsigned instance, const widgetAHIConfiguration_t *config);
bool (*drawAHI)(displayWidgets_t *widgets, unsigned instance, const widgetAHIData_t *data);
bool (*configureSidebar)(displayWidgets_t *widgets, unsigned instance, const widgetSidebarConfiguration_t *config);
bool (*drawSidebar)(displayWidgets_t *widgets, unsigned instance, int32_t data);
} displayWidgetsVTable_t;
int displayWidgetsSupportedInstances(displayWidgets_t *widgets, displayWidgetType_e widgetType);
bool displayWidgetsConfigureAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIConfiguration_t *config);
bool displayWidgetsDrawAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIData_t *data);
bool displayWidgetsConfigureSidebar(displayWidgets_t *widgets, unsigned instance, const widgetSidebarConfiguration_t *config);
bool displayWidgetsDrawSidebar(displayWidgets_t *widgets, unsigned instance, int32_t data);

View file

@ -287,6 +287,13 @@ void flashPartitionErase(flashPartition_t *partition)
{
const flashGeometry_t * const geometry = flashGetGeometry();
// if there's a single FLASHFS partition and it uses the entire flash then do a full erase
const bool doFullErase = (flashPartitionCount() == 1) && (FLASH_PARTITION_SECTOR_COUNT(partition) == geometry->sectors);
if (doFullErase) {
flashEraseCompletely();
return;
}
for (unsigned i = partition->startSector; i <= partition->endSector; i++) {
uint32_t flashAddress = geometry->sectorSize * i;
flashEraseSector(flashAddress);

View file

@ -483,7 +483,7 @@ void max7456Write(uint8_t x, uint8_t y, const char *buff, uint8_t mode)
}
}
// Must be called with the lock held. Returns wether any new characters
// Must be called with the lock held. Returns whether any new characters
// were drawn.
static bool max7456DrawScreenPartial(void)
{

View file

@ -23,8 +23,13 @@
*
*/
#include "platform.h"
#if defined(USE_OSD)
#include "drivers/display_canvas.h"
#include "drivers/osd.h"
#include "drivers/osd_symbols.h"
uint16_t osdCharacterGridBuffer[OSD_CHARACTER_GRID_BUFFER_SIZE] ALIGNED(4);
@ -32,8 +37,9 @@ void osdCharacterGridBufferClear(void)
{
uint32_t *ptr = (uint32_t *)osdCharacterGridBuffer;
uint32_t *end = (uint32_t *)(ARRAYEND(osdCharacterGridBuffer));
uint32_t blank32 = SYM_BLANK << 24 | SYM_BLANK << 16 | SYM_BLANK << 8 | SYM_BLANK;
for (; ptr < end; ptr++) {
*ptr = 0;
*ptr = blank32;
}
}
@ -74,7 +80,7 @@ void osdGridBufferClearGridRect(int x, int y, int w, int h)
int maxY = y + h;
for (int ii = x; ii <= maxX; ii++) {
for (int jj = y; jj <= maxY; jj++) {
*osdCharacterGridBufferGetEntryPtr(ii, jj) = 0;
*osdCharacterGridBufferGetEntryPtr(ii, jj) = SYM_BLANK;
}
}
}
@ -102,3 +108,5 @@ uint16_t *osdCharacterGridBufferGetEntryPtr(unsigned x, unsigned y)
unsigned pos = y * OSD_CHARACTER_GRID_MAX_WIDTH + x;
return &osdCharacterGridBuffer[pos];
}
#endif

View file

@ -61,14 +61,23 @@ typedef struct osdCharacter_s {
uint8_t data[OSD_CHAR_BYTES];
} osdCharacter_t;
typedef struct osdUnit_t
{
uint16_t scale; // The scale between the value and the represented unit. e.g. if you're providing cms but you want to draw meters this should be 100 ([0, 1023])
uint16_t symbol; // Symbol to append/prepend to the value when it's not scaled [0, 511]
uint16_t divisor; // If abs(value) > divisor, divide it by this. e.g. for meters and km you'd set this to 1000 [0, UINT16_MAX)
uint16_t divided_symbol; // Symbol to append/prepend to the value when it's divided (e.g. the km symbol) [0, 511]
} osdUnit_t;
#define OSD_CHARACTER_GRID_MAX_WIDTH 30
#define OSD_CHARACTER_GRID_MAX_HEIGHT 16
#define OSD_CHARACTER_GRID_BUFFER_SIZE (OSD_CHARACTER_GRID_MAX_WIDTH * OSD_CHARACTER_GRID_MAX_HEIGHT)
extern uint16_t osdCharacterGridBuffer[OSD_CHARACTER_GRID_BUFFER_SIZE] ALIGNED(4);
// Sets all buffer entries to 0
// Sets all buffer entries to SYM_BLANK
void osdCharacterGridBufferClear(void);
void osdGridBufferClearGridRect(int x, int y, int w, int h);
void osdGridBufferClearPixelRect(displayCanvas_t *canvas, int x, int y, int w, int h);
uint16_t *osdCharacterGridBufferGetEntryPtr(unsigned x, unsigned y);

View file

@ -32,19 +32,31 @@
/*** Hardware definitions ***/
const pinioHardware_t pinioHardware[] = {
#if defined(PINIO1_PIN)
{ .ioTag = IO_TAG(PINIO1_PIN), .ioMode = IOCFG_OUT_PP, .flags = 0 },
#if !defined(PINIO1_FLAGS)
#define PINIO1_FLAGS 0
#endif
{ .ioTag = IO_TAG(PINIO1_PIN), .ioMode = IOCFG_OUT_PP, .flags = PINIO1_FLAGS },
#endif
#if defined(PINIO2_PIN)
{ .ioTag = IO_TAG(PINIO2_PIN), .ioMode = IOCFG_OUT_PP, .flags = 0 },
#if !defined(PINIO2_FLAGS)
#define PINIO2_FLAGS 0
#endif
{ .ioTag = IO_TAG(PINIO2_PIN), .ioMode = IOCFG_OUT_PP, .flags = PINIO2_FLAGS },
#endif
#if defined(PINIO3_PIN)
{ .ioTag = IO_TAG(PINIO3_PIN), .ioMode = IOCFG_OUT_PP, .flags = 0 },
#if !defined(PINIO3_FLAGS)
#define PINIO3_FLAGS 0
#endif
{ .ioTag = IO_TAG(PINIO3_PIN), .ioMode = IOCFG_OUT_PP, .flags = PINIO3_FLAGS },
#endif
#if defined(PINIO4_PIN)
{ .ioTag = IO_TAG(PINIO4_PIN), .ioMode = IOCFG_OUT_PP, .flags = 0 },
#if !defined(PINIO4_FLAGS)
#define PINIO4_FLAGS 0
#endif
{ .ioTag = IO_TAG(PINIO4_PIN), .ioMode = IOCFG_OUT_PP, .flags = PINIO4_FLAGS },
#endif
};

View file

@ -810,27 +810,19 @@ static void cliSerial(char *cmdline)
switch (i) {
case 0:
if (baudRateIndex < BAUD_1200 || baudRateIndex > BAUD_2470000) {
continue;
}
baudRateIndex = constrain(baudRateIndex, BAUD_MIN, BAUD_MAX);
portConfig.msp_baudrateIndex = baudRateIndex;
break;
case 1:
if (baudRateIndex < BAUD_9600 || baudRateIndex > BAUD_115200) {
continue;
}
baudRateIndex = constrain(baudRateIndex, BAUD_MIN, BAUD_MAX);
portConfig.gps_baudrateIndex = baudRateIndex;
break;
case 2:
if (baudRateIndex != BAUD_AUTO && baudRateIndex > BAUD_115200) {
continue;
}
baudRateIndex = constrain(baudRateIndex, BAUD_MIN, BAUD_MAX);
portConfig.telemetry_baudrateIndex = baudRateIndex;
break;
case 3:
if (baudRateIndex < BAUD_19200 || baudRateIndex > BAUD_250000) {
continue;
}
baudRateIndex = constrain(baudRateIndex, BAUD_MIN, BAUD_MAX);
portConfig.peripheral_baudrateIndex = baudRateIndex;
break;
}
@ -1284,6 +1276,67 @@ static void cliTempSensor(char *cmdline)
}
#endif
#if defined(USE_SAFE_HOME)
static void printSafeHomes(uint8_t dumpMask, const navSafeHome_t *navSafeHome, const navSafeHome_t *defaultSafeHome)
{
const char *format = "safehome %u %u %d %d"; // uint8_t enabled, int32_t lat; int32_t lon
for (uint8_t i = 0; i < MAX_SAFE_HOMES; i++) {
bool equalsDefault = false;
if (defaultSafeHome) {
equalsDefault = navSafeHome[i].enabled == defaultSafeHome[i].enabled
&& navSafeHome[i].lat == defaultSafeHome[i].lat
&& navSafeHome[i].lon == defaultSafeHome[i].lon;
cliDefaultPrintLinef(dumpMask, equalsDefault, format, i,
defaultSafeHome[i].enabled, defaultSafeHome[i].lat, defaultSafeHome[i].lon);
}
cliDumpPrintLinef(dumpMask, equalsDefault, format, i,
navSafeHome[i].enabled, navSafeHome[i].lat, navSafeHome[i].lon);
}
}
static void cliSafeHomes(char *cmdline)
{
if (isEmpty(cmdline)) {
printSafeHomes(DUMP_MASTER, safeHomeConfig(0), NULL);
} else if (sl_strcasecmp(cmdline, "reset") == 0) {
resetSafeHomes();
} else {
int32_t lat, lon;
bool enabled;
uint8_t validArgumentCount = 0;
const char *ptr = cmdline;
int8_t i = fastA2I(ptr);
if (i < 0 || i >= MAX_SAFE_HOMES) {
cliShowArgumentRangeError("safehome index", 0, MAX_SAFE_HOMES - 1);
} else {
if ((ptr = nextArg(ptr))) {
enabled = fastA2I(ptr);
validArgumentCount++;
}
if ((ptr = nextArg(ptr))) {
lat = fastA2I(ptr);
validArgumentCount++;
}
if ((ptr = nextArg(ptr))) {
lon = fastA2I(ptr);
validArgumentCount++;
}
if ((ptr = nextArg(ptr))) {
// check for too many arguments
validArgumentCount++;
}
if (validArgumentCount != 3) {
cliShowParseError();
} else {
safeHomeConfigMutable(i)->enabled = enabled;
safeHomeConfigMutable(i)->lat = lat;
safeHomeConfigMutable(i)->lon = lon;
}
}
}
}
#endif
#if defined(USE_NAV) && defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE) && defined(NAV_NON_VOLATILE_WAYPOINT_CLI)
static void printWaypoints(uint8_t dumpMask, const navWaypoint_t *navWaypoint, const navWaypoint_t *defaultNavWaypoint)
{
@ -2122,7 +2175,7 @@ static void cliFlashRead(char *cmdline)
#endif
#ifdef USE_OSD
static void printOsdLayout(uint8_t dumpMask, const osdConfig_t *osdConfig, const osdConfig_t *osdConfigDefault, int layout, int item)
static void printOsdLayout(uint8_t dumpMask, const osdLayoutsConfig_t *config, const osdLayoutsConfig_t *configDefault, int layout, int item)
{
// "<layout> <item> <col> <row> <visible>"
const char *format = "osd_layout %d %d %d %d %c";
@ -2130,8 +2183,8 @@ static void printOsdLayout(uint8_t dumpMask, const osdConfig_t *osdConfig, const
if (layout >= 0 && layout != ii) {
continue;
}
const uint16_t *layoutItems = osdConfig->item_pos[ii];
const uint16_t *defaultLayoutItems = osdConfigDefault->item_pos[ii];
const uint16_t *layoutItems = config->item_pos[ii];
const uint16_t *defaultLayoutItems = configDefault->item_pos[ii];
for (int jj = 0; jj < OSD_ITEM_COUNT; jj++) {
if (item >= 0 && item != jj) {
continue;
@ -2223,15 +2276,15 @@ static void cliOsdLayout(char *cmdline)
// No args, or just layout or layout and item. If any of them not provided,
// it will be the -1 that we used during initialization, so printOsdLayout()
// won't use them for filtering.
printOsdLayout(DUMP_MASTER, osdConfig(), osdConfig(), layout, item);
printOsdLayout(DUMP_MASTER, osdLayoutsConfig(), osdLayoutsConfig(), layout, item);
break;
case 4:
// No visibility provided. Keep the previous one.
visible = OSD_VISIBLE(osdConfig()->item_pos[layout][item]);
visible = OSD_VISIBLE(osdLayoutsConfig()->item_pos[layout][item]);
FALLTHROUGH;
case 5:
// Layout, item, pos and visibility. Set the item.
osdConfigMutable()->item_pos[layout][item] = OSD_POS(col, row) | (visible ? OSD_VISIBLE_FLAG : 0);
osdLayoutsConfigMutable()->item_pos[layout][item] = OSD_POS(col, row) | (visible ? OSD_VISIBLE_FLAG : 0);
break;
default:
// Unhandled
@ -3222,6 +3275,10 @@ static void printConfig(const char *cmdline, bool doDiff)
cliPrintHashLine("servo");
printServo(dumpMask, servoParams_CopyArray, servoParams(0));
#if defined(USE_SAFE_HOME)
cliPrintHashLine("safehome");
printSafeHomes(dumpMask, safeHomeConfig_CopyArray, safeHomeConfig(0));
#endif
#ifdef USE_PROGRAMMING_FRAMEWORK
cliPrintHashLine("logic");
printLogic(dumpMask, logicConditions_CopyArray, logicConditions(0));
@ -3276,7 +3333,7 @@ static void printConfig(const char *cmdline, bool doDiff)
#ifdef USE_OSD
cliPrintHashLine("osd_layout");
printOsdLayout(dumpMask, &osdConfig_Copy, osdConfig(), -1, -1);
printOsdLayout(dumpMask, &osdLayoutsConfig_Copy, osdLayoutsConfig(), -1, -1);
#endif
cliPrintHashLine("master");
@ -3462,6 +3519,9 @@ const clicmd_t cmdTable[] = {
CLI_COMMAND_DEF("resource", "view currently used resources", NULL, cliResource),
#endif
CLI_COMMAND_DEF("rxrange", "configure rx channel ranges", NULL, cliRxRange),
#if defined(USE_SAFE_HOME)
CLI_COMMAND_DEF("safehome", "safe home list", NULL, cliSafeHomes),
#endif
CLI_COMMAND_DEF("save", "save and reboot", NULL, cliSave),
CLI_COMMAND_DEF("serial", "configure serial ports", NULL, cliSerial),
#ifdef USE_SERIAL_PASSTHROUGH

View file

@ -1092,7 +1092,7 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
sbufWriteU16(dst, osdConfig()->dist_alarm);
sbufWriteU16(dst, osdConfig()->neg_alt_alarm);
for (int i = 0; i < OSD_ITEM_COUNT; i++) {
sbufWriteU16(dst, osdConfig()->item_pos[0][i]);
sbufWriteU16(dst, osdLayoutsConfig()->item_pos[0][i]);
}
#else
sbufWriteU8(dst, OSD_DRIVER_NONE); // OSD not supported
@ -2305,7 +2305,7 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
} else {
// set a position setting
if ((dataSize >= 3) && (tmp_u8 < OSD_ITEM_COUNT)) // tmp_u8 == addr
osdConfigMutable()->item_pos[0][tmp_u8] = sbufReadU16(src);
osdLayoutsConfigMutable()->item_pos[0][tmp_u8] = sbufReadU16(src);
else
return MSP_RESULT_ERROR;
}
@ -2599,10 +2599,10 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
portConfig->identifier = identifier;
portConfig->functionMask = sbufReadU16(src);
portConfig->msp_baudrateIndex = sbufReadU8(src);
portConfig->gps_baudrateIndex = sbufReadU8(src);
portConfig->telemetry_baudrateIndex = sbufReadU8(src);
portConfig->peripheral_baudrateIndex = sbufReadU8(src);
portConfig->msp_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->gps_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->telemetry_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->peripheral_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
}
}
break;
@ -2627,10 +2627,10 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
portConfig->identifier = identifier;
portConfig->functionMask = sbufReadU32(src);
portConfig->msp_baudrateIndex = sbufReadU8(src);
portConfig->gps_baudrateIndex = sbufReadU8(src);
portConfig->telemetry_baudrateIndex = sbufReadU8(src);
portConfig->peripheral_baudrateIndex = sbufReadU8(src);
portConfig->msp_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->gps_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->telemetry_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
portConfig->peripheral_baudrateIndex = constrain(sbufReadU8(src), BAUD_MIN, BAUD_MAX);
}
}
break;
@ -2752,7 +2752,7 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
if (!sbufReadU8Safe(&item, src)) {
return MSP_RESULT_ERROR;
}
if (!sbufReadU16Safe(&osdConfigMutable()->item_pos[layout][item], src)) {
if (!sbufReadU16Safe(&osdLayoutsConfigMutable()->item_pos[layout][item], src)) {
return MSP_RESULT_ERROR;
}
// If the layout is not already overriden and it's different
@ -3172,11 +3172,11 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu
*ret = MSP_RESULT_ERROR;
break;
}
sbufWriteU16(dst, osdConfig()->item_pos[layout][item]);
sbufWriteU16(dst, osdLayoutsConfig()->item_pos[layout][item]);
} else {
// Asking for an specific layout
for (unsigned ii = 0; ii < OSD_ITEM_COUNT; ii++) {
sbufWriteU16(dst, osdConfig()->item_pos[layout][ii]);
sbufWriteU16(dst, osdLayoutsConfig()->item_pos[layout][ii]);
}
}
} else {

View file

@ -82,8 +82,8 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT + 1] = {
{ BOXOSDALT3, "OSD ALT 3", 44 },
{ BOXNAVCRUISE, "NAV CRUISE", 45 },
{ BOXBRAKING, "MC BRAKING", 46 },
{ BOXUSER1, "USER1", 47 },
{ BOXUSER2, "USER2", 48 },
{ BOXUSER1, "USER1", BOX_PERMANENT_ID_USER1 },
{ BOXUSER2, "USER2", BOX_PERMANENT_ID_USER2 },
{ BOXLOITERDIRCHN, "LOITER CHANGE", 49 },
{ BOXMSPRCOVERRIDE, "MSP RC OVERRIDE", 50 },
{ CHECKBOX_ITEM_COUNT, NULL, 0xFF }
@ -189,7 +189,7 @@ void initActiveBoxIds(void)
activeBoxIds[activeBoxIdCount++] = BOXCAMSTAB;
#ifdef USE_GPS
if (STATE(ALTITUDE_CONTROL) && (sensors(SENSOR_BARO) || (STATE(AIRPLANE) && feature(FEATURE_GPS)))) {
if (STATE(ALTITUDE_CONTROL) && (sensors(SENSOR_BARO) || (feature(FEATURE_GPS) && (STATE(AIRPLANE) || positionEstimationConfig()->use_gps_no_baro)))) {
activeBoxIds[activeBoxIdCount++] = BOXNAVALTHOLD;
activeBoxIds[activeBoxIdCount++] = BOXSURFACE;
}

View file

@ -19,7 +19,9 @@
#include "fc/rc_modes.h"
#define PERMANENT_ID_NONE 255 // A permanent ID for no box mode
#define BOX_PERMANENT_ID_USER1 47
#define BOX_PERMANENT_ID_USER2 48
#define BOX_PERMANENT_ID_NONE 255 // A permanent ID for no box mode
typedef struct box_s {
const uint8_t boxId; // see boxId_e

View file

@ -131,7 +131,7 @@ typedef struct adjustmentState_s {
#define MAX_SIMULTANEOUS_ADJUSTMENT_COUNT 4 // enough for 4 x 3position switches / 4 aux channel
#endif
#define MAX_ADJUSTMENT_RANGE_COUNT 12 // enough for 2 * 6pos switches.
#define MAX_ADJUSTMENT_RANGE_COUNT 20 // enough for 2 * 6pos switches.
PG_DECLARE_ARRAY(adjustmentRange_t, MAX_ADJUSTMENT_RANGE_COUNT, adjustmentRanges);

View file

@ -25,7 +25,7 @@ tables:
values: ["NONE", "PPM", "SERIAL", "MSP", "SPI", "UIB"]
enum: rxReceiverType_e
- name: serial_rx
values: ["SPEK1024", "SPEK2048", "SBUS", "SUMD", "SUMH", "XB-B", "XB-B-RJ01", "IBUS", "JETIEXBUS", "CRSF", "FPORT", "FPORT2", "SBUS_FAST"]
values: ["SPEK1024", "SPEK2048", "SBUS", "SUMD", "SUMH", "XB-B", "XB-B-RJ01", "IBUS", "JETIEXBUS", "CRSF", "FPORT", "SBUS_FAST", "FPORT2"]
- name: rx_spi_protocol
values: ["V202_250K", "V202_1M", "SYMA_X", "SYMA_X5C", "CX10", "CX10A", "H8_3D", "INAV", "ELERES"]
enum: rx_spi_protocol_e
@ -82,9 +82,9 @@ tables:
- name: debug_modes
values: ["NONE", "GYRO", "AGL", "FLOW_RAW",
"FLOW", "SBUS", "FPORT", "ALWAYS", "SAG_COMP_VOLTAGE",
"VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC", "ITERM_RELAX",
"VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC",
"ERPM", "RPM_FILTER", "RPM_FREQ", "NAV_YAW", "DYNAMIC_FILTER", "DYNAMIC_FILTER_FREQUENCY",
"IRLOCK", "CD", "KALMAN", "SPM_CELLS", "SPM_VS600", "SPM_VARIO", "PCF8574"]
"IRLOCK", "CD", "KALMAN_GAIN", "PID_MEASUREMENT", "SPM_CELLS", "SPM_VS600", "SPM_VARIO", "PCF8574"]
- name: async_mode
values: ["NONE", "GYRO", "ALL"]
- name: aux_operator
@ -124,9 +124,6 @@ tables:
- name: iterm_relax
values: ["OFF", "RP", "RPY"]
enum: itermRelax_e
- name: iterm_relax_type
values: ["GYRO", "SETPOINT"]
enum: itermRelaxType_e
- name: airmodeHandlingType
values: ["STICK_CENTER", "THROTTLE_THRESHOLD"]
- name: nav_extra_arming_safety
@ -144,6 +141,9 @@ tables:
- name: osd_ahi_style
values: ["DEFAULT", "LINE"]
enum: osd_ahi_style_e
- name: tristate
enum: tristate_e
values: ["AUTO", "ON", "OFF"]
groups:
- name: PG_GYRO_CONFIG
@ -229,25 +229,6 @@ groups:
condition: USE_DYNAMIC_FILTERS
min: 30
max: 1000
- name: gyro_kalman_enabled
condition: USE_GYRO_KALMAN
field: kalmanEnabled
type: bool
- name: gyro_kalman_q
field: kalman_q
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: gyro_kalman_w
field: kalman_w
condition: USE_GYRO_KALMAN
min: 1
max: 40
- name: gyro_kalman_sharpness
field: kalman_sharpness
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: gyro_to_use
condition: USE_DUAL_GYRO
min: 0
@ -597,10 +578,10 @@ groups:
min: PWM_PULSE_MIN
max: PWM_PULSE_MAX
- name: serialrx_halfduplex
description: "Allow serial receiver to operate on UART TX pin. With some receivers will allow control and telemetry over a single wire"
default_value: "OFF"
description: "Allow serial receiver to operate on UART TX pin. With some receivers will allow control and telemetry over a single wire."
default_value: "AUTO"
field: halfDuplex
type: bool
table: tristate
- name: msp_override_channels
description: "Mask of RX channels that may be overridden by MSP `SET_RAW_RC`. Note that this requires custom firmware with `USE_RX_MSP` and `USE_MSP_RC_OVERRIDE` compile options and the `MSP RC Override` flight mode."
default_value: "0"
@ -687,7 +668,7 @@ groups:
description: "The percentage of the throttle range (`max_throttle` - `min_command`) above `min_command` used for minimum / idle throttle."
default_value: "15"
field: throttleIdle
min: 4
min: 0
max: 30
- name: motor_poles
field: motorPoleCount
@ -1202,6 +1183,7 @@ groups:
max: 1000
- name: PG_GENERAL_SETTINGS
headers: ["config/general_settings.h"]
type: generalSettings_t
members:
- name: applied_defaults
@ -1213,6 +1195,7 @@ groups:
max: 3
- name: PG_RPM_FILTER_CONFIG
headers: ["flight/rpm_filter.h"]
condition: USE_RPM_FILTER
type: rpmFilterConfig_t
members:
@ -1762,9 +1745,6 @@ groups:
field: navFwPosHdgPidsumLimit
min: PID_SUM_LIMIT_MIN
max: PID_SUM_LIMIT_MAX
- name: mc_iterm_relax_type
field: iterm_relax_type
table: iterm_relax_type
- name: mc_iterm_relax
field: iterm_relax
table: iterm_relax
@ -1818,6 +1798,33 @@ groups:
field: controlDerivativeLpfHz
min: 0
max: 200
- name: setpoint_kalman_enabled
description: "Enable Kalman filter on the PID controller setpoint"
default_value: "OFF"
condition: USE_GYRO_KALMAN
field: kalmanEnabled
type: bool
- name: setpoint_kalman_q
description: "Quality factor of the setpoint Kalman filter. Higher values means less filtering and lower phase delay. On 3-7 inch multirotors can be usually increased to 200-300 or even higher of clean builds"
default_value: "100"
field: kalman_q
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: setpoint_kalman_w
description: "Window size for the setpoint Kalman filter. Wider the window, more samples are used to compute variance. In general, wider window results in smoother filter response"
default_value: "4"
field: kalman_w
condition: USE_GYRO_KALMAN
min: 1
max: 40
- name: setpoint_kalman_sharpness
description: "Dynamic factor for the setpoint Kalman filter. In general, the higher the value, the more dynamic Kalman filter gets"
default_value: "100"
field: kalman_sharpness
condition: USE_GYRO_KALMAN
min: 1
max: 16000
- name: PG_PID_AUTOTUNE_CONFIG
type: pidAutotuneConfig_t
@ -1874,6 +1881,9 @@ groups:
default_value: "ON"
field: use_gps_velned
type: bool
- name: inav_use_gps_no_baro
field: use_gps_no_baro
type: bool
- name: inav_allow_dead_reckoning
description: "Defines if inav will dead-reckon over short GPS outages. May also be useful for indoors OPFLOW navigation"
default_value: "OFF"
@ -2120,16 +2130,6 @@ groups:
default_value: "0"
field: general.rth_home_altitude
max: 65000
- name: nav_rth_home_offset_distance
description: "Distance offset from GPS established home to \"safe\" position used for RTH (cm, 0 disables)"
default_value: "0"
field: general.rth_home_offset_distance
max: 65000
- name: nav_rth_home_offset_direction
description: "Direction offset from GPS established home to \"safe\" position used for RTH (degrees, 0=N, 90=E, 180=S, 270=W, requires non-zero offset distance)"
default_value: "0"
field: general.rth_home_offset_direction
max: 359
- name: nav_mc_bank_angle
description: "Maximum banking angle (deg) that multicopter navigation is allowed to set. Machine must be able to satisfy this angle without loosing altitude"
default_value: "30"
@ -2854,6 +2854,57 @@ groups:
default_value: "DEFAULT"
table: osd_ahi_style
- name: osd_force_grid
field: force_grid
type: bool
default_value: "OFF"
description: Force OSD to work in grid mode even if the OSD device supports pixel level access (mainly used for development)
- name: osd_ahi_bordered
field: ahi_bordered
type: bool
description: Shows a border/corners around the AHI region (pixel OSD only)
default_value: "OFF"
- name: osd_ahi_width
field: ahi_width
max: 255
description: AHI width in pixels (pixel OSD only)
default_value: 132
- name: osd_ahi_height
field: ahi_height
max: 255
description: AHI height in pixels (pixel OSD only)
default_value: 162
- name: osd_ahi_vertical_offset
field: ahi_vertical_offset
min: -128
max: 127
description: AHI vertical offset from center (pixel OSD only)
default_value: 0
- name: osd_sidebar_horizontal_offset
field: sidebar_horizontal_offset
min: -128
max: 127
default_value: 0
description: Sidebar horizontal offset from default position. Positive values move the sidebars closer to the edges.
- name: osd_left_sidebar_scroll_step
field: left_sidebar_scroll_step
max: 255
default_value: 0
description: How many units each sidebar step represents. 0 means the default value for the scroll type.
- name: osd_right_sidebar_scroll_step
field: right_sidebar_scroll_step
max: 255
default_value: 0
description: Same as left_sidebar_scroll_step, but for the right sidebar
- name: PG_SYSTEM_CONFIG
type: systemConfig_t
headers: ["fc/config.h"]

View file

@ -29,30 +29,24 @@ FILE_COMPILE_FOR_SPEED
#include "build/debug.h"
kalman_t kalmanFilterStateRate[XYZ_AXIS_COUNT];
float setPoint[XYZ_AXIS_COUNT];
static void gyroKalmanInitAxis(kalman_t *filter)
static void gyroKalmanInitAxis(kalman_t *filter, uint16_t q, uint16_t w, uint16_t sharpness)
{
memset(filter, 0, sizeof(kalman_t));
filter->q = gyroConfig()->kalman_q * 0.03f; //add multiplier to make tuning easier
filter->q = q * 0.03f; //add multiplier to make tuning easier
filter->r = 88.0f; //seeding R at 88.0f
filter->p = 30.0f; //seeding P at 30.0f
filter->e = 1.0f;
filter->s = gyroConfig()->kalman_sharpness / 10.0f;
filter->w = gyroConfig()->kalman_w * 8;
filter->s = sharpness / 10.0f;
filter->w = w * 8;
filter->inverseN = 1.0f / (float)(filter->w);
}
void gyroKalmanSetSetpoint(uint8_t axis, float rate)
void gyroKalmanInitialize(uint16_t q, uint16_t w, uint16_t sharpness)
{
setPoint[axis] = rate;
}
void gyroKalmanInitialize(void)
{
gyroKalmanInitAxis(&kalmanFilterStateRate[X]);
gyroKalmanInitAxis(&kalmanFilterStateRate[Y]);
gyroKalmanInitAxis(&kalmanFilterStateRate[Z]);
gyroKalmanInitAxis(&kalmanFilterStateRate[X], q, w, sharpness);
gyroKalmanInitAxis(&kalmanFilterStateRate[Y], q, w, sharpness);
gyroKalmanInitAxis(&kalmanFilterStateRate[Z], q, w, sharpness);
}
float kalman_process(kalman_t *kalmanState, float input, float target)
@ -114,13 +108,13 @@ static void updateAxisVariance(kalman_t *kalmanState, float rate)
kalmanState->r = squirt * VARIANCE_SCALE;
}
float gyroKalmanUpdate(uint8_t axis, float input)
float gyroKalmanUpdate(uint8_t axis, float input, float setpoint)
{
updateAxisVariance(&kalmanFilterStateRate[axis], input);
DEBUG_SET(DEBUG_KALMAN, axis, kalmanFilterStateRate[axis].k * 1000.0f); //Kalman gain
DEBUG_SET(DEBUG_KALMAN_GAIN, axis, kalmanFilterStateRate[axis].k * 1000.0f); //Kalman gain
return kalman_process(&kalmanFilterStateRate[axis], input, setPoint[axis]);
return kalman_process(&kalmanFilterStateRate[axis], input, setpoint);
}
#endif

View file

@ -49,6 +49,5 @@ typedef struct kalman
uint16_t w;
} kalman_t;
void gyroKalmanInitialize(void);
float gyroKalmanUpdate(uint8_t axis, float input);
void gyroKalmanSetSetpoint(uint8_t axis, float rate);
void gyroKalmanInitialize(uint16_t q, uint16_t w, uint16_t sharpness);
float gyroKalmanUpdate(uint8_t axis, float input, float setpoint);

View file

@ -93,13 +93,13 @@ PG_RESET_TEMPLATE(mixerConfig_t, mixerConfig,
#define DEFAULT_PWM_PROTOCOL PWM_TYPE_BRUSHED
#define DEFAULT_PWM_RATE 16000
#else
#define DEFAULT_PWM_PROTOCOL PWM_TYPE_STANDARD
#define DEFAULT_PWM_PROTOCOL PWM_TYPE_ONESHOT125
#define DEFAULT_PWM_RATE 400
#endif
#define DEFAULT_MAX_THROTTLE 1850
PG_REGISTER_WITH_RESET_TEMPLATE(motorConfig_t, motorConfig, PG_MOTOR_CONFIG, 5);
PG_REGISTER_WITH_RESET_TEMPLATE(motorConfig_t, motorConfig, PG_MOTOR_CONFIG, 6);
PG_RESET_TEMPLATE(motorConfig_t, motorConfig,
.motorPwmProtocol = DEFAULT_PWM_PROTOCOL,

View file

@ -124,7 +124,6 @@ int32_t axisPID_P[FLIGHT_DYNAMICS_INDEX_COUNT], axisPID_I[FLIGHT_DYNAMICS_INDEX_
static EXTENDED_FASTRAM pidState_t pidState[FLIGHT_DYNAMICS_INDEX_COUNT];
static EXTENDED_FASTRAM pt1Filter_t windupLpf[XYZ_AXIS_COUNT];
static EXTENDED_FASTRAM uint8_t itermRelax;
static EXTENDED_FASTRAM uint8_t itermRelaxType;
#ifdef USE_ANTIGRAVITY
static EXTENDED_FASTRAM pt1Filter_t antigravityThrottleLpf;
@ -155,7 +154,7 @@ static EXTENDED_FASTRAM filterApplyFnPtr dTermLpfFilterApplyFn;
static EXTENDED_FASTRAM filterApplyFnPtr dTermLpf2FilterApplyFn;
static EXTENDED_FASTRAM bool levelingEnabled = false;
PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 13);
PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 15);
PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.bank_mc = {
@ -264,7 +263,6 @@ PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.navVelXyDtermAttenuation = 90,
.navVelXyDtermAttenuationStart = 10,
.navVelXyDtermAttenuationEnd = 60,
.iterm_relax_type = ITERM_RELAX_SETPOINT,
.iterm_relax_cutoff = MC_ITERM_RELAX_CUTOFF_DEFAULT,
.iterm_relax = ITERM_RELAX_RP,
.dBoostFactor = 1.25f,
@ -276,6 +274,10 @@ PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.pidControllerType = PID_TYPE_AUTO,
.navFwPosHdgPidsumLimit = PID_SUM_LIMIT_YAW_DEFAULT,
.controlDerivativeLpfHz = 30,
.kalman_q = 100,
.kalman_w = 4,
.kalman_sharpness = 100,
.kalmanEnabled = 0,
);
bool pidInitFilters(void)
@ -632,31 +634,20 @@ static void NOINLINE pidApplyFixedWingRateController(pidState_t *pidState, fligh
#endif
}
static void FAST_CODE applyItermRelax(const int axis, const float gyroRate, float currentPidSetpoint, float *itermErrorRate)
static float FAST_CODE applyItermRelax(const int axis, float currentPidSetpoint, float itermErrorRate)
{
const float setpointLpf = pt1FilterApply(&windupLpf[axis], currentPidSetpoint);
const float setpointHpf = fabsf(currentPidSetpoint - setpointLpf);
if (itermRelax) {
if (axis < FD_YAW || itermRelax == ITERM_RELAX_RPY) {
const float setpointLpf = pt1FilterApply(&windupLpf[axis], currentPidSetpoint);
const float setpointHpf = fabsf(currentPidSetpoint - setpointLpf);
const float itermRelaxFactor = MAX(0, 1 - setpointHpf / MC_ITERM_RELAX_SETPOINT_THRESHOLD);
if (itermRelaxType == ITERM_RELAX_SETPOINT) {
*itermErrorRate *= itermRelaxFactor;
} else if (itermRelaxType == ITERM_RELAX_GYRO ) {
*itermErrorRate = fapplyDeadbandf(setpointLpf - gyroRate, setpointHpf);
} else {
*itermErrorRate = 0.0f;
}
if (axis == FD_ROLL) {
DEBUG_SET(DEBUG_ITERM_RELAX, 0, lrintf(setpointHpf));
DEBUG_SET(DEBUG_ITERM_RELAX, 1, lrintf(itermRelaxFactor * 100.0f));
DEBUG_SET(DEBUG_ITERM_RELAX, 2, lrintf(*itermErrorRate));
}
return itermErrorRate * itermRelaxFactor;
}
}
return itermErrorRate;
}
#ifdef USE_D_BOOST
static float FAST_CODE applyDBoost(pidState_t *pidState, float dT) {
@ -723,8 +714,7 @@ static void FAST_CODE NOINLINE pidApplyMulticopterRateController(pidState_t *pid
const float newOutput = newPTerm + newDTerm + pidState->errorGyroIf + newCDTerm;
const float newOutputLimited = constrainf(newOutput, -pidState->pidSumLimit, +pidState->pidSumLimit);
float itermErrorRate = rateError;
applyItermRelax(axis, pidState->gyroRate, pidState->rateTarget, &itermErrorRate);
float itermErrorRate = applyItermRelax(axis, pidState->rateTarget, rateError);
#ifdef USE_ANTIGRAVITY
itermErrorRate *= iTermAntigravityGain;
@ -974,9 +964,13 @@ void FAST_CODE pidController(float dT)
// Limit desired rate to something gyro can measure reliably
pidState[axis].rateTarget = constrainf(rateTarget, -GYRO_SATURATION_LIMIT, +GYRO_SATURATION_LIMIT);
#ifdef USE_GYRO_KALMAN
gyroKalmanSetSetpoint(axis, pidState[axis].rateTarget);
if (pidProfile()->kalmanEnabled) {
pidState[axis].gyroRate = gyroKalmanUpdate(axis, pidState[axis].gyroRate, pidState[axis].rateTarget);
}
#endif
DEBUG_SET(DEBUG_PID_MEASUREMENT, axis, pidState[axis].gyroRate);
}
// Step 3: Run control for ANGLE_MODE, HORIZON_MODE, and HEADING_LOCK
@ -1038,7 +1032,6 @@ void pidInit(void)
pidGainsUpdateRequired = false;
itermRelax = pidProfile()->iterm_relax;
itermRelaxType = pidProfile()->iterm_relax_type;
yawLpfHz = pidProfile()->yaw_lpf_hz;
motorItermWindupPoint = 1.0f - (pidProfile()->itermWindupPointPercent / 100.0f);
@ -1108,6 +1101,11 @@ void pidInit(void)
}
pidResetTPAFilter();
#ifdef USE_GYRO_KALMAN
if (pidProfile()->kalmanEnabled) {
gyroKalmanInitialize(pidProfile()->kalman_q, pidProfile()->kalman_w, pidProfile()->kalman_sharpness);
}
#endif
}
const pidBank_t * pidBank(void) {

View file

@ -97,11 +97,6 @@ typedef enum {
ITERM_RELAX_RPY
} itermRelax_e;
typedef enum {
ITERM_RELAX_GYRO = 0,
ITERM_RELAX_SETPOINT
} itermRelaxType_e;
typedef struct pidProfile_s {
uint8_t pidControllerType;
pidBank_t bank_fw;
@ -138,7 +133,6 @@ typedef struct pidProfile_s {
uint8_t navVelXyDtermAttenuation; // VEL_XY dynamic Dterm scale: Dterm will be attenuatedby this value (in percent) when UAV is traveling with more than navVelXyDtermAttenuationStart percents of max velocity
uint8_t navVelXyDtermAttenuationStart; // VEL_XY dynamic Dterm scale: Dterm attenuation will begin at this percent of max velocity
uint8_t navVelXyDtermAttenuationEnd; // VEL_XY dynamic Dterm scale: Dterm will be fully attenuated at this percent of max velocity
uint8_t iterm_relax_type; // Specifies type of relax algorithm
uint8_t iterm_relax_cutoff; // This cutoff frequency specifies a low pass filter which predicts average response of the quad to setpoint
uint8_t iterm_relax; // Enable iterm suppression during stick input
@ -151,6 +145,10 @@ typedef struct pidProfile_s {
uint16_t navFwPosHdgPidsumLimit;
uint8_t controlDerivativeLpfHz;
uint16_t kalman_q;
uint16_t kalman_w;
uint16_t kalman_sharpness;
uint8_t kalmanEnabled;
} pidProfile_t;
typedef struct pidAutotuneConfig_s {

View file

@ -41,7 +41,7 @@
#ifdef USE_RPM_FILTER
#define RPM_TO_HZ 60.0f
#define HZ_TO_RPM 1/60.0f
#define RPM_FILTER_RPM_LPF_HZ 150
#define RPM_FILTER_HARMONICS 3
@ -66,7 +66,6 @@ typedef float (*rpmFilterApplyFnPtr)(rpmFilterBank_t *filter, uint8_t axis, floa
typedef void (*rpmFilterUpdateFnPtr)(rpmFilterBank_t *filterBank, uint8_t motor, float baseFrequency);
static EXTENDED_FASTRAM pt1Filter_t motorFrequencyFilter[MAX_SUPPORTED_MOTORS];
static EXTENDED_FASTRAM float erpmToHz;
static EXTENDED_FASTRAM rpmFilterBank_t gyroRpmFilters;
static EXTENDED_FASTRAM rpmFilterApplyFnPtr rpmGyroApplyFn;
static EXTENDED_FASTRAM rpmFilterUpdateFnPtr rpmGyroUpdateFn;
@ -163,7 +162,6 @@ void rpmFiltersInit(void)
{
pt1FilterInit(&motorFrequencyFilter[i], RPM_FILTER_RPM_LPF_HZ, RPM_FILTER_UPDATE_RATE_US * 1e-6f);
}
erpmToHz = ERPM_PER_LSB / (motorConfig()->motorPoleCount / 2) / RPM_TO_HZ;
rpmGyroUpdateFn = (rpmFilterUpdateFnPtr)nullRpmFilterUpdate;
@ -190,7 +188,7 @@ void rpmFilterUpdateTask(timeUs_t currentTimeUs)
for (uint8_t i = 0; i < motorCount; i++)
{
const escSensorData_t *escState = getEscTelemetry(i); //Get ESC telemetry
const float baseFrequency = pt1FilterApply(&motorFrequencyFilter[i], escState->rpm * erpmToHz); //Filter motor frequency
const float baseFrequency = pt1FilterApply(&motorFrequencyFilter[i], escState->rpm * HZ_TO_RPM); //Filter motor frequency
if (i < 4) {
DEBUG_SET(DEBUG_RPM_FREQ, i, (int)baseFrequency);

View file

@ -271,6 +271,10 @@ void servoMixer(float dT)
input[INPUT_GVAR_1] = constrain(gvGet(1), -1000, 1000);
input[INPUT_GVAR_2] = constrain(gvGet(2), -1000, 1000);
input[INPUT_GVAR_3] = constrain(gvGet(3), -1000, 1000);
input[INPUT_GVAR_4] = constrain(gvGet(4), -1000, 1000);
input[INPUT_GVAR_5] = constrain(gvGet(5), -1000, 1000);
input[INPUT_GVAR_6] = constrain(gvGet(6), -1000, 1000);
input[INPUT_GVAR_7] = constrain(gvGet(7), -1000, 1000);
#endif
if (IS_RC_MODE_ACTIVE(BOXCAMSTAB)) {

View file

@ -58,6 +58,10 @@ typedef enum {
INPUT_GVAR_1 = 31,
INPUT_GVAR_2 = 32,
INPUT_GVAR_3 = 33,
INPUT_GVAR_4 = 34,
INPUT_GVAR_5 = 35,
INPUT_GVAR_6 = 36,
INPUT_GVAR_7 = 37,
INPUT_SOURCE_COUNT
} inputSource_e;

View file

@ -22,11 +22,13 @@
#if defined(USE_FRSKYOSD)
#include "common/maths.h"
#include "common/utils.h"
#include "drivers/display.h"
#include "drivers/display_canvas.h"
#include "drivers/display_font_metadata.h"
#include "drivers/display_widgets.h"
#include "io/displayport_frsky_osd.h"
#include "io/frsky_osd.h"
@ -444,6 +446,111 @@ static void contextPop(displayCanvas_t *displayCanvas)
frskyOSDContextPop();
}
static int supportedInstances(displayWidgets_t *widgets, displayWidgetType_e widgetType)
{
UNUSED(widgets);
if (frskyOSDSupportsWidgets()) {
switch (widgetType) {
case DISPLAY_WIDGET_TYPE_AHI:
return 1;
case DISPLAY_WIDGET_TYPE_SIDEBAR:
return FRSKY_OSD_WIDGET_ID_SIDEBAR_LAST - FRSKY_OSD_WIDGET_ID_SIDEBAR_FIRST + 1;
}
}
return 0;
}
static bool configureAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIConfiguration_t *config)
{
UNUSED(widgets);
if (frskyOSDSupportsWidgets() && instance == 0) {
frskyOSDWidgetAHIConfig_t cfg = {
.rect.origin.x = config->rect.x,
.rect.origin.y = config->rect.y,
.rect.size.w = config->rect.w,
.rect.size.h = config->rect.h,
.style = config->style,
.options = config->options,
.crosshairMargin = config->crosshairMargin,
.strokeWidth = config->strokeWidth,
};
return frskyOSDSetWidgetConfig(FRSKY_OSD_WIDGET_ID_AHI, &cfg, sizeof(cfg));
}
return false;
}
static bool drawAHI(displayWidgets_t *widgets, unsigned instance, const widgetAHIData_t *data)
{
UNUSED(widgets);
if (frskyOSDSupportsWidgets() && instance == 0) {
frskyOSDWidgetAHIData_t ahiData = {
.pitch = frskyOSDQuantize(data->pitch, 0, 2 * M_PIf, 12),
.roll = frskyOSDQuantize(data->roll, 0, 2 * M_PIf, 12),
};
return frskyOSDDrawWidget(FRSKY_OSD_WIDGET_ID_AHI, &ahiData, sizeof(ahiData));
}
return false;
}
static bool configureSidebar(displayWidgets_t *widgets, unsigned instance, const widgetSidebarConfiguration_t *config)
{
UNUSED(widgets);
if (frskyOSDSupportsWidgets()) {
frskyOSDWidgetID_e id = FRSKY_OSD_WIDGET_ID_SIDEBAR_FIRST + instance;
if (id <= FRSKY_OSD_WIDGET_ID_SIDEBAR_LAST) {
frskyOSDWidgetSidebarConfig_t cfg = {
.rect.origin.x = config->rect.x,
.rect.origin.y = config->rect.y,
.rect.size.w = config->rect.w,
.rect.size.h = config->rect.h,
.options = config->options,
.divisions = config->divisions,
.counts_per_step = config->counts_per_step,
.unit = config->unit,
};
return frskyOSDSetWidgetConfig(id, &cfg, sizeof(cfg));
}
}
return false;
}
static bool drawSidebar(displayWidgets_t *widgets, unsigned instance, int32_t data)
{
UNUSED(widgets);
if (frskyOSDSupportsWidgets()) {
frskyOSDWidgetID_e id = FRSKY_OSD_WIDGET_ID_SIDEBAR_FIRST + instance;
if (id <= FRSKY_OSD_WIDGET_ID_SIDEBAR_LAST) {
frskyOSDWidgetSidebarData_t sidebarData = {
.value = data,
};
return frskyOSDDrawWidget(id, &sidebarData, sizeof(sidebarData));
}
}
return false;
}
static const displayWidgetsVTable_t frskyOSDWidgetsVTable = {
.supportedInstances = supportedInstances,
.configureAHI = configureAHI,
.drawAHI = drawAHI,
.configureSidebar = configureSidebar,
.drawSidebar = drawSidebar,
};
static bool getWidgets(displayWidgets_t *widgets, const displayCanvas_t *displayCanvas)
{
if (frskyOSDSupportsWidgets()) {
widgets->device = displayCanvas->device;
widgets->vTable = &frskyOSDWidgetsVTable;
return true;
}
return false;
}
static const displayCanvasVTable_t frskyOSDCanvasVTable = {
.setStrokeColor = setStrokeColor,
@ -484,12 +591,13 @@ static const displayCanvasVTable_t frskyOSDCanvasVTable = {
.contextPush = contextPush,
.contextPop = contextPop,
.getWidgets = getWidgets,
};
static bool getCanvas(displayCanvas_t *canvas, const displayPort_t *instance)
{
UNUSED(instance);
canvas->device = instance->device;
canvas->vTable = &frskyOSDCanvasVTable;
canvas->width = frskyOSDGetPixelWidth();
canvas->height = frskyOSDGetPixelHeight();

View file

@ -30,8 +30,13 @@
#include <stdbool.h>
#include <string.h>
#include "platform.h"
#if defined(USE_FLASHFS)
#include "drivers/flash.h"
#include "flashfs.h"
#include "io/flashfs.h"
static flashPartition_t *flashPartition;
@ -559,3 +564,5 @@ void flashfsInit(void)
flashfsSeekAbs(flashfsIdentifyStartOfFreeSpace());
}
}
#endif

View file

@ -18,8 +18,8 @@
#include "io/frsky_osd.h"
#include "io/serial.h"
#define FRSKY_OSD_BAUDRATE 115200
#define FRSKY_OSD_SUPPORTED_API_VERSION 1
#define FRSKY_OSD_DEFAULT_BAUDRATE_INDEX BAUD_115200
#define FRSKY_OSD_SUPPORTED_API_VERSION 2
#define FRSKY_OSD_PREAMBLE_BYTE_0 '$'
#define FRSKY_OSD_PREAMBLE_BYTE_1 'A'
@ -40,7 +40,8 @@
#define FRSKY_OSD_CMD_RESPONSE_ERROR 0
#define FRSKY_OSD_INFO_INTERVAL_MS 1000
#define FRSKY_OSD_INFO_INTERVAL_MS 100
#define FRSKY_OSD_INFO_READY_INTERVAL_MS 5000
#define FRSKY_OSD_TRACE(fmt, ...)
#define FRSKY_OSD_DEBUG(fmt, ...) LOG_D(OSD, "FrSky OSD: " fmt, ##__VA_ARGS__)
@ -114,6 +115,14 @@ typedef enum
// MAX7456 emulation commands
OSD_CMD_DRAW_GRID_CHR = 110,
OSD_CMD_DRAW_GRID_STR = 111,
OSD_CMD_DRAW_GRID_CHR_2 = 112, // API2
OSD_CMD_DRAW_GRID_STR_2 = 113, // API2
OSD_CMD_WIDGET_SET_CONFIG = 115, // API2
OSD_CMD_WIDGET_DRAW = 116, // API2
OSD_CMD_WIDGET_ERASE = 117, // API2
OSD_CMD_SET_DATA_RATE = 122,
} osdCommand_e;
typedef enum {
@ -159,23 +168,19 @@ typedef struct frskyOSDDrawGridStrHeaderCmd_s {
uint8_t gx;
uint8_t gy;
uint8_t opts;
// uvarint with size and blob folow
// uvarint with size and blob follow
// string IS null-terminated
} __attribute__((packed)) frskyOSDDrawGridStrHeaderCmd_t;
typedef struct frskyOSDPoint_s {
int x : 12;
int y : 12;
} __attribute__((packed)) frskyOSDPoint_t;
typedef struct frskyOSDSize_s {
int w : 12;
int h : 12;
} __attribute__((packed)) frskyOSDSize_t;
typedef struct frskyOSDRect_s {
frskyOSDPoint_t origin;
frskyOSDSize_t size;
} __attribute__((packed)) frskyOSDRect_t;
typedef struct frskyOSDDrawGridStrV2HeaderCmd_s {
unsigned gx : 5; // +5 = 5
unsigned gy : 4; // +4 = 9
unsigned opts : 3; // +3 = 12
unsigned size : 4; // +4 = 16 = 2 bytes
// if size == 0, uvarint with size follows
// blob with the given size follows
// string IS NOT null terminated
} __attribute__((packed)) frskyOSDDrawGridStrV2HeaderCmd_t;
typedef struct frskyOSDTriangle_s {
frskyOSDPoint_t p1;
@ -212,6 +217,21 @@ typedef struct frskyOSDDrawStrMaskCommandHeaderCmd_s {
// uvarint with size and blob follow
} __attribute__((packed)) frskyOSDDrawStrMaskCommandHeaderCmd_t;
typedef struct frskyOSDDrawGridChrV2Cmd_s
{
unsigned gx : 5; // +5 = 5
unsigned gy : 4; // +4 = 9
unsigned chr : 9; // +9 = 18
unsigned opts : 3; // +3 = 21, from osd_bitmap_opt_t
unsigned as_mask : 1; // +1 = 22
unsigned color : 2; // +2 = 24 = 3 bytes, only used when drawn as as_mask = 1
} __attribute__((packed)) frskyOSDDrawGridChrV2Cmd_t;
typedef struct frskyOSDError_s {
uint8_t command;
int8_t code;
} frskyOSDError_t;
typedef struct frskyOSDState_s {
struct {
@ -244,17 +264,27 @@ typedef struct frskyOSDState_s {
osdCharacter_t *chr;
} recvOsdCharacter;
serialPort_t *port;
baudRate_e baudrate;
bool keepBaudrate;
bool initialized;
frskyOSDError_t error;
timeMs_t nextInfoRequest;
} frskyOSDState_t;
static frskyOSDState_t state;
static bool frskyOSDDispatchResponse(void);
static uint8_t frskyOSDChecksum(uint8_t crc, uint8_t c)
{
return crc8_dvb_s2(crc, c);
}
static bool frskyOSDSpeaksV2(void)
{
return state.info.major >= 2 || (state.info.major == 1 && state.info.minor >= 99);
}
static void frskyOSDResetReceiveBuffer(void)
{
state.recvBuffer.state = RECV_STATE_NONE;
@ -294,7 +324,7 @@ static void frskyOSDSendCommand(uint8_t cmd, const void *payload, size_t size)
}
}
static void frskyOSDStateReset(serialPort_t *port)
static void frskyOSDStateReset(serialPort_t *port, baudRate_e baudrate)
{
frskyOSDResetReceiveBuffer();
frskyOSDResetSendBuffer();
@ -304,6 +334,8 @@ static void frskyOSDStateReset(serialPort_t *port)
state.info.viewport.height = 0;
state.port = port;
state.baudrate = baudrate;
state.keepBaudrate = false;
state.initialized = false;
}
@ -369,14 +401,55 @@ static bool frskyOSDIsResponseAvailable(void)
return state.recvBuffer.state == RECV_STATE_DONE;
}
static void frskyOSDClearReceiveBuffer(void)
{
frskyOSDUpdateReceiveBuffer();
if (frskyOSDIsResponseAvailable()) {
frskyOSDDispatchResponse();
} else if (state.recvBuffer.pos > 0) {
FRSKY_OSD_DEBUG("Discarding receive buffer with %u bytes", state.recvBuffer.pos);
frskyOSDResetReceiveBuffer();
}
}
static void frskyOSDSendAsyncCommand(uint8_t cmd, const void *data, size_t size)
{
FRSKY_OSD_TRACE("Send async cmd %u", cmd);
frskyOSDSendCommand(cmd, data, size);
}
static bool frskyOSDSendSyncCommand(uint8_t cmd, const void *data, size_t size, timeMs_t timeout)
{
FRSKY_OSD_TRACE("Send sync cmd %u", cmd);
frskyOSDClearReceiveBuffer();
frskyOSDSendCommand(cmd, data, size);
frskyOSDFlushSendBuffer();
timeMs_t end = millis() + timeout;
while (millis() < end) {
frskyOSDUpdateReceiveBuffer();
if (frskyOSDIsResponseAvailable() && frskyOSDDispatchResponse()) {
FRSKY_OSD_TRACE("Got sync response");
return true;
}
}
FRSKY_OSD_DEBUG("Sync response failed");
return false;
}
static bool frskyOSDHandleCommand(osdCommand_e cmd, const void *payload, size_t size)
{
const uint8_t *ptr = payload;
state.error.command = 0;
state.error.code = 0;
switch (cmd) {
case OSD_CMD_RESPONSE_ERROR:
{
if (size >= 2) {
state.error.command = *ptr;
state.error.code = *(ptr + 1);
FRSKY_OSD_ERROR("Received an error %02x in response to command %u", *(ptr + 1), *ptr);
return true;
}
@ -393,6 +466,21 @@ static bool frskyOSDHandleCommand(osdCommand_e cmd, const void *payload, size_t
resp->magic[0], resp->magic[1], resp->magic[2]);
return false;
}
FRSKY_OSD_TRACE("received OSD_CMD_INFO at %u", (unsigned)baudRates[state.baudrate]);
if (!state.keepBaudrate) {
const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_FRSKY_OSD);
if (portConfig && portConfig->peripheral_baudrateIndex > FRSKY_OSD_DEFAULT_BAUDRATE_INDEX &&
portConfig->peripheral_baudrateIndex != state.baudrate) {
// Try switching baudrates
uint32_t portBaudrate = baudRates[portConfig->peripheral_baudrateIndex];
FRSKY_OSD_TRACE("requesting baudrate switch from %u to %u",
(unsigned)baudRates[state.baudrate], (unsigned)portBaudrate);
frskyOSDSendAsyncCommand(OSD_CMD_SET_DATA_RATE, &portBaudrate, sizeof(portBaudrate));
frskyOSDFlushSendBuffer();
return true;
}
}
state.info.major = resp->versionMajor;
state.info.minor = resp->versionMinor;
state.info.grid.rows = resp->gridRows;
@ -431,6 +519,39 @@ static bool frskyOSDHandleCommand(osdCommand_e cmd, const void *payload, size_t
// We only wait for the confirmation, we're not interested in the data
return true;
}
case OSD_CMD_WIDGET_SET_CONFIG:
{
return true;
}
case OSD_CMD_SET_DATA_RATE:
{
if (size < sizeof(uint32_t)) {
break;
}
const uint32_t *newBaudrate = payload;
if (*newBaudrate && *newBaudrate != baudRates[state.baudrate]) {
FRSKY_OSD_TRACE("changed baudrate from %u to %u", (unsigned)baudRates[state.baudrate],
(unsigned)*newBaudrate);
serialSetBaudRate(state.port, *newBaudrate);
// OSD might have returned a different baudrate from our
// predefined ones. Be ready to handle that
state.baudrate = 0;
for (baudRate_e ii = 0; ii <= BAUD_MAX; ii++) {
if (baudRates[ii] == *newBaudrate) {
state.baudrate = ii;
break;
}
}
} else {
FRSKY_OSD_TRACE("baudrate refused, returned %u", (unsigned)*newBaudrate);
}
// Make sure we request OSD_CMD_INFO again as soon
// as possible and don't try to change the baudrate
// anymore.
state.nextInfoRequest = 0;
state.keepBaudrate = true;
return true;
}
default:
break;
}
@ -458,42 +579,6 @@ static bool frskyOSDDispatchResponse(void)
return ok;
}
static void frskyOSDClearReceiveBuffer(void)
{
frskyOSDUpdateReceiveBuffer();
if (frskyOSDIsResponseAvailable()) {
frskyOSDDispatchResponse();
} else if (state.recvBuffer.pos > 0) {
FRSKY_OSD_DEBUG("Discarding receive buffer with %u bytes", state.recvBuffer.pos);
frskyOSDResetReceiveBuffer();
}
}
static void frskyOSDSendAsyncCommand(uint8_t cmd, const void *data, size_t size)
{
FRSKY_OSD_TRACE("Send async cmd %u", cmd);
frskyOSDSendCommand(cmd, data, size);
}
static bool frskyOSDSendSyncCommand(uint8_t cmd, const void *data, size_t size, timeMs_t timeout)
{
FRSKY_OSD_TRACE("Send sync cmd %u", cmd);
frskyOSDClearReceiveBuffer();
frskyOSDSendCommand(cmd, data, size);
frskyOSDFlushSendBuffer();
timeMs_t end = millis() + timeout;
while (millis() < end) {
frskyOSDUpdateReceiveBuffer();
if (frskyOSDIsResponseAvailable() && frskyOSDDispatchResponse()) {
FRSKY_OSD_DEBUG("Got sync response");
return true;
}
}
FRSKY_OSD_DEBUG("Sync response failed");
return false;
}
static bool frskyOSDShouldRequestInfo(void)
{
return !frskyOSDIsReady() || millis() > state.nextInfoRequest;
@ -503,13 +588,52 @@ static void frskyOSDRequestInfo(void)
{
timeMs_t now = millis();
if (state.info.nextRequest < now) {
timeMs_t interval;
if (frskyOSDIsReady()) {
// We already contacted the OSD, so we're just
// polling it to see if the video changed.
interval = FRSKY_OSD_INFO_READY_INTERVAL_MS;
} else {
// We haven't yet heard from the OSD. If this is not
// the first request, switch to the next baudrate.
if (state.info.nextRequest > 0 && !state.keepBaudrate) {
if (state.baudrate == BAUD_MAX) {
state.baudrate = FRSKY_OSD_DEFAULT_BAUDRATE_INDEX;
} else {
state.baudrate++;
}
serialSetBaudRate(state.port, baudRates[state.baudrate]);
}
interval = FRSKY_OSD_INFO_INTERVAL_MS;
}
state.info.nextRequest = now + interval;
uint8_t version = FRSKY_OSD_SUPPORTED_API_VERSION;
FRSKY_OSD_TRACE("request OSD_CMD_INFO at %u", (unsigned)baudRates[state.baudrate]);
frskyOSDSendAsyncCommand(OSD_CMD_INFO, &version, sizeof(version));
frskyOSDFlushSendBuffer();
state.info.nextRequest = now + FRSKY_OSD_INFO_INTERVAL_MS;
}
}
static bool frskyOSDOpenPort(baudRate_e baudrate)
{
const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_FRSKY_OSD);
if (portConfig) {
FRSKY_OSD_TRACE("configured, trying to connect...");
portOptions_t portOptions = SERIAL_STOPBITS_1 | SERIAL_PARITY_NO;
serialPort_t *port = openSerialPort(portConfig->identifier,
FUNCTION_FRSKY_OSD, NULL, NULL, baudRates[baudrate],
MODE_RXTX, portOptions);
if (port) {
frskyOSDStateReset(port, baudrate);
frskyOSDRequestInfo();
return true;
}
}
return false;
}
static uint8_t frskyOSDEncodeAttr(textAttributes_t attr)
{
uint8_t frskyOSDAttr = 0;
@ -525,23 +649,8 @@ static uint8_t frskyOSDEncodeAttr(textAttributes_t attr)
bool frskyOSDInit(videoSystem_e videoSystem)
{
UNUSED(videoSystem);
// TODO: Use videoSystem to set the signal standard when
// no input is detected.
const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_FRSKY_OSD);
if (portConfig) {
FRSKY_OSD_TRACE("configured, trying to connect...");
portOptions_t portOptions = 0;
serialPort_t *port = openSerialPort(portConfig->identifier,
FUNCTION_FRSKY_OSD, NULL, NULL, FRSKY_OSD_BAUDRATE,
MODE_RXTX, portOptions);
if (port) {
frskyOSDStateReset(port);
frskyOSDRequestInfo();
return true;
}
}
return false;
return frskyOSDOpenPort(FRSKY_OSD_DEFAULT_BAUDRATE_INDEX);
}
bool frskyOSDIsReady(void)
@ -675,7 +784,33 @@ unsigned frskyOSDGetPixelHeight(void)
return state.info.viewport.height;
}
static void frskyOSDSendCharInGrid(unsigned x, unsigned y, uint16_t chr, textAttributes_t attr)
static void frskyOSDSendAsyncBlobCommand(uint8_t cmd, const void *header, size_t headerSize, const void *blob, size_t blobSize, bool explicitBlobSize)
{
uint8_t payload[128];
memcpy(payload, header, headerSize);
int uvarintSize;
if (explicitBlobSize) {
uvarintSize = uvarintEncode(blobSize, &payload[headerSize], sizeof(payload) - headerSize);
} else {
uvarintSize = 0;
}
memcpy(&payload[headerSize + uvarintSize], blob, blobSize);
frskyOSDSendAsyncCommand(cmd, payload, headerSize + uvarintSize + blobSize);
}
static void frskyOSDSendAsyncBlobWithExplicitSizeCommand(uint8_t cmd, const void *header, size_t headerSize, const void *blob, size_t blobSize)
{
frskyOSDSendAsyncBlobCommand(cmd, header, headerSize, blob, blobSize, true);
}
static void frskyOSDSendAsyncBlobWithoutExplicitSizeCommand(uint8_t cmd, const void *header, size_t headerSize, const void *blob, size_t blobSize)
{
frskyOSDSendAsyncBlobCommand(cmd, header, headerSize, blob, blobSize, false);
}
static void frskyOSDSendCharInGrid_V1(unsigned x, unsigned y, uint16_t chr, textAttributes_t attr)
{
uint8_t payload[] = {
x,
@ -687,15 +822,59 @@ static void frskyOSDSendCharInGrid(unsigned x, unsigned y, uint16_t chr, textAtt
frskyOSDSendAsyncCommand(OSD_CMD_DRAW_GRID_CHR, payload, sizeof(payload));
}
static void frskyOSDSendAsyncBlobCommand(uint8_t cmd, const void *header, size_t headerSize, const void *blob, size_t blobSize)
static void frskyOSDSendCharInGrid_V2(unsigned x, unsigned y, uint16_t chr, textAttributes_t attr)
{
uint8_t payload[128];
frskyOSDDrawGridChrV2Cmd_t payload = {
.gx = x,
.gy = y,
.chr = chr,
.opts = frskyOSDEncodeAttr(attr),
};
frskyOSDSendAsyncCommand(OSD_CMD_DRAW_GRID_CHR_2, &payload, sizeof(payload));
}
memcpy(payload, header, headerSize);
static void frskyOSDSendCharInGrid(unsigned x, unsigned y, uint16_t chr, textAttributes_t attr)
{
if (frskyOSDSpeaksV2()) {
frskyOSDSendCharInGrid_V2(x, y, chr, attr);
} else {
frskyOSDSendCharInGrid_V1(x, y, chr, attr);
}
}
int uvarintSize = uvarintEncode(blobSize, &payload[headerSize], sizeof(payload) - headerSize);
memcpy(&payload[headerSize + uvarintSize], blob, blobSize);
frskyOSDSendAsyncCommand(cmd, payload, headerSize + uvarintSize + blobSize);
static void frskyOSDSendCharSendStringInGrid_V1(unsigned x, unsigned y, const char *buff, textAttributes_t attr)
{
frskyOSDDrawGridStrHeaderCmd_t cmd = {
.gx = x,
.gy = y,
.opts = frskyOSDEncodeAttr(attr),
};
frskyOSDSendAsyncBlobWithExplicitSizeCommand(OSD_CMD_DRAW_GRID_STR, &cmd, sizeof(cmd), buff, strlen(buff) + 1);
}
static void frskyOSDSendCharSendStringInGrid_V2(unsigned x, unsigned y, const char *buff, textAttributes_t attr)
{
frskyOSDDrawGridStrV2HeaderCmd_t cmd = {
.gx = x,
.gy = y,
.opts = frskyOSDEncodeAttr(attr),
};
size_t len = strlen(buff);
if (len <= 15) {
cmd.size = len;
frskyOSDSendAsyncBlobWithoutExplicitSizeCommand(OSD_CMD_DRAW_GRID_STR_2, &cmd, sizeof(cmd), buff, len);
} else {
frskyOSDSendAsyncBlobWithExplicitSizeCommand(OSD_CMD_DRAW_GRID_STR_2, &cmd, sizeof(cmd), buff, len);
}
}
static void frskyOSDSendCharSendStringInGrid(unsigned x, unsigned y, const char *buff, textAttributes_t attr)
{
if (frskyOSDSpeaksV2()) {
frskyOSDSendCharSendStringInGrid_V2(x, y, buff, attr);
} else {
frskyOSDSendCharSendStringInGrid_V1(x, y, buff, attr);
}
}
void frskyOSDDrawStringInGrid(unsigned x, unsigned y, const char *buff, textAttributes_t attr)
@ -726,12 +905,7 @@ void frskyOSDDrawStringInGrid(unsigned x, unsigned y, const char *buff, textAttr
return;
}
frskyOSDDrawGridStrHeaderCmd_t cmd;
cmd.gx = x;
cmd.gy = y;
cmd.opts = frskyOSDEncodeAttr(attr);
frskyOSDSendAsyncBlobCommand(OSD_CMD_DRAW_GRID_STR, &cmd, sizeof(cmd), buff, strlen(buff) + 1);
frskyOSDSendCharSendStringInGrid(x, y, buff, attr);
}
void frskyOSDDrawCharInGrid(unsigned x, unsigned y, uint16_t chr, textAttributes_t attr)
@ -859,7 +1033,7 @@ void frskyOSDDrawString(int x, int y, const char *s, uint8_t opts)
cmd.p.y = y;
cmd.opts = opts;
frskyOSDSendAsyncBlobCommand(OSD_CMD_DRAWING_DRAW_STRING, &cmd, sizeof(cmd), s, strlen(s) + 1);
frskyOSDSendAsyncBlobWithExplicitSizeCommand(OSD_CMD_DRAWING_DRAW_STRING, &cmd, sizeof(cmd), s, strlen(s) + 1);
}
void frskyOSDDrawStringMask(int x, int y, const char *s, frskyOSDColor_e color, uint8_t opts)
@ -870,7 +1044,7 @@ void frskyOSDDrawStringMask(int x, int y, const char *s, frskyOSDColor_e color,
cmd.opts = opts;
cmd.maskColor = color;
frskyOSDSendAsyncBlobCommand(OSD_CMD_DRAWING_DRAW_STRING_MASK, &cmd, sizeof(cmd), s, strlen(s) + 1);
frskyOSDSendAsyncBlobWithExplicitSizeCommand(OSD_CMD_DRAWING_DRAW_STRING_MASK, &cmd, sizeof(cmd), s, strlen(s) + 1);
}
void frskyOSDMoveToPoint(int x, int y)
@ -987,5 +1161,45 @@ void frskyOSDContextPop(void)
frskyOSDSendAsyncCommand(OSD_CMD_CONTEXT_POP, NULL, 0);
}
bool frskyOSDSupportsWidgets(void)
{
return frskyOSDSpeaksV2();
}
bool frskyOSDSetWidgetConfig(frskyOSDWidgetID_e widget, const void *config, size_t configSize)
{
if (!frskyOSDSupportsWidgets()) {
return false;
}
uint8_t buffer[configSize + 1];
buffer[0] = widget;
memcpy(buffer + 1, config, configSize);
bool ok = frskyOSDSendSyncCommand(OSD_CMD_WIDGET_SET_CONFIG, buffer, sizeof(buffer), 500);
return ok && state.error.code == 0;
}
bool frskyOSDDrawWidget(frskyOSDWidgetID_e widget, const void *data, size_t dataSize)
{
if (!frskyOSDSupportsWidgets()) {
return false;
}
uint8_t buffer[dataSize + 1];
buffer[0] = widget;
memcpy(buffer + 1, data, dataSize);
frskyOSDSendAsyncCommand(OSD_CMD_WIDGET_DRAW, buffer, sizeof(buffer));
return true;
}
uint32_t frskyOSDQuantize(float val, float min, float max, int bits)
{
if (val < min) {
val = max - (min - val);
} else if (val > max) {
val = min + (val - max);
}
return (val * (1 << bits)) / max;
}
#endif

View file

@ -26,6 +26,77 @@ typedef enum {
FRSKY_OSD_OUTLINE_TYPE_LEFT = 1 << 3,
} frskyOSDLineOutlineType_e;
typedef enum
{
FRSKY_OSD_WIDGET_ID_AHI = 0,
FRSKY_OSD_WIDGET_ID_SIDEBAR_0 = 1,
FRSKY_OSD_WIDGET_ID_SIDEBAR_1 = 2,
FRSKY_OSD_WIDGET_ID_GRAPH_0 = 3,
FRSKY_OSD_WIDGET_ID_GRAPH_1 = 4,
FRSKY_OSD_WIDGET_ID_GRAPH_2 = 5,
FRSKY_OSD_WIDGET_ID_GRAPH_3 = 6,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_0 = 7,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_1 = 8,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_2 = 9,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_3 = 10,
FRSKY_OSD_WIDGET_ID_SIDEBAR_FIRST = FRSKY_OSD_WIDGET_ID_SIDEBAR_0,
FRSKY_OSD_WIDGET_ID_SIDEBAR_LAST = FRSKY_OSD_WIDGET_ID_SIDEBAR_1,
FRSKY_OSD_WIDGET_ID_GRAPH_FIRST = FRSKY_OSD_WIDGET_ID_GRAPH_0,
FRSKY_OSD_WIDGET_ID_GRAPH_LAST = FRSKY_OSD_WIDGET_ID_GRAPH_3,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_FIRST = FRSKY_OSD_WIDGET_ID_CHARGAUGE_0,
FRSKY_OSD_WIDGET_ID_CHARGAUGE_LAST = FRSKY_OSD_WIDGET_ID_CHARGAUGE_3,
} frskyOSDWidgetID_e;
typedef struct frskyOSDPoint_s {
int x : 12;
int y : 12;
} __attribute__((packed)) frskyOSDPoint_t;
typedef struct frskyOSDSize_s {
int w : 12;
int h : 12;
} __attribute__((packed)) frskyOSDSize_t;
typedef struct frskyOSDRect_s {
frskyOSDPoint_t origin;
frskyOSDSize_t size;
} __attribute__((packed)) frskyOSDRect_t;
typedef struct frskyOSDWidgetAHIData_s
{
uint16_t pitch : 12;
uint16_t roll : 12;
} __attribute__((packed)) frskyOSDWidgetAHIData_t;
typedef struct frskyOSDWidgetAHIConfig_s
{
frskyOSDRect_t rect;
uint8_t style;
uint8_t options;
uint8_t crosshairMargin;
uint8_t strokeWidth;
} __attribute__((packed)) frskyOSDWidgetAHIConfig_t;
typedef struct frskyOSDWidgetSidebarData_s
{
int32_t value : 24;
} __attribute__((packed)) frskyOSDWidgetSidebarData_t;
typedef struct frskyOSDWidgetSidebarConfig_s
{
frskyOSDRect_t rect;
uint8_t options;
uint8_t divisions;
uint16_t counts_per_step;
osdUnit_t unit;
} __attribute__((packed)) frskyOSDWidgetSidebarConfig_t;
bool frskyOSDInit(videoSystem_e videoSystem);
bool frskyOSDIsReady(void);
void frskyOSDUpdate(void);
@ -84,3 +155,9 @@ void frskyOSDCtmRotate(float r);
void frskyOSDContextPush(void);
void frskyOSDContextPop(void);
bool frskyOSDSupportsWidgets(void);
bool frskyOSDSetWidgetConfig(frskyOSDWidgetID_e widget, const void *config, size_t configSize);
bool frskyOSDDrawWidget(frskyOSDWidgetID_e widget, const void *data, size_t dataSize);
uint32_t frskyOSDQuantize(float val, float min, float max, int bits);

View file

@ -110,7 +110,6 @@ FILE_COMPILE_FOR_SPEED
#endif
#define VIDEO_BUFFER_CHARS_PAL 480
#define IS_DISPLAY_PAL (displayScreenSize(osdDisplayPort) == VIDEO_BUFFER_CHARS_PAL)
#define GFORCE_FILTER_TC 0.2
@ -160,19 +159,6 @@ typedef struct statistic_s {
static statistic_t stats;
typedef enum {
OSD_SIDEBAR_ARROW_NONE,
OSD_SIDEBAR_ARROW_UP,
OSD_SIDEBAR_ARROW_DOWN,
} osd_sidebar_arrow_e;
typedef struct osd_sidebar_s {
int32_t offset;
timeMs_t updated;
osd_sidebar_arrow_e arrow;
uint8_t idle;
} osd_sidebar_t;
static timeUs_t resumeRefreshAt = 0;
static bool refreshWaitForResumeCmdRelease;
@ -197,10 +183,9 @@ static bool osdDisplayHasCanvas;
#endif
#define AH_MAX_PITCH_DEFAULT 20 // Specify default maximum AHI pitch value displayed (degrees)
#define AH_SIDEBAR_WIDTH_POS 7
#define AH_SIDEBAR_HEIGHT_POS 3
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 12);
PG_REGISTER_WITH_RESET_TEMPLATE(osdConfig_t, osdConfig, PG_OSD_CONFIG, 13);
PG_REGISTER_WITH_RESET_FN(osdLayoutsConfig_t, osdLayoutsConfig, PG_OSD_LAYOUTS_CONFIG, 0);
static int digitCount(int32_t value)
{
@ -215,6 +200,11 @@ static int digitCount(int32_t value)
return digits;
}
bool osdDisplayIsPAL(void)
{
return displayScreenSize(osdDisplayPort) == VIDEO_BUFFER_CHARS_PAL;
}
/**
* Formats a number given in cents, to support non integer values
* without using floating point math. Value is always right aligned
@ -617,6 +607,19 @@ static void osdFormatCoordinate(char *buff, char sym, int32_t val)
buff[coordinateLength] = '\0';
}
static void osdFormatCraftName(char *buff)
{
if (strlen(systemConfig()->name) == 0)
strcpy(buff, "CRAFT_NAME");
else {
for (int i = 0; i < MAX_NAME_LENGTH; i++) {
buff[i] = sl_toupper((unsigned char)systemConfig()->name[i]);
if (systemConfig()->name[i] == 0)
break;
}
}
}
// Used twice, make sure it's exactly the same string
// to save some memory
#define RC_RX_LINK_LOST_MSG "!RC RX LINK LOST!"
@ -926,70 +929,6 @@ static inline int32_t osdGetAltitudeMsl(void)
#endif
}
static uint8_t osdUpdateSidebar(osd_sidebar_scroll_e scroll, osd_sidebar_t *sidebar, timeMs_t currentTimeMs)
{
// Scroll between SYM_AH_DECORATION_MIN and SYM_AH_DECORATION_MAX.
// Zero scrolling should draw SYM_AH_DECORATION.
uint8_t decoration = SYM_AH_DECORATION;
int offset;
int steps;
switch (scroll) {
case OSD_SIDEBAR_SCROLL_NONE:
sidebar->arrow = OSD_SIDEBAR_ARROW_NONE;
sidebar->offset = 0;
return decoration;
case OSD_SIDEBAR_SCROLL_ALTITUDE:
// Move 1 char for every 20cm
offset = osdGetAltitude();
steps = offset / 20;
break;
case OSD_SIDEBAR_SCROLL_GROUND_SPEED:
#if defined(USE_GPS)
offset = gpsSol.groundSpeed;
#else
offset = 0;
#endif
// Move 1 char for every 20 cm/s
steps = offset / 20;
break;
case OSD_SIDEBAR_SCROLL_HOME_DISTANCE:
#if defined(USE_GPS)
offset = GPS_distanceToHome;
#else
offset = 0;
#endif
// Move 1 char for every 5m
steps = offset / 5;
break;
}
if (offset) {
decoration -= steps % SYM_AH_DECORATION_COUNT;
if (decoration > SYM_AH_DECORATION_MAX) {
decoration -= SYM_AH_DECORATION_COUNT;
} else if (decoration < SYM_AH_DECORATION_MIN) {
decoration += SYM_AH_DECORATION_COUNT;
}
}
if (currentTimeMs - sidebar->updated > 100) {
if (offset > sidebar->offset) {
sidebar->arrow = OSD_SIDEBAR_ARROW_UP;
sidebar->idle = 0;
} else if (offset < sidebar->offset) {
sidebar->arrow = OSD_SIDEBAR_ARROW_DOWN;
sidebar->idle = 0;
} else {
if (sidebar->idle > 3) {
sidebar->arrow = OSD_SIDEBAR_ARROW_NONE;
} else {
sidebar->idle++;
}
}
sidebar->offset = offset;
sidebar->updated = currentTimeMs;
}
return decoration;
}
static bool osdIsHeadingValid(void)
{
return isImuHeadingValid();
@ -1265,7 +1204,7 @@ static void osdDisplayAdjustableDecimalValue(uint8_t elemPosX, uint8_t elemPosY,
static bool osdDrawSingleElement(uint8_t item)
{
uint16_t pos = osdConfig()->item_pos[currentLayout][item];
uint16_t pos = osdLayoutsConfig()->item_pos[currentLayout][item];
if (!OSD_VISIBLE(pos)) {
return false;
}
@ -1389,7 +1328,7 @@ static bool osdDrawSingleElement(uint8_t item)
else
{
int homeDirection = GPS_directionToHome - DECIDEGREES_TO_DEGREES(osdGetHeading());
osdDrawDirArrow(osdDisplayPort, osdGetDisplayPortCanvas(), OSD_DRAW_POINT_GRID(elemPosX, elemPosY), homeDirection, true);
osdDrawDirArrow(osdDisplayPort, osdGetDisplayPortCanvas(), OSD_DRAW_POINT_GRID(elemPosX, elemPosY), homeDirection);
}
} else {
// No home or no fix or unknown heading, blink.
@ -1661,15 +1600,7 @@ static bool osdDrawSingleElement(uint8_t item)
}
case OSD_CRAFT_NAME:
if (strlen(systemConfig()->name) == 0)
strcpy(buff, "CRAFT_NAME");
else {
for (int i = 0; i < MAX_NAME_LENGTH; i++) {
buff[i] = sl_toupper((unsigned char)systemConfig()->name[i]);
if (systemConfig()->name[i] == 0)
break;
}
}
osdFormatCraftName(buff);
break;
case OSD_THROTTLE_POS:
@ -1707,7 +1638,7 @@ static bool osdDrawSingleElement(uint8_t item)
case OSD_CROSSHAIRS: // Hud is a sub-element of the crosshair
osdCrosshairPosition(&elemPosX, &elemPosY);
osdHudDrawCrosshair(elemPosX, elemPosY);
osdHudDrawCrosshair(osdGetDisplayPortCanvas(), elemPosX, elemPosY);
if (osdConfig()->hud_homing && STATE(GPS_FIX) && STATE(GPS_FIX_HOME) && isImuHeadingValid()) {
osdHudDrawHoming(elemPosX, elemPosY);
@ -1812,43 +1743,7 @@ static bool osdDrawSingleElement(uint8_t item)
case OSD_HORIZON_SIDEBARS:
{
osdCrosshairPosition(&elemPosX, &elemPosY);
static osd_sidebar_t left;
static osd_sidebar_t right;
timeMs_t currentTimeMs = millis();
uint8_t leftDecoration = osdUpdateSidebar(osdConfig()->left_sidebar_scroll, &left, currentTimeMs);
uint8_t rightDecoration = osdUpdateSidebar(osdConfig()->right_sidebar_scroll, &right, currentTimeMs);
const int8_t hudwidth = AH_SIDEBAR_WIDTH_POS;
const int8_t hudheight = AH_SIDEBAR_HEIGHT_POS;
// Arrows
if (osdConfig()->sidebar_scroll_arrows) {
displayWriteChar(osdDisplayPort, elemPosX - hudwidth, elemPosY - hudheight - 1,
left.arrow == OSD_SIDEBAR_ARROW_UP ? SYM_AH_DECORATION_UP : SYM_BLANK);
displayWriteChar(osdDisplayPort, elemPosX + hudwidth, elemPosY - hudheight - 1,
right.arrow == OSD_SIDEBAR_ARROW_UP ? SYM_AH_DECORATION_UP : SYM_BLANK);
displayWriteChar(osdDisplayPort, elemPosX - hudwidth, elemPosY + hudheight + 1,
left.arrow == OSD_SIDEBAR_ARROW_DOWN ? SYM_AH_DECORATION_DOWN : SYM_BLANK);
displayWriteChar(osdDisplayPort, elemPosX + hudwidth, elemPosY + hudheight + 1,
right.arrow == OSD_SIDEBAR_ARROW_DOWN ? SYM_AH_DECORATION_DOWN : SYM_BLANK);
}
// Draw AH sides
for (int y = -hudheight; y <= hudheight; y++) {
displayWriteChar(osdDisplayPort, elemPosX - hudwidth, elemPosY + y, leftDecoration);
displayWriteChar(osdDisplayPort, elemPosX + hudwidth, elemPosY + y, rightDecoration);
}
// AH level indicators
displayWriteChar(osdDisplayPort, elemPosX - hudwidth + 1, elemPosY, SYM_AH_RIGHT);
displayWriteChar(osdDisplayPort, elemPosX + hudwidth - 1, elemPosY, SYM_AH_LEFT);
osdDrawSidebars(osdDisplayPort, osdGetDisplayPortCanvas());
return true;
}
@ -2604,197 +2499,203 @@ void osdDrawNextElement(void)
osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON);
}
void pgResetFn_osdConfig(osdConfig_t *osdConfig)
{
osdConfig->item_pos[0][OSD_ALTITUDE] = OSD_POS(1, 0) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 0) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1);
osdConfig->item_pos[0][OSD_RSSI_VALUE] = OSD_POS(23, 0) | OSD_VISIBLE_FLAG;
//line 2
osdConfig->item_pos[0][OSD_HOME_DIST] = OSD_POS(1, 1);
osdConfig->item_pos[0][OSD_TRIP_DIST] = OSD_POS(1, 2);
osdConfig->item_pos[0][OSD_MAIN_BATT_CELL_VOLTAGE] = OSD_POS(12, 1);
osdConfig->item_pos[0][OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE] = OSD_POS(12, 1);
osdConfig->item_pos[0][OSD_GPS_SPEED] = OSD_POS(23, 1);
osdConfig->item_pos[0][OSD_3D_SPEED] = OSD_POS(23, 1);
osdConfig->item_pos[0][OSD_THROTTLE_POS] = OSD_POS(1, 2) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_THROTTLE_POS_AUTO_THR] = OSD_POS(6, 2);
osdConfig->item_pos[0][OSD_HEADING] = OSD_POS(12, 2);
osdConfig->item_pos[0][OSD_CRUISE_HEADING_ERROR] = OSD_POS(12, 2);
osdConfig->item_pos[0][OSD_CRUISE_HEADING_ADJUSTMENT] = OSD_POS(12, 2);
osdConfig->item_pos[0][OSD_HEADING_GRAPH] = OSD_POS(18, 2);
osdConfig->item_pos[0][OSD_CURRENT_DRAW] = OSD_POS(2, 3) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_MAH_DRAWN] = OSD_POS(1, 4) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_WH_DRAWN] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_BATTERY_REMAINING_CAPACITY] = OSD_POS(1, 6);
osdConfig->item_pos[0][OSD_BATTERY_REMAINING_PERCENT] = OSD_POS(1, 7);
osdConfig->item_pos[0][OSD_POWER_SUPPLY_IMPEDANCE] = OSD_POS(1, 8);
osdConfig->item_pos[0][OSD_EFFICIENCY_MAH_PER_KM] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_EFFICIENCY_WH_PER_KM] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_ATTITUDE_ROLL] = OSD_POS(1, 7);
osdConfig->item_pos[0][OSD_ATTITUDE_PITCH] = OSD_POS(1, 8);
// avoid OSD_VARIO under OSD_CROSSHAIRS
osdConfig->item_pos[0][OSD_VARIO] = OSD_POS(23, 5);
// OSD_VARIO_NUM at the right of OSD_VARIO
osdConfig->item_pos[0][OSD_VARIO_NUM] = OSD_POS(24, 7);
osdConfig->item_pos[0][OSD_HOME_DIR] = OSD_POS(14, 11);
osdConfig->item_pos[0][OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_CRAFT_NAME] = OSD_POS(20, 2);
osdConfig->item_pos[0][OSD_VTX_CHANNEL] = OSD_POS(8, 6);
osdConfig->item_pos[0][OSD_ONTIME] = OSD_POS(23, 8);
osdConfig->item_pos[0][OSD_FLYTIME] = OSD_POS(23, 9);
osdConfig->item_pos[0][OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_RTC_TIME] = OSD_POS(23, 12);
osdConfig->item_pos[0][OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH] = OSD_POS(23, 7);
osdConfig->item_pos[0][OSD_REMAINING_DISTANCE_BEFORE_RTH] = OSD_POS(23, 6);
osdConfig->item_pos[0][OSD_GPS_SATS] = OSD_POS(0, 11) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_GPS_HDOP] = OSD_POS(0, 10);
osdConfig->item_pos[0][OSD_GPS_LAT] = OSD_POS(0, 12);
// Put this on top of the latitude, since it's very unlikely
// that users will want to use both at the same time.
osdConfig->item_pos[0][OSD_PLUS_CODE] = OSD_POS(0, 12);
osdConfig->item_pos[0][OSD_FLYMODE] = OSD_POS(13, 12) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_GPS_LON] = OSD_POS(18, 12);
osdConfig->item_pos[0][OSD_AZIMUTH] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_ROLL_PIDS] = OSD_POS(2, 10);
osdConfig->item_pos[0][OSD_PITCH_PIDS] = OSD_POS(2, 11);
osdConfig->item_pos[0][OSD_YAW_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_LEVEL_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_POS_XY_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_POS_Z_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_VEL_XY_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_VEL_Z_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_HEADING_P] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_BOARD_ALIGN_ROLL] = OSD_POS(2, 10);
osdConfig->item_pos[0][OSD_BOARD_ALIGN_PITCH] = OSD_POS(2, 11);
osdConfig->item_pos[0][OSD_RC_EXPO] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_RC_YAW_EXPO] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_THROTTLE_EXPO] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_PITCH_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_ROLL_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_YAW_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MANUAL_RC_EXPO] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MANUAL_RC_YAW_EXPO] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MANUAL_PITCH_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MANUAL_ROLL_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MANUAL_YAW_RATE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_NAV_FW_CRUISE_THR] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_NAV_FW_PITCH2THR] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_FW_ALT_PID_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_FW_POS_PID_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MC_VEL_X_PID_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MC_VEL_Y_PID_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MC_VEL_Z_PID_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_MC_POS_XYZ_P_OUTPUTS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_POWER] = OSD_POS(15, 1);
osdConfig->item_pos[0][OSD_IMU_TEMPERATURE] = OSD_POS(19, 2);
osdConfig->item_pos[0][OSD_BARO_TEMPERATURE] = OSD_POS(19, 3);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_0_TEMPERATURE] = OSD_POS(19, 4);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_1_TEMPERATURE] = OSD_POS(19, 5);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_2_TEMPERATURE] = OSD_POS(19, 6);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_3_TEMPERATURE] = OSD_POS(19, 7);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_4_TEMPERATURE] = OSD_POS(19, 8);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_5_TEMPERATURE] = OSD_POS(19, 9);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_6_TEMPERATURE] = OSD_POS(19, 10);
osdConfig->item_pos[0][OSD_TEMP_SENSOR_7_TEMPERATURE] = OSD_POS(19, 11);
osdConfig->item_pos[0][OSD_AIR_SPEED] = OSD_POS(3, 5);
osdConfig->item_pos[0][OSD_WIND_SPEED_HORIZONTAL] = OSD_POS(3, 6);
osdConfig->item_pos[0][OSD_WIND_SPEED_VERTICAL] = OSD_POS(3, 7);
osdConfig->item_pos[0][OSD_GFORCE] = OSD_POS(12, 4);
osdConfig->item_pos[0][OSD_GFORCE_X] = OSD_POS(12, 5);
osdConfig->item_pos[0][OSD_GFORCE_Y] = OSD_POS(12, 6);
osdConfig->item_pos[0][OSD_GFORCE_Z] = OSD_POS(12, 7);
osdConfig->item_pos[0][OSD_VTX_POWER] = OSD_POS(3, 5);
#if defined(USE_ESC_SENSOR)
osdConfig->item_pos[0][OSD_ESC_RPM] = OSD_POS(1, 2);
osdConfig->item_pos[0][OSD_ESC_TEMPERATURE] = OSD_POS(1, 3);
#endif
#if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
osdConfig->item_pos[0][OSD_RC_SOURCE] = OSD_POS(3, 4);
#endif
// Under OSD_FLYMODE. TODO: Might not be visible on NTSC?
osdConfig->item_pos[0][OSD_MESSAGES] = OSD_POS(1, 13) | OSD_VISIBLE_FLAG;
for (unsigned ii = 1; ii < OSD_LAYOUT_COUNT; ii++) {
for (unsigned jj = 0; jj < ARRAYLEN(osdConfig->item_pos[0]); jj++) {
osdConfig->item_pos[ii][jj] = osdConfig->item_pos[0][jj] & ~OSD_VISIBLE_FLAG;
}
}
osdConfig->rssi_alarm = 20;
osdConfig->time_alarm = 10;
osdConfig->alt_alarm = 100;
osdConfig->dist_alarm = 1000;
osdConfig->neg_alt_alarm = 5;
osdConfig->current_alarm = 0;
osdConfig->imu_temp_alarm_min = -200;
osdConfig->imu_temp_alarm_max = 600;
osdConfig->esc_temp_alarm_min = -200;
osdConfig->esc_temp_alarm_max = 900;
osdConfig->gforce_alarm = 5;
osdConfig->gforce_axis_alarm_min = -5;
osdConfig->gforce_axis_alarm_max = 5;
PG_RESET_TEMPLATE(osdConfig_t, osdConfig,
.rssi_alarm = 20,
.time_alarm = 10,
.alt_alarm = 100,
.dist_alarm = 1000,
.neg_alt_alarm = 5,
.current_alarm = 0,
.imu_temp_alarm_min = -200,
.imu_temp_alarm_max = 600,
.esc_temp_alarm_min = -200,
.esc_temp_alarm_max = 900,
.gforce_alarm = 5,
.gforce_axis_alarm_min = -5,
.gforce_axis_alarm_max = 5,
#ifdef USE_BARO
osdConfig->baro_temp_alarm_min = -200;
osdConfig->baro_temp_alarm_max = 600;
.baro_temp_alarm_min = -200,
.baro_temp_alarm_max = 600,
#endif
#ifdef USE_TEMPERATURE_SENSOR
osdConfig->temp_label_align = OSD_ALIGN_LEFT;
.temp_label_align = OSD_ALIGN_LEFT,
#endif
osdConfig->video_system = VIDEO_SYSTEM_AUTO;
.video_system = VIDEO_SYSTEM_AUTO,
osdConfig->ahi_reverse_roll = 0;
osdConfig->ahi_max_pitch = AH_MAX_PITCH_DEFAULT;
osdConfig->crosshairs_style = OSD_CROSSHAIRS_STYLE_DEFAULT;
osdConfig->horizon_offset = 0;
osdConfig->camera_uptilt = 0;
osdConfig->camera_fov_h = 135;
osdConfig->camera_fov_v = 85;
osdConfig->hud_margin_h = 3;
osdConfig->hud_margin_v = 3;
osdConfig->hud_homing = 0;
osdConfig->hud_homepoint = 0;
osdConfig->hud_radar_disp = 0;
osdConfig->hud_radar_range_min = 3;
osdConfig->hud_radar_range_max = 4000;
osdConfig->hud_radar_nearest = 0;
osdConfig->hud_wp_disp = 0;
osdConfig->left_sidebar_scroll = OSD_SIDEBAR_SCROLL_NONE;
osdConfig->right_sidebar_scroll = OSD_SIDEBAR_SCROLL_NONE;
osdConfig->sidebar_scroll_arrows = 0;
.ahi_reverse_roll = 0,
.ahi_max_pitch = AH_MAX_PITCH_DEFAULT,
.crosshairs_style = OSD_CROSSHAIRS_STYLE_DEFAULT,
.horizon_offset = 0,
.camera_uptilt = 0,
.camera_fov_h = 135,
.camera_fov_v = 85,
.hud_margin_h = 3,
.hud_margin_v = 3,
.hud_homing = 0,
.hud_homepoint = 0,
.hud_radar_disp = 0,
.hud_radar_range_min = 3,
.hud_radar_range_max = 4000,
.hud_radar_nearest = 0,
.hud_wp_disp = 0,
.left_sidebar_scroll = OSD_SIDEBAR_SCROLL_NONE,
.right_sidebar_scroll = OSD_SIDEBAR_SCROLL_NONE,
.sidebar_scroll_arrows = 0,
osdConfig->units = OSD_UNIT_METRIC;
osdConfig->main_voltage_decimals = 1;
.units = OSD_UNIT_METRIC,
.main_voltage_decimals = 1,
osdConfig->estimations_wind_compensation = true;
osdConfig->coordinate_digits = 9;
.estimations_wind_compensation = true,
.coordinate_digits = 9,
osdConfig->osd_failsafe_switch_layout = false;
.osd_failsafe_switch_layout = false,
osdConfig->plus_code_digits = 11;
.plus_code_digits = 11,
.ahi_width = OSD_AHI_WIDTH * OSD_CHAR_WIDTH,
.ahi_height = OSD_AHI_HEIGHT * OSD_CHAR_HEIGHT,
.ahi_vertical_offset = -OSD_CHAR_HEIGHT,
);
void pgResetFn_osdLayoutsConfig(osdLayoutsConfig_t *osdLayoutsConfig)
{
osdLayoutsConfig->item_pos[0][OSD_ALTITUDE] = OSD_POS(1, 0) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 0) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1);
osdLayoutsConfig->item_pos[0][OSD_RSSI_VALUE] = OSD_POS(23, 0) | OSD_VISIBLE_FLAG;
//line 2
osdLayoutsConfig->item_pos[0][OSD_HOME_DIST] = OSD_POS(1, 1);
osdLayoutsConfig->item_pos[0][OSD_TRIP_DIST] = OSD_POS(1, 2);
osdLayoutsConfig->item_pos[0][OSD_MAIN_BATT_CELL_VOLTAGE] = OSD_POS(12, 1);
osdLayoutsConfig->item_pos[0][OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE] = OSD_POS(12, 1);
osdLayoutsConfig->item_pos[0][OSD_GPS_SPEED] = OSD_POS(23, 1);
osdLayoutsConfig->item_pos[0][OSD_3D_SPEED] = OSD_POS(23, 1);
osdLayoutsConfig->item_pos[0][OSD_THROTTLE_POS] = OSD_POS(1, 2) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_THROTTLE_POS_AUTO_THR] = OSD_POS(6, 2);
osdLayoutsConfig->item_pos[0][OSD_HEADING] = OSD_POS(12, 2);
osdLayoutsConfig->item_pos[0][OSD_CRUISE_HEADING_ERROR] = OSD_POS(12, 2);
osdLayoutsConfig->item_pos[0][OSD_CRUISE_HEADING_ADJUSTMENT] = OSD_POS(12, 2);
osdLayoutsConfig->item_pos[0][OSD_HEADING_GRAPH] = OSD_POS(18, 2);
osdLayoutsConfig->item_pos[0][OSD_CURRENT_DRAW] = OSD_POS(2, 3) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_MAH_DRAWN] = OSD_POS(1, 4) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_WH_DRAWN] = OSD_POS(1, 5);
osdLayoutsConfig->item_pos[0][OSD_BATTERY_REMAINING_CAPACITY] = OSD_POS(1, 6);
osdLayoutsConfig->item_pos[0][OSD_BATTERY_REMAINING_PERCENT] = OSD_POS(1, 7);
osdLayoutsConfig->item_pos[0][OSD_POWER_SUPPLY_IMPEDANCE] = OSD_POS(1, 8);
osdLayoutsConfig->item_pos[0][OSD_EFFICIENCY_MAH_PER_KM] = OSD_POS(1, 5);
osdLayoutsConfig->item_pos[0][OSD_EFFICIENCY_WH_PER_KM] = OSD_POS(1, 5);
osdLayoutsConfig->item_pos[0][OSD_ATTITUDE_ROLL] = OSD_POS(1, 7);
osdLayoutsConfig->item_pos[0][OSD_ATTITUDE_PITCH] = OSD_POS(1, 8);
// avoid OSD_VARIO under OSD_CROSSHAIRS
osdLayoutsConfig->item_pos[0][OSD_VARIO] = OSD_POS(23, 5);
// OSD_VARIO_NUM at the right of OSD_VARIO
osdLayoutsConfig->item_pos[0][OSD_VARIO_NUM] = OSD_POS(24, 7);
osdLayoutsConfig->item_pos[0][OSD_HOME_DIR] = OSD_POS(14, 11);
osdLayoutsConfig->item_pos[0][OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_CRAFT_NAME] = OSD_POS(20, 2);
osdLayoutsConfig->item_pos[0][OSD_VTX_CHANNEL] = OSD_POS(8, 6);
osdLayoutsConfig->item_pos[0][OSD_ONTIME] = OSD_POS(23, 8);
osdLayoutsConfig->item_pos[0][OSD_FLYTIME] = OSD_POS(23, 9);
osdLayoutsConfig->item_pos[0][OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_RTC_TIME] = OSD_POS(23, 12);
osdLayoutsConfig->item_pos[0][OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH] = OSD_POS(23, 7);
osdLayoutsConfig->item_pos[0][OSD_REMAINING_DISTANCE_BEFORE_RTH] = OSD_POS(23, 6);
osdLayoutsConfig->item_pos[0][OSD_GPS_SATS] = OSD_POS(0, 11) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_GPS_HDOP] = OSD_POS(0, 10);
osdLayoutsConfig->item_pos[0][OSD_GPS_LAT] = OSD_POS(0, 12);
// Put this on top of the latitude, since it's very unlikely
// that users will want to use both at the same time.
osdLayoutsConfig->item_pos[0][OSD_PLUS_CODE] = OSD_POS(0, 12);
osdLayoutsConfig->item_pos[0][OSD_FLYMODE] = OSD_POS(13, 12) | OSD_VISIBLE_FLAG;
osdLayoutsConfig->item_pos[0][OSD_GPS_LON] = OSD_POS(18, 12);
osdLayoutsConfig->item_pos[0][OSD_AZIMUTH] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_ROLL_PIDS] = OSD_POS(2, 10);
osdLayoutsConfig->item_pos[0][OSD_PITCH_PIDS] = OSD_POS(2, 11);
osdLayoutsConfig->item_pos[0][OSD_YAW_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_LEVEL_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_POS_XY_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_POS_Z_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_VEL_XY_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_VEL_Z_PIDS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_HEADING_P] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_BOARD_ALIGN_ROLL] = OSD_POS(2, 10);
osdLayoutsConfig->item_pos[0][OSD_BOARD_ALIGN_PITCH] = OSD_POS(2, 11);
osdLayoutsConfig->item_pos[0][OSD_RC_EXPO] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_RC_YAW_EXPO] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_THROTTLE_EXPO] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_PITCH_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_ROLL_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_YAW_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MANUAL_RC_EXPO] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MANUAL_RC_YAW_EXPO] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MANUAL_PITCH_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MANUAL_ROLL_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MANUAL_YAW_RATE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_NAV_FW_CRUISE_THR] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_NAV_FW_PITCH2THR] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_FW_ALT_PID_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_FW_POS_PID_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MC_VEL_X_PID_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MC_VEL_Y_PID_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MC_VEL_Z_PID_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_MC_POS_XYZ_P_OUTPUTS] = OSD_POS(2, 12);
osdLayoutsConfig->item_pos[0][OSD_POWER] = OSD_POS(15, 1);
osdLayoutsConfig->item_pos[0][OSD_IMU_TEMPERATURE] = OSD_POS(19, 2);
osdLayoutsConfig->item_pos[0][OSD_BARO_TEMPERATURE] = OSD_POS(19, 3);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_0_TEMPERATURE] = OSD_POS(19, 4);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_1_TEMPERATURE] = OSD_POS(19, 5);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_2_TEMPERATURE] = OSD_POS(19, 6);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_3_TEMPERATURE] = OSD_POS(19, 7);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_4_TEMPERATURE] = OSD_POS(19, 8);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_5_TEMPERATURE] = OSD_POS(19, 9);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_6_TEMPERATURE] = OSD_POS(19, 10);
osdLayoutsConfig->item_pos[0][OSD_TEMP_SENSOR_7_TEMPERATURE] = OSD_POS(19, 11);
osdLayoutsConfig->item_pos[0][OSD_AIR_SPEED] = OSD_POS(3, 5);
osdLayoutsConfig->item_pos[0][OSD_WIND_SPEED_HORIZONTAL] = OSD_POS(3, 6);
osdLayoutsConfig->item_pos[0][OSD_WIND_SPEED_VERTICAL] = OSD_POS(3, 7);
osdLayoutsConfig->item_pos[0][OSD_GFORCE] = OSD_POS(12, 4);
osdLayoutsConfig->item_pos[0][OSD_GFORCE_X] = OSD_POS(12, 5);
osdLayoutsConfig->item_pos[0][OSD_GFORCE_Y] = OSD_POS(12, 6);
osdLayoutsConfig->item_pos[0][OSD_GFORCE_Z] = OSD_POS(12, 7);
osdLayoutsConfig->item_pos[0][OSD_VTX_POWER] = OSD_POS(3, 5);
#if defined(USE_ESC_SENSOR)
osdLayoutsConfig->item_pos[0][OSD_ESC_RPM] = OSD_POS(1, 2);
osdLayoutsConfig->item_pos[0][OSD_ESC_TEMPERATURE] = OSD_POS(1, 3);
#endif
#if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
osdLayoutsConfig->item_pos[0][OSD_RC_SOURCE] = OSD_POS(3, 4);
#endif
// Under OSD_FLYMODE. TODO: Might not be visible on NTSC?
osdLayoutsConfig->item_pos[0][OSD_MESSAGES] = OSD_POS(1, 13) | OSD_VISIBLE_FLAG;
for (unsigned ii = 1; ii < OSD_LAYOUT_COUNT; ii++) {
for (unsigned jj = 0; jj < ARRAYLEN(osdLayoutsConfig->item_pos[0]); jj++) {
osdLayoutsConfig->item_pos[ii][jj] = osdLayoutsConfig->item_pos[0][jj] & ~OSD_VISIBLE_FLAG;
}
}
}
static void osdSetNextRefreshIn(uint32_t timeMs) {
@ -2815,7 +2716,11 @@ static void osdCompleteAsyncInitialization(void)
osdDisplayIsReady = true;
#if defined(USE_CANVAS)
osdDisplayHasCanvas = displayGetCanvas(&osdCanvas, osdDisplayPort);
if (osdConfig()->force_grid) {
osdDisplayHasCanvas = false;
} else {
osdDisplayHasCanvas = displayGetCanvas(&osdCanvas, osdDisplayPort);
}
#endif
displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
@ -2972,7 +2877,7 @@ static void osdShowStats(void)
displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
displayClearScreen(osdDisplayPort);
if (IS_DISPLAY_PAL)
if (osdDisplayIsPAL())
displayWrite(osdDisplayPort, statNameX, top++, " --- STATS ---");
if (STATE(GPS_FIX)) {
@ -3074,15 +2979,22 @@ static void osdShowArmed(void)
{
dateTime_t dt;
char buf[MAX(32, FORMATTED_DATE_TIME_BUFSIZE)];
char craftNameBuf[MAX_NAME_LENGTH];
char *date;
char *time;
// We need 7 visible rows
uint8_t y = MIN((osdDisplayPort->rows / 2) - 1, osdDisplayPort->rows - 7 - 1);
// We need 10 visible rows
uint8_t y = MIN((osdDisplayPort->rows / 2) - 1, osdDisplayPort->rows - 10 - 1);
displayClearScreen(osdDisplayPort);
displayWrite(osdDisplayPort, 12, y, "ARMED");
y += 2;
if (strlen(systemConfig()->name) > 0) {
osdFormatCraftName(craftNameBuf);
displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(systemConfig() -> name)) / 2, y, craftNameBuf );
y += 2;
}
#if defined(USE_GPS)
if (feature(FEATURE_GPS)) {
if (STATE(GPS_FIX_HOME)) {
@ -3094,6 +3006,16 @@ static void osdShowArmed(void)
olc_encode(GPS_home.lat, GPS_home.lon, digits, buf, sizeof(buf));
displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(buf)) / 2, y + 2, buf);
y += 4;
#if defined (USE_SAFE_HOME)
if (isSafeHomeInUse()) {
textAttributes_t elemAttr = _TEXT_ATTRIBUTES_BLINK_BIT;
char buf2[12]; // format the distance first
osdFormatDistanceStr(buf2, safehome_distance);
tfp_sprintf(buf, "%c - %s -> SAFEHOME %u", SYM_HOME, buf2, safehome_used);
// write this message above the ARMED message to make it obvious
displayWriteWithAttr(osdDisplayPort, (osdDisplayPort->cols - strlen(buf)) / 2, y - 8, buf, elemAttr);
}
#endif
} else {
strcpy(buf, "!NO HOME POSITION!");
displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(buf)) / 2, y, buf);
@ -3153,7 +3075,12 @@ static void osdRefresh(timeUs_t currentTimeUs)
if (ARMING_FLAG(ARMED)) {
osdResetStats();
osdShowArmed(); // reset statistic etc
osdSetNextRefreshIn(ARMED_SCREEN_DISPLAY_TIME);
uint32_t delay = ARMED_SCREEN_DISPLAY_TIME;
#if defined(USE_SAFE_HOME)
if (isSafeHomeInUse())
delay *= 3;
#endif
osdSetNextRefreshIn(delay);
} else {
osdShowStats(); // show statistic
osdSetNextRefreshIn(STATS_SCREEN_DISPLAY_TIME);
@ -3289,7 +3216,7 @@ void osdStartFullRedraw(void)
void osdOverrideLayout(int layout, timeMs_t duration)
{
layoutOverride = constrain(layout, -1, ARRAYLEN(osdConfig()->item_pos) - 1);
layoutOverride = constrain(layout, -1, ARRAYLEN(osdLayoutsConfig()->item_pos) - 1);
if (layoutOverride >= 0 && duration > 0) {
layoutOverrideUntil = millis() + duration;
} else {

View file

@ -160,6 +160,8 @@ typedef enum {
OSD_UNIT_IMPERIAL,
OSD_UNIT_METRIC,
OSD_UNIT_UK, // Show speed in mp/h, other values in metric
OSD_UNIT_MAX = OSD_UNIT_UK,
} osd_unit_e;
typedef enum {
@ -182,6 +184,8 @@ typedef enum {
OSD_SIDEBAR_SCROLL_ALTITUDE,
OSD_SIDEBAR_SCROLL_GROUND_SPEED,
OSD_SIDEBAR_SCROLL_HOME_DISTANCE,
OSD_SIDEBAR_SCROLL_MAX = OSD_SIDEBAR_SCROLL_HOME_DISTANCE,
} osd_sidebar_scroll_e;
typedef enum {
@ -194,10 +198,14 @@ typedef enum {
OSD_AHI_STYLE_LINE,
} osd_ahi_style_e;
typedef struct osdConfig_s {
typedef struct osdLayoutsConfig_s {
// Layouts
uint16_t item_pos[OSD_LAYOUT_COUNT][OSD_ITEM_COUNT];
} osdLayoutsConfig_t;
PG_DECLARE(osdLayoutsConfig_t, osdLayoutsConfig);
typedef struct osdConfig_s {
// Alarms
uint8_t rssi_alarm; // rssi %
uint16_t time_alarm; // fly minutes
@ -255,6 +263,15 @@ typedef struct osdConfig_s {
bool osd_failsafe_switch_layout;
uint8_t plus_code_digits; // Number of digits to use in OSD_PLUS_CODE
uint8_t osd_ahi_style;
uint8_t force_grid; // Force a pixel based OSD to use grid mode.
uint8_t ahi_bordered; // Only used by the AHI widget
uint8_t ahi_width; // In pixels, only used by the AHI widget
uint8_t ahi_height; // In pixels, only used by the AHI widget
int8_t ahi_vertical_offset; // Offset from center in pixels. Positive moves the AHI down. Widget only.
int8_t sidebar_horizontal_offset; // Horizontal offset from default position. Units are grid slots for grid OSDs, pixels for pixel based OSDs. Positive values move sidebars closer to the edges.
uint8_t left_sidebar_scroll_step; // How many units each sidebar step represents. 0 means the default value for the scroll type.
uint8_t right_sidebar_scroll_step; // Same as left_sidebar_scroll_step, but for the right sidebar.
} osdConfig_t;
PG_DECLARE(osdConfig_t, osdConfig);
@ -263,6 +280,7 @@ typedef struct displayPort_s displayPort_t;
typedef struct displayCanvas_s displayCanvas_t;
void osdInit(displayPort_t *osdDisplayPort);
bool osdDisplayIsPAL(void);
void osdUpdate(timeUs_t currentTimeUs);
void osdStartFullRedraw(void);
// Sets a fixed OSD layout ignoring the RC input. Set it

View file

@ -30,10 +30,15 @@
#if defined(USE_CANVAS)
#define AHI_MIN_DRAW_INTERVAL_MS 100
#define AHI_MIN_DRAW_INTERVAL_MS 50
#define AHI_MAX_DRAW_INTERVAL_MS 1000
#define AHI_CROSSHAIR_MARGIN 6
#define SIDEBAR_REDRAW_INTERVAL_MS 100
#define WIDGET_SIDEBAR_LEFT_INSTANCE 0
#define WIDGET_SIDEBAR_RIGHT_INSTANCE 1
#include "common/constants.h"
#include "common/log.h"
#include "common/maths.h"
#include "common/typeconversion.h"
@ -41,6 +46,7 @@
#include "drivers/display.h"
#include "drivers/display_canvas.h"
#include "drivers/display_widgets.h"
#include "drivers/osd.h"
#include "drivers/osd_symbols.h"
#include "drivers/time.h"
@ -48,36 +54,21 @@
#include "io/osd_common.h"
#include "io/osd.h"
void osdCanvasDrawVarioShape(displayCanvas_t *canvas, unsigned ex, unsigned ey, float zvel, bool erase)
#include "navigation/navigation.h"
#define OSD_CANVAS_VARIO_ARROWS_PER_SLOT 2.0f
static void osdCanvasVarioRect(int *y, int *h, displayCanvas_t *canvas, int midY, float zvel)
{
char sym;
float ratio = zvel / (OSD_VARIO_CM_S_PER_ARROW * 2);
int height = -ratio * canvas->gridElementHeight;
int step;
int x = ex * canvas->gridElementWidth;
int start;
int dstart;
if (zvel > 0) {
sym = SYM_VARIO_UP_2A;
start = ceilf(OSD_VARIO_HEIGHT_ROWS / 2.0f);
dstart = start - 1;
step = -canvas->gridElementHeight;
int maxHeight = ceilf(OSD_VARIO_HEIGHT_ROWS /OSD_CANVAS_VARIO_ARROWS_PER_SLOT) * canvas->gridElementHeight;
// We use font characters with 2 arrows per slot
int height = MIN(fabsf(zvel) / (OSD_VARIO_CM_S_PER_ARROW * OSD_CANVAS_VARIO_ARROWS_PER_SLOT) * canvas->gridElementHeight, maxHeight);
if (zvel >= 0) {
*y = midY - height;
} else {
sym = SYM_VARIO_DOWN_2A;
start = floorf(OSD_VARIO_HEIGHT_ROWS / 2.0f);
dstart = start;
step = canvas->gridElementHeight;
}
int y = (start + ey) * canvas->gridElementHeight;
displayCanvasClipToRect(canvas, x, y, canvas->gridElementWidth, height);
int dy = (dstart + ey) * canvas->gridElementHeight;
for (int ii = 0, yy = dy; ii < (OSD_VARIO_HEIGHT_ROWS + 1) / 2; ii++, yy += step) {
if (erase) {
displayCanvasDrawCharacterMask(canvas, x, yy, sym, DISPLAY_CANVAS_COLOR_TRANSPARENT, 0);
} else {
displayCanvasDrawCharacter(canvas, x, yy, sym, 0);
}
*y = midY;
}
*h = height;
}
void osdCanvasDrawVario(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float zvel)
@ -86,46 +77,84 @@ void osdCanvasDrawVario(displayPort_t *display, displayCanvas_t *canvas, const o
static float prev = 0;
if (fabsf(prev - zvel) < (OSD_VARIO_CM_S_PER_ARROW / 20.0f)) {
if (fabsf(prev - zvel) < (OSD_VARIO_CM_S_PER_ARROW / (OSD_CANVAS_VARIO_ARROWS_PER_SLOT * 10))) {
return;
}
uint8_t ex;
uint8_t ey;
// Make sure we clear the grid buffer
uint8_t gx;
uint8_t gy;
osdDrawPointGetGrid(&gx, &gy, display, canvas, p);
osdGridBufferClearGridRect(gx, gy, 1, OSD_VARIO_HEIGHT_ROWS);
osdDrawPointGetGrid(&ex, &ey, display, canvas, p);
int midY = gy * canvas->gridElementHeight + (OSD_VARIO_HEIGHT_ROWS * canvas->gridElementHeight) / 2;
// Make sure we're aligned with the center-ish of the grid based variant
midY -= canvas->gridElementHeight;
osdCanvasDrawVarioShape(canvas, ex, ey, prev, true);
osdCanvasDrawVarioShape(canvas, ex, ey, zvel, false);
int x = gx * canvas->gridElementWidth;
int w = canvas->gridElementWidth;
int y;
int h;
osdCanvasVarioRect(&y, &h, canvas, midY, zvel);
if (signbit(prev) != signbit(zvel) || fabsf(prev) > fabsf(zvel)) {
// New rectangle doesn't overwrite the old one, we need to erase
int py;
int ph;
osdCanvasVarioRect(&py, &ph, canvas, midY, prev);
if (ph != h) {
displayCanvasClearRect(canvas, x, py, w, ph);
}
}
displayCanvasClipToRect(canvas, x, y, w, h);
if (zvel > 0) {
for (int yy = midY - canvas->gridElementHeight; yy > midY - h - canvas->gridElementHeight; yy -= canvas->gridElementHeight) {
displayCanvasDrawCharacter(canvas, x, yy, SYM_VARIO_UP_2A, DISPLAY_CANVAS_BITMAP_OPT_ERASE_TRANSPARENT);
}
} else {
for (int yy = midY; yy < midY + h; yy += canvas->gridElementHeight) {
displayCanvasDrawCharacter(canvas, x, yy, SYM_VARIO_DOWN_2A, DISPLAY_CANVAS_BITMAP_OPT_ERASE_TRANSPARENT);
}
}
prev = zvel;
}
void osdCanvasDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees, bool eraseBefore)
void osdCanvasDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees)
{
UNUSED(display);
const int top = 6;
const int topInset = -2;
const int bottom = -6;
const int width = 5;
// Since grid slots are not square, when we rotate the arrow
// it overflows horizontally a bit. Making a square-ish arrow
// doesn't look good, so it's better to overstep the grid slot
// boundaries a bit and then clean after ourselves.
const int overflow = 3;
int px;
int py;
osdDrawPointGetPixels(&px, &py, display, canvas, p);
displayCanvasClipToRect(canvas, px, py, canvas->gridElementWidth, canvas->gridElementHeight);
if (eraseBefore) {
displayCanvasSetFillColor(canvas, DISPLAY_CANVAS_COLOR_TRANSPARENT);
displayCanvasFillRect(canvas, px, py, canvas->gridElementWidth, canvas->gridElementHeight);
}
displayCanvasClearRect(canvas, px - overflow, py, canvas->gridElementWidth + overflow * 2, canvas->gridElementHeight);
displayCanvasSetFillColor(canvas, DISPLAY_CANVAS_COLOR_WHITE);
displayCanvasSetStrokeColor(canvas, DISPLAY_CANVAS_COLOR_BLACK);
displayCanvasCtmRotate(canvas, -DEGREES_TO_RADIANS(180 + degrees));
displayCanvasCtmTranslate(canvas, px + canvas->gridElementWidth / 2, py + canvas->gridElementHeight / 2);
displayCanvasFillStrokeTriangle(canvas, 0, 6, 5, -6, -5, -6);
// Main triangle
displayCanvasFillStrokeTriangle(canvas, 0, top, width, bottom, -width, bottom);
// Inset triangle
displayCanvasSetFillColor(canvas, DISPLAY_CANVAS_COLOR_TRANSPARENT);
displayCanvasFillStrokeTriangle(canvas, 0, -2, 6, -7, -6, -7);
displayCanvasMoveToPoint(canvas, 6, -7);
displayCanvasSetStrokeColor(canvas, DISPLAY_CANVAS_COLOR_TRANSPARENT);
displayCanvasStrokeLineToPoint(canvas, -6, -7);
displayCanvasFillTriangle(canvas, 0, topInset, width + 1, bottom - 1, -width, bottom - 1);
// Draw bottom strokes of the triangle
displayCanvasMoveToPoint(canvas, -width, bottom - 1);
displayCanvasStrokeLineToPoint(canvas, 0, topInset);
displayCanvasStrokeLineToPoint(canvas, width, bottom - 1);
}
static void osdDrawArtificialHorizonLevelLine(displayCanvas_t *canvas, int width, int pos, int margin)
@ -300,6 +329,69 @@ void osdDrawArtificialHorizonLine(displayCanvas_t *canvas, float pitchAngle, flo
displayCanvasContextPop(canvas);
}
static bool osdCanvasDrawArtificialHorizonWidget(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float pitchAngle, float rollAngle)
{
UNUSED(display);
UNUSED(p);
const int instance = 0;
static int iterations = 0;
static bool configured = false;
displayWidgets_t widgets;
if (displayCanvasGetWidgets(&widgets, canvas) &&
displayWidgetsSupportedInstances(&widgets, DISPLAY_WIDGET_TYPE_AHI) >= instance) {
int ahiWidth = osdConfig()->ahi_width;
int ahiX = (canvas->width - ahiWidth) / 2;
int ahiHeight = osdConfig()->ahi_height;
int ahiY = ((canvas->height - ahiHeight) / 2) + osdConfig()->ahi_vertical_offset;
if (ahiY < 0) {
ahiY = 0;
}
if (ahiY + ahiHeight >= canvas->height) {
ahiY = canvas->height - ahiHeight - 1;
}
if (!configured) {
widgetAHIStyle_e ahiStyle = 0;
switch ((osd_ahi_style_e)osdConfig()->osd_ahi_style) {
case OSD_AHI_STYLE_DEFAULT:
ahiStyle = DISPLAY_WIDGET_AHI_STYLE_STAIRCASE;
break;
case OSD_AHI_STYLE_LINE:
ahiStyle = DISPLAY_WIDGET_AHI_STYLE_LINE;
break;
}
widgetAHIConfiguration_t config = {
.rect.x = ahiX,
.rect.y = ahiY,
.rect.w = ahiWidth,
.rect.h = ahiHeight,
.style = ahiStyle,
.options = osdConfig()->ahi_bordered ? DISPLAY_WIDGET_AHI_OPTION_SHOW_CORNERS : 0,
.crosshairMargin = AHI_CROSSHAIR_MARGIN,
.strokeWidth = 1,
};
if (!displayWidgetsConfigureAHI(&widgets, instance, &config)) {
return false;
}
configured = true;
}
widgetAHIData_t data = {
.pitch = pitchAngle,
.roll = rollAngle,
};
if (displayWidgetsDrawAHI(&widgets, instance, &data)) {
if (++iterations == 10) {
iterations = 0;
osdGridBufferClearPixelRect(canvas, ahiX, ahiY, ahiWidth, ahiHeight);
}
return true;
}
}
return false;
}
void osdCanvasDrawArtificialHorizon(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float pitchAngle, float rollAngle)
{
UNUSED(display);
@ -314,20 +406,24 @@ void osdCanvasDrawArtificialHorizon(displayPort_t *display, displayCanvas_t *can
float totalError = fabsf(prevPitchAngle - pitchAngle) + fabsf(prevRollAngle - rollAngle);
if ((now > nextDrawMinMs && totalError > 0.05f)|| now > nextDrawMaxMs) {
switch ((osd_ahi_style_e)osdConfig()->osd_ahi_style) {
case OSD_AHI_STYLE_DEFAULT:
{
int x, y, w, h;
osdArtificialHorizonRect(canvas, &x, &y, &w, &h);
displayCanvasClearRect(canvas, x, y, w, h);
osdDrawArtificialHorizonShapes(canvas, pitchAngle, rollAngle);
break;
if (!osdCanvasDrawArtificialHorizonWidget(display, canvas, p, pitchAngle, rollAngle)) {
switch ((osd_ahi_style_e)osdConfig()->osd_ahi_style) {
case OSD_AHI_STYLE_DEFAULT:
{
int x, y, w, h;
osdArtificialHorizonRect(canvas, &x, &y, &w, &h);
displayCanvasClearRect(canvas, x, y, w, h);
osdDrawArtificialHorizonShapes(canvas, pitchAngle, rollAngle);
break;
}
case OSD_AHI_STYLE_LINE:
osdDrawArtificialHorizonLine(canvas, prevPitchAngle, prevRollAngle, true);
osdDrawArtificialHorizonLine(canvas, pitchAngle, rollAngle, false);
break;
}
case OSD_AHI_STYLE_LINE:
osdDrawArtificialHorizonLine(canvas, prevPitchAngle, prevRollAngle, true);
osdDrawArtificialHorizonLine(canvas, pitchAngle, rollAngle, false);
break;
}
prevPitchAngle = pitchAngle;
prevRollAngle = rollAngle;
nextDrawMinMs = now + AHI_MIN_DRAW_INTERVAL_MS;
@ -394,4 +490,223 @@ void osdCanvasDrawHeadingGraph(displayPort_t *display, displayCanvas_t *canvas,
displayCanvasFillStrokeTriangle(canvas, rmx - 2, py - 1, rmx + 2, py - 1, rmx, py + 1);
}
static int32_t osdCanvasSidebarGetValue(osd_sidebar_scroll_e scroll)
{
switch (scroll) {
case OSD_SIDEBAR_SCROLL_NONE:
break;
case OSD_SIDEBAR_SCROLL_ALTITUDE:
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
return CENTIMETERS_TO_CENTIFEET(osdGetAltitude());
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_METRIC:
return osdGetAltitude();
}
break;
case OSD_SIDEBAR_SCROLL_GROUND_SPEED:
#if defined(USE_GPS)
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_IMPERIAL:
// cms/s to (mi/h) * 100
return gpsSol.groundSpeed * 224 / 100;
case OSD_UNIT_METRIC:
// cm/s to (km/h) * 100
return gpsSol.groundSpeed * 36 / 10;
}
#endif
break;
case OSD_SIDEBAR_SCROLL_HOME_DISTANCE:
#if defined(USE_GPS)
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
return CENTIMETERS_TO_CENTIFEET(GPS_distanceToHome * 100);
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_METRIC:
return GPS_distanceToHome * 100;
#endif
}
break;
}
return 0;
}
static uint8_t osdCanvasSidebarGetOptions(int *width, osd_sidebar_scroll_e scroll)
{
switch (scroll) {
case OSD_SIDEBAR_SCROLL_NONE:
break;
case OSD_SIDEBAR_SCROLL_ALTITUDE:
FALLTHROUGH;
case OSD_SIDEBAR_SCROLL_GROUND_SPEED:
FALLTHROUGH;
case OSD_SIDEBAR_SCROLL_HOME_DISTANCE:
*width = OSD_CHAR_WIDTH * 5; // 4 numbers + unit
return 0;
}
*width = OSD_CHAR_WIDTH;
return DISPLAY_WIDGET_SIDEBAR_OPTION_STATIC | DISPLAY_WIDGET_SIDEBAR_OPTION_UNLABELED;
}
static void osdCanvasSidebarGetUnit(osdUnit_t *unit, uint16_t *countsPerStep, osd_sidebar_scroll_e scroll)
{
// We always count in 1/100 units, converting to
// "centifeet" when displaying imperial units
unit->scale = 100;
switch (scroll) {
case OSD_SIDEBAR_SCROLL_NONE:
unit->symbol = 0;
unit->divisor = 0;
unit->divided_symbol = 0;
*countsPerStep = 1;
break;
case OSD_SIDEBAR_SCROLL_ALTITUDE:
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
unit->symbol = SYM_ALT_FT;
unit->divisor = FEET_PER_KILOFEET;
unit->divided_symbol = SYM_ALT_KFT;
*countsPerStep = 50;
break;
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_METRIC:
unit->symbol = SYM_ALT_M;
unit->divisor = METERS_PER_KILOMETER;
unit->divided_symbol = SYM_ALT_KM;
*countsPerStep = 20;
break;
}
break;
case OSD_SIDEBAR_SCROLL_GROUND_SPEED:
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_IMPERIAL:
unit->symbol = SYM_MPH;
unit->divisor = 0;
unit->divided_symbol = 0;
*countsPerStep = 5;
break;
case OSD_UNIT_METRIC:
unit->symbol = SYM_KMH;
unit->divisor = 0;
unit->divided_symbol = 0;
*countsPerStep = 10;
break;
}
break;
case OSD_SIDEBAR_SCROLL_HOME_DISTANCE:
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
unit->symbol = SYM_FT;
unit->divisor = FEET_PER_MILE;
unit->divided_symbol = SYM_MI;
*countsPerStep = 300;
break;
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_METRIC:
unit->symbol = SYM_M;
unit->divisor = METERS_PER_KILOMETER;
unit->divided_symbol = SYM_KM;
*countsPerStep = 100;
break;
}
break;
}
}
static bool osdCanvasDrawSidebar(uint32_t *configured, displayWidgets_t *widgets,
displayCanvas_t *canvas,
int instance,
osd_sidebar_scroll_e scroll, unsigned scrollStep)
{
STATIC_ASSERT(OSD_SIDEBAR_SCROLL_MAX <= 3, adjust_scroll_shift);
STATIC_ASSERT(OSD_UNIT_MAX <= 3, adjust_units_shift);
// Configuration
uint32_t configuration = scrollStep << 16 | (unsigned)osdConfig()->sidebar_horizontal_offset << 8 | scroll << 6 | osdConfig()->units << 4;
if (configuration != *configured) {
int width;
uint8_t options = osdCanvasSidebarGetOptions(&width, scroll);
uint8_t ex;
uint8_t ey;
osdCrosshairPosition(&ex, &ey);
const int height = 2 * OSD_AH_SIDEBAR_HEIGHT_POS * OSD_CHAR_HEIGHT;
const int y = (ey - OSD_AH_SIDEBAR_HEIGHT_POS) * OSD_CHAR_HEIGHT;
widgetSidebarConfiguration_t config = {
.rect.y = y,
.rect.w = width,
.rect.h = height,
.options = options,
.divisions = OSD_AH_SIDEBAR_HEIGHT_POS * 2,
};
uint16_t countsPerStep;
osdCanvasSidebarGetUnit(&config.unit, &countsPerStep, scroll);
int center = ex * OSD_CHAR_WIDTH;
int horizontalOffset = OSD_AH_SIDEBAR_WIDTH_POS * OSD_CHAR_WIDTH + osdConfig()->sidebar_horizontal_offset;
if (instance == WIDGET_SIDEBAR_LEFT_INSTANCE) {
config.rect.x = MAX(center - horizontalOffset - width, 0);
config.options |= DISPLAY_WIDGET_SIDEBAR_OPTION_LEFT;
} else {
config.rect.x = MIN(center + horizontalOffset, canvas->width - width - 1);
}
if (scrollStep > 0) {
countsPerStep = scrollStep;
}
config.counts_per_step = config.unit.scale * countsPerStep;
if (!displayWidgetsConfigureSidebar(widgets, instance, &config)) {
return false;
}
*configured = configuration;
}
// Actual drawing
int32_t data = osdCanvasSidebarGetValue(scroll);
return displayWidgetsDrawSidebar(widgets, instance, data);
}
bool osdCanvasDrawSidebars(displayPort_t *display, displayCanvas_t *canvas)
{
UNUSED(display);
static uint32_t leftConfigured = UINT32_MAX;
static uint32_t rightConfigured = UINT32_MAX;
static timeMs_t nextRedraw = 0;
timeMs_t now = millis();
if (now < nextRedraw) {
return true;
}
displayWidgets_t widgets;
if (displayCanvasGetWidgets(&widgets, canvas)) {
if (!osdCanvasDrawSidebar(&leftConfigured, &widgets, canvas,
WIDGET_SIDEBAR_LEFT_INSTANCE,
osdConfig()->left_sidebar_scroll,
osdConfig()->left_sidebar_scroll_step)) {
return false;
}
if (!osdCanvasDrawSidebar(&rightConfigured, &widgets, canvas,
WIDGET_SIDEBAR_RIGHT_INSTANCE,
osdConfig()->right_sidebar_scroll,
osdConfig()->right_sidebar_scroll_step)) {
return false;
}
nextRedraw = now + SIDEBAR_REDRAW_INTERVAL_MS;
return true;
}
return false;
}
#endif

View file

@ -33,6 +33,7 @@ typedef struct displayCanvas_s displayCanvas_t;
typedef struct osdDrawPoint_s osdDrawPoint_t;
void osdCanvasDrawVario(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float zvel);
void osdCanvasDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees, bool eraseBefore);
void osdCanvasDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees);
void osdCanvasDrawArtificialHorizon(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float pitchAngle, float rollAngle);
void osdCanvasDrawHeadingGraph(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, int heading);
bool osdCanvasDrawSidebars(displayPort_t *display, displayCanvas_t *canvas);

View file

@ -88,17 +88,14 @@ void osdDrawVario(displayPort_t *display, displayCanvas_t *canvas, const osdDraw
#endif
}
void osdDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees, bool eraseBefore)
void osdDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees)
{
#if !defined(USE_CANVAS)
UNUSED(eraseBefore);
#endif
uint8_t gx;
uint8_t gy;
#if defined(USE_CANVAS)
if (canvas) {
osdCanvasDrawDirArrow(display, canvas, p, degrees, eraseBefore);
osdCanvasDrawDirArrow(display, canvas, p, degrees);
} else {
#endif
osdDrawPointGetGrid(&gx, &gy, display, canvas, p);
@ -139,3 +136,15 @@ void osdDrawHeadingGraph(displayPort_t *display, displayCanvas_t *canvas, const
}
#endif
}
void osdDrawSidebars(displayPort_t *display, displayCanvas_t *canvas)
{
#if defined(USE_CANVAS)
if (osdCanvasDrawSidebars(display, canvas)) {
return;
}
#else
UNUSED(canvas);
#endif
osdGridDrawSidebars(display);
}

View file

@ -41,6 +41,9 @@
#define OSD_HEADING_GRAPH_WIDTH 9
#define OSD_HEADING_GRAPH_DECIDEGREES_PER_CHAR 225
#define OSD_AH_SIDEBAR_WIDTH_POS 7
#define OSD_AH_SIDEBAR_HEIGHT_POS 3
typedef struct displayPort_s displayPort_t;
typedef struct displayCanvas_s displayCanvas_t;
@ -73,8 +76,9 @@ void osdDrawVario(displayPort_t *display, displayCanvas_t *canvas, const osdDraw
// Draws an arrow at the given point, where 0 degrees points to the top of the viewport and
// positive angles result in clockwise rotation. If eraseBefore is true, the rect surrouing
// the arrow will be erased first (need for e.g. the home arrow, since it uses multiple symbols)
void osdDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees, bool eraseBefore);
void osdDrawDirArrow(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float degrees);
void osdDrawArtificialHorizon(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, float rollAngle, float pitchAngle);
// Draws a heading graph with heading given as 0.1 degree steps i.e. [0, 3600). It uses 9 horizontal
// grid slots.
void osdDrawHeadingGraph(displayPort_t *display, displayCanvas_t *canvas, const osdDrawPoint_t *p, int heading);
void osdDrawSidebars(displayPort_t *display, displayCanvas_t *canvas);

View file

@ -303,7 +303,7 @@ static void djiSerializeOSDConfigReply(sbuf_t *dst)
// Position & visibility encoded in 16 bits. Position encoding is the same between BF/DJI and INAV
// However visibility is different. INAV has 3 layouts, while BF only has visibility profiles
// Here we use only one OSD layout mapped to first OSD BF profile
uint16_t itemPos = osdConfig()->item_pos[0][inavOSDIdx];
uint16_t itemPos = osdLayoutsConfig()->item_pos[0][inavOSDIdx];
// Workarounds for certain OSD element positions
// INAV calculates these dynamically, while DJI expects the config to have defined coordinates

View file

@ -34,11 +34,28 @@
#include "common/utils.h"
#include "drivers/display.h"
#include "drivers/osd.h"
#include "drivers/osd_symbols.h"
#include "drivers/time.h"
#include "io/osd.h"
#include "io/osd_common.h"
#include "navigation/navigation.h"
typedef enum {
OSD_SIDEBAR_ARROW_NONE,
OSD_SIDEBAR_ARROW_UP,
OSD_SIDEBAR_ARROW_DOWN,
} osd_sidebar_arrow_e;
typedef struct osd_sidebar_s {
int32_t offset;
timeMs_t updated;
osd_sidebar_arrow_e arrow;
uint8_t idle;
} osd_sidebar_t;
void osdGridDrawVario(displayPort_t *display, unsigned gx, unsigned gy, float zvel)
{
int v = zvel / OSD_VARIO_CM_S_PER_ARROW;
@ -87,6 +104,11 @@ void osdGridDrawDirArrow(displayPort_t *display, unsigned gx, unsigned gy, float
displayWriteChar(display, gx, gy, SYM_ARROW_UP + arrowOffset);
}
static float osdGetAspectRatioCorrection(void)
{
return osdDisplayIsPAL() ? 12.0f/15.0f : 12.0f/18.46f;
}
void osdGridDrawArtificialHorizon(displayPort_t *display, unsigned gx, unsigned gy, float pitchAngle, float rollAngle)
{
UNUSED(gx);
@ -101,10 +123,11 @@ void osdGridDrawArtificialHorizon(displayPort_t *display, unsigned gx, unsigned
static int8_t previous_written[OSD_AHI_PREV_SIZE];
static int8_t previous_orient = -1;
float pitch_rad_to_char = (float)(OSD_AHI_HEIGHT / 2 + 0.5) / DEGREES_TO_RADIANS(osdConfig()->ahi_max_pitch);
const float pitch_rad_to_char = (float)(OSD_AHI_HEIGHT / 2 + 0.5) / DEGREES_TO_RADIANS(osdConfig()->ahi_max_pitch);
float ky = sin_approx(rollAngle);
float kx = cos_approx(rollAngle);
const float ky = sin_approx(rollAngle);
const float kx = cos_approx(rollAngle);
const float ratio = osdGetAspectRatioCorrection();
if (previous_orient != -1) {
for (int i = 0; i < OSD_AHI_PREV_SIZE; ++i) {
@ -122,7 +145,7 @@ void osdGridDrawArtificialHorizon(displayPort_t *display, unsigned gx, unsigned
previous_orient = 0;
for (int8_t dx = -OSD_AHI_WIDTH / 2; dx <= OSD_AHI_WIDTH / 2; dx++) {
float fy = dx * (ky / kx) + pitchAngle * pitch_rad_to_char + 0.49f;
float fy = (ratio * dx) * (ky / kx) + pitchAngle * pitch_rad_to_char + 0.49f;
int8_t dy = floorf(fy);
const uint8_t chX = elemPosX + dx, chY = elemPosY - dy;
uint16_t c;
@ -139,7 +162,7 @@ void osdGridDrawArtificialHorizon(displayPort_t *display, unsigned gx, unsigned
previous_orient = 1;
for (int8_t dy = -OSD_AHI_HEIGHT / 2; dy <= OSD_AHI_HEIGHT / 2; dy++) {
const float fx = (dy - pitchAngle * pitch_rad_to_char) * (kx / ky) + 0.5f;
const float fx = ((dy / ratio) - pitchAngle * pitch_rad_to_char) * (kx / ky) + 0.5f;
const int8_t dx = floorf(fx);
const uint8_t chX = elemPosX + dx, chY = elemPosY - dy;
uint16_t c;
@ -197,4 +220,113 @@ void osdGridDrawHeadingGraph(displayPort_t *display, unsigned gx, unsigned gy, i
displayWrite(display, gx, gy, buf);
}
static uint8_t osdUpdateSidebar(osd_sidebar_scroll_e scroll, osd_sidebar_t *sidebar, timeMs_t currentTimeMs)
{
// Scroll between SYM_AH_DECORATION_MIN and SYM_AH_DECORATION_MAX.
// Zero scrolling should draw SYM_AH_DECORATION.
uint8_t decoration = SYM_AH_DECORATION;
int offset;
int steps;
switch (scroll) {
case OSD_SIDEBAR_SCROLL_NONE:
sidebar->arrow = OSD_SIDEBAR_ARROW_NONE;
sidebar->offset = 0;
return decoration;
case OSD_SIDEBAR_SCROLL_ALTITUDE:
// Move 1 char for every 20cm
offset = osdGetAltitude();
steps = offset / 20;
break;
case OSD_SIDEBAR_SCROLL_GROUND_SPEED:
#if defined(USE_GPS)
offset = gpsSol.groundSpeed;
#else
offset = 0;
#endif
// Move 1 char for every 20 cm/s
steps = offset / 20;
break;
case OSD_SIDEBAR_SCROLL_HOME_DISTANCE:
#if defined(USE_GPS)
offset = GPS_distanceToHome;
#else
offset = 0;
#endif
// Move 1 char for every 5m
steps = offset / 5;
break;
}
if (offset) {
decoration -= steps % SYM_AH_DECORATION_COUNT;
if (decoration > SYM_AH_DECORATION_MAX) {
decoration -= SYM_AH_DECORATION_COUNT;
} else if (decoration < SYM_AH_DECORATION_MIN) {
decoration += SYM_AH_DECORATION_COUNT;
}
}
if (currentTimeMs - sidebar->updated > 100) {
if (offset > sidebar->offset) {
sidebar->arrow = OSD_SIDEBAR_ARROW_UP;
sidebar->idle = 0;
} else if (offset < sidebar->offset) {
sidebar->arrow = OSD_SIDEBAR_ARROW_DOWN;
sidebar->idle = 0;
} else {
if (sidebar->idle > 3) {
sidebar->arrow = OSD_SIDEBAR_ARROW_NONE;
} else {
sidebar->idle++;
}
}
sidebar->offset = offset;
sidebar->updated = currentTimeMs;
}
return decoration;
}
void osdGridDrawSidebars(displayPort_t *display)
{
uint8_t elemPosX;
uint8_t elemPosY;
osdCrosshairPosition(&elemPosX, &elemPosY);
static osd_sidebar_t left;
static osd_sidebar_t right;
timeMs_t currentTimeMs = millis();
uint8_t leftDecoration = osdUpdateSidebar(osdConfig()->left_sidebar_scroll, &left, currentTimeMs);
uint8_t rightDecoration = osdUpdateSidebar(osdConfig()->right_sidebar_scroll, &right, currentTimeMs);
const int hudwidth = OSD_AH_SIDEBAR_WIDTH_POS;
const int hudheight = OSD_AH_SIDEBAR_HEIGHT_POS;
// Arrows
if (osdConfig()->sidebar_scroll_arrows) {
displayWriteChar(display, elemPosX - hudwidth, elemPosY - hudheight - 1,
left.arrow == OSD_SIDEBAR_ARROW_UP ? SYM_AH_DECORATION_UP : SYM_BLANK);
displayWriteChar(display, elemPosX + hudwidth, elemPosY - hudheight - 1,
right.arrow == OSD_SIDEBAR_ARROW_UP ? SYM_AH_DECORATION_UP : SYM_BLANK);
displayWriteChar(display, elemPosX - hudwidth, elemPosY + hudheight + 1,
left.arrow == OSD_SIDEBAR_ARROW_DOWN ? SYM_AH_DECORATION_DOWN : SYM_BLANK);
displayWriteChar(display, elemPosX + hudwidth, elemPosY + hudheight + 1,
right.arrow == OSD_SIDEBAR_ARROW_DOWN ? SYM_AH_DECORATION_DOWN : SYM_BLANK);
}
// Draw AH sides
int leftX = MAX(elemPosX - hudwidth - osdConfig()->sidebar_horizontal_offset, 0);
int rightX = MIN(elemPosX + hudwidth + osdConfig()->sidebar_horizontal_offset, display->cols - 1);
for (int y = -hudheight; y <= hudheight; y++) {
displayWriteChar(display, leftX, elemPosY + y, leftDecoration);
displayWriteChar(display, rightX, elemPosY + y, rightDecoration);
}
// AH level indicators
displayWriteChar(display, leftX + 1, elemPosY, SYM_AH_RIGHT);
displayWriteChar(display, rightX - 1, elemPosY, SYM_AH_LEFT);
}
#endif

View file

@ -32,3 +32,4 @@ void osdGridDrawVario(displayPort_t *display, unsigned gx, unsigned gy, float zv
void osdGridDrawDirArrow(displayPort_t *display, unsigned gx, unsigned gy, float degrees);
void osdGridDrawArtificialHorizon(displayPort_t *display, unsigned gx, unsigned gy, float pitchAngle, float rollAngle);
void osdGridDrawHeadingGraph(displayPort_t *display, unsigned gx, unsigned gy, int heading);
void osdGridDrawSidebars(displayPort_t *display);

View file

@ -28,6 +28,7 @@
#include "io/osd_hud.h"
#include "drivers/display.h"
#include "drivers/display_canvas.h"
#include "drivers/osd.h"
#include "drivers/osd_symbols.h"
#include "drivers/time.h"
@ -215,7 +216,7 @@ void osdHudDrawPoi(uint32_t poiDistance, int16_t poiDirection, int32_t poiAltitu
/*
* Draw the crosshair
*/
void osdHudDrawCrosshair(uint8_t px, uint8_t py)
void osdHudDrawCrosshair(displayCanvas_t *canvas, uint8_t px, uint8_t py)
{
static const uint16_t crh_style_all[] = {
SYM_AH_CH_LEFT, SYM_AH_CH_CENTER, SYM_AH_CH_RIGHT,
@ -227,11 +228,21 @@ void osdHudDrawCrosshair(uint8_t px, uint8_t py)
SYM_AH_CH_TYPE7, SYM_AH_CH_TYPE7 + 1, SYM_AH_CH_TYPE7 + 2,
};
// Center on the screen
if (canvas) {
displayCanvasContextPush(canvas);
displayCanvasCtmTranslate(canvas, -canvas->gridElementWidth / 2, -canvas->gridElementHeight / 2);
}
uint8_t crh_crosshair = (osd_crosshairs_style_e)osdConfig()->crosshairs_style;
displayWriteChar(osdGetDisplayPort(), px - 1, py,crh_style_all[crh_crosshair * 3]);
displayWriteChar(osdGetDisplayPort(), px, py, crh_style_all[crh_crosshair * 3 + 1]);
displayWriteChar(osdGetDisplayPort(), px + 1, py, crh_style_all[crh_crosshair * 3 + 2]);
if (canvas) {
displayCanvasContextPop(canvas);
}
}

View file

@ -19,9 +19,11 @@
#include <stdint.h>
typedef struct displayCanvas_s displayCanvas_t;
void osdHudClear(void);
void osdHudDrawCrosshair(uint8_t px, uint8_t py);
void osdHudDrawCrosshair(displayCanvas_t *canvas, uint8_t px, uint8_t py);
void osdHudDrawHoming(uint8_t px, uint8_t py);
void osdHudDrawPoi(uint32_t poiDistance, int16_t poiDirection, int32_t poiAltitude, uint8_t poiType, uint16_t poiSymbol, int16_t poiP1, int16_t poiP2);
void osdHudDrawExtras(uint8_t poi_id);

View file

@ -40,7 +40,7 @@
PG_REGISTER_WITH_RESET_TEMPLATE(pinioBoxConfig_t, pinioBoxConfig, PG_PINIOBOX_CONFIG, 1);
PG_RESET_TEMPLATE(pinioBoxConfig_t, pinioBoxConfig,
{ PERMANENT_ID_NONE, PERMANENT_ID_NONE, PERMANENT_ID_NONE, PERMANENT_ID_NONE }
{ BOX_PERMANENT_ID_NONE, BOX_PERMANENT_ID_NONE, BOX_PERMANENT_ID_NONE, BOX_PERMANENT_ID_NONE }
);
typedef struct pinioBoxRuntimeConfig_s {

View file

@ -74,7 +74,10 @@ typedef enum {
BAUD_1000000,
BAUD_1500000,
BAUD_2000000,
BAUD_2470000
BAUD_2470000,
BAUD_MIN = BAUD_AUTO,
BAUD_MAX = BAUD_2470000,
} baudRate_e;
extern const uint32_t baudRates[];

View file

@ -18,7 +18,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include <string.h>
#include "platform.h"
@ -74,6 +74,13 @@ uint32_t GPS_distanceToHome; // distance to home point in meters
int16_t GPS_directionToHome; // direction to home point in degrees
radar_pois_t radar_pois[RADAR_MAX_POIS];
#if defined(USE_SAFE_HOME)
int8_t safehome_used; // -1 if no safehome, 0 to MAX_SAFEHOMES -1 otherwise
uint32_t safehome_distance; // distance to the selected safehome
PG_REGISTER_ARRAY(navSafeHome_t, MAX_SAFE_HOMES, safeHomeConfig, PG_SAFE_HOME_CONFIG , 0);
#endif
#if defined(USE_NAV)
#if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
@ -115,8 +122,6 @@ PG_RESET_TEMPLATE(navConfig_t, navConfig,
.rth_home_altitude = 0, // altitude in centimeters
.rth_abort_threshold = 50000, // centimeters - 500m should be safe for all aircraft
.max_terrain_follow_altitude = 100, // max altitude in centimeters in terrain following mode
.rth_home_offset_distance = 0, // Distance offset from GPS established home to "safe" position used for RTH (cm, 0 disables)
.rth_home_offset_direction = 0, // Direction offset from GPS established home to "safe" position used for RTH (degrees, 0=N, 90=E, 180=S, 270=W, requires non-zero offset distance)
},
// MC-specific
@ -2403,6 +2408,42 @@ static navigationHomeFlags_t navigationActualStateHomeValidity(void)
return flags;
}
#if defined(USE_SAFE_HOME)
/*******************************************************
* Is a safehome being used instead of the arming point?
*******************************************************/
bool isSafeHomeInUse(void)
{
return (safehome_used > -1 && safehome_used < MAX_SAFE_HOMES);
}
/***********************************************************
* See if there are any safehomes near where we are arming.
* If so, use it instead of the arming point for home.
**********************************************************/
bool foundNearbySafeHome(void)
{
safehome_used = -1;
fpVector3_t safeHome;
gpsLocation_t shLLH;
shLLH.alt = 0;
for (uint8_t i = 0; i < MAX_SAFE_HOMES; i++) {
shLLH.lat = safeHomeConfig(i)->lat;
shLLH.lon = safeHomeConfig(i)->lon;
geoConvertGeodeticToLocal(&safeHome, &posControl.gpsOrigin, &shLLH, GEO_ALT_RELATIVE);
safehome_distance = calculateDistanceToDestination(&safeHome);
if (safehome_distance < 20000) { // 200 m
safehome_used = i;
setHomePosition(&safeHome, 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING, navigationActualStateHomeValidity());
return true;
}
}
safehome_distance = 0;
return false;
}
#endif
/*-----------------------------------------------------------
* Update home position, calculate distance and bearing to home
*-----------------------------------------------------------*/
@ -2424,15 +2465,10 @@ void updateHomePosition(void)
break;
}
if (setHome) {
if (navConfig()->general.rth_home_offset_distance != 0) { // apply user defined offset
fpVector3_t offsetHome;
offsetHome.x = posControl.actualState.abs.pos.x + navConfig()->general.rth_home_offset_distance * cos_approx(DEGREES_TO_RADIANS(navConfig()->general.rth_home_offset_direction));
offsetHome.y = posControl.actualState.abs.pos.y + navConfig()->general.rth_home_offset_distance * sin_approx(DEGREES_TO_RADIANS(navConfig()->general.rth_home_offset_direction));
offsetHome.z = posControl.actualState.abs.pos.z;
setHomePosition(&offsetHome, 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING, navigationActualStateHomeValidity());
} else {
#if defined(USE_SAFE_HOME)
if (!foundNearbySafeHome())
#endif
setHomePosition(&posControl.actualState.abs.pos, posControl.actualState.yaw, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING, navigationActualStateHomeValidity());
}
}
}
}
@ -2880,6 +2916,14 @@ bool saveNonVolatileWaypointList(void)
}
#endif
#if defined(USE_SAFE_HOME)
void resetSafeHomes(void)
{
memset(safeHomeConfigMutable(0), 0, sizeof(navSafeHome_t) * MAX_SAFE_HOMES);
}
#endif
static void mapWaypointToLocalPosition(fpVector3_t * localPos, const navWaypoint_t * waypoint)
{
gpsLocation_t wpLLH;
@ -3097,6 +3141,11 @@ static bool canActivateNavigationModes(void)
return (posControl.flags.estPosStatus == EST_TRUSTED) && (posControl.flags.estVelStatus == EST_TRUSTED) && (posControl.flags.estHeadingStatus >= EST_USABLE);
}
static bool isWaypointMissionValid(void)
{
return posControl.waypointListValid && (posControl.waypointCount > 0);
}
static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
{
static bool canActivateWaypoint = false;
@ -3110,9 +3159,16 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
}
// Flags if we can activate certain nav modes (check if we have required sensors and they provide valid data)
bool canActivateAltHold = canActivateAltHoldMode();
bool canActivatePosHold = canActivatePosHoldMode();
bool canActivateNavigation = canActivateNavigationModes();
const bool canActivateAltHold = canActivateAltHoldMode();
const bool canActivatePosHold = canActivatePosHoldMode();
const bool canActivateNavigation = canActivateNavigationModes();
const bool isExecutingRTH = navGetStateFlags(posControl.navState) & NAV_AUTO_RTH;
// Keep canActivateWaypoint flag at FALSE if there is no mission loaded
// Also block WP mission if we are executing RTH
if (!isWaypointMissionValid() || isExecutingRTH) {
canActivateWaypoint = false;
}
// LAUNCH mode has priority over any other NAV mode
if (STATE(FIXED_WING_LEGACY)) {
@ -3139,21 +3195,32 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
}
// RTH/Failsafe_RTH can override MANUAL
if (posControl.flags.forcedRTHActivated || (IS_RC_MODE_ACTIVE(BOXNAVRTH) && canActivatePosHold && canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME))) {
if (posControl.flags.forcedRTHActivated) {
// If we request forced RTH - attempt to activate it no matter what
// This might switch to emergency landing controller if GPS is unavailable
canActivateWaypoint = false; // Block WP mode if we switched to RTH for whatever reason
return NAV_FSM_EVENT_SWITCH_TO_RTH;
}
// Pilot-triggered RTH, also fall-back for WP if there is no mission loaded
if (IS_RC_MODE_ACTIVE(BOXNAVRTH) || (IS_RC_MODE_ACTIVE(BOXNAVWP) && !canActivateWaypoint)) {
// Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
// If don't keep this, loss of any of the canActivatePosHold && canActivateNavigation && canActivateAltHold
// will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
// logic to kick in (waiting for GPS on airplanes, switch to emergency landing etc)
if (isExecutingRTH || (canActivatePosHold && canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME))) {
return NAV_FSM_EVENT_SWITCH_TO_RTH;
}
}
// MANUAL mode has priority over WP/PH/AH
if (IS_RC_MODE_ACTIVE(BOXMANUAL)) {
canActivateWaypoint = false; // Block WP mode if we are in PASSTHROUGH mode
return NAV_FSM_EVENT_SWITCH_TO_IDLE;
}
// Pilot-activated waypoint mission. Fall-back to RTH in case of no mission loaded
if (IS_RC_MODE_ACTIVE(BOXNAVWP)) {
if ((FLIGHT_MODE(NAV_WP_MODE)) || (canActivateNavigation && canActivateWaypoint && canActivatePosHold && canActivateAltHold && STATE(GPS_FIX_HOME) && ARMING_FLAG(ARMED) && posControl.waypointListValid && (posControl.waypointCount > 0)))
if (FLIGHT_MODE(NAV_WP_MODE) || (canActivateWaypoint && canActivatePosHold && canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME)))
return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT;
}
else {

View file

@ -39,6 +39,26 @@ extern bool autoThrottleManuallyIncreased;
/* Navigation system updates */
void onNewGPSData(void);
#if defined(USE_SAFE_HOME)
#define MAX_SAFE_HOMES 8
typedef struct {
uint8_t enabled;
int32_t lat;
int32_t lon;
} navSafeHome_t;
PG_DECLARE_ARRAY(navSafeHome_t, MAX_SAFE_HOMES, safeHomeConfig);
extern int8_t safehome_used; // -1 if no safehome, 0 to MAX_SAFEHOMES -1 otherwise
extern uint32_t safehome_distance; // distance to the selected safehome
void resetSafeHomes(void); // remove all safehomes
bool isSafeHomeInUse(void); // Are we using a safehome instead of the arming point?
bool foundNearbySafeHome(void); // Did we find a safehome nearby?
#endif // defined(USE_SAFE_HOME)
#if defined(USE_NAV)
#if defined(USE_BLACKBOX)
@ -136,6 +156,8 @@ typedef struct positionEstimationConfig_s {
float max_eph_epv; // Max estimated position error acceptable for estimation (cm)
float baro_epv; // Baro position error
uint8_t use_gps_no_baro;
} positionEstimationConfig_t;
PG_DECLARE(positionEstimationConfig_t, positionEstimationConfig);
@ -172,8 +194,6 @@ typedef struct navConfig_s {
uint16_t min_rth_distance; // 0 Disables. Minimal distance for RTH in cm, otherwise it will just autoland
uint16_t rth_abort_threshold; // Initiate emergency landing if during RTH we get this much [cm] away from home
uint16_t max_terrain_follow_altitude; // Max altitude to be used in SURFACE TRACKING mode
uint16_t rth_home_offset_distance; // Distance offset from GPS established home to "safe" position used for RTH (cm, 0 disables)
uint16_t rth_home_offset_direction; // Direction offset from GPS established home to "safe" position used for RTH (degrees, 0=N, 90=E, 180=S, 270=W, requires non-zero offset distance)
} general;
struct {

View file

@ -158,19 +158,7 @@ static void updateAltitudeVelocityAndPitchController_FW(timeDelta_t deltaMicros)
void applyFixedWingAltitudeAndThrottleController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimePositionUpdate; // Occurs @ altitude sensor update rate (max MAX_ALTITUDE_UPDATE_RATE_HZ)
static timeUs_t previousTimeUpdate; // Occurs @ looptime rate
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
// If last time Z-controller was called is too far in the past - ignore it (likely restarting altitude controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
resetFixedWingAltitudeController();
return;
}
static timeUs_t previousTimePositionUpdate = 0; // Occurs @ altitude sensor update rate (max MAX_ALTITUDE_UPDATE_RATE_HZ)
if ((posControl.flags.estAltStatus >= EST_USABLE)) {
if (posControl.flags.verticalPositionDataNew) {
@ -182,7 +170,7 @@ void applyFixedWingAltitudeAndThrottleController(timeUs_t currentTimeUs)
updateAltitudeVelocityAndPitchController_FW(deltaMicrosPositionUpdate);
}
else {
// due to some glitch position update has not occurred in time, reset altitude controller
// Position update has not occurred in time (first iteration or glitch), reset altitude controller
resetFixedWingAltitudeController();
}
@ -398,19 +386,7 @@ static void updatePositionHeadingController_FW(timeUs_t currentTimeUs, timeDelta
void applyFixedWingPositionController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimePositionUpdate; // Occurs @ GPS update rate
static timeUs_t previousTimeUpdate; // Occurs @ looptime rate
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
// If last position update was too long in the past - ignore it (likely restarting altitude controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
resetFixedWingPositionController();
return;
}
static timeUs_t previousTimePositionUpdate = 0; // Occurs @ GPS update rate
// Apply controller only if position source is valid. In absence of valid pos sensor (GPS loss), we'd stick in forced ANGLE mode
if ((posControl.flags.estPosStatus >= EST_USABLE)) {
@ -429,6 +405,7 @@ void applyFixedWingPositionController(timeUs_t currentTimeUs)
updatePositionHeadingController_FW(currentTimeUs, deltaMicrosPositionUpdate);
}
else {
// Position update has not occurred in time (first iteration or glitch), reset altitude controller
resetFixedWingPositionController();
}
@ -448,19 +425,7 @@ void applyFixedWingPositionController(timeUs_t currentTimeUs)
int16_t applyFixedWingMinSpeedController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimePositionUpdate; // Occurs @ GPS update rate
static timeUs_t previousTimeUpdate; // Occurs @ looptime rate
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
// If last position update was too long in the past - ignore it (likely restarting altitude controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
throttleSpeedAdjustment = 0;
return 0;
}
static timeUs_t previousTimePositionUpdate = 0; // Occurs @ GPS update rate
// Apply controller only if position source is valid
if ((posControl.flags.estPosStatus >= EST_USABLE)) {
@ -480,6 +445,7 @@ int16_t applyFixedWingMinSpeedController(timeUs_t currentTimeUs)
throttleSpeedAdjustment = constrainf(throttleSpeedAdjustment, 0.0f, 500.0f);
}
else {
// Position update has not occurred in time (first iteration or glitch), reset altitude controller
throttleSpeedAdjustment = 0;
}

View file

@ -199,35 +199,13 @@ void resetMulticopterAltitudeController(void)
navPidReset(&posControl.pids.surface);
posControl.rcAdjustment[THROTTLE] = 0;
if (prepareForTakeoffOnReset) {
/* If we are preparing for takeoff - start with lowset possible climb rate, adjust alt target and make sure throttle doesn't jump */
posControl.desiredState.vel.z = -navConfig()->general.max_manual_climb_rate;
posControl.desiredState.pos.z = posToUse->pos.z - (navConfig()->general.max_manual_climb_rate / posControl.pids.pos[Z].param.kP);
posControl.pids.vel[Z].integrator = -500.0f;
pt1FilterReset(&altholdThrottleFilterState, -500.0f);
prepareForTakeoffOnReset = false;
}
else {
posControl.desiredState.vel.z = posToUse->vel.z; // Gradually transition from current climb
pt1FilterReset(&altholdThrottleFilterState, 0.0f);
}
posControl.desiredState.vel.z = posToUse->vel.z; // Gradually transition from current climb
pt1FilterReset(&altholdThrottleFilterState, 0.0f);
}
static void applyMulticopterAltitudeController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimePositionUpdate; // Occurs @ altitude sensor update rate (max MAX_ALTITUDE_UPDATE_RATE_HZ)
static timeUs_t previousTimeUpdate; // Occurs @ looptime rate
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
// If last position update was too long in the past - ignore it (likely restarting altitude controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
resetMulticopterAltitudeController();
return;
}
static timeUs_t previousTimePositionUpdate = 0; // Occurs @ altitude sensor update rate (max MAX_ALTITUDE_UPDATE_RATE_HZ)
// If we have an update on vertical position data - update velocity and accel targets
if (posControl.flags.verticalPositionDataNew) {
@ -236,11 +214,23 @@ static void applyMulticopterAltitudeController(timeUs_t currentTimeUs)
// Check if last correction was too log ago - ignore this update
if (deltaMicrosPositionUpdate < HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
// If we are preparing for takeoff - start with lowset possible climb rate, adjust alt target and make sure throttle doesn't jump
if (prepareForTakeoffOnReset) {
const navEstimatedPosVel_t * posToUse = navGetCurrentActualPositionAndVelocity();
posControl.desiredState.vel.z = -navConfig()->general.max_manual_climb_rate;
posControl.desiredState.pos.z = posToUse->pos.z - (navConfig()->general.max_manual_climb_rate / posControl.pids.pos[Z].param.kP);
posControl.pids.vel[Z].integrator = -500.0f;
pt1FilterReset(&altholdThrottleFilterState, -500.0f);
prepareForTakeoffOnReset = false;
}
// Execute actual altitude controllers
updateAltitudeVelocityController_MC(deltaMicrosPositionUpdate);
updateAltitudeThrottleController_MC(deltaMicrosPositionUpdate);
}
else {
// due to some glitch position update has not occurred in time, reset altitude controller
// Position update has not occurred in time (first start or glitch), reset altitude controller
resetMulticopterAltitudeController();
}
@ -625,24 +615,12 @@ static void updatePositionAccelController_MC(timeDelta_t deltaMicros, float maxA
static void applyMulticopterPositionController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimePositionUpdate; // Occurs @ GPS update rate
static timeUs_t previousTimeUpdate; // Occurs @ looptime rate
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
static timeUs_t previousTimePositionUpdate = 0; // Occurs @ GPS update rate
bool bypassPositionController;
// We should passthrough rcCommand is adjusting position in GPS_ATTI mode
bypassPositionController = (navConfig()->general.flags.user_control_mode == NAV_GPS_ATTI) && posControl.flags.isAdjustingPosition;
// If last call to controller was too long in the past - ignore it (likely restarting position controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
resetMulticopterPositionController();
return;
}
// Apply controller only if position source is valid. In absence of valid pos sensor (GPS loss), we'd stick in forced ANGLE mode
// and pilots input would be passed thru to PID controller
if ((posControl.flags.estPosStatus >= EST_USABLE)) {
@ -660,6 +638,7 @@ static void applyMulticopterPositionController(timeUs_t currentTimeUs)
updatePositionAccelController_MC(deltaMicrosPositionUpdate, NAV_ACCELERATION_XY_MAX, maxSpeed);
}
else {
// Position update has not occurred in time (first start or glitch), reset altitude controller
resetMulticopterPositionController();
}
}
@ -749,48 +728,14 @@ bool isMulticopterLandingDetected(void)
*-----------------------------------------------------------*/
static void applyMulticopterEmergencyLandingController(timeUs_t currentTimeUs)
{
static timeUs_t previousTimeUpdate;
static timeUs_t previousTimePositionUpdate;
const timeDelta_t deltaMicros = currentTimeUs - previousTimeUpdate;
previousTimeUpdate = currentTimeUs;
static timeUs_t previousTimePositionUpdate = 0;
/* Attempt to stabilise */
rcCommand[ROLL] = 0;
rcCommand[PITCH] = 0;
rcCommand[YAW] = 0;
if ((posControl.flags.estAltStatus >= EST_USABLE)) {
// If last position update was too long in the past - ignore it (likely restarting altitude controller)
if (deltaMicros > HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
previousTimeUpdate = currentTimeUs;
previousTimePositionUpdate = currentTimeUs;
resetMulticopterAltitudeController();
return;
}
if (posControl.flags.verticalPositionDataNew) {
const timeDelta_t deltaMicrosPositionUpdate = currentTimeUs - previousTimePositionUpdate;
previousTimePositionUpdate = currentTimeUs;
// Check if last correction was too log ago - ignore this update
if (deltaMicrosPositionUpdate < HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
updateClimbRateToAltitudeController(-1.0f * navConfig()->general.emerg_descent_rate, ROC_TO_ALT_NORMAL);
updateAltitudeVelocityController_MC(deltaMicrosPositionUpdate);
updateAltitudeThrottleController_MC(deltaMicrosPositionUpdate);
}
else {
// due to some glitch position update has not occurred in time, reset altitude controller
resetMulticopterAltitudeController();
}
// Indicate that information is no longer usable
posControl.flags.verticalPositionDataConsumed = 1;
}
// Update throttle controller
rcCommand[THROTTLE] = constrain((int16_t)navConfig()->mc.hover_throttle + posControl.rcAdjustment[THROTTLE], getThrottleIdleValue(), motorConfig()->maxthrottle);
}
else {
if ((posControl.flags.estAltStatus < EST_USABLE)) {
/* Sensors has gone haywire, attempt to land regardless */
if (failsafeConfig()->failsafe_procedure == FAILSAFE_PROCEDURE_DROP_IT) {
rcCommand[THROTTLE] = getThrottleIdleValue();
@ -798,7 +743,32 @@ static void applyMulticopterEmergencyLandingController(timeUs_t currentTimeUs)
else {
rcCommand[THROTTLE] = failsafeConfig()->failsafe_throttle;
}
return;
}
// Normal sensor data
if (posControl.flags.verticalPositionDataNew) {
const timeDelta_t deltaMicrosPositionUpdate = currentTimeUs - previousTimePositionUpdate;
previousTimePositionUpdate = currentTimeUs;
// Check if last correction was too log ago - ignore this update
if (deltaMicrosPositionUpdate < HZ2US(MIN_POSITION_UPDATE_RATE_HZ)) {
updateClimbRateToAltitudeController(-1.0f * navConfig()->general.emerg_descent_rate, ROC_TO_ALT_NORMAL);
updateAltitudeVelocityController_MC(deltaMicrosPositionUpdate);
updateAltitudeThrottleController_MC(deltaMicrosPositionUpdate);
}
else {
// due to some glitch position update has not occurred in time, reset altitude controller
resetMulticopterAltitudeController();
}
// Indicate that information is no longer usable
posControl.flags.verticalPositionDataConsumed = 1;
}
// Update throttle controller
rcCommand[THROTTLE] = constrain((int16_t)navConfig()->mc.hover_throttle + posControl.rcAdjustment[THROTTLE], getThrottleIdleValue(), motorConfig()->maxthrottle);
}
/*-----------------------------------------------------------

View file

@ -54,7 +54,7 @@
navigationPosEstimator_t posEstimator;
PG_REGISTER_WITH_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig, PG_POSITION_ESTIMATION_CONFIG, 4);
PG_REGISTER_WITH_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig, PG_POSITION_ESTIMATION_CONFIG, 5);
PG_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig,
// Inertial position estimator parameters
@ -62,7 +62,8 @@ PG_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig,
.reset_altitude_type = NAV_RESET_ON_FIRST_ARM,
.reset_home_type = NAV_RESET_ON_FIRST_ARM,
.gravity_calibration_tolerance = 5, // 5 cm/s/s calibration error accepted (0.5% of gravity)
.use_gps_velned = 1, // "Disabled" is mandatory with gps_dyn_model = Pedestrian
.use_gps_velned = 1, // "Disabled" is mandatory with gps_dyn_model = Pedestrian
.use_gps_no_baro = 0, // Use GPS altitude if no baro is available on all aircrafts
.allow_dead_reckoning = 0,
.max_surface_altitude = 200,
@ -581,7 +582,7 @@ static bool estimationCalculateCorrection_Z(estimationContext_t * ctx)
return true;
}
else if (STATE(FIXED_WING_LEGACY) && (ctx->newFlags & EST_GPS_Z_VALID)) {
else if ((STATE(FIXED_WING_LEGACY) || positionEstimationConfig()->use_gps_no_baro) && (ctx->newFlags & EST_GPS_Z_VALID)) {
// If baro is not available - use GPS Z for correction on a plane
// Reset current estimate to GPS altitude if estimate not valid
if (!(ctx->newFlags & EST_Z_VALID)) {

View file

@ -38,7 +38,7 @@ FILE_COMPILE_FOR_SIZE
static EXTENDED_FASTRAM int32_t globalVariableState[MAX_GLOBAL_VARIABLES];
PG_REGISTER_ARRAY_WITH_RESET_FN(globalVariableConfig_t, MAX_GLOBAL_VARIABLES, globalVariableConfigs, PG_GLOBAL_VARIABLE_CONFIG, 0);
PG_REGISTER_ARRAY_WITH_RESET_FN(globalVariableConfig_t, MAX_GLOBAL_VARIABLES, globalVariableConfigs, PG_GLOBAL_VARIABLE_CONFIG, 1);
void pgResetFn_globalVariableConfigs(globalVariableConfig_t *globalVariableConfigs)
{

View file

@ -26,7 +26,7 @@
#include "config/parameter_group.h"
#define MAX_GLOBAL_VARIABLES 4
#define MAX_GLOBAL_VARIABLES 8
typedef struct globalVariableConfig_s {
int32_t min;

View file

@ -49,7 +49,7 @@
#include "io/vtx.h"
#include "drivers/vtx_common.h"
PG_REGISTER_ARRAY_WITH_RESET_FN(logicCondition_t, MAX_LOGIC_CONDITIONS, logicConditions, PG_LOGIC_CONDITIONS, 1);
PG_REGISTER_ARRAY_WITH_RESET_FN(logicCondition_t, MAX_LOGIC_CONDITIONS, logicConditions, PG_LOGIC_CONDITIONS, 2);
EXTENDED_FASTRAM uint64_t logicConditionsGlobalFlags;
EXTENDED_FASTRAM int logicConditionValuesByType[LOGIC_CONDITION_LAST];

View file

@ -26,7 +26,7 @@
#include "config/parameter_group.h"
#include "common/time.h"
#define MAX_LOGIC_CONDITIONS 16
#define MAX_LOGIC_CONDITIONS 32
typedef enum {
LOGIC_CONDITION_TRUE = 0,

View file

@ -305,7 +305,7 @@ bool crsfRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
NULL,
CRSF_BAUDRATE,
CRSF_PORT_MODE,
CRSF_PORT_OPTIONS | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
CRSF_PORT_OPTIONS | (tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
return serialPort != NULL;

View file

@ -394,7 +394,9 @@ bool fportRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
NULL,
FPORT_BAUDRATE,
MODE_RXTX,
FPORT_PORT_OPTIONS | (rxConfig->serialrx_inverted ? SERIAL_INVERTED : 0) | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
FPORT_PORT_OPTIONS |
(rxConfig->serialrx_inverted ? 0 : SERIAL_INVERTED) |
(tristateWithDefaultOnIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
if (fportPort) {

672
src/main/rx/fport2.c Normal file
View file

@ -0,0 +1,672 @@
/*
* This file is part of iNav.
*
* iNav are free software. You can redistribute
* this software and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* iNav is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "platform.h"
#ifdef USE_SERIALRX_FPORT2
#include "build/debug.h"
#include "common/log.h"
#include "common/maths.h"
#include "common/utils.h"
#include "drivers/time.h"
#ifdef MSP_FIRMWARE_UPDATE
#include "fc/firmware_update.h"
#endif
#include "io/serial.h"
#include "io/smartport_master.h"
#ifdef USE_TELEMETRY
#include "telemetry/telemetry.h"
#include "telemetry/smartport.h"
#endif
#include "rx/frsky_crc.h"
#include "rx/rx.h"
#include "rx/sbus_channels.h"
#include "rx/fport2.h"
#define FPORT2_MAX_TELEMETRY_RESPONSE_DELAY_US 3000
#define FPORT2_MIN_TELEMETRY_RESPONSE_DELAY_US 500
#define FPORT2_MIN_OTA_RESPONSE_DELAY_US 50
#define FPORT2_MAX_TELEMETRY_AGE_MS 500
#define FPORT2_FC_COMMON_ID 0x1B
#define FPORT2_FC_MSP_ID 0x0D
#define FPORT2_BAUDRATE 115200
#define FPORT2_PORT_OPTIONS (SERIAL_STOPBITS_1 | SERIAL_PARITY_NO)
#define FPORT2_RX_TIMEOUT 120 // µs
#define FPORT2_CONTROL_FRAME_LENGTH 24
#define FPORT2_OTA_DATA_FRAME_LENGTH 32
#define FPORT2_DOWNLINK_FRAME_LENGTH 8
#define FPORT2_UPLINK_FRAME_LENGTH 8
#define FPORT2_TELEMETRY_MAX_CONSECUTIVE_TELEMETRY_FRAMES 2
#define FPORT2_OTA_DATA_FRAME_BYTES 32
enum {
DEBUG_FPORT2_FRAME_INTERVAL = 0,
DEBUG_FPORT2_FRAME_ERRORS,
DEBUG_FPORT2_FRAME_LAST_ERROR,
DEBUG_FPORT2_TELEMETRY_INTERVAL,
DEBUG_FPORT2_MAX_BUFFER_USAGE,
DEBUG_FPORT2_OTA_FRAME_RESPONSE_TIME,
DEBUG_FPORT2_OTA_RECEIVED_BYTES,
};
enum {
DEBUG_FPORT2_NO_ERROR = 0,
DEBUG_FPORT2_ERROR_TIMEOUT,
DEBUG_FPORT2_ERROR_OVERSIZE,
DEBUG_FPORT2_ERROR_SIZE,
DEBUG_FPORT2_ERROR_CHECKSUM,
DEBUG_FPORT2_ERROR_PHYID_CRC,
DEBUG_FPORT2_ERROR_TYPE,
DEBUG_FPORT2_ERROR_TYPE_SIZE,
DEBUG_FPORT2_ERROR_OTA_BAD_ADDRESS,
};
typedef enum {
CFT_RC = 0xFF,
CFT_OTA_START = 0xF0,
CFT_OTA_DATA = 0xF1,
CFT_OTA_STOP = 0xF2
} fport2_control_frame_type_e;
typedef enum {
FT_CONTROL,
FT_DOWNLINK
} frame_type_e;
typedef enum {
FS_CONTROL_FRAME_START,
FS_CONTROL_FRAME_TYPE,
FS_CONTROL_FRAME_DATA,
FS_DOWNLINK_FRAME_START,
FS_DOWNLINK_FRAME_DATA
} frame_state_e;
enum {
FPORT2_FRAME_ID_NULL = 0x00,
FPORT2_FRAME_ID_DATA = 0x10,
FPORT2_FRAME_ID_READ = 0x30,
FPORT2_FRAME_ID_WRITE = 0x31,
FPORT2_FRAME_ID_RESPONSE = 0x32,
FPORT2_FRAME_ID_OTA_START = 0xF0,
FPORT2_FRAME_ID_OTA_DATA = 0xF1,
FPORT2_FRAME_ID_OTA_STOP = 0xF2
};
typedef struct {
sbusChannels_t channels;
uint8_t rssi;
} rcData_t;
typedef struct {
uint8_t phyID;
/*uint8_t phyID : 5;*/
/*uint8_t phyXOR : 3;*/
smartPortPayload_t telemetryData;
} PACKED fportDownlinkData_t;
typedef struct {
uint8_t type;
union {
rcData_t rc;
uint8_t ota[FPORT2_OTA_DATA_FRAME_BYTES];
};
} PACKED fportControlFrame_t;
typedef struct {
uint8_t type;
union {
fportControlFrame_t control;
fportDownlinkData_t downlink;
};
} fportFrame_t;
// RX frames ring buffer
#define NUM_RX_BUFFERS 15
typedef struct fportBuffer_s {
uint8_t data[sizeof(fportFrame_t)+1]; // +1 for CRC
uint8_t length;
} fportBuffer_t;
typedef struct {
uint32_t size;
uint8_t crc;
} PACKED firmwareUpdateHeader_t;
static volatile fportBuffer_t rxBuffer[NUM_RX_BUFFERS];
static volatile uint8_t rxBufferWriteIndex = 0;
static volatile uint8_t rxBufferReadIndex = 0;
static serialPort_t *fportPort;
#ifdef USE_TELEMETRY_SMARTPORT
static smartPortPayload_t *mspPayload = NULL;
static bool telemetryEnabled = false;
static volatile timeUs_t lastTelemetryFrameReceivedUs;
static volatile bool clearToSend = false;
static volatile bool sendNullFrame = false;
static uint8_t downlinkPhyID;
static const smartPortPayload_t emptySmartPortFrame = { .frameId = 0, .valueId = 0, .data = 0 };
static smartPortPayload_t *otaResponsePayload = NULL;
static bool otaMode = false;
static bool otaDataNeedsProcessing = false;
static uint32_t otaDataAddress;
static uint8_t otaDataBuffer[FPORT2_OTA_DATA_FRAME_BYTES];
static timeUs_t otaFrameEndTimestamp = 0;
static bool firmwareUpdateError = false;
#ifdef MSP_FIRMWARE_UPDATE
static uint8_t firmwareUpdateCRC;
static timeUs_t readyToUpdateFirmwareTimestamp = 0;
#endif
#endif
static volatile uint16_t frameErrors = 0;
static void reportFrameError(uint8_t errorReason) {
frameErrors++;
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_FRAME_ERRORS, frameErrors);
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_FRAME_LAST_ERROR, errorReason);
}
static uint8_t bufferCount(void)
{
if (rxBufferReadIndex > rxBufferWriteIndex) {
return NUM_RX_BUFFERS - rxBufferReadIndex + rxBufferWriteIndex;
} else {
return rxBufferWriteIndex - rxBufferReadIndex;
}
}
static void clearWriteBuffer(void)
{
rxBuffer[rxBufferWriteIndex].length = 0;
}
static bool nextWriteBuffer(void)
{
const uint8_t nextWriteIndex = (rxBufferWriteIndex + 1) % NUM_RX_BUFFERS;
if (nextWriteIndex != rxBufferReadIndex) {
rxBufferWriteIndex = nextWriteIndex;
clearWriteBuffer();
return true;
} else {
clearWriteBuffer();
return false;
}
}
static uint8_t writeBuffer(uint8_t byte)
{
volatile uint8_t * const buffer = rxBuffer[rxBufferWriteIndex].data;
volatile uint8_t * const buflen = &rxBuffer[rxBufferWriteIndex].length;
buffer[*buflen] = byte;
*buflen += 1;
return *buflen;
}
// UART RX ISR
static void fportDataReceive(uint16_t byte, void *callback_data)
{
UNUSED(callback_data);
static volatile frame_state_e state = FS_CONTROL_FRAME_START;
static volatile timeUs_t lastRxByteTimestamp = 0;
const timeUs_t currentTimeUs = micros();
const timeUs_t timeSincePreviousRxByte = lastRxByteTimestamp ? currentTimeUs - lastRxByteTimestamp : 0;
static unsigned controlFrameSize;
lastRxByteTimestamp = currentTimeUs;
#if defined(USE_TELEMETRY_SMARTPORT)
clearToSend = false;
#endif
if ((state != FS_CONTROL_FRAME_START) && (timeSincePreviousRxByte > FPORT2_RX_TIMEOUT)) {
state = FS_CONTROL_FRAME_START;
}
switch (state) {
case FS_CONTROL_FRAME_START:
if ((byte == FPORT2_CONTROL_FRAME_LENGTH) || (byte == FPORT2_OTA_DATA_FRAME_LENGTH)) {
clearWriteBuffer();
writeBuffer(FT_CONTROL);
state = FS_CONTROL_FRAME_TYPE;
}
break;
case FS_CONTROL_FRAME_TYPE:
if ((byte == CFT_RC) || ((byte >= CFT_OTA_START) && (byte <= CFT_OTA_STOP))) {
unsigned controlFrameType = byte;
writeBuffer(controlFrameType);
controlFrameSize = (controlFrameType == CFT_OTA_DATA ? FPORT2_OTA_DATA_FRAME_LENGTH : FPORT2_CONTROL_FRAME_LENGTH) + 2; // +2 = General frame type + CRC
state = FS_CONTROL_FRAME_DATA;
} else {
state = FS_CONTROL_FRAME_START;
}
break;
case FS_CONTROL_FRAME_DATA: {
if (writeBuffer(byte) > controlFrameSize) {
nextWriteBuffer();
state = FS_DOWNLINK_FRAME_START;
}
break;
}
case FS_DOWNLINK_FRAME_START:
if (byte == FPORT2_DOWNLINK_FRAME_LENGTH) {
writeBuffer(FT_DOWNLINK);
state = FS_DOWNLINK_FRAME_DATA;
} else {
state = FS_CONTROL_FRAME_START;
}
break;
case FS_DOWNLINK_FRAME_DATA:
if (writeBuffer(byte) > (FPORT2_DOWNLINK_FRAME_LENGTH + 1)) {
#if defined(USE_TELEMETRY_SMARTPORT)
if ((rxBuffer[rxBufferWriteIndex].data[2] >= FPORT2_FRAME_ID_OTA_START) && (rxBuffer[rxBufferWriteIndex].data[2] <= FPORT2_FRAME_ID_OTA_STOP)) {
otaFrameEndTimestamp = currentTimeUs;
}
lastTelemetryFrameReceivedUs = currentTimeUs;
#endif
nextWriteBuffer();
state = FS_CONTROL_FRAME_START;
}
break;
default:
state = FS_CONTROL_FRAME_START;
break;
}
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_MAX_BUFFER_USAGE, MAX(bufferCount(), debug[DEBUG_FPORT2_MAX_BUFFER_USAGE]));
}
#if defined(USE_TELEMETRY_SMARTPORT)
static void writeUplinkFramePhyID(uint8_t phyID, const smartPortPayload_t *payload)
{
serialWrite(fportPort, FPORT2_UPLINK_FRAME_LENGTH);
serialWrite(fportPort, phyID);
uint16_t checksum = 0;
frskyCheckSumStep(&checksum, phyID);
uint8_t *data = (uint8_t *)payload;
for (unsigned i = 0; i < sizeof(smartPortPayload_t); ++i, ++data) {
serialWrite(fportPort, *data);
frskyCheckSumStep(&checksum, *data);
}
frskyCheckSumFini(&checksum);
serialWrite(fportPort, checksum);
}
static void writeUplinkFrame(const smartPortPayload_t *payload)
{
writeUplinkFramePhyID(FPORT2_FC_COMMON_ID, payload);
}
#endif
static uint8_t frameStatus(rxRuntimeConfig_t *rxRuntimeConfig)
{
#ifdef USE_TELEMETRY_SMARTPORT
static smartPortPayload_t payloadBuffer;
static bool hasTelemetryRequest = false;
static uint32_t otaPrevDataAddress;
static bool otaJustStarted = false;
static bool otaGotData = false;
#endif
static timeUs_t frameReceivedTimestamp = 0;
uint8_t result = RX_FRAME_PENDING;
timeUs_t currentTimeUs = micros();
if (rxBufferReadIndex != rxBufferWriteIndex) {
volatile uint8_t *buffer = rxBuffer[rxBufferReadIndex].data;
uint8_t buflen = rxBuffer[rxBufferReadIndex].length;
fportFrame_t *frame = (fportFrame_t *)buffer;
if (!frskyCheckSumIsGood((uint8_t *)buffer + 1, buflen - 1)) {
reportFrameError(DEBUG_FPORT2_ERROR_CHECKSUM);
} else {
switch (frame->type) {
case FT_CONTROL:
switch (frame->control.type) {
case CFT_RC:
result = sbusChannelsDecode(rxRuntimeConfig, &frame->control.rc.channels);
lqTrackerSet(rxRuntimeConfig->lqTracker, scaleRange(frame->control.rc.rssi, 0, 100, 0, RSSI_MAX_VALUE));
frameReceivedTimestamp = currentTimeUs;
#if defined(USE_TELEMETRY_SMARTPORT)
otaMode = false;
#endif
break;
#if defined(USE_TELEMETRY_SMARTPORT)
case CFT_OTA_START:
otaMode = true;
break;
case CFT_OTA_DATA:
if (otaMode) {
memcpy(otaDataBuffer, frame->control.ota, sizeof(otaDataBuffer));
otaGotData = true;
} else {
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
}
break;
case CFT_OTA_STOP:
if (!otaMode) {
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
}
break;
#endif
default:
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
break;
}
break;
case FT_DOWNLINK:
#if defined(USE_TELEMETRY_SMARTPORT)
if (!telemetryEnabled) {
break;
}
downlinkPhyID = frame->downlink.phyID;
uint8_t frameId = frame->downlink.telemetryData.frameId;
switch (frameId) {
case FPORT2_FRAME_ID_NULL:
hasTelemetryRequest = true;
sendNullFrame = true;
break;
case FPORT2_FRAME_ID_DATA:
hasTelemetryRequest = true;
break;
case FPORT2_FRAME_ID_OTA_START:
case FPORT2_FRAME_ID_OTA_DATA:
case FPORT2_FRAME_ID_OTA_STOP:
switch (frameId) {
case FPORT2_FRAME_ID_OTA_START:
if (otaMode) {
otaJustStarted = true;
otaPrevDataAddress = 0;
hasTelemetryRequest = true;
otaDataNeedsProcessing = false;
firmwareUpdateError = false;
} else {
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
}
break;
case FPORT2_FRAME_ID_OTA_DATA:
if (otaMode) {
otaDataAddress = frame->downlink.telemetryData.data;
if (otaGotData && (otaDataAddress == (otaJustStarted ? 0 : otaPrevDataAddress + FPORT2_OTA_DATA_FRAME_BYTES))) { // check that we got a control frame with data and check address
otaPrevDataAddress = otaDataAddress;
otaGotData = false;
otaDataNeedsProcessing = true;
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_OTA_RECEIVED_BYTES, debug[DEBUG_FPORT2_OTA_RECEIVED_BYTES] + FPORT2_OTA_DATA_FRAME_BYTES);
}
hasTelemetryRequest = true;
otaJustStarted = false;
} else {
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
}
break;
case FPORT2_FRAME_ID_OTA_STOP:
if (otaMode) {
hasTelemetryRequest = true;
} else {
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
}
break;
}
if (hasTelemetryRequest) {
memcpy(&payloadBuffer, &frame->downlink.telemetryData, sizeof(payloadBuffer));
otaResponsePayload = &payloadBuffer;
}
break;
default:
if ((frameId == FPORT2_FRAME_ID_READ) && (downlinkPhyID == FPORT2_FC_MSP_ID)) {
memcpy(&payloadBuffer, &frame->downlink.telemetryData, sizeof(payloadBuffer));
mspPayload = &payloadBuffer;
hasTelemetryRequest = true;
} else if (downlinkPhyID != FPORT2_FC_COMMON_ID) {
#if defined(USE_SMARTPORT_MASTER)
int8_t smartportPhyID = smartportMasterStripPhyIDCheckBits(downlinkPhyID);
if (smartportPhyID != -1) {
smartportMasterForward(smartportPhyID, &frame->downlink.telemetryData);
hasTelemetryRequest = true;
}
#endif
}
break;
}
#endif
break;
default:
reportFrameError(DEBUG_FPORT2_ERROR_TYPE);
break;
}
}
rxBufferReadIndex = (rxBufferReadIndex + 1) % NUM_RX_BUFFERS;
}
#if defined(USE_TELEMETRY_SMARTPORT)
if (((mspPayload || hasTelemetryRequest) && cmpTimeUs(currentTimeUs, lastTelemetryFrameReceivedUs) >= FPORT2_MIN_TELEMETRY_RESPONSE_DELAY_US)
|| (otaResponsePayload && cmpTimeUs(currentTimeUs, lastTelemetryFrameReceivedUs) >= FPORT2_MIN_OTA_RESPONSE_DELAY_US)) {
hasTelemetryRequest = false;
clearToSend = true;
result |= RX_FRAME_PROCESSING_REQUIRED;
}
#endif
if (frameReceivedTimestamp && (cmpTimeUs(currentTimeUs, frameReceivedTimestamp) > FPORT2_MAX_TELEMETRY_AGE_MS * 1000)) {
lqTrackerSet(rxRuntimeConfig->lqTracker, 0);
frameReceivedTimestamp = 0;
}
#ifdef MSP_FIRMWARE_UPDATE
if (readyToUpdateFirmwareTimestamp && (cmpTimeUs(currentTimeUs, readyToUpdateFirmwareTimestamp) > 2000000)) {
readyToUpdateFirmwareTimestamp = 0;
firmwareUpdateExec(firmwareUpdateCRC);
}
#endif
return result;
}
static bool processFrame(const rxRuntimeConfig_t *rxRuntimeConfig)
{
UNUSED(rxRuntimeConfig);
#if defined(USE_TELEMETRY_SMARTPORT)
static timeUs_t lastTelemetryFrameSentUs;
timeUs_t currentTimeUs = micros();
if (cmpTimeUs(currentTimeUs, lastTelemetryFrameReceivedUs) > FPORT2_MAX_TELEMETRY_RESPONSE_DELAY_US) {
clearToSend = false;
}
if (clearToSend) {
if (otaResponsePayload) {
switch (otaResponsePayload->frameId) {
case FPORT2_FRAME_ID_OTA_DATA: {
if (otaDataNeedsProcessing && !firmwareUpdateError) { // We have got fresh data
#ifdef MSP_FIRMWARE_UPDATE
static uint32_t firmwareUpdateSize;
uint32_t receivedSize = otaDataAddress - FPORT2_OTA_DATA_FRAME_BYTES;
if (otaDataAddress == 0) {
static firmwareUpdateHeader_t *header = (firmwareUpdateHeader_t *)otaDataBuffer;
firmwareUpdateSize = header->size;
firmwareUpdateCRC = header->crc;
firmwareUpdateError = !firmwareUpdatePrepare(firmwareUpdateSize);
} else if (receivedSize < firmwareUpdateSize) {
uint8_t firmwareDataSize = MIN((uint8_t)FPORT2_OTA_DATA_FRAME_BYTES, firmwareUpdateSize - receivedSize);
firmwareUpdateError = !firmwareUpdateStore(otaDataBuffer, firmwareDataSize);
}
#else
firmwareUpdateError = true;
#endif
}
otaDataNeedsProcessing = false;
break;
}
case FPORT2_FRAME_ID_OTA_STOP:
otaMode = false;
#ifdef MSP_FIRMWARE_UPDATE
readyToUpdateFirmwareTimestamp = currentTimeUs;
#endif
break;
}
timeUs_t otaResponseTime = cmpTimeUs(micros(), otaFrameEndTimestamp);
if (!firmwareUpdateError && (otaResponseTime < 400)) { // We can answer in time (firmwareUpdateStore can take time because it might need to erase flash)
writeUplinkFramePhyID(downlinkPhyID, otaResponsePayload);
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_OTA_FRAME_RESPONSE_TIME, otaResponseTime);
}
otaResponsePayload = NULL;
clearToSend = false;
} else if ((downlinkPhyID == FPORT2_FC_COMMON_ID) || (downlinkPhyID == FPORT2_FC_MSP_ID)) {
if ((downlinkPhyID == FPORT2_FC_MSP_ID) && !mspPayload) {
clearToSend = false;
} else if (!sendNullFrame) {
processSmartPortTelemetry(mspPayload, &clearToSend, NULL);
mspPayload = NULL;
}
} else {
#if defined(USE_SMARTPORT_MASTER)
int8_t smartportPhyID = smartportMasterStripPhyIDCheckBits(downlinkPhyID);
if (smartportPhyID != -1) {
if (sendNullFrame) {
if (!smartportMasterPhyIDIsActive(smartportPhyID)) { // send null frame only if the sensor is active
clearToSend = false;
}
} else {
smartPortPayload_t forwardPayload;
if (smartportMasterNextForwardResponse(smartportPhyID, &forwardPayload) || smartportMasterGetSensorPayload(smartportPhyID, &forwardPayload)) {
writeUplinkFramePhyID(downlinkPhyID, &forwardPayload);
}
clearToSend = false; // either we answered or the sensor is not active, do not send null frame
}
} else {
clearToSend = false;
}
#else
clearToSend = false;
#endif
}
if (clearToSend) {
writeUplinkFramePhyID(downlinkPhyID, &emptySmartPortFrame);
clearToSend = false;
}
sendNullFrame = false;
DEBUG_SET(DEBUG_FPORT, DEBUG_FPORT2_TELEMETRY_INTERVAL, currentTimeUs - lastTelemetryFrameSentUs);
lastTelemetryFrameSentUs = currentTimeUs;
}
#endif
return true;
}
bool fport2RxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
{
static uint16_t sbusChannelData[SBUS_MAX_CHANNEL];
rxRuntimeConfig->channelData = sbusChannelData;
sbusChannelsInit(rxRuntimeConfig);
rxRuntimeConfig->channelCount = SBUS_MAX_CHANNEL;
rxRuntimeConfig->rxRefreshRate = 11000;
rxRuntimeConfig->rcFrameStatusFn = frameStatus;
rxRuntimeConfig->rcProcessFrameFn = processFrame;
const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_RX_SERIAL);
if (!portConfig) {
return false;
}
fportPort = openSerialPort(portConfig->identifier,
FUNCTION_RX_SERIAL,
fportDataReceive,
NULL,
FPORT2_BAUDRATE,
MODE_RXTX,
FPORT2_PORT_OPTIONS | (rxConfig->serialrx_inverted ? 0 : SERIAL_INVERTED) | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
);
if (fportPort) {
#if defined(USE_TELEMETRY_SMARTPORT)
telemetryEnabled = initSmartPortTelemetryExternal(writeUplinkFrame);
#endif
}
return fportPort != NULL;
}
#endif

27
src/main/rx/fport2.h Normal file
View file

@ -0,0 +1,27 @@
/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight are free software. You can redistribute
* this software and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Cleanflight and Betaflight are distributed in the hope that they
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef USE_SERIALRX_FPORT2
bool fport2RxInit(const rxConfig_t *initialRxConfig, rxRuntimeConfig_t *rxRuntimeConfig);
#endif

View file

@ -241,7 +241,7 @@ bool ibusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
NULL,
IBUS_BAUDRATE,
portShared ? MODE_RXTX : MODE_RX,
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex || portShared ? SERIAL_BIDIR : 0)
SERIAL_NOT_INVERTED | ((tristateWithDefaultOffIsActive(rxConfig->halfDuplex) || portShared) ? SERIAL_BIDIR : 0)
);
#if defined(USE_TELEMETRY) && defined(USE_TELEMETRY_IBUS)

View file

@ -607,7 +607,7 @@ bool jetiExBusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfi
NULL,
JETIEXBUS_BAUDRATE,
MODE_RXTX,
JETIEXBUS_OPTIONS | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
JETIEXBUS_OPTIONS | (tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
serialSetMode(jetiExBusPort, MODE_RX);

View file

@ -54,6 +54,7 @@
#include "rx/ibus.h"
#include "rx/jetiexbus.h"
#include "rx/fport.h"
#include "rx/fport2.h"
#include "rx/msp.h"
#include "rx/msp_override.h"
#include "rx/pwm.h"
@ -121,7 +122,7 @@ PG_REGISTER_WITH_RESET_TEMPLATE(rxConfig_t, rxConfig, PG_RX_CONFIG, 9);
PG_RESET_TEMPLATE(rxConfig_t, rxConfig,
.receiverType = DEFAULT_RX_TYPE,
.rcmap = {0, 1, 3, 2}, // Default to AETR map
.halfDuplex = 0,
.halfDuplex = TRISTATE_AUTO,
.serialrx_provider = SERIALRX_PROVIDER,
.rx_spi_protocol = RX_SPI_DEFAULT_PROTOCOL,
.spektrum_sat_bind = 0,
@ -234,6 +235,11 @@ bool serialRxInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig
case SERIALRX_FPORT:
enabled = fportRxInit(rxConfig, rxRuntimeConfig);
break;
#endif
#ifdef USE_SERIALRX_FPORT2
case SERIALRX_FPORT2:
enabled = fport2RxInit(rxConfig, rxRuntimeConfig);
break;
#endif
default:
enabled = false;

View file

@ -21,6 +21,8 @@
#include <stdbool.h>
#include "common/time.h"
#include "common/tristate.h"
#include "config/parameter_group.h"
#define STICK_CHANNEL_COUNT 4
@ -79,6 +81,7 @@ typedef enum {
SERIALRX_CRSF = 9,
SERIALRX_FPORT = 10,
SERIALRX_SBUS_FAST = 11,
SERIALRX_FPORT2 = 12,
} rxSerialReceiverType_e;
#define MAX_SUPPORTED_RC_PPM_CHANNEL_COUNT 16
@ -108,7 +111,7 @@ typedef struct rxConfig_s {
uint8_t rcmap[MAX_MAPPABLE_RX_INPUTS]; // mapping of radio channels to internal RPYTA+ order
uint8_t serialrx_provider; // Type of UART-based receiver (rxSerialReceiverType_e enum). Only used if receiverType is RX_TYPE_SERIAL
uint8_t serialrx_inverted; // Flip the default inversion of the protocol - e.g. sbus (Futaba, FrSKY) is inverted if this is false, uninverted if it's true. Support for uninverted OpenLRS (and modified FrSKY) receivers.
uint8_t halfDuplex; // allow rx to operate in half duplex mode on F4, ignored for F1 and F3.
uint8_t halfDuplex; // allow rx to operate in half duplex mode. From tristate_e.
uint8_t rx_spi_protocol; // type of SPI receiver protocol (rx_spi_protocol_e enum). Only used if receiverType is RX_TYPE_SPI
uint32_t rx_spi_id;
uint8_t rx_spi_rf_channel_count;

View file

@ -195,7 +195,9 @@ static bool sbusInitEx(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeC
&sbusFrameData,
sbusBaudRate,
portShared ? MODE_RXTX : MODE_RX,
SBUS_PORT_OPTIONS | (rxConfig->serialrx_inverted ? 0 : SERIAL_INVERTED) | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
SBUS_PORT_OPTIONS |
(rxConfig->serialrx_inverted ? 0 : SERIAL_INVERTED) |
(tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
#ifdef USE_TELEMETRY

View file

@ -294,7 +294,7 @@ bool spektrumInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig
NULL,
SPEKTRUM_BAUDRATE,
portShared ? MODE_RXTX : MODE_RX,
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
SERIAL_NOT_INVERTED | (tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
#ifdef USE_TELEMETRY

View file

@ -189,7 +189,7 @@ bool sumdInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
NULL,
SUMD_BAUDRATE,
portShared ? MODE_RXTX : MODE_RX,
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
SERIAL_NOT_INVERTED | (tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
#ifdef USE_TELEMETRY

View file

@ -334,7 +334,7 @@ bool xBusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
NULL,
baudRate,
portShared ? MODE_RXTX : MODE_RX,
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
SERIAL_NOT_INVERTED | (tristateWithDefaultOffIsActive(rxConfig->halfDuplex) ? SERIAL_BIDIR : 0)
);
#ifdef USE_TELEMETRY

View file

@ -72,7 +72,6 @@ FILE_COMPILE_FOR_SPEED
#include "flight/gyroanalyse.h"
#include "flight/rpm_filter.h"
#include "flight/dynamic_gyro_notch.h"
#include "flight/kalman.h"
#ifdef USE_HARDWARE_REVISION_DETECTION
#include "hardware_revision.h"
@ -102,7 +101,7 @@ EXTENDED_FASTRAM dynamicGyroNotchState_t dynamicGyroNotchState;
#endif
PG_REGISTER_WITH_RESET_TEMPLATE(gyroConfig_t, gyroConfig, PG_GYRO_CONFIG, 9);
PG_REGISTER_WITH_RESET_TEMPLATE(gyroConfig_t, gyroConfig, PG_GYRO_CONFIG, 10);
PG_RESET_TEMPLATE(gyroConfig_t, gyroConfig,
.gyro_lpf = GYRO_LPF_42HZ, // 42HZ value is defined for Invensense/TDK gyros
@ -121,10 +120,6 @@ PG_RESET_TEMPLATE(gyroConfig_t, gyroConfig,
.dynamicGyroNotchQ = 120,
.dynamicGyroNotchMinHz = 150,
.dynamicGyroNotchEnabled = 0,
.kalman_q = 100,
.kalman_w = 4,
.kalman_sharpness = 100,
.kalmanEnabled = 0,
);
STATIC_UNIT_TESTED gyroSensor_e gyroDetect(gyroDev_t *dev, gyroSensor_e gyroHardware)
@ -312,11 +307,6 @@ bool gyroInit(void)
}
gyroInitFilters();
#ifdef USE_GYRO_KALMAN
if (gyroConfig()->kalmanEnabled) {
gyroKalmanInitialize();
}
#endif
#ifdef USE_DYNAMIC_FILTERS
dynamicGyroNotchFiltersInit(&dynamicGyroNotchState);
gyroDataAnalyseStateInit(
@ -460,14 +450,6 @@ void FAST_CODE NOINLINE gyroUpdate()
gyro.gyroADCf[axis] = gyroADCf;
}
#ifdef USE_GYRO_KALMAN
if (gyroConfig()->kalmanEnabled) {
gyro.gyroADCf[X] = gyroKalmanUpdate(X, gyro.gyroADCf[X]);
gyro.gyroADCf[Y] = gyroKalmanUpdate(Y, gyro.gyroADCf[Y]);
gyro.gyroADCf[Z] = gyroKalmanUpdate(Z, gyro.gyroADCf[Z]);
}
#endif
#ifdef USE_DYNAMIC_FILTERS
if (dynamicGyroNotchState.enabled) {
gyroDataAnalyse(&gyroAnalyseState);

View file

@ -74,10 +74,6 @@ typedef struct gyroConfig_s {
uint16_t dynamicGyroNotchQ;
uint16_t dynamicGyroNotchMinHz;
uint8_t dynamicGyroNotchEnabled;
uint16_t kalman_q;
uint16_t kalman_w;
uint16_t kalman_sharpness;
uint8_t kalmanEnabled;
} gyroConfig_t;
PG_DECLARE(gyroConfig_t, gyroConfig);

View file

@ -0,0 +1,7 @@
file(GLOB entries *)
foreach(subdir ${entries})
set(entry "${subdir}/CMakeLists.txt")
if (EXISTS ${entry})
add_subdirectory(${subdir})
endif()
endforeach()

View file

@ -31,5 +31,5 @@ void targetConfiguration(void)
featureSet(FEATURE_OSD);
}
rxConfigMutable()->halfDuplex = false;
rxConfigMutable()->halfDuplex = TRISTATE_OFF;
}

View file

@ -27,6 +27,6 @@
void targetConfiguration(void)
{
rxConfigMutable()->halfDuplex = false;
rxConfigMutable()->halfDuplex = TRISTATE_OFF;
batteryMetersConfigMutable()->current.scale = CURRENTSCALE;
}

View file

@ -0,0 +1,41 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <platform.h>
#include "drivers/io.h"
#include "drivers/dma.h"
#include "drivers/timer.h"
#include "drivers/bus.h"
#include "drivers/pwm_mapping.h"
const timerHardware_t timerHardware[] = {
DEF_TIM(TIM9, CH1, PA2, TIM_USE_PPM, 0, 0), // PPM IN
DEF_TIM(TIM1, CH1, PA8, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 1), // S1_OUT 2,1
DEF_TIM(TIM1, CH2, PA9, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 1), // S2_OUT 2,2
DEF_TIM(TIM1, CH3, PA10, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 0), // S3_OUT 2,6
DEF_TIM(TIM3, CH3, PB0, TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR, 0, 0), // S4_OUT 1,7
DEF_TIM(TIM3, CH4, PB1, TIM_USE_ANY, 0, 0), // RSSI 1,2
DEF_TIM(TIM5, CH4, PA3, TIM_USE_ANY, 0, 1), // RX2 1,0
DEF_TIM(TIM2, CH1, PA15, TIM_USE_LED, 0, 0), // LED 1,5
};
const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]);

View file

@ -0,0 +1,141 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define TARGET_BOARD_IDENTIFIER "FW41"
#define USBD_PRODUCT_STRING "FLYWOOF411"
#define LED0 PC13
#define BEEPER PC14
#define BEEPER_INVERTED
// *************** SPI **********************
#define USE_SPI
#define USE_SPI_DEVICE_1
#define SPI1_SCK_PIN PA5
#define SPI1_MISO_PIN PA6
#define SPI1_MOSI_PIN PA7
#define USE_SPI_DEVICE_2
#define SPI2_SCK_PIN PB13
#define SPI2_MISO_PIN PB14
#define SPI2_MOSI_PIN PB15
// *************** SPI Gyro & ACC **********************
#define USE_IMU_MPU6000
#define MPU6000_CS_PIN PA4
#define MPU6000_SPI_BUS BUS_SPI1
#define IMU_MPU6000_ALIGN CW180_DEG
#define USE_IMU_ICM20689
#define ICM20689_CS_PIN PA4
#define ICM20689_SPI_BUS BUS_SPI1
#define IMU_ICM20689_ALIGN CW180_DEG
#define USE_EXTI
#define GYRO_INT_EXTI PB3
#define USE_MPU_DATA_READY_SIGNAL
// *************** Baro *****************************
#define USE_I2C
#define USE_I2C_DEVICE_1
#define I2C1_SCL PB8
#define I2C1_SDA PB9
#define USE_BARO
#define BARO_I2C_BUS BUS_I2C1
#define USE_BARO_BMP280
#define USE_BARO_MS5611
#define USE_MAG
#define MAG_I2C_BUS BUS_I2C1
#define USE_MAG_HMC5883
#define USE_MAG_QMC5883
#define USE_MAG_IST8310
#define USE_MAG_IST8308
#define USE_MAG_MAG3110
#define USE_MAG_LIS3MDL
// *************** SPI OSD *****************************
#define USE_OSD
#define USE_MAX7456
#define MAX7456_SPI_BUS BUS_SPI2
#define MAX7456_CS_PIN PB12
// *************** SPI FLASH **************************
#define USE_FLASHFS
#define USE_FLASH_M25P16
#define M25P16_CS_PIN PB2
#define M25P16_SPI_BUS BUS_SPI2
#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT
// *************** UART *****************************
#define USE_VCP
#define USE_UART1
#define UART1_TX_PIN PB6
#define UART1_RX_PIN PB7
#define USE_UART2
#define UART2_TX_PIN NONE //PA2
#define UART2_RX_PIN PA3
#define USE_SOFTSERIAL1
#define SOFTSERIAL_1_TX_PIN PA2 // Clash with TX2, possible to use as S.Port or VTX control
#define SOFTSERIAL_1_RX_PIN PA2
#define SERIAL_PORT_COUNT 4 // VCP, USART1, USART2, SS1
#define DEFAULT_RX_TYPE RX_TYPE_SERIAL
#define SERIALRX_PROVIDER SERIALRX_SBUS
#define SERIALRX_UART SERIAL_PORT_USART2
// *************** ADC *****************************
#define USE_ADC
#define ADC_INSTANCE ADC1
#define ADC_CHANNEL_1_PIN PA1
#define ADC_CHANNEL_2_PIN PA0
#define ADC_CHANNEL_3_PIN PB1
#define CURRENT_METER_ADC_CHANNEL ADC_CHN_1
#define VBAT_ADC_CHANNEL ADC_CHN_2
#define RSSI_ADC_CHANNEL ADC_CHN_3
// *************** LED2812 ************************
#define USE_LED_STRIP
#define WS2811_PIN PA15
// *************** OTHERS *************************
#define DEFAULT_FEATURES (FEATURE_TX_PROF_SEL | FEATURE_OSD | FEATURE_VBAT | FEATURE_TELEMETRY | FEATURE_SOFTSERIAL)
#define USE_DSHOT
#define USE_ESC_SENSOR
#define USE_SERIALSHOT
#define USE_SERIAL_4WAY_BLHELI_INTERFACE
#define TARGET_IO_PORTA 0xffff
#define TARGET_IO_PORTB 0xffff
#define TARGET_IO_PORTC 0xffff
#define TARGET_IO_PORTD (BIT(2))
#define MAX_PWM_OUTPUT_PORTS 4

View file

@ -0,0 +1,19 @@
F411_TARGETS += $(TARGET)
FEATURES += VCP ONBOARDFLASH MSC
TARGET_SRC = \
drivers/accgyro/accgyro_mpu6000.c \
drivers/accgyro/accgyro_mpu6500.c \
drivers/accgyro/accgyro_icm20689.c \
drivers/barometer/barometer_bmp280.c \
drivers/barometer/barometer_bmp085.c \
drivers/barometer/barometer_ms56xx.c \
drivers/compass/compass_hmc5883l.c \
drivers/compass/compass_qmc5883l.c \
drivers/compass/compass_ist8310.c \
drivers/compass/compass_ist8308.c \
drivers/compass/compass_mag3110.c \
drivers/compass/compass_lis3mdl.c \
drivers/light_ws2811strip.c \
drivers/flash_m25p16.c \
drivers/max7456.c

View file

@ -0,0 +1,59 @@
/*
* This file is part of INAV.
*
* INAV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* INAV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with INAV. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#include <platform.h>
#include "drivers/io.h"
#include "drivers/bus.h"
#include "drivers/timer.h"
#include "drivers/sensor.h"
#include "drivers/pwm_mapping.h"
#include "fc/fc_msp_box.h"
#include "io/piniobox.h"
// IMU 1
BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_1, DEVHW_MPU6000, GYRO_1_SPI_BUS, GYRO_1_CS_PIN, GYRO_1_EXTI_PIN, 0, DEVFLAGS_NONE, GYRO_1_ALIGN);
BUSDEV_REGISTER_SPI_TAG(busdev_mpu6500_1, DEVHW_MPU6500, GYRO_1_SPI_BUS, GYRO_1_CS_PIN, GYRO_1_EXTI_PIN, 0, DEVFLAGS_NONE, GYRO_1_ALIGN);
// IMU 2
BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_2, DEVHW_MPU6000, GYRO_2_SPI_BUS, GYRO_2_CS_PIN, GYRO_2_EXTI_PIN, 1, DEVFLAGS_NONE, GYRO_2_ALIGN);
BUSDEV_REGISTER_SPI_TAG(busdev_mpu6500_2, DEVHW_MPU6500, GYRO_2_SPI_BUS, GYRO_2_CS_PIN, GYRO_2_EXTI_PIN, 1, DEVFLAGS_NONE, GYRO_2_ALIGN);
const timerHardware_t timerHardware[] = {
DEF_TIM(TIM8, CH1, PC6, TIM_USE_PPM, 0, 0), // PPM&SBUS
DEF_TIM(TIM3, CH4, PB1, TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR, 0, 0), // S1 - D(1,2)
DEF_TIM(TIM3, CH1, PB4, TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR, 0, 0), // S2 - D(1,4)
DEF_TIM(TIM2, CH2, PB3, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 0), // S3 - D(1,6)
DEF_TIM(TIM2, CH1, PA15, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 0), // S4 - D(1,5)
DEF_TIM(TIM8, CH3, PC8, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 0), // S5 - D(2,4)
DEF_TIM(TIM8, CH4, PC9, TIM_USE_MC_MOTOR | TIM_USE_FW_SERVO, 0, 0), // S6 - D(2,1)
DEF_TIM(TIM1, CH1, PA8, TIM_USE_LED, 0, 0), // LED STRIP(1,5)
};
const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]);
void targetConfiguration(void)
{
pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1;
pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2;
}

View file

@ -0,0 +1,162 @@
/*
* This file is part of INAV.
*
* INAV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* INAV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with INAV. If not, see <http://www.gnu.org/licenses/>.
*/
#define TARGET_BOARD_IDENTIFIER "FWF7"
#define USBD_PRODUCT_STRING "FLYWOOF7DUAL"
/*** Indicators ***/
#define LED0 PC15
#define BEEPER PC14
#define BEEPER_INVERTED
/*** IMU sensors ***/
#define USE_EXTI
// We use dual IMU sensors, they have to be described in the target file
#define USE_TARGET_IMU_HARDWARE_DESCRIPTORS
#define USE_MPU_DATA_READY_SIGNAL
#define USE_DUAL_GYRO
#define USE_IMU_MPU6000
#define USE_IMU_MPU6500
#define GYRO_1_SPI_BUS BUS_SPI1
#define GYRO_1_CS_PIN PA4
#define GYRO_1_EXTI_PIN PC3
#define GYRO_1_ALIGN CW180_DEG_FLIP
#define GYRO_2_SPI_BUS BUS_SPI1
#define GYRO_2_CS_PIN PB2
#define GYRO_2_EXTI_PIN PC4
#define GYRO_2_ALIGN CW270_DEG
/*** SPI/I2C bus ***/
#define USE_SPI
#define USE_SPI_DEVICE_1 // Gyro 1/2
#define SPI1_SCK_PIN PA5
#define SPI1_MISO_PIN PA6
#define SPI1_MOSI_PIN PA7
#define USE_SPI_DEVICE_2 // MAX7456
#define SPI2_SCK_PIN PB13
#define SPI2_MISO_PIN PB14
#define SPI2_MOSI_PIN PB15
#define USE_SPI_DEVICE_3 // FLASH
#define SPI3_SCK_PIN PC10
#define SPI3_MISO_PIN PC11
#define SPI3_MOSI_PIN PB5
#define USE_I2C
#define USE_I2C_DEVICE_1 // I2C pads
#define I2C1_SCL PB6
#define I2C1_SDA PB7
/*** Onboard flash ***/
#define USE_FLASHFS
#define USE_FLASH_M25P16
#define M25P16_CS_PIN PC13
#define M25P16_SPI_BUS BUS_SPI3
/*** OSD ***/
#define USE_OSD
#define USE_MAX7456
#define MAX7456_CS_PIN PB12
#define MAX7456_SPI_BUS BUS_SPI2
/*** Serial ports ***/
#define USE_VCP
#define USE_UART1
#define UART1_TX_PIN PA9
#define UART1_RX_PIN PA10
#define USE_UART2
#define UART2_TX_PIN PA2
#define UART2_RX_PIN PA3
#define USE_UART3
#define UART3_TX_PIN PB10
#define UART3_RX_PIN PB11
#define USE_UART4
#define UART4_TX_PIN PA0
#define UART4_RX_PIN PA1
#define USE_UART5
#define UART5_TX_PIN PC12
#define UART5_RX_PIN PD2
#define USE_UART6
#define UART6_TX_PIN PC6
#define UART6_RX_PIN PC7
#define SERIAL_PORT_COUNT 7
/*** BARO & MAG ***/
#define USE_BARO
#define BARO_I2C_BUS BUS_I2C1
#define USE_BARO_BMP280
#define USE_BARO_MS5611
#define USE_MAG
#define MAG_I2C_BUS BUS_I2C1
#define USE_MAG_HMC5883
#define USE_MAG_QMC5883
#define USE_MAG_IST8310
#define USE_MAG_MAG3110
#define USE_MAG_LIS3MDL
/*** ADC ***/
#define USE_ADC
#define ADC_CHANNEL_1_PIN PC0
#define ADC_CHANNEL_2_PIN PC1
#define ADC_CHANNEL_3_PIN PC2
#define CURRENT_METER_ADC_CHANNEL ADC_CHN_1
#define VBAT_ADC_CHANNEL ADC_CHN_2
#define RSSI_ADC_CHANNEL ADC_CHN_3
/*** PINIO ***/
#define USE_PINIO
#define USE_PINIOBOX
#define PINIO1_PIN PB0
#define PINIO2_PIN PB9
/*** LED STRIP ***/
#define USE_LED_STRIP
#define WS2811_PIN PA8
/*** Default settings ***/
#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT
#define CURRENT_METER_SCALE_DEFAULT 170
#define SERIALRX_UART SERIAL_PORT_USART1
#define DEFAULT_RX_TYPE RX_TYPE_SERIAL
#define SERIALRX_PROVIDER SERIALRX_SBUS
/*** Timer/PWM output ***/
#define USE_SERIAL_4WAY_BLHELI_INTERFACE
#define MAX_PWM_OUTPUT_PORTS 6
#define USE_DSHOT
#define USE_ESC_SENSOR
/*** Used pins ***/
#define TARGET_IO_PORTA 0xffff
#define TARGET_IO_PORTB 0xffff
#define TARGET_IO_PORTC 0xffff
#define TARGET_IO_PORTD (BIT(2))

Some files were not shown because too many files have changed in this diff Show more